Programmatic Tool Calling(简称PTC)是指程序化工具调用,模型通过生成一段程序代码,对工具调用进行合并,聚合,最后过滤出需要的结果返回模型,这样可以减少多次工具调用的往返和模型的中间结果。最终效果取决于生成程序的质量。典型应用场景是在数据中提取需要的结果,如筛选数据库提取关键数据。
Tool 赋予模型读写文件,调用命令的能力,但不是每一次推理决策后产生的工具调用都能返回正确结果,所以需要把工具调用结果重新发送给模型,让模型不断推理,直到完成任务。

下面是从DeepSeek Harness 中采集的trace,Turn 2是一轮对话,可以看到有多个ASSISTANT和TOOL块,每一个代表了一次工具调用往返。

多轮调用解决的是如何完成任务的问题,ai发展到现在,模型能力已经很强,如何用最少的token完成任务已经成为影响模型选择的因素。想象一次日志读取过程,需要在成千上万行日志中查找有问题的地方。模型可以一次性读入几千行进行分析,然后再继续读入几千行,也可以使用更好的方式,编写一个程序,筛选出带"error","fail"等关键字的行,再读取筛选后的结果。还有需要进行网络搜索,抓取到网页html后,除了将整篇html读入模型,先编写一个程序,筛选出关键html后再读取会更高效。由此引入了PTC概念,但PTC也不是一蹴而就的,在很久之前已经有这方面的探索。
在PTC概念出来之前,主流的编程工具已经在不断探索:
advanced-tool-use-2025-11-20),正式命名并推出 Programmatic Tool Calling:Claude 在 code execution 沙箱中写 Python 编排工具,通过 allowed_callers 字段声明工具可被代码调用;同批发布 Tool Search Tool 和 Tool Use Examples。Claude for Excel 已用 PTC 处理数千行表格;内部测试 token 降 37%。code_execution_20260120 版本,PTC 转入该版本 API(响应中新增 caller 字段标识程序化调用来源,支持容器复用与暂停/恢复)。program / program_output 响应项和 allowed_callers: ["programmatic"],并支持 MCP、shell、code_interpreter 等工具类型;Agents SDK (Python) 同步提供 ProgrammaticToolCallingTool()。为了更直观的感受PTC与普通模式的区别,设计了一套实验:让模型从1万行日志中统计success/failed的数量。通过比较token差异来对比两者区别,以及通过trace研究两者的行为差异。
biglog-10k.jsonl,每行格式为 {"result":...,"text":"我是第{n}行"},result 随机分配success,failed,warning。{"result":"success","text":"我是第1行"}
{"result":"warning","text":"我是第2行"}
{"result":"success","text":"我是第3行"}
{"result":"failed","text":"我是第4行"}
{"result":"success","text":"我是第5行"}使用一致的模型输入:
User: 请读取文件
biglog-10k.jsonl,统计其中 result 字段为"success"的行数,以及 result 字段为"failed"的行数。每行是 JSON 格式{"result":"...","text":"..."}。请只报告这两个数字(例如:success=6055 failed=2456)。
使用结果统计如下,其中native消耗token数为66496,PTC模式消耗55043,同比减少17%。tool calls调用数量上native是4次,PTC是1次,符合PTC的定义和预期。通过生成代码来获取结果的预期,整个流程只有一个代码执行的工具调用。
指标 | native | code (PTC) | Δ |
|---|---|---|---|
总 token | 66,496 | 55,043 | +11,453(PTC 省 17%) |
sub tool calls | 4 | 1 | 3 |
native 模式里一共调用了两次grep 工具,筛选出所有success/failed的行,再将这些行回传给模型,调用的原始参数如下:
grep 调用 #1 —— 统计 success
{ "pattern": "\"result\":\"success\"", "path": "biglog-10k.jsonl"}grep 调用 #2 —— 统计 failed
{ "pattern": "\"result\":\"failed\"", "path": "biglog-10k.jsonl"}PTC模式使用 Node fs.readFileSync + JSON.parse 逐行统计,通过执行代码直接获取结果,模型生成代码如下:
import { readFileSync } from "fs";
const file = "biglog-10k.jsonl";
const data = readFileSync(file, "utf8");
const lines = data.split("\n").filter(l => l.trim().length > 0);
let success = 0, failed = 0, other = 0;
const others = {};
for (const line of lines) {
try {
const obj = JSON.parse(line);
const r = obj.result;
if (r === "success") success++;
else if (r === "failed") failed++;
else { other++; others[r] = (others[r]||0)+1; }
} catch(e) {
other++;
}
}
return { total: lines.length, success, failed, other, others };Native模式调用了grep工具,把所有success/failed的行读取出来重新发给模型,再由模型进行计算。PTC模式生成了一份代码,通过程序运行获取结果。显而易见的,在这类场景下表现更佳,token消耗更少,随着量级和规模不断增大,收益也会随之增大。但通过深入分析会发现,如果模型直接调用base工具,执行grep -c success && grep -c failed ,是可以快速完成任务,消耗token也只是一句简单的bash调用命令,比PTC生成的程序消耗token更少。所以实际使用中,如何平衡PTC模式不产生负优化,是个需要实践的过程。
通过实验对比,我们可以很直观的看到PTC模式在某些场景表现更佳,但在某些场景下,也会产生负优化,那么如何衡量PTC的收益呢?答案藏在token的消耗上,当生成的代码所需token小于正常多轮调用所需token时,就能产生正向收益。日常实践中可能需要不断调优,可以通过优化System Prompt,Skill的方式进行优化。
参考资料
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。