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 # 处理解析错误
)

