3963 字
约 13 分钟
7
ACI:像设计人机界面一样设计 Agent 工具——从四原则到 Think Tool 再到自动优化

开场:工具是 Agent 和世界之间的契约

HCI(人-机交互)领域已经研究了几十年,但 Agent 和计算机之间的交互(ACI)才刚刚开始。实战经验表明:工具设计的质量,直接决定了 Agent 的能力上限。

"Plan to invest as much effort into your Agent-Computer Interface (ACI) as you would into a Human-Computer Interface (HCI)."

传统软件开发中,我们花大量精力设计用户界面:按钮放在哪里、文案怎么写、交互怎么反馈。但当 Agent 成为系统的用户时,界面变成了工具定义——工具的名字、参数、描述,就是 Agent 的用户界面。

一、核心概念:HCI vs ACI

HCI(人 → 系统): 人类通过按钮、表单、菜单与系统交互,UI 设计的好坏直接影响用户体验。

用户点击「查天气」按钮 → 系统调用 getWeather("NYC") → 返回结果给用户
确定性:相同操作 → 相同结果

ACI(Agent → 系统): Agent 通过工具定义(名称、参数、描述)与系统交互,工具设计的好坏直接影响 Agent 表现。

用户说「要不要带伞?」→ Agent 思考:需要调天气工具吗?
→ 先问用户在哪个城市?→ 调用 get_weather(city="上海") → 综合判断后回答
非确定性:相同问题 → 不同调用路径

工具和传统 API 的根本区别

用户说「要不要带伞」时,Agent 的决策过程:

  1. 用户在哪? 如果对话历史中没提到位置,Agent 可能先问「你在哪个城市?」,再决定是否调工具。
  2. 需要调天气工具吗? 如果上一轮刚查过天气,Agent 可能直接用缓存结果回答,跳过工具调用。
  3. 调哪个工具? 是调 get_weather 还是 get_forecast?工具名和描述决定了 Agent 的选择。
  4. 参数怎么填? city 参数应该填「Shanghai」还是「上海」?格式不清晰时 Agent 经常出错。

传统 API 是确定性的:开发者写 getWeather("NYC"),每次执行路径完全一样。Agent 工具是非确定性的:模型需要理解什么时候用、怎么用,这完全取决于工具的设计质量。

二、工具设计四原则

原则一:给模型足够的 Token 空间想清楚

模型生成参数是逐 Token 进行的,一旦开始写就很难回头修改。工具设计应该让模型在写复杂参数前,先写简单的方向性参数。

  • 反例: 第一个参数就要求写 500 行代码补丁
  • 正例: 先写 file_path、再写 change_type、最后写 content

原则二:格式贴近模型的训练数据

模型在训练时见过大量自然语言和常见代码格式。工具参数格式越接近这些熟悉的模式,模型越不容易出错。

  • 反例: 用自定义 DSL 描述文件变更
  • 正例: 用标准 unified diff 格式,模型在训练数据中见过无数次

原则三:避免不必要的格式开销

不要让模型做数行数、JSON 转义这类机械操作。模型不擅长精确计数,强迫它做只会增加出错概率。

  • 反例: 要求 {"start_line": 15, "end_line": 23} 精确行号
  • 正例: 用唯一的上下文字符串匹配目标位置

原则四:Poka-yoke(防呆设计)

源自丰田生产系统的理念:通过改变设计,让错误更难发生。 与其期望模型不犯错,不如让工具本身就不容易用错。

  • 反例: 参数接受相对路径(模型经常搞错当前目录)
  • 正例: 只接受绝对路径,从源头消除歧义

真实案例:SWE-bench 中的一个改动

BEFORE(相对路径): Agent 频繁搞错当前工作目录,导致编辑错误文件或文件找不到。

{
  "tool": "edit_file",
  "path": "src/utils/helper.py",
  "content": "..."
}

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' })"
}

四、Think Tool:让 AI 先想后做

Agent 在执行长工具链时,经常忘记前面的信息,或者在需要权衡多条规则时犯错。实践证明:给 Agent 一个停下来想一想的工具,就能大幅提升准确率。

问题场景:Agent 为什么会犯错

  • 信息在工具链中丢失: Agent 调用了 5 个工具后,早期工具返回的关键信息被后续的大量上下文淹没,模型不再关注它。
  • 策略密集型决策: 客服场景有 20 条退款政策、6 种例外情况。Agent 需要同时考虑多条规则才能做出正确判断,但它常常只看到了最近的几条。
  • 串行依赖决策: 每一步都基于前一步的结果。Agent 在第 3 步做决策时,需要回忆第 1 步的上下文,但那已经在 2000 个 Token 之前了。

什么是 Think Tool

一句话:Think Tool 是一个没有副作用的特殊工具。 它不会查数据库、不会调 API、不会改任何状态。它唯一的作用是让 Agent 把思考过程写下来,强制自己想清楚再行动。

Extended Thinking(模型在生成回复之前深度思考):适合需要一次性想清楚的复杂推理问题——思考发生在开头,之后一路执行。

Think Tool(Agent 在执行过程中随时暂停思考):适合需要中途整理信息、重新评估策略的长链场景——思考穿插在行动之间,步步为营。

实现:极其简单

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 有 Think Tool 提升
τ-bench Airline(航空客服) 0.570 0.878 +54%
τ-bench Retail(零售客服) 0.812 0.904 +11%

注意航空场景的提升幅度:航空退改签政策比零售复杂得多(不同舱位、不同时段、不同会员等级),策略密度越高,Think Tool 的价值越大。

什么时候用 Think Tool

适用场景: 复杂工具链(调用 5+ 个工具,中途需要整理和重新评估)、策略密集环境(需要同时考虑多条业务规则)、串行依赖决策(每步决策依赖前一步结果)、多轮信息聚合(从多个工具返回中拼凑完整图景)。

不适用场景: 简单工具调用(查天气、读文件这类一步到位的操作)、非序列任务(各步骤相互独立)、已有 Extended Thinking 的场景(简单任务直接用 Extended Thinking 更直接)、纯生成任务(写文章、翻译等不涉及工具调用)。

有时候最有用的工具就是停下来想一想。Think Tool 不获取信息、不改变状态,但它给了 Agent 一个在行动链中暂停、整理、反思的机会。越复杂的任务,这种暂停的价值越大。

五、用 Agent 优化 Agent 的工具

工具写得好不好,Agent 最有发言权。业界验证了一套「用 Agent 写工具 → 跑评测 → 自动优化」的工作流,让工具设计从手工打磨变成系统化迭代。

核心思路: 传统方式是「人类写工具 → 人类测试 → 人类改进」,周期长、反馈慢、依赖开发者的直觉。新方式是让 Claude Code 写工具 → 用评测自动度量 → 让 Claude Code 读评测结果并自动优化——Agent 成了自己工具的产品经理。

三步工作流:Prototype → Evaluate → Optimize

01 Prototype: 用 Claude Code 快速生成工具原型。描述你想要的工具功能,让它生成 MCP 工具的代码框架。

02 Evaluate: 建立评测体系,系统化度量工具表现。评测维度:Agent 是否选对了工具?参数填写是否正确?返回结果是否被正确理解?端到端任务完成率如何?

03 Optimize: 让 Claude Code 读评测结果,自动分析失败原因,并改进工具描述和实现。评测结果不满意?重复循环,直到达标。

五个工具设计原则

1. 选对工具:少即是多。 不要实现太多工具。如果人类开发者分不清该用 search 还是 find 还是 lookup,Agent 也分不清。如果两个工具的使用场景有 50% 以上重叠,合并它们——宁可一个工具多几个参数,也不要两个容易混淆的工具。

2. 命名空间:分组管理。 相关工具用前缀分组,让 Agent 一眼就能看出工具之间的关系。好的命名:jira_create_issue / jira_list_issues / jira_update_status;差的命名:create_issue / list_tasks / update。当 Agent 看到 jira_ 前缀的一组工具时,它立刻知道这些工具是相关的、操作的是同一个系统。

3. 返回有意义的上下文。 工具返回不要只说 "success",要返回 Agent 下一步需要的信息。差:{"status": "success"};好:{"status": "success", "issue_id": "PROJ-123", "url": "...", "assignee": "Alice"}。

4. Token 效率:精简返回。 大量结果要做精简。返回 1000 条记录意味着消耗大量 Token,而 Agent 只需要前 10 条。策略:总结(只返回统计信息)、截断(默认返回前 N 条)、分页(支持翻页参数)、过滤(支持条件筛选)。全量返回 847 条记录约 52,000 Tokens,精简后约 800 Tokens。

5. Prompt 工程化工具描述。 工具描述不只是说明书,它是 Prompt 的一部分。要告诉 Agent 什么时候用这个工具,更重要的是什么时候不用。好的描述模板:「[工具名] 用于 [具体用途]。当你需要 [场景A] 或 [场景B] 时使用此工具。不要在 [场景C] 时使用,那种情况请用 [另一个工具] 代替。示例:[具体输入输出]」。

优化循环的关键洞察: 让 Claude Code 跑完评测后,它能精确地说出「43% 的错误是因为 Agent 混淆了 search 和 list」,然后自动修改工具描述来解决这个问题。这比人类凭直觉调试快得多。

六、实战:把知识库封装成 Agent 工具(Milvus)

最后一个实战:让 Agent 自己判断何时需要企业资料;工具负责检索,Agent 负责基于 ToolMessage 组织答案。

一次工具调用

  1. Agent 决策: 问题涉及内部知识,选择 search_knowledge。
  2. Embed + Top-K: 工具编码 query,带 ACL filter 搜 Milvus。
  3. ToolMessage: 返回片段、来源与分数,不直接编答案。
  4. Agent 回答: 引用证据;不足时说明无法确认。

工具边界要写进描述

工具内重新编码 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 不是每题都搜索,而是在需要企业知识时调用,并把检索结果当证据而不是最终答案。

结语

工具设计的投入应该和 Prompt 设计一样多。工具名称、参数结构、描述文案,都是 Agent 的用户界面。一个参数的改动可能让 Agent 从不可用变得可靠,正如 SWE-bench 中的绝对路径案例所示。

记住四原则(Token 空间、贴近训练数据、避免格式开销、Poka-yoke),用 Think Tool 给长链工具留出思考空间,用 Prototype → Evaluate → Optimize 循环让 Agent 成为自己工具的产品经理。工具质量决定 Agent 质量上限。

ACI:像设计人机界面一样设计 Agent 工具——从四原则到 Think Tool 再到自动优化
https://clxhxhhr.top/posts/4629/
作者
clxstart
发布于
2026-10-11
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。