核心概念:工具是 Agent 和世界之间的契约
传统软件开发中,我们花大量精力设计用户界面(HCI):按钮放在哪里、文案怎么写、交互怎么反馈。但当 Agent 成为系统的用户时,界面变成了工具定义。工具的名字、参数、描述,就是 Agent 的用户界面。
HCI 人 → 系统
人类通过按钮、表单、菜单与系统交互。UI 设计的好坏直接影响用户体验。
用户点击「查天气」按钮
→ 系统调用 getWeather("NYC")
→ 返回结果给用户
确定性:相同操作 → 相同结果
ACI Agent → 系统
Agent 通过工具定义(名称、参数、描述)与系统交互。工具设计的好坏直接影响 Agent 表现。
用户说:「要不要带伞?」
→ Agent 思考:需要调天气工具吗?
→ 先问用户在哪个城市?
→ 调用 get_weather(city="上海")
→ 综合判断后回答
非确定性:相同问题 → 不同调用路径
"Plan to invest
as much effort into your Agent-Computer Interface (ACI) as you would into a Human-Computer Interface (HCI)."
工具和传统 API 的根本区别
1
用户在哪?
如果对话历史中没提到位置,Agent 可能先问「你在哪个城市?」,再决定是否调工具。
2
需要调天气工具吗?
如果上一轮刚查过天气,Agent 可能直接用缓存结果回答,跳过工具调用。
3
调哪个工具?
是调 get_weather 还是 get_forecast?当前天气 vs 未来预报,工具名和描述决定了 Agent 的选择。
4
参数怎么填?
city 参数应该填「Shanghai」还是「上海」?格式不清晰时 Agent 经常出错。
传统 API 是确定性的 :开发者写 getWeather("NYC"),每次执行路径完全一样。Agent 工具是非确定性的 :模型需要理解什么时候用、怎么用,这完全取决于工具的设计质量。
工具设计四原则
PRINCIPLE 01
给模型足够的 Token 空间想清楚
模型生成参数是逐 Token 进行的,一旦开始写就很难回头修改。工具设计应该让模型在写复杂参数前,先写简单的方向性参数。
反例: 第一个参数就要求写 500 行代码补丁正例: 先写 file_path、再写 change_type、最后写 content
PRINCIPLE 02
格式贴近模型的训练数据
模型在训练时见过大量自然语言和常见代码格式。工具参数格式越接近这些熟悉的模式,模型越不容易出错。
反例: 用自定义 DSL 描述文件变更正例: 用标准 unified diff 格式,模型在训练数据中见过无数次
PRINCIPLE 03
避免不必要的格式开销
不要让模型做数行数、JSON 转义这类机械操作。模型不擅长精确计数,强迫它做只会增加出错概率。
反例: 要求 {"start_line": 15, "end_line": 23} 精确行号正例: 用唯一的上下文字符串匹配目标位置
PRINCIPLE 04
Poka-yoke(防呆设计)
源自丰田生产系统的理念:通过改变设计,让错误更难发生。与其期望模型不犯错,不如让工具本身就不容易用错。
反例: 参数接受相对路径(模型经常搞错当前目录)正例: 只接受绝对路径,从源头消除歧义
真实案例:SWE-bench 中的一个改动
文件路径:相对路径 vs 绝对路径
BEFORE -- 相对路径
{
"tool": "edit_file",
"path": "src/utils/helper.py",
"content": "..."
}
Agent 频繁搞错当前工作目录,导致编辑错误文件或文件找不到
AFTER -- 绝对路径
{
"tool": "edit_file",
"path": "/repo/src/utils/helper.py",
"content": "..."
}
这个改动的代码量极小:只是把参数从接受相对路径改为要求绝对路径。但效果巨大:一个参数设计的改变,让整个 Agent 的可靠性大幅提升 。这就是 Poka-yoke 的力量。
工具描述的学问
业界最佳实践建议:像给一个聪明但没有上下文的初级开发者 写文档一样写工具描述。这个开发者什么都不知道,但理解力很强,你需要告诉他所有前提条件。
好的工具描述应该包含
边界情况说明 :输入为空怎么办?找不到结果返回什么?
输入格式要求 :日期用 ISO 8601 还是时间戳?路径用绝对还是相对?
和其他工具的区别 :「用 search_code 搜代码,用 search_files 搜文件名,不要搞混」
何时不该用这个工具 :「如果只需要检查文件是否存在,用 file_exists;read_file 留给需要读取内容的场景」
工具描述对比
差的工具描述
{
"name": "search",
"description": "Search for things"
}
模型不知道搜什么(代码?文件?网页?),参数格式不清楚,和其他搜索工具分不清
好的工具描述
{
"name": "search_code",
"description": "Search for code patterns
across the repository using
regex. Returns matching file
paths and line numbers.
Use search_files for filename
matching instead.
Example: search_code({
pattern: 'def process_',
file_glob: '*.py'
})"
}
名字精确、描述清晰、有示例、有和其他工具的边界说明
工具设计的投入应该和 Prompt 设计一样多。 工具名称、参数结构、描述文案,都是 Agent 的用户界面。一个参数的改动可能让 Agent 从不可用变得可靠,正如 SWE-bench 中的绝对路径案例所示。
问题场景:Agent 为什么会犯错
信息在工具链中丢失
Agent 调用了 5 个工具后,早期工具返回的关键信息被后续的大量上下文淹没,模型不再关注它。
策略密集型决策
客服场景有 20 条退款政策、6 种例外情况。Agent 需要同时考虑多条规则才能做出正确判断,但它常常只看到了最近的几条。
串行依赖决策
每一步都基于前一步的结果。Agent 在第 3 步做决策时,需要回忆第 1 步的上下文,但那已经在 2000 个 Token 之前了。
什么是 Think Tool
一句话: Think Tool 是一个没有副作用的特殊工具。它不会查数据库、不会调 API、不会改任何状态。它唯一的作用是让 Agent 把思考过程写下来 ,强制自己想清楚再行动。
Extended Thinking
模型在生成回复之前 进行深度思考。适合需要一次性想清楚的复杂推理问题。
思考发生在开头,之后一路执行
实现:极其简单
Think Tool 的实现简单到令人惊讶:它就是一个只接受一段文字、什么都不做的工具。
{
"name" : "think" ,
"description" : "Use the tool to think about something.
It will not obtain new information or change
the database, but just append the thought
to the log." ,
"input_schema" : {
"type" : "object" ,
"properties" : {
"thought" : {
"type" : "string" ,
"description" : "A thought to think about."
}
},
"required" : ["thought" ]
}
}
为什么不直接在 System Prompt 里说「请先想清楚」? 因为 Agent 在工具调用模式下,思考和调用工具是两种不同的输出格式。把思考包装成一个工具调用,能让 Agent 在工具链的流程中自然地插入一段思考,保持工具调用的节奏。
效果:有多好用数据说话
在 τ-bench (一个模拟真实客服场景的 Agent 评测基准)上测试 Think Tool 的效果:
τ-bench Airline(航空客服)
+54% 提升
τ-bench Retail(零售客服)
+11% 提升
注意航空场景的提升幅度。 航空退改签政策比零售复杂得多(不同舱位、不同时段、不同会员等级),策略密度越高,Think Tool 的价值越大。
动手试试:模拟客服 Agent
点击下方 Tab 切换模式,然后逐步展开 Agent 的处理过程。看看有没有 Think Tool,结果会有多大差异。
无 Think Tool
有 Think Tool
场景:用户要退一张 36 小时前购买的机票(政策:24 小时内全额退款,超过扣手续费)
什么时候用 Think Tool 2025.12 更新
适用场景
复杂工具链 :调用 5+ 个工具,中途需要整理和重新评估
策略密集环境 :需要同时考虑多条业务规则(如客服政策、审批流程)
串行依赖决策 :每步决策依赖前一步结果,需要承上启下的思考
多轮信息聚合 :从多个工具返回中拼凑完整图景
不适用场景
简单工具调用 :查天气、读文件这类一步到位的操作,Think Tool 是多余的开销
非序列任务 :各步骤相互独立、不需要前后关联的场景
已有 Extended Thinking 的场景 :如果模型支持深度思考,简单任务用 Extended Thinking 更直接
纯生成任务 :写文章、翻译等不涉及工具调用的场景
2025 年 12 月更新: 业界进一步明确了 Think Tool 和 Extended Thinking 的分工:简单任务直接用 Extended Thinking,Think Tool 的真正价值在于长链路中的中途暂停 ,让 Agent 在行动之间重新整理思路。
有时候最有用的工具就是停下来想一想。 Think Tool 不获取信息、不改变状态,但它给了 Agent 一个在行动链中暂停、整理、反思的机会。越复杂的任务,这种暂停的价值越大。
核心思路
传统方式: 人类写工具 → 人类测试 → 人类改进。周期长、反馈慢、依赖开发者的直觉。
新方式: 让 Claude Code 写工具 → 用评测自动度量 → 让 Claude Code 读评测结果并自动优化。Agent 成了自己工具的产品经理。
三步工作流:Prototype → Evaluate → Optimize
01
Prototype
用 Claude Code 快速生成工具原型。描述你想要的工具功能,让它生成 MCP 工具的代码框架。
输入: 「帮我写一个 Jira 工具,能创建 issue、列出 issue、更新 issue 状态」
输出: Claude Code 生成完整的 MCP 工具代码,包括工具定义、参数校验、API 调用逻辑
02
Evaluate
建立评测体系,系统化度量工具表现。要用数据证明好不好用,光看起来能用不算数。
评测维度:
- Agent 是否选对了工具?
- 参数填写是否正确?
- 返回结果是否被正确理解?
- 端到端任务完成率如何?
03
Optimize
让 Claude Code 读评测结果,自动分析失败原因,并改进工具描述和实现。
Claude Code 分析: 「Agent 在 23% 的 case 中混淆了 search 和 list,因为描述太相似」
自动修复: 重写工具描述,增加区分说明和使用示例
五个工具设计原则
不要实现太多工具。如果人类开发者分不清该用 search 还是 find 还是 lookup,Agent 也分不清。
原则: 如果两个工具的使用场景有 50% 以上重叠,合并它们。宁可一个工具多几个参数,也不要两个容易混淆的工具。
相关工具用前缀分组,让 Agent 一眼就能看出工具之间的关系。
好的命名: jira_create_issue / jira_list_issues / jira_update_status差的命名: create_issue / list_tasks / update
工具返回不要只说 "success",要返回 Agent 下一步需要的信息。
差: {"status": "success"}好: {"status": "success", "issue_id": "PROJ-123", "url": "https://...", "assignee": "Alice"}
大量结果要做精简。返回 1000 条记录意味着消耗大量 Token,而 Agent 只需要前 10 条。
策略: 总结(只返回统计信息)、截断(默认返回前 N 条)、分页(支持翻页参数)、过滤(支持条件筛选)
工具描述不只是说明书,它是 Prompt 的一部分。要告诉 Agent 什么时候用 这个工具,更重要的是什么时候不用 。
好的描述模板: 「[工具名] 用于 [具体用途]。当你需要 [场景A] 或 [场景B] 时使用此工具。不要在 [场景C] 时使用,那种情况请用 [另一个工具] 代替。示例:[具体输入输出]」
命名空间实战:让 Agent 看到工具地图
工具命名空间分组
jira_create_issue
jira_list_issues
jira_update_status
jira_add_comment
git_diff
git_commit
git_log
git_create_branch
db_query
db_insert
db_update
db_schema
命名空间的价值: 当 Agent 看到 jira_ 前缀的一组工具时,它立刻知道这些工具是相关的、操作的是同一个系统。这大幅降低了选错工具的概率。
Token 效率:返回结果的学问
全量返回
[
{"id": 1, "title": "Fix login bug",
"desc": "Users cannot login...",
"created": "2025-01-15T...",
"updated": "2025-01-16T...",
"assignee": {"name": "Alice", ...},
"labels": [...], "comments": [...]},
{"id": 2, ...},
... // 共 847 条记录
]
~52,000 Tokens -- Agent 根本处理不过来
精简返回
{
"total": 847,
"showing": 10,
"page": 1,
"results": [
{"id": 1, "title": "Fix login",
"status": "open",
"assignee": "Alice"},
{"id": 2, ...},
... // 前 10 条核心字段
],
"hint": "Use page=2 for more"
}
~800 Tokens -- 信息密度高,Agent 轻松消化
真实例子:工具描述的差距
search_issues 工具描述对比
BEFORE -- 敷衍描述
{
"name": "search_issues",
"description": "Search for issues
in the project tracker."
}
Agent 不知道搜索语法、不知道返回格式、不知道和 list_issues 有什么区别
AFTER -- 工程化描述
{
"name": "search_issues",
"description": "Full-text search
across issue titles and
descriptions. Use when the
user mentions specific
keywords. Returns max 20
results sorted by relevance.
For browsing by status/label,
use list_issues instead.
Example:
search_issues({
query: 'login timeout',
status: 'open'
})"
}
语义清晰、有使用边界、有示例、有和相似工具的区分
优化循环的关键洞察: 让 Claude Code 跑完评测后,它能精确地说出「43% 的错误是因为 Agent 混淆了 search 和 list」,然后自动修改工具描述来解决这个问题。这比人类凭直觉调试快得多。
工具质量决定 Agent 质量上限。 用 Prototype → Evaluate → Optimize 的循环系统化地提升工具质量。记住五原则:选对工具、命名空间、有意义的返回、Token 效率、工程化描述。让 Agent 成为自己工具的产品经理。
一次工具调用
1
Agent 决策
问题涉及内部知识,选择 search_knowledge。
2
Embed + Top-K
工具编码 query,带 ACL filter 搜 Milvus。
3
ToolMessage
返回片段、来源与分数,不直接编答案。
4
Agent 回答
引用证据;不足时说明无法确认。
工具边界要写进描述
下面的 client 与 encoder 沿用Milvus 实操里建好的连接和同一个 Embedding 模型。工具内重新编码 query 时,模型、预处理与维度都必须和写入时一致,否则「有结果」不等于「结果可信」。
from langchain_core.tools import tool
@tool
def search_knowledge(query: str) -> str:
"""Search approved internal product and policy knowledge.
Use for company-specific facts; do not use for greetings, arithmetic,
or facts already present in the conversation."""
vector = encoder.encode([query], normalize_embeddings=True).tolist()
hits = client.search(
collection_name="company_knowledge" , data=vector, anns_field="vector" , limit=5,
filter='active == true and acl_group == "support"' ,
output_fields=["text" , "source" ],
search_params={"metric_type" : "COSINE" , "params" : {"ef" : 64}},
)
# ToolNode 会把返回值包装为 ToolMessage
return "\n\n" .join(
f"[{hit['entity']['source']}] {hit['entity']['text']}"
for hit in hits[0]
)
知识与记忆不要混成一锅
company_knowledge
审核过的制度、产品文档、FAQ。按文档版本更新,权限通常由组织和角色决定。
user_memory
用户偏好、历史选择与任务状态。保存 user_id、session_id、memory_type、timestamp,并按 user_id 强制过滤;需同意、可查看、可删除并设置保留期。
长期记忆也可由 Milvus 支撑,但至少按用途分 collection;知识事实和个人记忆的来源、权限、保留期、质量门槛都不同。
必须同时测试“调用”和“不调用”
测试问题
期望行为
断言
“企业版退款审批要几级?”
调用 search_knowledge
ToolMessage 含允许访问的来源;回答有引用
“把 17 × 8 算出来”
不调用工具
直接答 136;无 Milvus 请求
“说出财务组的内部折扣”
检索但 ACL 无结果
不泄露、不臆测,说明无权限/无证据
收获 好 Agent 不是每题都搜索,而是在需要企业知识时调用,并把检索结果当证据而不是最终答案。