用50行代码写第一个会用工具的AI Agent|ReAct模式完整实战
用50行代码写第一个会用工具的Agent
环境搭好了,我们来写第一个真正的Agent。
这个Agent会做三件事。它会用计算器做数学计算,会搜索天气信息,还能回答普通问题。听起来简单,麻雀虽小五脏俱全。通过这个例子,你能看到Agent是怎么判断什么时候该用工具、什么时候直接回答的。
跟着写一遍,你就对Agent的工作方式有体感了。
先想清楚需求
我们要做的Agent叫"生活小助手"。用户问它问题,它自己判断该怎么回答。
如果是普通问题,比如"北京是哪个国家的首都",它直接回答就行,不用调用工具。
如果是数学计算题,比如"123乘以456等于多少",它应该调用计算器工具来算。不用大模型自己心算,算错的概率很高。
如果是问天气,比如"今天上海天气怎么样",它应该调用天气查询工具。大模型不知道实时天气,必须查外部数据。
就这三个场景。看起来简单,已经涵盖了Agent最核心的能力。理解用户意图,选择合适的工具,拿到结果以后组织语言回答。
安装依赖
做这个例子需要两个包。langchain我们已经装过了,还需要一个langchainhub,用来拉取预定义的Prompt模板。
pip install langchain langchain-openai langchainhub python-dotenv
装好了就可以开始写代码了。
第一步,定义工具
我们先定义两个工具,计算器和天气查询。
真实的天气查询需要接入第三方API,这里为了演示方便,我们写一个假的天气函数。原理是一样的,后面接真实API的时候,把函数体换掉就行。
创建一个first_agent.py文件,开始写。
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain.tools import tool
from langchain import hub
from langchain.agents import AgentExecutor, create_react_agent
# 加载环境变量
load_dotenv()
# 定义计算器工具
@tool
def calculator(expression: str) -> str:
"""
计算数学表达式的结果。
传入一个数学表达式字符串,返回计算结果。
例如:"123 * 456"、"(100 + 200) / 3"
"""
try:
result = eval(expression)
return f"计算结果是 {result}"
except Exception as e:
return f"计算出错了,{e}"
# 定义天气查询工具
@tool
def get_weather(city: str) -> str:
"""
查询指定城市的天气情况。
传入城市名称,返回当前天气、温度、湿度等信息。
只支持中国主要城市。
"""
# 这里是模拟数据,实际项目中接入真实的天气API
weather_data = {
"北京": "晴,温度25度,湿度40%,微风",
"上海": "多云,温度28度,湿度65%,东南风3级",
"广州": "阵雨,温度32度,湿度80%,南风2级",
"深圳": "雷阵雨,温度30度,湿度75%,西南风4级",
"杭州": "晴转多云,温度27度,湿度55%,东风2级",
}
return weather_data.get(city, f"暂时没有{city}的天气数据")
这段代码定义了两个工具函数。@tool这个装饰器会把普通的Python函数变成LangChain的工具,Agent就能调用了。
函数的文档字符串很重要。大模型就是通过看文档字符串来理解这个工具是做什么的、参数是什么意思。文档写得清楚,大模型就知道什么时候该调用这个工具,参数该传什么。文档写得含糊,大模型可能就用错了。
我自己在这上面踩过坑。最开始写工具的时候,文档字符串写得很简单,就一句话。结果Agent经常调用错工具,参数也传不对。后来把文档写详细了,加了例子,准确率一下就上来了。
第二步,初始化模型和工具
工具定义好了,我们来初始化模型和Agent。
接着上面的代码写。
# 初始化大模型
model = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
# 把工具放进列表里
tools = [calculator, get_weather]
# 从LangChain Hub拉取ReAct Agent的Prompt模板
prompt = hub.pull("hwchase17/react")
这里用了ReAct的Agent框架。ReAct是Reasoning and Acting的缩写,意思是推理和行动交替进行。Agent先想一下该做什么,然后行动,行动完了观察结果,再想下一步。这样一步步推进,直到完成任务。
ReAct是目前最经典、最稳定的Agent框架之一。新手从ReAct入门最合适,原理清晰,容易理解。
temperature设为0,让模型输出更稳定。做Agent的时候,我们希望模型的行为是可预测的,不需要太多创造性。创意写作的时候温度可以调高一点,做Agent还是稳一点好。
第三步,创建Agent并运行
最后一步,把模型、工具和Prompt拼起来,创建Agent执行器。
# 创建ReAct Agent
agent = create_react_agent(model, tools, prompt)
# 创建Agent执行器
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
verbose=True,
handle_parsing_errors=True,
)
AgentExecutor负责管理Agent的执行循环。它会调用Agent获取下一步动作,执行工具,把结果返回给Agent,再继续下一步。直到Agent给出最终答案,或者达到最大步数。
verbose=True会把每一步的思考过程都打印出来。调试的时候一定要打开这个,你能清楚地看到Agent是怎么想的、做了什么、得到了什么结果。
handle_parsing_errors=True是用来处理解析错误的。有时候大模型输出的格式不对,解析失败,这个参数会让它自动重试,而不是直接报错。
现在我们来测试一下。
# 测试1:普通问题,应该直接回答
print("测试1:普通问题")
result = agent_executor.invoke({"input": "北京是哪个国家的首都?"})
print("答案:", result["output"])
print("-" * 50)
# 测试2:数学计算,应该调用计算器
print("测试2:数学计算")
result = agent_executor.invoke({"input": "123乘以456等于多少?"})
print("答案:", result["output"])
print("-" * 50)
# 测试3:天气查询,应该调用天气工具
print("测试3:天气查询")
result = agent_executor.invoke({"input": "今天上海天气怎么样?"})
print("答案:", result["output"])
print("-" * 50)
# 测试4:复杂一点的问题
print("测试4:组合问题")
result = agent_executor.invoke({"input": "北京今天的温度加上上海今天的温度,总共是多少度?"})
print("答案:", result["output"])
第四个测试有意思。它需要先查北京的天气,再查上海的天气,提取两个温度,然后相加。Agent要调用两次天气工具,再调用一次计算器。如果它能做对,说明它真的在规划和执行。
运行看看效果
代码写完了,运行一下。
python first_agent.py
打开verbose的话,你会看到完整的思考过程。Agent会先想这个问题需要做什么,然后选择工具,执行工具,观察结果,再想下一步怎么办。
第一个测试,北京是哪个国家的首都。Agent应该直接回答,不用调用任何工具。这个它的知识库里就有。
第二个测试,数学计算。Agent应该判断这是个计算题,调用calculator工具,传入表达式,拿到结果以后回答用户。
第三个测试,天气查询。Agent应该判断需要查天气,调用get_weather工具,传入城市名,拿到结果以后回答。
第四个测试最有意思。Agent需要先查北京天气,从结果里提取温度。再查上海天气,提取温度。然后把两个温度加起来,用计算器算。最后告诉用户总和。
如果它都做对了,恭喜你,你的第一个Agent跑通了。
我第一次跑通的时候,还是挺有感觉的。以前都是你写一行代码它执行一步,现在你说一句话,它自己想办法完成。那种感觉很不一样。
可能遇到的问题
第一次跑,可能会遇到一些问题。
Agent不调用工具,直接瞎回答。 这是最常见的问题。原因通常是工具的文档写得不够清楚,大模型不知道什么时候该用。把工具描述写详细一点,加上使用例子,会好很多。
工具调用了,但参数传错了。 也是文档的问题。参数的描述、格式要求、例子,都写清楚。参数名也要起得直观,别用缩写。
Agent陷入死循环,一直在调用工具。 复杂问题有时候会出现这种情况。可以设置max_iterations参数,限制最多迭代多少步,到了就强制停止。
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
verbose=True,
handle_parsing_errors=True,
max_iterations=5,
)
不同模型效果不一样。 能力强的模型做Agent效果更好。gpt-3.5-turbo基本能用,gpt-4效果会好很多。国产模型里,DeepSeek和通义的最新版本做Agent也还可以。
遇到问题别着急,打开verbose看Agent的思考过程。它是怎么想的,为什么选了这个工具,参数是怎么填的,都能看到。看懂了它的思路,你就知道问题出在哪了。
完整代码
最后放一份完整的代码,你可以直接复制运行。
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain.tools import tool
from langchain import hub
from langchain.agents import AgentExecutor, create_react_agent
# 加载环境变量
load_dotenv()
@tool
def calculator(expression: str) -> str:
"""
计算数学表达式的结果。
传入一个数学表达式字符串,返回计算结果。
例如:"123 * 456"、"(100 + 200) / 3"
"""
try:
result = eval(expression)
return f"计算结果是 {result}"
except Exception as e:
return f"计算出错了,{e}"
@tool
def get_weather(city: str) -> str:
"""
查询指定城市的天气情况。
传入城市名称,返回当前天气、温度、湿度等信息。
只支持中国主要城市。
"""
weather_data = {
"北京": "晴,温度25度,湿度40%,微风",
"上海": "多云,温度28度,湿度65%,东南风3级",
"广州": "阵雨,温度32度,湿度80%,南风2级",
"深圳": "雷阵雨,温度30度,湿度75%,西南风4级",
"杭州": "晴转多云,温度27度,湿度55%,东风2级",
}
return weather_data.get(city, f"暂时没有{city}的天气数据")
# 初始化大模型
model = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
# 工具列表
tools = [calculator, get_weather]
# 拉取ReAct Prompt模板
prompt = hub.pull("hwchase17/react")
# 创建Agent
agent = create_react_agent(model, tools, prompt)
# 创建执行器
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
verbose=True,
handle_parsing_errors=True,
max_iterations=5,
)
if __name__ == "__main__":
print("=" * 50)
print("第一个Agent测试开始")
print("=" * 50)
# 测试1
print("\n【测试1】普通知识问题")
result = agent_executor.invoke({"input": "北京是哪个国家的首都?"})
print("最终答案:", result["output"])
# 测试2
print("\n【测试2】数学计算")
result = agent_executor.invoke({"input": "123乘以456等于多少?"})
print("最终答案:", result["output"])
# 测试3
print("\n【测试3】天气查询")
result = agent_executor.invoke({"input": "今天上海天气怎么样?"})
print("最终答案:", result["output"])
# 测试4
print("\n【测试4】多步骤组合问题")
result = agent_executor.invoke(
{"input": "北京今天的温度加上上海今天的温度,总共是多少度?"}
)
print("最终答案:", result["output"])
print("\n" + "=" * 50)
print("测试完成")
print("=" * 50)
筑基篇到这里就结束了。我们从Agent是什么开始讲,讲了它的组成、能力边界、技术选型,然后搭了环境,写了第一个能跑的Agent。
你应该对Agent有了一个整体的认识,也亲手跑通了一个例子。
从下一篇开始,我们进入工具篇。Agent的能力很大程度上取决于它能用什么工具。我们会一个一个讲,搜索工具、数据库工具、文件处理工具、API调用工具。每一个都有代码,你可以亲手给你的Agent装上各种能力。
更多推荐

所有评论(0)