☰
工具结果不必都经过模型:读 claude-cookbooks/tool_use
2026/10/19 16:09:09 网站建设 项目流程

普通 tool use 的执行路径很直白:模型生成工具名和参数,程序执行工具,把结果放回对话,模型继续判断。tool_choice、Pydantic 和 structured JSON 改善了参数约束与返回格式,没有改变这条路径。

tool_use/中值得单独分析的两组 notebook 改了路径本身。Programmatic Tool Calling 让工具结果先进入程序,Tool Search 则让工具定义按需进入上下文。前者控制模型看到哪些结果,后者控制模型看到哪些能力。

普通调用把工具结果变成了模型输入

programmatic_tool_calling_ptc.ipynb先用传统方式处理一个差旅审计任务:找出工程团队成员,读取每个人第三季度的费用明细,汇总已批准的差旅支出,再检查超预算人员是否有自定义额度。

传统循环需要模型先取员工列表,再取八个人的费用,阅读每条费用的金额、商户、收据地址、审批链等字段,算出总额后继续查询预算。数据库本来已经返回结构化数据,系统却把全部原始记录变成 token,让模型承担过滤、求和和连接。

这次运行用了 4 次 Claude API 调用和 110,473 个 token,耗时 35.38 秒。问题不在工具调用次数,而在所有中间数据都必须经过模型上下文:

模型选择工具 → 工具返回大量记录 → 记录进入模型上下文 → 模型决定循环、过滤和下一次调用

工具接上模型以后,外部数据并没有绕开模型,反而可能成为上下文里最大的部分。

PTC 把确定性工作移进代码

PTC 给工具增加allowed_callers,允许 code execution 环境调用它们。模型先生成一段 Python,由 Python 在容器内循环调用工具、过滤已批准记录、按类别求和并挑出超预算员工。只有处理结果返回模型,收据地址和无关元数据留在容器里。

同一个任务的 PTC 运行使用 15,919 个 token,比传统版本少 85.6%;API 调用仍是 4 次,时间从 35.38 秒变为 34.88 秒,只少 1.4%。这组结果明确显示上下文用量下降,却不足以说明执行明显提速。若工具本身耗时占主导,少传 token 不会显著缩短总时间。

PTC 也不是把 Agent 改成固定脚本。模型仍决定要查什么,并现场写出执行计划;变化发生在循环内部:

传统方式:每批结果 → 模型 → 下一次工具调用 PTC: 每批结果 → Python 过滤和聚合 → 精简结果 → 模型

它适合批量读取、连接、排序、去重、统计,以及“先算出 A,再只对满足 A 的对象查询 B”这种依赖。模型负责把任务编成小程序,解释器负责可靠地运行循环和算术。

相应的风险也换了位置。允许代码批量调用工具,会扩大一次错误计划的影响范围。notebook 因此要求工具显式声明可被 code execution 调用,并提醒只开放适合重复执行的工具。只读查询和计算容易控制;发邮件、退款、删数据之类有外部副作用的操作不能因为能写进循环就批量开放。PTC 还要处理容器状态和过期:示例把container_id带到下一轮,因为后续代码依赖前一轮容器中的状态;容器失效后,这种连续性也会消失。

验证问题随之出现。传统轨迹虽然昂贵,但每次工具结果都在模型消息中;PTC 把大量中间状态留在容器里。如果只记录最终答案,便很难知道模型生成的代码有没有漏记录、用错过滤条件或吞掉异常。节省上下文不能等于删除审计轨迹。生产实现至少要单独保存生成代码、实际调用清单、关键中间计数和异常,而不是把它们全部塞回模型。

Prime Agent 的 RLM 可以看作 PTC 思想的一种更完整实现,把 PTC 的“模型写代码组织工具”扩展成了长期运行的 Agent 工作台 :

  • 模型主要看到一个ipython工具。
  • 模型写 Python,再由 Python 调用 skill、MCP、shell 和子 Agent。
  • 大量数据留在运行时中处理,只把摘要和结论返回模型。

PTC是 API 提供的单轮执行机制,代码调用显式授权的工具。Prime Agent自己实现了持久 IPython kernel、host bridge、状态恢复和子 Agent,范围更大,也能跨轮保存变量。

Tool Search 让能力列表也按需加载

当工具从十几个增长到几百个,另一个输入开始膨胀:工具 schema。每个工具的名称、说明、参数和约束都会放入请求。模型还没处理用户问题,就先收到一份越来越长的 API 目录。

tool_search_with_embeddings.ipynb把工具定义当作检索对象。系统预先将工具名、描述和 schema 编成向量;运行时,模型先调用唯一可见的tool_search,程序对查询做 embedding,按余弦相似度返回 top-ktool_reference,被命中的完整定义才在那个位置进入上下文。

示例只有 8 个工具,但机制针对更大的库。查询“check the weather”时,前三名依次是get_weather、get_forecast和get_air_quality;查询“stock market data”时,前三名是get_stock_price、get_market_news和calculate_compound_interest。第三名已经说明语义检索并不理解任务,只是在描述空间里找近邻。

Tool Search 增加了一道新的失败条件。以前工具选错,至少所有工具都在模型眼前;现在检索器若漏掉正确工具,模型甚至不知道它存在。系统的能力上限由两部分共同决定:

能否召回正确工具 × 模型能否在召回集合中正确使用它

因此,评测不能只看最终任务成功率,还要单独测工具召回率,尤其要覆盖别名、缩写、跨语言描述和需要多工具组合的请求。top-k 太小会漏工具,太大又把 schema 开销带回来。embedding 只按语义相关性排序,也不会自动考虑权限、成本、延迟、地区和可靠性;这些条件必须进入过滤或重排。

动态加载还会碰到 prompt cache

tool_search_alternate_approaches.ipynb给出一个不需要 embedding 的版本:system prompt 只列工具名,模型调用describe_tool(name)取得完整定义。它适合工具名清晰、模型容易从目录中直接选中的场景,也暴露了动态工具与 prompt cache 的关系。

如果每发现一个工具就把它普通地追加到tools列表,后续请求的前缀随之变化,原来的 prompt cache 会失效。示例把新工具标记为defer_loading=True,同时在 tool result 中返回tool_reference,API 从这个位置把完整定义加载进上下文,而不是改动开头的 prompt。

这不是一个 API 小技巧。工具发现改变了上下文的结构:系统既要让新能力在当前回合可用,又要避免每次发现都重建此前相同的长前缀。Tool Search 解决的是工具可见性,defer_loading解决的是可见性变化后怎样保持缓存稳定。

两种发现方式的适用边界也不同。describe_tool不会检索错,但前提是模型已经知道正确工具名;embedding 允许自然语言发现,却可能召回错误。工具库规模大、命名不稳定时,需要语义或混合检索;目录短而清楚时,直接列名更容易调试。

控制进入模型的边界

PTC 与 Tool Search 看起来一个管执行、一个管发现,处理的却是同一个成本来源:模型上下文被当成了所有信息的默认中转站。

PTC 让原始工具结果停在代码环境,只把筛选和聚合后的内容交给模型。Tool Search 让大部分工具定义留在目录,只把当前任务需要的 schema 交给模型。二者都没有减少系统拥有的信息,只缩小了模型每一轮必须阅读的信息。

缩小以后,可靠性检查必须移到边界上。对 PTC,要验证程序实际调用和聚合了什么;对 Tool Search,要验证正确工具有没有被召回。只检查最终文本是否流畅,会同时漏掉两类静默错误:程序少算了一部分数据,或者检索阶段从未给模型正确工具。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询