PTC 模式的设计初衷:让 AI 自己写调度逻辑
DeepSeek Harness 的四种运行模式里,PTC(Programmatic Tool Calling,程序化工具调用)是最值得玩味的一个。标准模式走的是"预设工具链"路线——开发者提前配置好模型能调用哪些工具,模型在每一步根据上下文选择用哪个。PTC 则反其道而行:它把工具调度的决策权进一步下放,让模型先写一段代码,再用这段代码去编排多轮工具调用。
这个设计的出发点很清晰:面对复杂任务时,单步决策容易陷入"只见树木不见森林"的困境。模型可能在第 3 步才发现第 1 步的数据格式选错了,但标准模式下它已经消耗了宝贵的上下文窗口。PTC 模式希望模型在动手前先画一张"施工蓝图",把多步操作的依赖关系显式表达出来,从而减少反复试错的成本。
代码编排 vs 人工预设:两种范式的核心差异
标准模式的工作流是感知-决策-执行的循环:模型读取当前状态,从预定义的工具集中选一个调用,等待结果后再进入下一轮。这种模式对工具的边界要求很严格,每个工具的职责必须清晰无歧义,否则模型容易在相似工具间"犹豫不决"。
PTC 模式则引入了计划-生成-执行的三段式结构。模型首先生成一段 JavaScript/TypeScript 代码,这段代码可以包含条件判断、循环、变量传递等逻辑,然后 Harness 的执行引擎运行这段代码,在代码的驱动下完成多轮工具调用。这意味着模型不再是在"工具菜单"里点菜,而是在写一份"定制菜谱"。
这种差异带来一个关键优势:工具间的数据流可以被显式编排。比如一个"分析项目依赖并生成报告"的任务,PTC 代码可以先调用readFile读取package.json,将依赖列表解析为数组,再循环调用searchPackage查询每个包的最新版本,最后调用writeFile汇总结果。标准模式下,这些数据传递需要依赖模型的上下文记忆,而 PTC 模式下它们被写死在代码的变量赋值里。
但代价同样明显:代码生成本身成了新的故障点。如果模型生成的代码有语法错误、引用了不存在的工具名、或者循环条件写成了死循环,整个任务会在执行阶段直接崩溃。
实测对比:同一任务的双模式表现
为了验证 PTC 的实际价值,我设计了一组对比实验。任务选取遵循"步骤可预测、工具调用有明确依赖关系"的原则,同时覆盖不同复杂度层级。
实验一:批量文件格式转换
任务描述:将工作区内所有.md文件转换为.html,并统一在文件名后追加-converted后缀。
| 指标 | 标准模式 | PTC 模式 |
|---|---|---|
| 首次成功率 | 3/5 | 4/5 |
| 平均完成时间 | 45 秒 | 38 秒 |
| 典型失败模式 | 遗漏嵌套目录下的文件 | 生成的glob模式语法错误 |
| 调试耗时 | 需手动提示"检查子目录" | 修正代码中的路径匹配规则即可 |
标准模式在第三轮才意识到需要递归遍历,而 PTC 模式下模型在代码中直接使用了**/*.md,一次性覆盖了全部层级。不过 PTC 那次失败也是因为模型把glob写成了globSync的调用方式但漏掉了同步参数,导致运行时抛异常。
实验二:多源数据聚合分析
任务描述:从三个不同的 API 端点拉取数据,合并后按日期排序,输出 CSV 并计算周均值。
这个任务中,PTC 的优势被放大了。模型生成的代码清晰地表达了"先并行请求、再合并、最后计算"的流水线:
const [users, orders, logs] = await Promise.all([ tools.http.get('/api/users'), tools.http.get('/api/orders'), tools.http.get('/api/logs') ]); // 数据合并与转换逻辑...标准模式则倾向于串行执行,且多次出现"拿到第一批数据后忘记还要请求另外两个端点"的情况,需要多轮提示才能补全。
实验三:需要实时判断的开放任务
任务描述:根据用户模糊描述"优化一下这个页面",自主决定修改哪些 CSS 和 HTML。
这是 PTC 的典型反面教材。模型生成的代码要么过度保守(只改了颜色变量),要么过度激进(重写了整个布局),且无法在执行过程中根据中间结果调整策略。标准模式虽然也有类似问题,但至少每一步的决策都是基于最新反馈的,用户可以在中途介入纠正。PTC 的"一锤子买卖"特性在这里成了劣势。
PTC 的回退机制:当代码跑不通时
Harness 对 PTC 的失败处理提供了两层保护。第一层是语法预检:执行引擎会先用esbuild快速扫描生成的代码,捕获明显的语法错误并立即反馈给模型,请求重新生成。这层拦截能过滤掉约 60% 的初级错误。
第二层是运行时异常捕获。如果代码在执行某行时抛出异常,Harness 会将错误堆栈、当前变量状态、以及已执行到的代码位置打包成上下文,让模型分析失败原因。这个设计很聪明——它把"调试"也变成了模型可以参与的任务。实测中,约 70% 的运行时错误能在第二轮生成中被修正,常见模式包括:工具参数类型不匹配、异步操作未 await、以及数组越界。
但仍有约 30% 的错误会陷入循环:模型反复生成语义等价但细节略有不同的错误代码。此时 Harness 会触发模式降级,自动切换到标准模式继续执行任务,同时在 Trajectory 日志中标记此次 PTC 尝试的失败原因。这种" graceful degradation "的设计避免了用户卡在死胡同里。
适合与不适合 PTC 的任务画像
经过多轮测试,我总结了一个粗略的判断框架:
适合 PTC 的特征:
- 步骤序列在事前可以较完整地被描述(即使细节待填充)
- 工具调用之间存在明确的数据依赖,需要传递中间结果
- 分支逻辑有限且条件清晰(如"如果文件存在则覆盖,否则创建")
- 对一致性要求高,不希望模型在中间步骤"发挥创意"
不适合 PTC 的信号:
- 任务目标本身模糊,需要探索性执行来逐步澄清
- 每步执行后都需要人类审核或外部反馈才能决定下一步
- 涉及创造性决策(如 UI 设计、文案撰写)
- 工具调用结果高度不确定,需要大量异常分支处理
一个直观的类比:PTC 像提前写好的剧本,适合流程固定的场景;标准模式像即兴表演,更适合需要灵活应变的场合。
给开发者的实践建议
如果你打算在 Harness 中尝试 PTC 模式,有几个配置点值得注意。首先是代码生成模型的选择:PTC 对模型的代码能力要求明显高于标准模式,建议至少使用 DeepSeek-V4-Pro 或同等级别的模型,否则生成代码的语法正确率会大幅下降。
其次是工具接口的文档质量。PTC 模式下模型需要从零"想象"工具的 API 签名,如果工具描述含糊(比如参数类型写"any"),代码中很容易出现类型不匹配的错误。建议在cordis.config.ts中为每个工具补充详细的 JSDoc 注释。
最后是善用 Trajectory 进行复盘。PTC 的失败模式往往具有规律性——某类任务总是生成类似的错误代码。通过分析 Trajectory 日志中的失败聚类,可以反向优化工具命名、补充示例代码,甚至调整系统提示词中的 PTC 模板。
目前 PTC 模式在 Harness v0.1 中仍是实验性质,核心插件接口标注了"未来几个月可能快速演化"的提示。但对于那些已经被标准模式的"一步一步试探"折磨过的开发者来说,PTC 提供了一种值得期待的替代方案:不是让 AI 更聪明地选择工具,而是让 AI 学会自己写工具的使用说明书。