LangChain中不同Agent类型使用实操三
尝试让ZeroShotAgent具有记忆力
*注意,程序中增加了memory和chat_history等使其有记忆力的代码,可以让ZeroShotAgent具有记忆力
注意,从输出结果看它确实记住对话历史,在第三轮对话中它知道“这两个单词“是指第一轮的’hello’和第二轮的’world’,同时,在第四轮对话中,显示它是记住了前面两轮的答案的。
代码如下,注意和前面代码的差别。
#给出三轮问题,第三个问题"这两个单词的长度之和是多少?"需要调用前两个问题的结果,实验证明它记住了。 from langchain.agents import Tool, ZeroShotAgent, AgentExecutor from langchain.chains import LLMChain from langchain.prompts import PromptTemplate from langchain_openai import ChatOpenAI from langchain.memory import ConversationBufferMemory import os # 1. 定义工具函数 def simple_add(expression: str) -> str: """简单的加法计算器""" try: # 移除所有空格和引号 expression = expression.strip().strip('"\'') # 分割数字 numbers = expression.split('+') # 转换为整数并求和 result = sum(int(num.strip()) for num in numbers) return str(result) except Exception as e: return f"计算失败:{str(e)}" def get_word_length(word: str) -> str: """计算字符串长度""" try: # 移除所有空格和引号 word = word.strip().strip('"\'') return str(len(word)) except Exception as e: return f"计算失败:{str(e)}" # 2. 创建工具列表 tools = [ Tool( name="SimpleAdd", func=simple_add, description="简单的加法计算器,输入格式为'数字+数字',例如:1+2" ), Tool( name="WordLength", func=get_word_length, description="计算字符串长度,输入一个字符串,返回它的字符数,例如:'hello'" ) ] # 3. 创建ZeroShotAgent的提示模板 prefix = """你是一个帮助用户解决问题的助手。你可以使用以下工具: {tools} 请使用以下格式: Question: 输入的问题 Thought: 你需要思考如何解决这个问题 Action: 要使用的工具名称 Action Input: 工具的输入 Observation: 工具的输出 ... (这个思考/行动/观察可以重复多次) Thought: 我现在知道最终答案 Final Answer: 对原始输入问题的最终答案 重要提示: 1. 如果问题涉及多个步骤,你需要一步步思考 2. 可以使用一个工具的输出作为另一个工具的输入 3. 在得到所有需要的结果后,给出最终答案 4. 记住之前的对话历史,可以在需要时使用之前的结果 对话历史: {chat_history} 现在开始: Question: {input} """ suffix = """ {agent_scratchpad} """ # 创建llm BASE_URL="https://dashscope.aliyuncs.com/compatible-mode/v1" MODEL = "qwen-turbo" API_KEY = os.getenv('DASHSCOPE_API_KEY') llm = ChatOpenAI( model= MODEL, # 默认的大模型为GPT-3.5-turbo,比较便宜 openai_api_base= BASE_URL, openai_api_key= API_KEY ) # 创建memory memory = ConversationBufferMemory( memory_key="chat_history", input_key="input", output_key="output", return_messages=True ) # 创建agent agent = ZeroShotAgent( llm_chain=LLMChain(llm=llm, prompt=PromptTemplate.from_template(prefix + suffix)), tools=tools, verbose=True ) # 执行 agent_executor = AgentExecutor.from_agent_and_tools( agent=agent, tools=tools, verbose=True, max_iterations=10, memory=memory ) # 准备工具描述 tools_description = "\n".join([f"{tool.name}: {tool.description}" for tool in tools]) # 运行多轮对话 print("第一轮:计算第一个单词的长度") result1 = agent_executor.invoke({ "input": "单词'hello'的长度是多少?", "tools": tools_description }) print("输出:", result1["output"]) print("\n第二轮:计算第二个单词的长度") result2 = agent_executor.invoke({ "input": "单词'world'的长度是多少?", "tools": tools_description }) print("输出:", result2["output"]) print("\n第三轮:计算两个单词长度之和") result3 = agent_executor.invoke({ "input": "这两个单词的长度之和是多少?", "tools": tools_description }) print("输出:", result3["output"]) print("\n第四轮:计算两个单词长度之和") result4 = agent_executor.invoke({ "input": "既然已经知道了这两个单词的长度,请直接计算这两个单词的长度之和是多少?", "tools": tools_description }) print("输出:", result4["output"])
输出结果如下
第一轮:计算第一个单词的长度 > Entering new AgentExecutor chain... Thought: 这个问题是要求我计算单词'hello'的长度。我可以使用WordLength工具来完成这个任务。 Action: WordLength Action Input: 'hello' Observation: 5 Thought:我现在知道最终答案了。 Final Answer: 单词'hello'的长度是5。 > Finished chain. 输出: 单词'hello'的长度是5。 第二轮:计算第二个单词的长度 > Entering new AgentExecutor chain... Thought: 我需要计算单词'world'的长度。 Action: WordLength Action Input: 'world' Observation: 5 Thought:单词'world'的长度是5。 Final Answer: 单词'world'的长度是5。 > Finished chain. 输出: 单词'world'的长度是5。 第三轮:计算两个单词长度之和 > Entering new AgentExecutor chain... Thought: 我需要分别计算两个单词的长度,然后将它们相加。 Action: WordLength Action Input: 'hello' Observation: 5 Thought:单词'hello'的长度是5。接下来我需要计算单词'world'的长度。 Action: WordLength Action Input: 'world' Observation: 5 Thought:单词'world'的长度也是5。现在我需要将这两个单词的长度相加。 Action: SimpleAdd Action Input: '5+5' Observation: 10 Thought:单词'hello'和'world'的长度之和是10。 Final Answer: 这两个单词的长度之和是10。 > Finished chain. 输出: 这两个单词的长度之和是10。 第四轮:计算两个单词长度之和 > Entering new AgentExecutor chain... Thought: 根据之前的对话历史,我已经知道'hello'的长度是5,'world'的长度也是5。现在我需要计算这两个长度之和。 Final Answer: 这两个单词的长度之和是10。 > Finished chain. 输出: 这两个单词的长度之和是10。
继续尝试让ZeroShotAgent既具有记忆力,又能同时调用自定义函数和内置函数(serpAPI llm-math)
from langchain.agents import Tool, ZeroShotAgent, AgentExecutor from langchain.chains import LLMChain from langchain.prompts import PromptTemplate from langchain_openai import ChatOpenAI from langchain.memory import ConversationBufferMemory from langchain_community.utilities import SerpAPIWrapper from langchain.chains import LLMMathChain import os os.environ["SERPAPI_API_KEY"] = 'Your_Own_key' # 1. 定义自定义工具函数 def get_word_length(word: str) -> str: """计算字符串长度""" try: # 移除所有空格和引号 word = word.strip().strip('"\'') return str(len(word)) except Exception as e: return f"计算失败:{str(e)}" def get_temperature_difference(cities: str) -> str: """计算两个城市的温度差""" try: # 分割输入字符串获取两个城市名 city1, city2 = [city.strip() for city in cities.split(',')] # 使用serpapi查询两个城市的温度 search = SerpAPIWrapper() temp1 = search.run(f"{city1} 今天温度") temp2 = search.run(f"{city2} 今天温度") # 提取温度数值(假设温度格式为"XX°C") temp1 = int(temp1.split('°')[0]) temp2 = int(temp2.split('°')[0]) # 计算温度差 diff = abs(temp1 - temp2) return f"{city1}和{city2}的温度差是{diff}°C" except Exception as e: return f"计算温度差失败:{str(e)}" # 2. 创建工具列表 # 创建serpapi工具 search = SerpAPIWrapper() serpapi_tool = Tool( name="Search", func=search.run, description="用于搜索信息的工具,可以查询历史人物、天气等信息" ) # 创建llm-math工具 BASE_URL="https://dashscope.aliyuncs.com/compatible-mode/v1" MODEL = "qwen-turbo" API_KEY = os.getenv('DASHSCOPE_API_KEY') llm = ChatOpenAI( model= MODEL, # 默认的大模型为GPT-3.5-turbo,比较便宜 openai_api_base= BASE_URL, openai_api_key= API_KEY ) math_chain = LLMMathChain.from_llm(llm=llm) math_tool = Tool( name="Calculator", func=math_chain.run, description="用于进行数学计算的工具,可以处理各种数学运算" ) # 创建自定义工具 word_length_tool = Tool( name="WordLength", func=get_word_length, description="计算字符串长度,输入一个字符串,返回它的字符数" ) temp_diff_tool = Tool( name="TemperatureDiff", func=get_temperature_difference, description="计算两个城市的温度差,输入格式为'城市1,城市2',例如:'北京,上海'" ) tools = [serpapi_tool, math_tool, word_length_tool, temp_diff_tool] # 3. 创建ZeroShotAgent的提示模板 prefix = """你是一个帮助用户解决问题的助手。你可以使用以下工具: {tools} 请使用以下格式: Question: 输入的问题 Thought: 你需要思考如何解决这个问题 Action: 要使用的工具名称 Action Input: 工具的输入 Observation: 工具的输出 ... (这个思考/行动/观察可以重复多次) Thought: 我现在知道最终答案 Final Answer: 对原始输入问题的最终答案 重要提示: 1. 如果问题涉及多个步骤,你需要一步步思考 2. 可以使用一个工具的输出作为另一个工具的输入 3. 在得到所有需要的结果后,给出最终答案 4. 记住之前的对话历史,可以在需要时使用之前的结果 5. 使用TemperatureDiff工具时,输入格式必须是'城市1,城市2',例如:'北京,上海' 对话历史: {chat_history} 现在开始: Question: {input} """ suffix = """ {agent_scratchpad} """ # 创建memory memory = ConversationBufferMemory( memory_key="chat_history", input_key="input", output_key="output", return_messages=True ) # 创建agent agent = ZeroShotAgent( llm_chain=LLMChain(llm=llm, prompt=PromptTemplate.from_template(prefix + suffix)), tools=tools, verbose=True ) # 执行 agent_executor = AgentExecutor.from_agent_and_tools( agent=agent, tools=tools, verbose=True, max_iterations=10, memory=memory ) # 准备工具描述 tools_description = "\n".join([f"{tool.name}: {tool.description}" for tool in tools]) # 5. 运行交互式对话 def print_welcome_message(): print("\n欢迎使用智能助手!我可以帮您:") print("1. 查询信息(如历史人物、天气等)") print("2. 进行数学计算") print("3. 计算字符串长度") print("4. 计算两个城市之间的温度差") print("\n您可以随时输入 'quit' 或 'exit' 退出对话。") print("=" * 50) def main(): print_welcome_message() while True: try: user_input = input("\n请输入您的问题: ").strip() if user_input.lower() in ['quit', 'exit']: print("\n感谢使用,再见!") break if not user_input: print("请输入有效的问题!") continue result = agent_executor.invoke({ "input": user_input, "tools": tools_description }) print("\n助手回答:", result["output"]) except KeyboardInterrupt: print("\n\n程序被中断,正在退出...") break except Exception as e: print(f"\n发生错误:{str(e)}") print("请重新输入您的问题。") if __name__ == "__main__": main()
输出结果如下:
欢迎使用智能助手!我可以帮您: 1. 查询信息(如历史人物、天气等) 2. 进行数学计算 3. 计算字符串长度 4. 计算两个城市之间的温度差 您可以随时输入 'quit' 或 'exit' 退出对话。 ================================================== 请输入您的问题: 请问特朗普的年纪 > Entering new AgentExecutor chain... Thought: 我需要查询特朗普的年龄。 Action: Search Action Input: 特朗普 年龄 Observation: 78 years Thought:我现在知道最终答案。 Final Answer: 特朗普的年龄是78岁。 > Finished chain. 助手回答: 特朗普的年龄是78岁。 请输入您的问题: 请问奥巴马年纪 > Entering new AgentExecutor chain... Thought: 用户想了解奥巴马的年龄,我需要使用Search工具来获取相关信息。 Action: Search Action Input: 奥巴马的年纪 Observation: 63 years Thought:我现在知道最终答案。 Final Answer: 奥巴马的年龄是63岁。 > Finished chain. 助手回答: 奥巴马的年龄是63岁。 请输入您的问题: 请问这两个人谁年轻 > Entering new AgentExecutor chain... Thought: 我需要知道这两个人的年龄,然后比较他们的年龄。 Final Answer: 根据之前的对话历史,特朗普的年龄是78岁,奥巴马的年龄是63岁。所以奥巴马比特朗普年轻。 > Finished chain. 助手回答: 根据之前的对话历史,特朗普的年龄是78岁,奥巴马的年龄是63岁。所以奥巴马比特朗普年轻。 请输入您的问题: 请问毛泽东的出生地和周恩来的出生地现在哪个温度高? > Entering new AgentExecutor chain... Thought: 我需要查询毛泽东和周恩来出生地的当前温度,并比较它们的高低。 Action: Search Action Input: 毛泽东出生地 Observation: Shaoshan, Xiangtan, China Thought:我找到了毛泽东的出生地是韶山,位于中国湘潭。接下来,我需要查询韶山的当前温度。 Action: Search Action Input: 韶山天气 Observation: {'type': 'weather_result', 'temperature': '72', 'unit': 'Fahrenheit', 'precipitation': '15%', 'humidity': '87%', 'wind': '2 mph', 'location': '湘潭市韶山市', 'date': 'Tuesday 11:00 PM', 'weather': 'Cloudy'} Thought:韶山的当前温度是72华氏度。接下来,我需要查询周恩来出生地的当前温度。 Action: Search Action Input: 周恩来出生地 Observation: Huaian, Huaian, China Thought:我找到了周恩来的出生地是淮安。接下来,我需要查询淮安的当前温度。 Action: Search Action Input: 淮安天气 Observation: {'type': 'weather_result', 'temperature': '61', 'unit': 'Fahrenheit', 'precipitation': '0%', 'humidity': '71%', 'wind': '7 mph', 'location': '中国江苏省淮安市', 'date': 'Tuesday 11:00 PM', 'weather': 'Cloudy'} Thought:淮安的当前温度是61华氏度。韶山的温度是72华氏度,因此韶山的温度更高。 Final Answer: 毛泽东出生地韶山的温度比周恩来出生地淮安的温度高。 > Finished chain.
2. 在agent的调用中用AgentType.ZERO_SHOT_REACT_DESCRIPTION
注意,定义方式如下,此时不再需要prompt。
agent = initialize_agent( tools=tools, llm=llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True, memory=memory, handle_parsing_errors=True, max_iterations=10, early_stopping_method="generate", )
此时发现,memory不再起作用。我先查了特朗普的年纪,又查了奥巴马的年纪,然后问这两个的年纪差。结果,它就不知道”这两个人”是谁。
示例代码如下:
from langchain.agents import Tool, initialize_agent, AgentType from langchain.chains import LLMChain from langchain.prompts import PromptTemplate from langchain_openai import ChatOpenAI from langchain.memory import ConversationBufferMemory from langchain_community.utilities import SerpAPIWrapper from langchain.chains import LLMMathChain import os os.environ["SERPAPI_API_KEY"] = 'YourOwnKey' # 1. 定义自定义工具函数 def get_word_length(word: str) -> str: """计算字符串长度""" try: # 移除所有空格和引号 word = word.strip().strip('"\'') return str(len(word)) except Exception as e: return f"计算失败:{str(e)}" def get_temperature_difference(cities: str) -> str: """计算两个城市的温度差""" try: # 分割输入字符串获取两个城市名 city1, city2 = [city.strip() for city in cities.split(',')] # 使用serpapi查询两个城市的温度 search = SerpAPIWrapper() temp1 = search.run(f"{city1} 今天温度") temp2 = search.run(f"{city2} 今天温度") # 提取温度数值(假设温度格式为"XX°C") temp1 = int(temp1.split('°')[0]) temp2 = int(temp2.split('°')[0]) # 计算温度差 diff = abs(temp1 - temp2) return f"{city1}和{city2}的温度差是{diff}°C" except Exception as e: return f"计算温度差失败:{str(e)}" # 2. 创建工具列表 # 创建serpapi工具 search = SerpAPIWrapper() serpapi_tool = Tool( name="Search", func=search.run, description="用于搜索信息的工具,可以查询历史人物、天气等信息" ) # 创建llm-math工具 BASE_URL="https://dashscope.aliyuncs.com/compatible-mode/v1" MODEL = "qwen-turbo" API_KEY = os.getenv('DASHSCOPE_API_KEY') llm = ChatOpenAI( model= MODEL, # 默认的大模型为GPT-3.5-turbo,比较便宜 openai_api_base= BASE_URL, openai_api_key= API_KEY ) math_chain = LLMMathChain.from_llm(llm=llm) math_tool = Tool( name="Calculator", func=math_chain.run, description="用于进行数学计算的工具,可以处理各种数学运算" ) # 创建自定义工具 word_length_tool = Tool( name="WordLength", func=get_word_length, description="计算字符串长度,输入一个字符串,返回它的字符数" ) temp_diff_tool = Tool( name="TemperatureDiff", func=get_temperature_difference, description="计算两个城市的温度差,输入格式为'城市1,城市2',例如:'北京,上海'" ) tools = [serpapi_tool, math_tool, word_length_tool, temp_diff_tool] # 3. 创建memory memory = ConversationBufferMemory( memory_key="chat_history", input_key="input", output_key="output", return_messages=True ) #4,创建agent agent = initialize_agent( tools=tools, llm=llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True, memory=memory, handle_parsing_errors=True, max_iterations=10, early_stopping_method="generate", ) # 5. 运行交互式对话 def print_welcome_message(): print("\n欢迎使用智能助手!我可以帮您:") print("1. 查询信息(如历史人物、天气等)") print("2. 进行数学计算") print("3. 计算字符串长度") print("4. 计算两个城市之间的温度差") print("\n您可以随时输入 'quit' 或 'exit' 退出对话。") print("=" * 50) def main(): print_welcome_message() while True: try: user_input = input("\n请输入您的问题: ").strip() if user_input.lower() in ['quit', 'exit']: print("\n感谢使用,再见!") break if not user_input: print("请输入有效的问题!") continue result = agent.invoke({ "input": user_input }) print("\n助手回答:", result["output"]) except KeyboardInterrupt: print("\n\n程序被中断,正在退出...") break except Exception as e: print(f"\n发生错误:{str(e)}") print("请重新输入您的问题。") if __name__ == "__main__": main()
输出结果如下
请输入您的问题: 请查一下特朗普的年纪 > Entering new AgentExecutor chain... 我需要查找特朗普的出生日期,以便计算他的年龄。 Action: Search Action Input: "特朗普出生日期" Observation: June 14, 1946 (age 78 years), Jamaica Hospital Medical Center, New York, NY Thought:特朗普出生于1946年6月14日。现在是2023年,所以我可以计算他的年龄。 Action: Calculator Action Input: 2023 - 1946 Observation: Answer: 77 Thought:特朗普在2023年满77岁,但由于他的生日在6月14日,所以在此之前他是76岁。 Final Answer: 特朗普现在77岁。 > Finished chain. 助手回答: 特朗普现在77岁。 请输入您的问题: 请查一下奥巴马的年纪 > Entering new AgentExecutor chain... 我需要查找奥巴马的出生年份,以便计算他的年龄。 Action: Search Action Input: "奥巴马出生年份" Observation: August 4, 1961 (age 63 years), Kapiʻolani Medical Center for Women & Children, Honolulu, HI Thought:奥巴马出生于1961年8月4日,现在是2023年,所以我可以计算他的年龄。 Action: Calculator Action Input: 2023 - 1961 Observation: Answer: 62 Thought:奥巴马在2023年时62岁,但由于他的生日在8月4日,所以在2023年8月4日之后,他将满63岁。 Final Answer: 奥巴马现在62岁,2023年8月4日将满63岁。 > Finished chain. 助手回答: 奥巴马现在62岁,2023年8月4日将满63岁。 请输入您的问题: 请计算一下这两个人的年龄差 > Entering new AgentExecutor chain... 我需要知道这两个人的出生年份或年龄,以便计算他们的年龄差。请提供这两个人的相关信息。 Observation: Invalid Format: Missing 'Action:' after 'Thought: Thought:Question: 请计算一下这两个人的年龄差 Thought: 我需要知道这两个人的出生年份或年龄,以便计算他们的年龄差。请提供这两个人的相关信息。 Observation: Invalid Format: Missing 'Action:' after 'Thought: Thought:Question: 请计算一下这两个人的年龄差 Thought: 我需要知道这两个人的出生年份或年龄,以便计算他们的年龄差。请提供这两个人的相关信息。 Action: None ......
3. STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION
3. STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION
特点:支持结构化输入
结构化输入处理:能够理解并处理结构化数据(如字典、列表)
多工具协同:可以协调多个工具完成复杂任务
零样本学习:不需要示例就能理解任务
对话记忆:通过memory参数保留对话上下文
适用场景:需要处理结构化数据的任务
已跑通的示例代码如下
from langchain_openai import ChatOpenAI from langchain.tools import Tool from langchain.memory import ConversationBufferMemory import math,os from typing import Dict, List, Union import json # 1. 定义结构化工具集 def get_employee_info(employee_id: str) -> Dict[str, str]: """获取员工基本信息""" employee_db = { "1001": {"name": "张三", "department": "研发部", "position": "高级工程师"}, "1002": {"name": "李四", "department": "市场部", "position": "市场经理"}, "1003": {"name": "王五", "department": "财务部", "position": "会计"} } return employee_db.get(employee_id, {"error": "员工不存在"}) def calculate_salary(base: float, bonus: float, tax_rate: float) -> Dict[str, float]: """计算薪资""" gross = base + bonus tax = gross * tax_rate net = gross - tax return { "gross_salary": round(gross, 2), "tax": round(tax, 2), "net_salary": round(net, 2) } def get_department_employees(department: str) -> List[Dict[str, str]]: """获取部门员工列表""" department_db = { "研发部": [ {"id": "1001", "name": "张三", "position": "高级工程师"}, {"id": "1004", "name": "赵六", "position": "初级工程师"} ], "市场部": [ {"id": "1002", "name": "李四", "position": "市场经理"}, {"id": "1005", "name": "钱七", "position": "市场专员"} ], "财务部": [ {"id": "1003", "name": "王五", "position": "会计"} ] } return department_db.get(department, []) # 2. 创建工具实例 def parse_department_input(input_str: str) -> str: """解析部门输入参数""" try: # 尝试解析JSON输入 params = json.loads(input_str) if isinstance(params, dict) and "department" in params: return params["department"] elif isinstance(params, dict) and "department_name" in params: return params["department_name"] return input_str except: # 如果不是JSON,直接返回输入字符串 return input_str tools = [ Tool( name="GetEmployeeInfo", func=lambda x: str(get_employee_info(x)), description="根据员工ID获取员工详细信息,输入应为员工ID字符串" ), Tool( name="CalculateSalary", func=lambda x: str(calculate_salary(**eval(x))), description="计算薪资,输入应为字典格式的JSON字符串,包含base(基本工资), bonus(奖金), tax_rate(税率)字段" ), Tool( name="GetDepartmentEmployees", func=lambda x: str(get_department_employees(parse_department_input(x))), description="获取部门所有员工列表,输入可以是部门名称字符串或JSON格式(包含department或department_name字段)" ) ] # 3. 初始化记忆和LLM memory = ConversationBufferMemory( memory_key="chat_history", return_messages=True, input_key="input", output_key="output" ) BASE_URL="https://dashscope.aliyuncs.com/compatible-mode/v1" MODEL = "qwen-turbo" API_KEY = os.getenv('DASHSCOPE_API_KEY') llm = ChatOpenAI( model=MODEL, openai_api_base=BASE_URL, openai_api_key=API_KEY, temperature=0 # 降低随机性 ) # 4. 创建结构化聊天代理 agent = initialize_agent( tools, llm, agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION, memory=memory, verbose=True, handle_parsing_errors=True, max_iterations=3 ) # 5. 执行多轮结构化对话 print("===== 第一轮:查询员工信息 =====") response1 = agent.run({"input": "请查询员工ID 1001的信息"}) print(response1) print("\n===== 第二轮:薪资计算 =====") response2 = agent.run({"input": "假设这位员工的基本工资是15000,奖金3000,税率0.2,请计算他的税后薪资"}) print(response2) print("\n===== 第三轮:部门查询 =====") response3 = agent.run({"input": "请列出研发部的所有员工"}) print(response3) print("\n===== 第四轮:综合问题 =====") response4 = agent.run({"input": "根据之前查询的研发部员工信息,计算研发部所有员工的基本工资总和,假设每人基本工资都是15000"}) print(response4)
结果输出如下:
> Entering new AgentExecutor chain... Thought: 我需要使用GetEmployeeInfo工具来获取员工信息 Action: { “action”: “GetEmployeeInfo”, “action_input”: “1001” } Observation: {'name': '张三', 'department': '研发部', 'position': '高级工程师'} Thought:我找到了员工1001的信息 Action: { “action”: “Final Answer”, “action_input”: “员工1001的信息如下:姓名:张三,部门:研发部,职位:高级工程师。” } > Finished chain. 员工1001的信息如下:姓名:张三,部门:研发部,职位:高级工程师。 ===== 第二轮:薪资计算 ===== > Entering new AgentExecutor chain... Thought: 需要使用CalculateSalary工具来计算税后薪资。 Action: { “action”: “CalculateSalary”, “action_input”: “{“base”: 15000, “bonus”: 3000, “tax_rate”: 0.2}” } Observation: {'gross_salary': 18000, 'tax': 3600.0, 'net_salary': 14400.0} Thought:I now know the answer to the user's question. Action: { “action”: “Final Answer”, “action_input”: “该员工的税后薪资为14400.0元。” } > Finished chain. 该员工的税后薪资为14400.0元。 ===== 第三轮:部门查询 ===== > Entering new AgentExecutor chain... Thought: 我需要使用GetDepartmentEmployees工具来获取研发部的所有员工列表。 Action: { “action”: “GetDepartmentEmployees”, “action_input”: “{“department”: “研发部”}” } Observation: [{'id': '1001', 'name': '张三', 'position': '高级工程师'}, {'id': '1004', 'name': '赵六', 'position': '初级工程师'}] Thought:我得到了研发部的员工列表。 Action: { “action”: “Final Answer”, “action_input”: “研发部的员工包括:张三(高级工程师),赵六(初级工程师)。” } > Finished chain. 研发部的员工包括:张三(高级工程师),赵六(初级工程师)。 ===== 第四轮:综合问题 ===== > Entering new AgentExecutor chain... Thought: 首先需要获取研发部的所有员工信息,然后计算基本工资总和。 Action: { “action”: “GetDepartmentEmployees”, “action_input”: “{“department”: “研发部”}” } Observation: [{'id': '1001', 'name': '张三', 'position': '高级工程师'}, {'id': '1004', 'name': '赵六', 'position': '初级工程师'}] Thought:现在我已经获得了研发部的员工信息,接下来我将计算他们的基本工资总和。 Action: { “action”: “CalculateSalary”, “action_input”: “{“base”: 15000, “bonus”: 0, “tax_rate”: 0}” } Observation: {'gross_salary': 15000, 'tax': 0, 'net_salary': 15000} Thought:计算结果显示每位员工的基本工资为15000,现在我将计算研发部所有员工的基本工资总和。 Action: { “action”: “Final Answer”, “action_input”: “研发部所有员工的基本工资总和为30000。” } > Finished chain. 研发部所有员工的基本工资总和为30000。
4. CONVERSATIONAL_REACT_DESCRIPTION
特点:保留对话历史,支持多轮交互
适用场景:聊天机器人等需要记忆上下文的应用
*** 这是使agent有记忆力的最正确的选择,简单易用***
注意agent定义方式如下,此法也不要定义prompt
agent = initialize_agent( tools=tools, llm=llm, agent=AgentType.CONVERSATIONAL_REACT_DESCRIPTION, verbose=True, memory=memory, handle_parsing_errors=True )
可执行的示例代码如下
from langchain.agents import Tool, AgentExecutor, initialize_agent from langchain.agents.agent_types import AgentType from langchain.chains import LLMChain from langchain.prompts import PromptTemplate from langchain_openai import ChatOpenAI from langchain.memory import ConversationBufferMemory from langchain_community.utilities import SerpAPIWrapper from langchain.chains import LLMMathChain from langchain.agents import load_tools import os os.environ["SERPAPI_API_KEY"] = 'your-own-key' # 1. 定义自定义工具函数 def get_word_length(word: str) -> str: """计算字符串长度""" try: # 移除所有空格和引号 word = word.strip().strip('"\'') return str(len(word)) except Exception as e: return f"计算失败:{str(e)}" def get_temperature_difference(cities: str) -> str: """计算两个城市的温度差""" try: # 分割输入字符串获取两个城市名 city1, city2 = [city.strip() for city in cities.split(',')] # 使用serpapi查询两个城市的温度 search = SerpAPIWrapper() temp1 = search.run(f"{city1} 今天温度") temp2 = search.run(f"{city2} 今天温度") # 提取温度数值(假设温度格式为"XX°C") temp1 = int(temp1.split('°')[0]) temp2 = int(temp2.split('°')[0]) # 计算温度差 diff = abs(temp1 - temp2) return f"{city1}和{city2}的温度差是{diff}°C" except Exception as e: return f"计算温度差失败:{str(e)}" # 2. 创建LLM BASE_URL="https://dashscope.aliyuncs.com/compatible-mode/v1" MODEL = "qwen-turbo" API_KEY = os.getenv('DASHSCOPE_API_KEY') llm = ChatOpenAI( model= MODEL, # 默认的大模型为GPT-3.5-turbo,比较便宜 openai_api_base= BASE_URL, openai_api_key= API_KEY ) # 3. 加载内置工具 builtin_tools = load_tools( ["serpapi", "llm-math"], llm=llm ) # 4. 创建自定义工具 custom_tools = [ Tool( name="WordLength", func=get_word_length, description="计算字符串长度,输入一个字符串,返回它的字符数" ), Tool( name="TemperatureDiff", func=get_temperature_difference, description="计算两个城市的温度差,输入格式为'城市1,城市2',例如:'北京,上海'" ) ] # 5. 合并所有工具 tools = builtin_tools + custom_tools # 6. 创建memory memory = ConversationBufferMemory( memory_key="chat_history", return_messages=True ) # 7. 创建agent agent = initialize_agent( tools=tools, llm=llm, agent=AgentType.CONVERSATIONAL_REACT_DESCRIPTION, verbose=True, memory=memory, handle_parsing_errors=True ) # 8. 打印欢迎信息和使用说明 print("欢迎使用智能助手!我可以帮你:") print("1. 查询历史人物信息(使用serpapi)") print("2. 进行数学计算(使用llm-math)") print("3. 计算字符串长度(使用WordLength工具)") print("4. 计算两个城市的温度差(使用TemperatureDiff工具)") print("5. 回答其他问题(如果工具可用)") print("\n示例问题:") print("- 历史上今天出生的两个最重要的人是谁?") print("- 计算 123 + 456 等于多少?") print("- 单词'hello'的长度是多少?") print("- 北京和上海今天的温度差是多少?") print("\n输入 'quit' 或 'exit' 退出对话") print("-" * 50) # 9. 开始对话循环 while True: try: # 获取用户输入 user_input = input("\n请输入你的问题: ").strip() # 检查是否退出 if user_input.lower() in ['quit', 'exit']: print("感谢使用,再见!") break # 如果输入为空,继续下一轮 if not user_input: continue # 执行agent result = agent.invoke({"input": user_input}) print("\n助手:", result["output"]) except KeyboardInterrupt: print("\n\n程序被中断,正在退出...") break except Exception as e: print(f"\n发生错误:{str(e)}") print("请尝试用其他方式提问,或输入 'quit' 退出程序")
输出结果如下
欢迎使用智能助手!我可以帮你: 1. 查询历史人物信息(使用serpapi) 2. 进行数学计算(使用llm-math) 3. 计算字符串长度(使用WordLength工具) 4. 计算两个城市的温度差(使用TemperatureDiff工具) 5. 回答其他问题(如果工具可用) 示例问题: - 历史上今天出生的两个最重要的人是谁? - 计算 123 + 456 等于多少? - 单词'hello'的长度是多少? - 北京和上海今天的温度差是多少? 输入 'quit' 或 'exit' 退出对话 -------------------------------------------------- 请输入你的问题: 请问特朗普多大了 > Entering new AgentExecutor chain... Thought: Do I need to use a tool? Yes Action: Search Action Input: 特朗普年龄 Observation: 78 years Thought:Do I need to use a tool? No AI: 特朗普现在78岁。 > Finished chain. 助手: 特朗普现在78岁。 请输入你的问题: 请问奥巴马多大了? > Entering new AgentExecutor chain... Thought: Do I need to use a tool? No AI: 奥巴马现在的年龄是63岁。 > Finished chain. 助手: 奥巴马现在的年龄是63岁。 请输入你的问题: 请问他们谁老 > Entering new AgentExecutor chain... Thought: Do I need to use a tool? No AI: 特朗普现在78岁,而奥巴马现在63岁,所以特朗普比奥巴马年长。 > Finished chain. 助手: 特朗普现在78岁,而奥巴马现在63岁,所以特朗普比奥巴马年长。 请输入你的问题: 请问年长几岁? > Entering new AgentExecutor chain... Thought: 需要计算两者的年龄差。 Action: Calculator Action Input: 78 - 63 Observation: Answer: 15 Thought:Do I need to use a tool? No AI: 特朗普比奥巴马年长15岁。 > Finished chain. 助手: 特朗普比奥巴马年长15岁。 请输入你的问题:
5. SELF_ASK_WITH_SEARCH
特点:自问自答模式,适合需要中间推理步骤的任务
适用场景:需要分解问题并逐步回答的场景
前面已经使用过了SerpAPI工具,下面可执行示例单独使用该工具,以实现SELF_ASK_WITH_SEARCH
from langchain.agents import initialize_agent, AgentType from langchain_community.utilities import SerpAPIWrapper from langchain_openai import ChatOpenAI from langchain.tools import Tool from langchain.prompts import PromptTemplate import os # 需要设置SerpAPI的API key os.environ["SERPAPI_API_KEY"] = "your_api_key" # 替换为实际key # 1. 创建llm BASE_URL="https://dashscope.aliyuncs.com/compatible-mode/v1" MODEL = "qwen-turbo" API_KEY = os.getenv('DASHSCOPE_API_KEY') llm = ChatOpenAI( model=MODEL, openai_api_base=BASE_URL, openai_api_key=API_KEY, temperature=0 # 降低随机性 ) # 2. 配置搜索工具(需 SerpAPI Key) search = SerpAPIWrapper() tools = [ Tool( name="Intermediate Answer", func=search.run, description="用于搜索事实性问题的答案。输入应该是一个具体的搜索查询。", ) ] # 3. 定义自定义Prompt模板 template = """问题: {input} 让我们一步一步地思考这个问题。 首先,我们需要确定需要搜索的具体问题。 然后,使用搜索工具来获取信息。 最后,基于搜索结果给出完整的答案。 搜索工具: {agent_scratchpad} 请按照以下格式回答: 思考: [你的思考过程] 搜索: [具体的搜索查询] 中间答案: [搜索结果] 最终答案: [完整的答案] 注意: - 确保搜索查询具体且明确 - 如果信息不完整,继续提出新的搜索查询 - 最终答案必须包含所有重要信息 最终答案:""" prompt = PromptTemplate( input_variables=["input", "agent_scratchpad"], template=template ) # 4. 创建Agent agent = initialize_agent( tools, llm, agent=AgentType.SELF_ASK_WITH_SEARCH, verbose=True, handle_parsing_errors=True, # 添加错误处理 max_iterations=5, # 增加迭代次数以支持更多轮次的问答 early_stopping_method="generate", # 添加提前停止方法 prompt=prompt # 使用自定义prompt ) # 5. 执行示例 print("=== 搜索任务 ===") try: result = agent.invoke({ "input": "特斯拉的CEO是谁?他目前还领导哪些公司?" }) print("\n最终答案:", result["output"]) except Exception as e: print(f"\n执行过程中出现错误: {str(e)}")
结果如下
=== 搜索任务 === > Entering new AgentExecutor chain... Yes. Follow up: 谁是特斯拉的CEO? Intermediate answer: Elon Musk Follow up: Elon Musk 还领导哪些公司? Intermediate answer: [' entity_type: related_questions.', '他是SpaceX的创始人、董事长、首席执行官、首席工程师,特斯拉投资人、首席执行官、产品设计师、 前董事长,无聊公司创始人,Neuralink、OpenAI联合创始人,同时也是X公司的首席 ...', '马斯克的公司 · 1、SpaceX(太空探索技术公司) · 2、Tesla(特斯拉) · 3、SolarCity(太阳城公司) · 4、Starlink(卫星互连公司) · 5、The Boring Company(解决交通 ...', '埃隆马斯克之所以被称为“硅谷钢铁侠”,并非浪得虚名。 从网上公开的资料来看,他参与创立,投资或管理着多家公司,就大众熟知的就有Paypal,SpaceX,特斯 ...', '马斯克(Musk)是OpenAI的联合创始人,但于2019年离开了公司,并成立了自己的AI公司XAI。在过去的几年中,他在公司的方向上与奥特曼(Altman)争吵不休。他去年就 ...', '除了可能放松的政府监管外,马斯克与特朗普 的联盟可能还有助于他获得联邦合同。据报道,在过去十年中,SpaceX和特斯拉已经获得了至少154亿美元的政府合同。', '他是SpaceX的創始人、董事長、執行長、首席 工程師,特斯拉投資人、執行長、產品設計師、前董事長,無聊公司創始人,Neuralink、OpenAI聯合創始人,同時也是X公司的技術長、董事 ...', '据了解,马斯克的收购行动获得了多家投资者的支持,包括Valor Equity Partners、Baron Capital、Atreides Management、Vy Capital以及由Palantir联合创始人乔·朗斯代尔领导的 ...', 'Elon Musk 参与创办并领导着Tesla、SpaceX、Neuralink 和The Boring Company。 作为Tesla 的联合创始人兼首席执行官,Elon 负责引领公司电动汽车、电池产品和 太阳能产品的 ...', 'Elon Musk是Technoking of Tesla,自2008年10月起担任Tesla Motors, Inc.首席执行官,自2004年4月起担任Tesla Motors, Inc.成员。Musk先生 自2002年5月起担任先进火箭和航天器 ...', '2002年:Musk投资1亿美元,创办美国太空探索技术公司(SPACE X),出任CEO兼首席技术官。 2004年:Musk向特斯拉汽车 公司投资630万美元,出任该公司董事长。 2006年:Musk投资1000 ...'] So the final answer is: Elon Musk is the CEO of Tesla and he currently also leads SpaceX, Neuralink, The Boring Company, and is involved with other companies like OpenAI and X (formerly Twitter). > Finished chain. 最终答案: Elon Musk is the CEO of Tesla and he currently also leads SpaceX, Neuralink, The Boring Company, and is involved with other companies like OpenAI and X (formerly Twitter).
五、选择指南
简单查询/单工具调用:
优先选择LLMSingleActionAgent
示例:单词长度计算、简单问答
需要上下文记忆:
选择CONVERSATIONAL_REACT_DESCRIPTION
示例:聊天机器人
也可选择agent = ZeroShotAgent()的方式,但定义比较复杂,还需要prompt进行配合。
复杂多步任务:
选择ZERO_SHOT_REACT_DESCRIPTION或agent = ZeroShotAgent()或SELF_ASK_WITH_SEARCH
示例:数学计算+搜索组合任务
结构化数据处理:
选择STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION
示例:表格数据处理
六、性能优化建议
对于高频简单查询,使用SingleAction Agent减少延迟
复杂任务合理设置max_iterations防止无限循环
对话场景使用ConversationBufferWindowMemory控制内存增长
生产环境考虑添加异常处理和超时机制
# 带安全限制的Agent执行示例 from langchain.agents import AgentExecutor safe_executor = AgentExecutor.from_agent_and_tools( agent=agent, tools=tools, max_iterations=5, # 限制最大迭代次数 early_stopping_method="generate", # 提前停止策略 handle_parsing_errors=True # 处理解析错误 )