根据共享对话中的第二、第三个问题整理。内容聚焦于:Tool 为什么由 Agent 框架执行、Tool Schema 如何描述工具、工具很多时由谁筛选,以及 Tools Retrieval 如何工作。

核心结论

模型负责决定“做什么”,Agent 框架负责真正“去做”。

大模型本质上接收 Context,并输出文本或结构化数据。模型生成的 Tool Call 不是执行结果,而是一份调用请求:它说明希望调用哪个工具、传入哪些参数。Agent 框架收到请求后,负责校验、执行,并把工具结果放回 Context,再次调用模型生成最终回答。

因此,Tool Calling 并没有让模型本身获得执行能力,而是为模型提供了选择和编排外部能力的接口

一次完整的 Tool Calling 流程

以查询北京天气为例:

用户询问北京天气

Agent 框架把用户消息和可用 Tool Schema 发给模型

模型返回工具调用请求:get_weather(city="北京")

Agent 框架校验参数、权限和风险,并执行真实函数或 API

框架把天气结果作为 tool 消息加入 Context

模型读取工具结果,组织成自然语言回答

Agent 框架把最终答案展示给用户

模型返回的请求可能类似:

{
  "name": "get_weather",
  "arguments": {
    "city": "北京"
  }
}

框架执行工具后,将结果重新加入消息:

{
  "role": "tool",
  "name": "get_weather",
  "content": "{\"temperature\": 31, \"condition\": \"\"}"
}

这一步之后,模型才能基于真实天气数据组织最终答复。

为什么必须由 Agent 框架执行

1. 能力边界

模型通常运行在独立的推理服务中,只负责理解、推理和生成,本身不能直接访问应用所处的网络、数据库、文件系统或内部业务服务。真实环境中的能力必须由应用程序或 Agent 框架提供。

2. 安全控制

模型提出调用请求,并不意味着框架应该无条件执行。框架可以在执行前后加入确定性的控制:

  • 参数校验
  • 身份认证和权限检查
  • 用户确认
  • 超时与重试控制
  • 调用次数限制
  • 日志与审计
  • 敏感或高风险操作拦截

例如,模型请求转账时,框架仍需检查用户身份、余额和权限,并在实际执行前取得明确确认。

3. 结果的确定性

大模型擅长理解意图和组织语言,但不适合承担要求精确、可重复的操作。天气查询、数学计算、数据库查询、订单修改等工作应交给确定性程序完成,模型负责选择这些能力并解释结果。

Tool 在 Agent 框架中的存在形式

一个 Tool 通常由两部分组成:

  1. 可执行函数:真正访问 API、数据库、文件系统或其他服务。
  2. Tool Schema:告诉模型工具的名称、用途、参数和参数类型。

例如,框架中可能有一个真实函数:

def get_weather(city: str) -> dict:
    return weather_api.query(city)

同时向模型提供它的 Schema:

{
  "name": "get_weather",
  "description": "查询指定城市当前的天气",
  "parameters": {
    "type": "object",
    "properties": {
      "city": {
        "type": "string",
        "description": "城市名称,例如北京"
      }
    },
    "required": ["city"]
  }
}

模型通常只看到 Schema,不需要看到函数内部实现。Agent 框架则维护名称到函数的映射:

tools = {
    "get_weather": get_weather,
    "search_web": search_web,
    "query_database": query_database,
}
 
tool_function = tools[tool_call.name]
result = tool_function(**tool_call.arguments)

深入理解 Tool Schema

Tool Schema 是模型与真实程序之间的接口契约。它既用于约束参数,也直接影响模型能否选对工具、填对参数。不同平台的包装格式略有区别,但核心信息通常包括:

字段作用设计重点
name稳定、唯一的程序标识使用明确的动宾结构,如 orders_get_detail
description告诉模型何时以及为何使用写清适用条件、边界和容易混淆的相邻工具
parameters / inputSchema用 JSON Schema 描述输入明确类型、必填项、枚举、格式和字段含义
outputSchema描述结构化输出便于后续程序组合、校验和生成类型定义
annotations / metadata描述风险和运行特征如只读、破坏性、幂等、权限域、所属服务

OpenAI 的 Function Tool 使用 namedescription 和 JSON Schema 参数,并可通过严格模式提高参数对 Schema 的遵循程度;MCP Tool 使用 inputSchema,还可以声明 outputSchema。无论采用哪种协议,框架在执行前都仍应验证模型生成的参数,不能把 Schema 当作安全边界。

一个更完整的 Schema 示例

{
  "name": "orders_get_detail",
  "description": "读取一个已有订单的详情。仅用于查询,不会修改订单;必须使用内部订单 ID,不接受商品名称。",
  "inputSchema": {
    "type": "object",
    "properties": {
      "order_id": {
        "type": "string",
        "description": "内部订单 ID,例如 ord_12345"
      },
      "include_items": {
        "type": "boolean",
        "description": "是否返回商品明细",
        "default": true
      }
    },
    "required": ["order_id"],
    "additionalProperties": false
  },
  "outputSchema": {
    "type": "object",
    "properties": {
      "order_id": { "type": "string" },
      "status": {
        "type": "string",
        "enum": ["pending", "paid", "shipped", "completed", "cancelled"]
      },
      "items": { "type": "array" }
    },
    "required": ["order_id", "status"]
  }
}

Schema 的设计原则

  1. 工具名表达业务动作。 orders_get_detailquery 更容易检索和选择;命名空间还能减少不同服务之间的重名。
  2. 描述包含选择规则。 不只写“查询订单”,还要写何时使用、何时不要使用、是否有副作用,以及和相似工具的区别。
  3. 参数尽量结构化。 能用枚举、布尔值、日期格式或独立字段表达的内容,不要塞进一个自由文本参数。
  4. 控制参数自由度。 明确 required,对封闭对象使用 additionalProperties: false,减少幻觉字段和歧义。
  5. 一个工具完成一个清晰操作。 过于万能的 do_everything(action, payload) 难检索、难授权、难审计。
  6. 输出也应稳定。 结构化输出方便下一个工具或代码消费;错误最好有可识别的类型、错误码和可重试信息。
  7. 把权限放在执行层。 Schema 可以描述“只读”或“破坏性”,但真正的身份、权限、额度和用户确认仍由框架检查。

可以把一份 Tool Schema 的质量理解为三件事:可检索、可选择、可执行。名称和描述帮助召回候选工具;完整描述帮助模型正确选择;输入输出 Schema 帮助框架可靠执行与组合。

Tool 可以封装哪些能力

Tool 不是某种特殊的“AI 程序”,而是对普通软件能力的统一封装。

类型示例
外部 HTTP API天气、地图、搜索、股票、快递服务
本地函数计算器、格式转换、业务规则计算
文件与运行环境读写文件、搜索代码、运行终端或测试
数据库查询用户、订单、库存和报表
内部业务服务创建订单、取消订单、退款、审批
开发工具Git、构建系统、测试框架、浏览器
其他 Agent研究 Agent、编程 Agent、审查 Agent

生产系统通常不会把“任意 SQL”或“任意 Shell”直接暴露给模型,而会优先提供边界清晰的受控函数,例如 get_user_orders(user_id)get_order_detail(order_id)

工具数量级

Tool 的数量取决于 Agent 的职责和系统规模:

系统类型大致数量特点
简单 Agent3~10 个如天气、计算或单一查询助手
单一业务 Agent10~50 个如客服、订单或内部工作流 Agent
复杂开发 Agent几十到上百个涉及文件、代码、Shell、Git、测试和浏览器等
企业级平台几百甚至上千个汇集多个业务系统和内部服务的全部能力

系统拥有的工具总数,不等于每次模型调用都会看到的工具数。一次性把上千个 Tool Schema 全部放进 Context 会带来以下问题:

  • 占用大量 token
  • 工具描述和名称容易混淆
  • 模型选错工具的概率上升
  • 推理成本和延迟增加

因此,复杂系统通常会先按领域、任务或当前环境筛选工具,只把少量相关 Tool Schema 提供给主模型。例如用户询问天气时,只暴露当前天气、天气预报和空气质量工具,而不暴露退款、发邮件或数据库管理工具。

还要区分“能力数量”和“Tool 数量”。一个通用 terminal Tool 可以运行很多命令,一个 edit_file Tool 可以修改任意文件。因此 Cursor、Codex 一类编程 Agent 可能拥有非常广的能力,但暴露给模型的独立 Tool 未必真的达到上千个。

第三个问题:由谁决定把哪些工具交给模型

Agent 框架本身通常是普通程序,不天然具有大模型式的理解能力。它负责保存状态、组装 Context、注册和执行工具、控制循环、处理权限与错误。Agent 系统表现出的智能主要来自其中的模型,以及检索、规则、状态机等其他组件。

工具选择实际上分成两个阶段:

  1. 候选工具选择:从整个工具库中找出当前可能相关、并且允许使用的一小组工具。可由规则、Tools Retrieval 或 Router 模型完成。
  2. 具体 Tool Call 决策:主模型从候选工具中选择下一步真正调用的工具,并生成参数。

例如:

系统工具库:500 个
      ↓  环境与权限过滤
当前可用:80 个
      ↓  规则、检索或 Router
候选工具:15 个
      ↓  主模型结合任务状态判断
本轮调用:search_code(query="LoginManager")

因此,“Agent 决定给模型哪些工具”和“模型决定调用哪个工具”并不矛盾:前者是候选集路由,后者是候选集内的具体决策。

三种常见的候选工具选择方式

方式工作方法优点局限
固定规则根据页面、文件类型、用户选区、仓库状态等筛选快、便宜、确定性强难覆盖复杂自然语言意图
Router 模型先让小模型或主模型分类任务领域能理解复杂意图多一次模型调用,有成本和误判风险
Tools Retrieval在 Tool Catalog 中检索与请求最相关的工具适合数百、数千个工具依赖索引质量、召回率和权限过滤

生产系统更常采用混合方式:

用户消息与当前环境

硬过滤:已连接服务、运行环境、租户、身份和权限

领域路由:代码、订单、日历、搜索……

关键词 + 向量检索 + 重排

只加载少量候选工具的完整 Schema

主模型选择具体 Tool Call

框架再次校验并执行

Tools Retrieval:检索的不是答案,而是能力

Tools Retrieval 和 RAG 的结构很像,但两者检索的对象与目的不同:

对比项文档 RAGTools Retrieval
检索对象文档片段、事实、知识Tool 名称、描述、Schema 和元数据
返回给模型支撑回答的内容当前可调用的能力接口
下一步模型基于资料回答模型选择工具和参数,框架执行
主要风险检索到错误或过时知识漏召回工具、选错工具或暴露越权工具

Tool Catalog 应保存什么

完整 Schema 不一定直接进入初始 Context,但框架需要维护一个可检索目录。每条工具记录至少可以包含:

  • 唯一名称、标题和简短描述
  • 适用场景与不适用场景
  • 参数字段及其语义摘要
  • 输出摘要和错误行为
  • 所属服务、领域和版本
  • 只读、幂等、破坏性等风险属性
  • 所需权限、租户和运行环境
  • 搜索关键词、别名和示例请求
  • 完整 inputSchema / outputSchema 的引用

搜索索引主要使用名称、描述、标签、示例和参数语义;完整 Schema 可以延迟到工具进入候选集后再加载。

Progressive Tool Discovery:Catalog → Inspect → Execute

当工具很多时,一个实用模式是渐进式发现:

  1. Catalog:模型或框架只看到工具名和一句话摘要,并可调用 search_tools(query) 搜索能力。
  2. Inspect:确定候选后,通过 get_tool_details(name) 加载少数工具的完整输入输出 Schema。
  3. Execute:主模型生成具体 Tool Call,框架验证并执行。

MCP 官方客户端最佳实践建议,在工具定义开始明显占用 Context 时,不要继续一次性注入全部 Schema,而应使用渐进式发现。工具很少时全量加载反而更简单;是否切换应根据 Schema 实际 token 占比、延迟和评测结果决定,而不是机械地以工具个数为界。

检索与排序策略

  • 关键词检索:BM25、倒排索引或规则匹配。适合名称和术语稳定的工具。
  • 向量检索:对用户请求与工具描述生成 embedding,擅长处理同义表达。
  • Router 分类:先判断领域或服务,再在较小目录内检索。
  • 混合检索:合并关键词、向量、规则和模型评分,通常比单一方法稳健。
  • 重排:对初步召回结果按任务匹配度、参数可满足性、权限和风险再次排序。

查询不应只有用户的一句话,还可以组合当前环境:

retrieval_query = 用户请求
                + 当前应用/仓库/页面
                + 已知任务计划
                + 当前缺失的信息

但权限不能只作为相关性分数。**无权限、未安装、当前环境不可执行的工具,应在进入模型 Context 之前硬过滤掉。**检索决定相关性,不授予执行权限。

一个简化实现

def select_tools(user_message, runtime_context, identity):
    available = registry.filter(
        connected=True,
        environment=runtime_context.environment,
        permissions=identity.permissions,
    )
 
    query = build_tool_query(user_message, runtime_context)
    keyword_hits = bm25.search(query, corpus=available)
    semantic_hits = vector_index.search(query, corpus=available)
    candidates = rerank(keyword_hits + semantic_hits, query=query)
 
    return load_full_schemas(candidates[:15])

这里的 15 只是示例。Top-K 应根据任务复杂度、工具相似度、Schema 长度和模型能力通过评测确定;召回不足时,Agent 还可以根据执行结果再次检索,而不是强行在第一次候选集中完成整个任务。

Tools Retrieval 的常见失败

  • 召回遗漏:真正需要的工具没有进入 Top-K,主模型根本没有机会选择它。
  • 工具碰撞:多个工具名称和描述过于相似,模型难以区分。
  • 描述失真:Schema 文档和真实实现不一致,导致正确检索但错误执行。
  • 索引过期:服务新增、删除或修改工具后没有刷新 Catalog。
  • 权限后置:先把越权工具暴露给模型,执行时才拦截,增加误导和安全风险。
  • 过度裁剪:候选集太小,后续步骤需要的工具无法使用。
  • Schema 过长:虽然工具数量减少,但单个定义仍塞入大量示例和说明,继续浪费 Context。

应该评估哪些指标

指标回答的问题
Candidate Recall@K正确工具是否进入候选集
Tool Selection Accuracy主模型是否从候选集中选对工具
Argument Validity参数是否通过 Schema 和业务校验
Task Success Rate整个任务最终是否完成
Unsafe Exposure Rate无权限或高风险工具是否错误进入 Context
Schema Tokens工具定义占用了多少 Context
Routing Latency / Cost检索和 Router 增加了多少延迟与成本

只优化“选对工具”还不够。一个系统可能检索指标很好,却因为参数不完整、权限设计不清或工具输出不稳定而无法完成任务,因此最终仍要以端到端任务成功率为主。

一个实用心智模型

可以把模型的 Tool Call 理解为填写申请单:

我要调用:get_weather
参数:北京

模型负责填写这张申请单;Agent 框架负责审核申请、找到真实工具、执行调用、记录结果,并把结果交回模型。

来源