工具封装与 Agent 构建 — 知识汇总
主题:LangChain工具封装与Agent构建 + 智能体多工具调用机制
整体架构总览

1.1 城市编码动态获取
将原本硬编码的城市标识修改为动态参数 city_code。用户输入城市名(如"北京"、"太原")后,先调用 get_city_code 函数获取对应的城市编码,再将该编码传递给天气 API 进行查询,从而实现不同城市天气的动态获取。
1.2 响应数据精简优化
原始 API 返回的 JSON 数据冗余较多,需提取关键字段:实时温度和天气状况。通过 response.json() 获取数据,依据官网文档层级结构,从 data['result']['real_time'] 中提取具体数值。最终输出格式化为简洁字符串,例如:"天气晴,温度 XX 度",提升信息可读性。
二、LangChain 工具注册机制

2.1 @tool 装饰器的作用
普通 Python 函数无法被 LangChain 智能体直接识别,必须使用 @tool 装饰器进行修饰。装饰后,智能体可通过内省机制读取函数的功能描述、参数定义等元数据,从而理解该工具的用途及调用方式。

from langchain.tools import tool
@tool
def get_weather(city: str) -> str:
"""调用天气查询接口,实时查询城市的天气"""
# ... 函数实现
return f"天气:{wd}, 温度:{temp}摄氏度"
关键要点:
@tool 装饰器自动解析函数元数据,生成标准 JSON Schema
Schema 包含:函数名称、功能描述(docstring)、参数名称与类型、必填项标识
docstring 内容会被大模型读取,用于判断何时调用该工具
函数必须有 docstring,否则报 ValueError
装饰后函数变为 StructuredTool 对象,不能用 () 直接调用,需用 .invoke()
三、智能体执行工作流
智能体调用工具的核心流程分三步:
意图解析。 接收用户输入的 Prompt 需求,智能体分析并解析用户的具体意图。
工具匹配。 根据解析后的意图判断是否需要调用工具,在已注册的工具列表中匹配最合适的工具。
执行与反馈。 执行对应的 Python 函数获取天气数据结果,将执行结果整合进最终回复,返回给用户。
四、单工具与多工具调用
4.1 单工具调用

from langgraph.prebuilt import create_react_agent
tools = [get_weather]
agent = create_react_agent(chat, tools)
result = agent.invoke({"messages": [("user", "今天上海天气怎么样?")]})
print(result["messages"][-1].content)
4.2 多工具调用
只需将新工具名称添加到现有的工具序列中即可,智能体支持注册多个工具(2个、5个或10个以上)。

tools = [get_weather, cheng]
agent = create_react_agent(chat, tools)
questions = ["今天上海天气怎么样", "2乘3等于几"]
for q in questions:
result = agent.invoke({"messages": [("user", q)]})
print(f"Q: {q}")
print(f"A: {result['messages'][-1].content}\n")
确保内存中存在对应的函数定义是工具被成功调用的前提条件。
五、多场景综合测试验证


构建包含三个问题的测试集:

结论: 系统能准确区分可调用工具的任务与不可处理的任务,并给出相应响应。智能体应具备清晰的工具边界意识,对于未注册工具对应的需求,应优雅降级为文本建议而非强行调用或报错。
六、智能体代码实现细节

7.1 关键组件导入与创建
导入 create_react_agent 用于创建智能体(langgraph 包) LLM 对象:复用之前配置的大语言模型实例(如智谱 AI 模型) 工具列表:将已装饰的函数封装为列表传入
7.2 提示词模板
create_react_agent 内部已内置 ReAct prompt,不需要手动 hub.pull。ReAct 范式定义了推理与行动的循环逻辑:思考 → 选择工具 → 观察结果 → 再思考 → 最终回答。
7.3 执行日志查看
遍历 result["messages"] 可查看完整执行过程:
for msg in result["messages"]:
print(f"[{type(msg).__name__}] {msg.content if msg.content else msg.tool_calls}")
输出示例:
[HumanMessage] 今天上海天气怎么样?
[AIMessage] tool_calls=[{'name': 'get_weather', 'args': {'city': '上海'}}]
[ToolMessage] 天气:晴,温度:28摄氏度
[AIMessage] 上海今天晴,温度28°C
七、常见问题排查与核心要点

变量未定义错误: 确保工具函数在调用前已正确定义并导入,tools 列表需在 create_react_agent 之前定义。
索引越界错误:检查输入数据的结构,确保 Prompt 输入格式正确(如使用字典 {"messages": [...]} 而非纯字符串),避免解析失败导致的下标越界。
参数类型错误: 模型可能将参数封装为字符串而非独立类型,采用"宽松输入+内部解析"策略(如接收字符串后正则提取)比强制要求模型输出特定 JSON 格式更具鲁棒性。
工具抽象的重要性:将业务逻辑(如天气查询)封装为标准化工具并通过装饰器注册,是实现 LLM 能力扩展的关键,使得模型能像调用本地函数一样使用外部服务。
Prompt 模板化策略:create_react_agent 内置 ReAct 模板,大幅降低构建复杂 Agent 的门槛,同时保证推理逻辑的稳定性。
调试可视化的必要性:在 Agent 开发初期,通过遍历 messages 观察模型的"思考链"(Chain of Thought),是定位工具调用失败或逻辑偏差的最有效手段。
接口兼容性设计:当 LLM 无法精确传递结构化参数时,采用"宽松输入+内部解析"的策略(如接收字符串后正则提取)比强制要求模型输出特定 JSON 格式更具鲁棒性。
工具边界管理:智能体应具备清晰的工具边界意识,对于未注册工具对应的需求,应优雅降级为文本建议而非强行调用或报错。
夜雨聆风