ESSAY·

DeepSeek Harness 架构(四):工具、PTC 和多 Agent,怎样不放大副作用

从冻结参数、单调 Guard、并发分类到沙箱边界,理解可扩展执行的约束。

最近修订:2026.09.05

阅读导航 · 第 4 / 5 篇

DeepSeek Harness 架构系列:1 · 系统全景 · 2 · 组合与生命周期 · 3 · 状态与上下文 · 4 · 执行与安全 · 5 · 深入思考

阅读时间为估计,包含图表理解;动手实验另计。

版本边界:本系列沿用官方源码快照 76fda72。它是 developer preview;以下解读不是稳定接口或生产安全承诺。

上一页的日志可以证明“发生了什么”,却不能保证事情本来就应该发生。真正的执行边界必须在动作之前检查,动作之后再用证据核验。

工具系统:先冻结事实,再允许策略介入

工具调用是 Agent 最危险也最容易失真的边界:模型看到的参数、UI 展示的参数、审批时判断的参数和实际执行的参数,必须是同一个事实。

DeepSeek Harness 的做法是先把参数解析成无损 JSON、物化并冻结,再进入策略流水线。参数不能被中途改写,因为一旦改写,历史、审计、UI 和执行就会出现四个版本。

一次调用经过:

model tool call
  → materialize & freeze arguments
  → tools/pre-execute     // allow / deny / ask
  → monotonic guards      // 只能新增 deny,不能重新 allow
  → tools/execute         // 超时、追踪等 around wrapper
  → tool body
  → tools/post-execute    // 接受、替换投影或反馈阻断
  → finalizeContent
  → tools/result          // 冻结后的权威结果
  → durable tool/result

这里有三个成熟的设计信号。

Tool Definition 同时约束输入、规范输出和展示

一个工具不只声明名字、描述和参数,还必须声明 Canonical Output Schema,以及如何把规范值渲染成模型可见的 ContentBlock。运行时回调、超时、并发安全分类和 UI Presenter 永远不会泄漏给模型,模型只看到白名单生成的 Schema。

这样一来,“工具返回了对象,但模型只该看到摘要”成为显式投影;原始结构可以在执行链中保持类型化,展示也可以在 Replay 时纯函数重建。

Guard 只能单调收紧权限

tools/pre-execute 是可排序的插件策略,适合 Allow / Deny / Ask。随后执行的 ToolGuard 刻意没有 Allow 返回值:它只能不表态或给出拒绝原因。于是无论 Listener 顺序如何,一个安全 Guard 的拒绝都不能被后加载插件翻回允许。

这是“万物插件”架构不可缺少的补丁:扩展性允许多方参与决策,单调策略保证安全边界不会因为组合顺序意外变宽。

并行是工具自己的可证明属性

只有工具的 isConcurrencySafe(args) 明确返回 true,调用才能和兄弟调用重叠;省略、异常或任何非 true 结果都按 Exclusive 处理。并行因此不是模型一句“请并行”就能获得的权力,而是工具作者对共享状态和副作用作出的声明。

这些细节都记录在官方 Tools 子系统中。它们反映出 DeepSeek Harness 的设计重心:模型协议要短,Host 侧契约要严格。

PTC:把五次工具往返压缩成一段程序

PTC,即 Programmatic Tool Calling,是 DeepSeek Harness 区别于普通 Native Function Calling 的另一条路线。

在 Native 模式里,模型每调用一组工具,就要等待结果进入上下文,再发起下一次推理。如果任务是“搜索十个文件、读取命中项、过滤内容、汇总结构化结果”,大量中间数据和模型往返并不产生新的推理价值。

PTC 模式只向模型暴露 run_code 和自动生成的工具 SDK。模型写一段 TypeScript,把控制流、循环、并发和中间变量留在代码运行时,只有外层结果进入对话上下文:

以下是表达控制流的伪代码,工具名称不构成可直接运行的 SDK 示例。

const hits = await tools.fs_search({ pattern: "AgentLoop" })
const files = await Promise.all(
  hits.slice(0, 8).map((hit) => tools.fs_read({ path: hit.path }))
)

return files
  .filter((file) => file.content.includes("turn/start"))
  .map((file) => ({ path: file.path, relevant: true }))

它的潜在收益是:减少模型往返、避免大量中间结果污染上下文,并让确定性控制流回到代码。PTC Preset 因此关闭了通用 workflow 工具,避免同时给模型两个重叠的编排表面。

但不要把 Worker Thread 误读成安全沙箱。官方code-runtime-worker-thread 文档明确把它定义为 containment, not a security boundary:每次运行使用新 Worker、空环境、堆限制、计算时间和墙钟时间限制,也能硬终止死循环;但模型代码仍可访问 Node API,信任等级接近 Bash,派生的 OS 进程甚至可能在 Worker 终止后继续存在。

因此 PTC 的本质是性能与上下文工程机制,不是权限机制。它是否真的提高任务成功率,还需要针对具体模型和任务做评测。官方仓库当前只提供运行 Benchmark 的说明,没有公布足以支持横向性能结论的结果,所以不能仅从架构推导收益数字。

多 Agent:先统一委派语义,再叠加团队协作

DeepSeek Harness 把 Subagent 和 Agent Team 分成两层。

Subagent 是能力接口

ctx.subagents 可以同时挂载多个 Provider:

  • spawn:创建全新上下文的进程内子 Agent;
  • fork:从父 Agent 已完成历史复制上下文;
  • acp:通过 Agent Client Protocol 委派给外部 Agent;
  • codex / claude-code:调用真实的 Codex 或 Claude Code;
  • dsh-sdk:启动另一个完整 Harness Runtime。

Provider 可以是一轮即结束,也可以是可继续的 Child。父 Agent 通过同一接口发现、发送后续消息、中断和读取状态,而不需要知道子方运行在当前进程、另一个进程还是另一个产品中。详见官方 Subagent 包总览

Agent Team 是有状态协作域

实验性的 Agent Team 在 Subagent 之上增加:

  • 持久 Roster;
  • 可恢复 Mailbox;
  • 共享 Task DAG;
  • 基于 Revision 的 Compare-and-set 更新;
  • 对写入路径重叠的提示。

消息先完整写进 Lead Session,只有目标 Session 记录后才确认送达;“已排队但未送达”因此可以在 Replay 时恢复。任务的 writeScopes 目前只是建议性路径前缀,不是锁,这一点避免了把提示误当隔离。

它和普通“并行调用多个 Agent”最大的不同是:团队状态也进入可重放日志。代价则是协调协议、恢复语义和共享工作区冲突都变成平台责任。官方仍将它标为 Experimental,见 Agent Teams 文档

安全架构:插件自由必须由不可逆的边界兜底

DeepSeek Harness 的工具流水线已经体现了 Fail-closed 思想:没有审批通道、审批者异常、返回非法结果或请求被取消,都不能放行动作;只有 allowed-once 是授权。

默认权限 Preset 把两个独立旋钮组合起来:

权限层可选值控制什么
Sandbox Moderead-only / workspace-write / danger-full-access子进程的文件写入范围
Approval Policyask / never风险动作是否询问;never 表示请求直接拒绝,不是自动允许

本地 Sandbox 在 Linux 使用 bwrap / Landlock,在 macOS 使用 Seatbelt,在 Windows 使用 ACL Restricted Token。Provider 必须返回实际受限的命令行,失败时关闭执行;不允许在受限模式下悄悄退回裸命令。

但边界必须准确表达。官方 Sandbox 文档明确说明,当前 SandboxMode 只治理文件副作用,不覆盖网络和进程可见性;部分旧 Landlock ABI 和 Windows ACL 场景只能报告 partial enforcement。公开 Web Fetch 默认不逐次审批,虽然 Provider 会阻止访问非公开目标,但模型仍可能向公开 URL 发送数据。

更高风险的两个模式是:

  • PTC 的 Worker Thread 只是资源隔离,不是安全边界;
  • cordis 创造模式允许模型检查和修改自己运行的插件组合,信任等级等同 Shell Access。

官方 Safety Notice明确写着:项目尚未经过安全审计,不应被视为安全或生产就绪;对于不可信工作负载,应使用一次性 VM、容器或专用环境,并遵循最小权限。

所以正确的生产拓扑不是“相信内置 Sandbox”,而是:

flowchart LR
  H[Harness Policy<br/>Tool Guard + Approval] --> C[Disposable Container / VM]
  C --> W[Workspace Mount<br/>最小可写范围]
  C --> N[Network Policy<br/>域名 / 出站限制]
  C --> K[Scoped Credentials<br/>短期、最小权限]
  W --> V[Git Diff / Tests / Artifact Verification]
  N --> V
  K --> V

图 1|业务策略、外层隔离和结果验证的分工。矩形表示模块或步骤,圆柱表示存储,菱形表示判断;实线表示主路径,虚线表示约束、信息支撑或反馈。图为教学抽象,不代表全部实现细节。

Harness 内策略用于表达业务意图,操作系统或云基础设施负责硬隔离,Git / 测试 / 外部状态读取负责验证结果。三者缺一不可。

一组必须分别验证的反例

  • 审批后参数被替换:执行应基于同一冻结事实,不能“批 A 做 B”。
  • 只读工具被误标可并行:用共享状态测试验证声明,分类函数不是自动证明器。
  • Worker 超时后派生进程仍在:资源限制与系统级清理需分别检查。
  • 两个子 Agent 写同一路径:写范围提示不等于文件锁,必须有隔离或协调。
  • 任务禁止外发,但 URL 可访问:文件沙箱不替代网络与凭证策略。

“单调收紧”也有信任前提:Guard 约束的是经过该工具流水线的调用。拥有宿主同进程执行权的恶意插件不能仅靠另一段插件代码来隔离,应通过可信供应链和外层环境限制。

本篇不是部署安全保证;上面的验收表应该进入隔离环境中的测试,并与真实数据流、凭证和出站策略一起审查。


上一篇:状态与上下文

执行边界越看越清楚,也会发现代价分散到了插件作者、平台和部署者身上。终篇把这些代价合起来,讨论什么时候值得选择可重组运行时。

下一篇:自由不会消灭复杂度,它会改变复杂度的归属