工具集成
工具是 agent 与世界的接口。LangChain 的工具体系核心考点:怎么定义工具、调用链怎么走、描述为什么决定成败。
Tool 定义
from langchain.tools import tool
@tool
def search_products(keyword: str, limit: int = 10) -> list[dict]:
"""按关键词搜索商品。
Args:
keyword: 商品关键词
limit: 返回数量上限
"""
...三种定义方式:
| 方式 | 适用 |
|---|---|
@tool 装饰函数 |
默认选择,参数签名即 schema |
继承 BaseTool |
需要自定义 name/description/错误处理 |
| 从 Pydantic 模型构造 | 复杂参数结构(@tool(args_schema=...)) |
参数类型注解(str/int/list)自动转成模型的工具 schema(JSON Schema),模型据此生成调用参数。类型和默认值就是协议。
工具调用流程
工具调用: 模型只发请求, 框架负责执行和回填
- 模型输出
tool_calls(工具名 + 参数 JSON) - 框架按名字路由到对应工具函数执行
- 结果包装成 ToolMessage(带 tool_call_id),回填对话历史
- 模型看到结果,决定继续调用还是直接回答
关键点:模型不直接执行工具,只生成调用请求。执行权在 harness,这保证了可控性和审计。
描述质量:决定成败
工具描述是给模型看的说明书,质量直接决定调用准确率:
| 好描述 | 坏描述 |
|---|---|
| 说清做什么、何时用 | “查询函数” |
| 参数说明边界和单位 | 参数含义靠猜 |
| 给出典型用法例子 | 无例子 |
| 说清返回值 | 返回格式不明 |
工程建议:
- 写“何时用”:模型靠描述做路由决策,描述像搜索引擎的索引
- 参数默认值给安全值:limit 默认 10 防大查询
- 工具数量控制:几十个工具时模型选择困难,考虑分组/子代理(见 DeepAgents 子代理篇)
- 错误处理在工具内:工具返回友好错误消息,模型能据此重试或换方案
工具与 MCP
LangChain 支持把 MCP 服务器直接当工具用(langchain-mcp-adapters):
- 一个 MCP 服务器暴露多个工具,自动转换
- 复用已有 MCP 生态(数据库、浏览器、GitHub 工具)
- 与自定义 @tool 混用,同一循环内调用
面试追问
- 工具怎么定义? @tool 装饰函数,参数签名和类型注解自动转成模型 schema。描述即模型的路由依据
- 模型直接执行工具吗? 不。模型只输出 tool_calls 请求,框架路由执行并把结果回填。执行权在 harness
- ToolMessage 为什么带 tool_call_id? 关联模型发起的调用和工具结果,多工具并发调用时能一一对应
- 描述怎么写好? 说清做什么、何时用、参数边界、返回格式。给例子。描述差模型就不调或调错
- MCP 和自定义工具的关系? MCP 服务器是现成工具源(适配器接入),自定义 @tool 是自有能力,两者在同一循环里混用