只给 create_agent 一个模型,得到的是一张 start → model → end 的直线图,说白了还是个问答机。接上工具之后,图里才会长出 tools 节点和回边——那才是 Agent。
24.1 绑定一个工具
工具就是上一章定义过的 @tool 函数,直接放进 tools 列表:
from langchain.agents import create_agent
from langchain.tools import tool
@tool(parse_docstring=True)
def get_weather(city: str) -> str:
"""
天气查询工具
Args:
city: 城市名称
"""
return f"{city} 的天气为晴朗,25°C。"
agent = create_agent(model=model, tools=[get_weather])
resp = agent.invoke({
"messages": [
{"role": "system", "content": "你是一个天气查询助手,只回答天气相关的问题"},
{"role": "user", "content": "北京的天气怎么样?"},
]
})
返回的消息列表能清楚看到四步:
SystemMessage 你是一个天气查询助手...
HumanMessage 北京的天气怎么样?
AIMessage tool_calls=[{'name': 'get_weather', 'args': {'city': '北京'}}]
ToolMessage 北京的天气为晴朗,25°C。
AIMessage 北京天气晴朗,25°C。
这就是一次典型的 Function Calling:模型给调用请求,框架执行工具,结果回传,模型再说人话。整个循环由 Agent 替你跑完,不用像工具那一章那样自己写 while True。
24.2 内置工具:少写代码,多接能力
LangChain 生态集成了大量现成工具(官方列表见 integrations/tools)。以 Tavily 搜索为例,先到官网注册拿 API-KEY 写进 .env 的 TAVILY_API_KEY:
from langchain_tavily import TavilySearch
web_search = TavilySearch(max_results=2)
web_search.invoke("请问 2026 年足球世界杯有哪些参赛队?")
# {'query': '...', 'results': [{'url': ..., 'title': ..., 'content': ..., 'score': 0.997}]}
agent = create_agent(model=model, tools=[web_search])
result = agent.invoke({"messages": [
{"role": "user", "content": "请帮我查询 2024 年诺贝尔物理学奖得主是谁?"}
]})
print(result["messages"][-1].content)
内置工具的价值是省掉「定义 + 解析」的活,但代价是黑盒:它的 docstring 你改不了,模型误用时只能靠提示词纠正。
24.3 多个工具:一次响应可以要两个
@tool(parse_docstring=True)
def get_weather(city: str):
"""
天气查询工具
Args:
city: 城市名称
"""
return f"{city} 今天天气挺好"
@tool(parse_docstring=True)
def get_news():
"""
新闻查询工具
"""
return "近期废旧手机回收市场迎来涨价潮。"
agent = create_agent(model, tools=[get_weather, get_news])
response = agent.invoke({
"messages": ["你好,杭州今天的天气如何?今天有哪些新闻?"]
})
模型在一个 AIMessage 里同时给出两个 tool_calls(get_weather(city="杭州") 和 get_news()),框架并行执行、各自回一条 ToolMessage,最后一条 AIMessage 汇总。
工具数量要克制:一般 2–5 个最佳。工具太多,模型在相似描述之间反复犹豫,选错率明显上升。
24.4 图长什么样:ReAct 循环
接上工具后,图会变成 start → model → (tools → model)* → end。在 LangGraph 里 tools 是条件边的一个分支,判断依据就是「这条 AIMessage 里有没有 tool_calls」。
这张图换成实现视角,就是经典的 ReAct 结构——「思考-行动-观察」不断循环:
流程可以概括成一句话:用户问题 → AI 思考 → 调用工具 → 观察结果 → 继续思考 → … → 最终答案。结束条件是某次模型输出里不再包含工具调用。
举个具体例子,用户问「找出当前最流行的无线耳机并检查库存」:
- 输入解析与初始推理:模型判断「要判断‘最流行’,需要最新市场信息,先搜索」;
- 第一次行动与观察:调用
search_products("wireless headphones"),拿到「找到 5 款,Top 结果 WH-1000XM5」; - 迭代推理:认定 WH-1000XM5 就是答案候选,但还缺库存;
- 第二次行动与观察:调用
check_inventory("WH-1000XM5"),得到「库存 10 件」; - 最终输出:信息齐了,生成答案并且不再调用工具。
这五步没有任何一步是你在代码里写的——你只提供了两个工具和一段提示词。
24.5 重试机制:靠约定,不靠魔法
工具失败时 Agent 可以自主重试,但前提是你把失败信息写成模型能看懂的约定。下面这个工具前两次返回固定前缀的错误串,第三次才成功:
from langchain.messages import SystemMessage, HumanMessage
flag = 0
@tool
def get_weather(city: str):
"""
天气查询工具
Args:
city: 城市名称
"""
global flag
flag += 1
if flag < 3:
return "TEMP_UNAVAILABLE: 天气服务暂时不可用,请稍后重试"
return f"{city} 今天天气挺好"
messages = [
SystemMessage("""
你是一个天气助手。
当工具返回以 'TEMP_UNAVAILABLE:' 开头的结果时,
说明是临时故障,不要立即放弃;
你应再次调用同一个工具,最多重试 3 次。
如果 3 次后仍失败,再向用户说明服务暂时不可用。
"""),
HumanMessage("你好,杭州今天的天气如何?"),
]
agent = create_agent(model, tools=[get_weather])
response = agent.invoke({"messages": messages})
输出里能看到三轮 get_weather 调用:前两次拿到 TEMP_UNAVAILABLE,第三次成功,最后给出答案。两个细节值得记:
- 用返回值表达失败,而不是抛异常。抛异常会中断流程(除非中间件接管),返回约定字符串则把决策权交回模型;
- 重试策略必须写在提示词里,包括「什么算临时故障」「最多几次」「几次之后怎么回」。
24.6 常见问题
Agent 怎么选工具?靠工具的 docstring。模型综合问题内容和每个工具的描述,选最匹配的那个。描述是唯一的信息来源,这一点和上一章讲工具时完全一致。
为什么没调用我的工具?三种可能:docstring 不清晰;问题表述模糊;模型判断不需要工具。前两种改描述和提示词,第三种加一句「必须使用工具核实」。
为什么选错工具?多个工具功能描述相似,或者工具太多。解决办法是只给必要的工具、把描述写出明确区分、在 system_prompt 里说明各自的使用场景。
# ❌ 太模糊,模型无法判断
@tool
def tool1(x: str) -> str:
"""做一些事情"""
# ✅ 用途、参数、返回值都写清楚
@tool
def get_weather(city: str) -> str:
"""
获取指定城市的实时天气信息
Args:
city: 城市名称,如 "北京"、"上海"
"""
怎么知道 Agent 完成了?看 AIMessage 有没有 tool_calls,没有就是最终答案:
for msg in response["messages"]:
if isinstance(msg, AIMessage):
if hasattr(msg, "tool_calls") and msg.tool_calls:
print("还在调用工具...")
else:
print("完成!最终答案:", msg.content)
能调用多少次工具?默认没有限制,直到模型给出最终答案。风险是超时、撞上 token 上限,或者模型自己停不下来。要限制就用 recursion_limit:
config = {"recursion_limit": 5} # 最多 5 步
response = agent.invoke({"messages": messages}, config=config)
24.7 小结
接上工具,Agent 才真正成立:调用一次 invoke,内部可能是多轮 ReAct 循环;工具靠 docstring 被选中,数量控制在 2–5 个;失败重试要用「返回值 + 提示词约定」来实现,而不是依赖框架猜你的意图;要防止无限循环,用 recursion_limit。
下一篇讲 Agent 的三个高级开关:给 Agent 起名字、写系统提示词,以及最容易被低估的——让 Agent 输出结构化数据。
上一篇:Agent 是什么——create_agent 与第一张图;系列第一篇:为什么需要 LangChain。
本文根据公开课程课件整理,并结合 LangChain 官方文档核对与补充。