DeepSeek Harness 的 Harness 到底做了什么

DeepSeek Harness 解决的核心问题,是多轮模型调用、工具执行与会话状态之间的协调:输入何时进入请求,工具结果如何写回历史,请求失败后从哪里重试,上下文压缩后保留哪些信息,以及任务中断后能够恢复到什么状态。

围绕这些问题,框架将运行过程拆分为插件装配、回合驱动、会话日志、工具管道和异常处理。分析其架构的关键,在于各部分的状态边界,以及这些边界提供的保证与限制。

本篇文章限定在提交 477b4f4205(2026 年 9 月 25 日)对应的实现。

1. 插件装配

插件是 DeepSeek Harness 的核心能力,插件的配置决定 Agent 的运行能力。

DeepSeek Harness 通过 Cordis 插件组织运行能力。Cordis 提供 Context、Service、Plugin 和 Event 等抽象,插件通过依赖注入与事件系统协作,并由框架管理生命周期。

LLM 服务、Session、Agent、AgentLoop 和工具注册表等基础组件,由 bundle 统一挂载。启动时,dsh 以 profile 为入口加载配置,按 id 定位配置行,并替换对应的整段 config。

这里的配置语义是整段替换,而非字段级合并。覆盖某个组件的配置时,原配置中未被保留的字段也可能随之消失。

插件装配因此直接影响 Agent 的实际行为:模型服务是否可用、哪些工具被注册、哪些事件处理器参与执行,都取决于最终加载的插件与配置。仓库包含某项能力,并不等于当前运行实例已经启用该能力。

这种设计便于替换组件和组合能力,但也让执行逻辑分布在多个插件中。分析一次请求的行为,需要同时查看核心循环、插件注册关系和最终生效的配置。

2. 核心循环

Agent 循环是 Agent 的核心逻辑,其主要区分回合、步骤与请求重试。

从实际的工作逻辑上来看,agent-loop 负责驱动模型与工具之间的循环。其主要执行路径包括:

  1. 准备模型请求,解析配置并选择适配器。
  2. 投影系统提示词,提交本次需要进入历史的用户输入。
  3. 从会话构造请求,接收并累积流式响应。
  4. 将 assistant 消息写入 Session。
  5. 执行模型提出的工具调用,并将结果交给后续模型请求。
  6. 在没有工具调用或满足结束条件时,结束本轮执行。

理解这条路径,需要分三个层次:

  • turn:由用户输入驱动的回合。
  • step:回合内部的执行边界。
  • 请求尝试:一次实际发往模型的调用,失败后可以在同一个 step 内重试。

因此,step 不能简单等同于一次网络请求。重试可能产生多次请求尝试,但不应重复消费输入或重复提交用户消息。

Agent 维护 next-turn 和 next-step 两个队列,分别承接下一回合与下一步的输入。队列修改以 agent/inbox/spliced 事件写入 Session,待处理输入可以从日志投影恢复。

这一设计区分了「系统已经收到消息」和「模型已经消费消息」。

执行过程中到达的补充要求,需要在相应边界进入后续请求。它不能自动改变已经发出的模型请求,也不能撤销已经发生的工具副作用。输入排队与执行取消因此属于不同机制。

将队列变化写入日志,还能避免恢复时只找回聊天记录,却丢失尚未处理的输入。

3. Session

Session 机制实现了运行记录与模型历史分离。

Session 被设计为只追加的事件日志,事件通过递增序列号确定顺序,以 type 区分类型,以 body 保存内容。

日志不仅记录用户与 assistant 消息,也记录请求失败、输入队列变化、工具执行和压缩过程等运行事实。

但日志中存在的事件,不一定进入模型上下文。

真正发送给模型的 messages,由 Session 的 surface 通过 deriveMessages() 重建,再冻结为不可变请求。只有进入 surface 的消息事件参与历史投影;assistant/attempt 等运行记录不会自动成为模型消息。

这一分离有两个作用:

  • 保留排障信息:请求失败与恢复过程可以完整记录,而不必全部占用上下文窗口。
  • 允许调整可见历史:压缩或替换模型上下文时,仍能保留原始事件。

Session 的 append 还会检查 JSON 可序列化性、事件规则及 surface 替换的合法性,以减少无法持久化或无法正确投影的数据进入日志。

日志恢复不等于外部动作回滚

只追加日志为审计与状态重建提供了基础,但不能单独保证任意中断点都能无损恢复。

例如,工具已经修改文件,但结果尚未写入 Session 时进程退出,外部环境与日志之间就可能出现状态差异。恢复日志可以还原已提交的记录,却不能仅凭「缺少结果」判断工具没有执行。

因此,恢复能力需要区分:

  • 已提交事件及其投影的恢复;
  • 外部副作用的确认与处理。

后者还依赖工具的幂等性、状态检查能力或额外的事务机制,不能仅由 Session 的追加式设计推导出来。

4. 工具管道

工具管道实现了从模型意图到受控执行。

工具注册表只向模型暴露 name、description、parameters 等白名单字段。执行函数、超时设置和并发属性保留在运行时。

这将模型侧的工具描述与运行时的执行控制分开:模型负责提出调用,框架负责判断调用是否可执行,以及如何执行。

一次工具调用依次经过:

  1. 解析模型生成的参数。
  2. 查找当前 Agent 可见的工具。
  3. 通过 tools/pre-execute 进行执行前检查,允许或拒绝调用。
  4. 进入 tools/execute 包装链。
  5. 通过 tools/post-execute 处理结果。
  6. 将结果写回 Session,供后续模型请求使用。

权限检查、执行包装和结果处理因而拥有独立的介入位置,不必全部写进工具函数。

并发执行与顺序提交分离

同一条 assistant 消息可能提出多个工具调用。调度器通过工具的 isConcurrencySafe 属性分类:

  • 明确返回 true 的调用进入并行池。
  • 其他调用作为独占屏障处理。

并发是显式声明的能力,未声明安全的工具不会默认并行执行。

工具本体可以并发,但准备、后处理、tool/result 写入及附加 context 入队,按模型调用顺序提交。这样可以避免工具完成时间的随机性直接改变会话中的结果顺序。

代价是可能出现等待:后面的工具即使先完成,也需要等待前面的调用提交。并发能够缩短部分执行时间,却不保证结果可以立即进入历史;提前完成的结果还可能需要暂存。

此外,提交顺序稳定不等于外部副作用确定。并发工具是否真正安全,仍取决于它们是否访问共享状态、是否存在执行依赖,以及并发声明是否准确。

5. 安全隔离

DSH 在实现上让审批与执行限制分别生效,以实现安全隔离。

Harness 的沙箱模式包括:

type SandboxMode =
  | 'read-only'
  | 'workspace-write'
  | 'danger-full-access'

策略涉及文件路径权限、命令执行限制和权限提升。工具可以通过 justification 与 sandbox_permissions 请求提升权限,再由 approval 机制处理审批。

基础 bundle 默认挂载 workspace-write 文件沙箱与 ask 审批策略;sandbox-policy 将当前文件操作模式解析给实际后端。

这一结构包含三个不同层次:

  • 工具可见性:模型能够提出哪些调用。
  • 执行审批:某次调用是否获得许可。
  • 后端隔离:获准执行后,实际能够访问哪些资源。

三者不能相互替代。审批通过只说明动作获得许可,不代表执行环境具有无限权限;模型看不到某个工具,也不能据此证明其他工具无法访问相同资源。

安全边界最终取决于策略配置与实际后端的共同约束,而不只是沙箱模式的名称。

6. 异常收敛

异常是应用中很常见的东西,也是 Harness 的重点处理对象。

DSH 通过重试、压缩与取消各自处理不同问题。

6.1 请求重试

通过请求重试来保留失败记录,避免重复提交输入。

模型请求失败后,系统先写入 assistant/attempt,再交给 agent/request-error 瀑布链处理。

llm-retry 根据适配器提供的 retry policy,判断错误类型、重试次数与退避方式。计划重试与实际启动分别记录为 llm/retry 和 llm/retry-started。

重试在同一个 step 中重新准备请求,不重复提交首次用户消息。这一边界避免了网络层重试演变成会话层的重复输入。

分别记录重试计划与启动,也使日志能够区分“决定重试”与“已经开始下一次尝试”。

6.2 上下文压缩

上下文压缩实现了替换可见历史,保留原始事件。

自动压缩在 agent/pre-step 检测上下文压力,并记录:

  • compaction/start
  • compaction/summary
  • compaction/end

随后,系统将选中的连续历史区间替换为一个 checkpoint 用户消息。

这里发生的是 surface replace:模型可见历史缩短,原始日志仍然保留。因此,压缩减少的是请求上下文,不等于同步减少持久化存储。

压缩也引入了信息损失的可能。原始约束即使仍在日志中,只要没有进入摘要或剩余 surface,后续模型就无法直接使用。压缩是否有效,除了看上下文长度,还取决于任务目标、限制条件和未完成事项是否得到保留。

6.3 取消

通过取消来传递停止信号,而非撤销既有结果。

AbortSignal 贯穿回合、请求和工具,使取消信号能够沿执行链传递。

但信号传递不等于立即停止所有动作。工具是否及时退出,取决于其是否检查并响应取消;已经完成的文件写入或远端操作,也不会因 AbortSignal 自动回滚。

取消机制提供的是停止继续执行的控制路径,外部副作用的撤销仍需要独立实现。

7. 小结

DSH 的 Harness 实际上是一种架构的取舍,其取舍考量的是可追溯性与运行成本。

如下的机制共同形成了 DeepSeek Harness 的主要工程取舍:

机制 提供的能力 成本或边界
Cordis 插件装配 组件替换与能力组合 行为分布在配置和多个处理器中
turn / step 与输入队列 明确输入归属和执行边界 增加队列状态与事件维护
追加式 Session 审计与已提交状态重建 日志持续增长,不保证外部动作恰好执行一次
surface 投影 分离运行记录与模型上下文 需要维护投影一致性与替换规则
并发执行、顺序提交 利用并发,同时稳定历史顺序 可能产生提交等待与结果暂存
沙箱与审批 分层控制动作权限 实际保证依赖配置和执行后端
自动压缩 降低上下文窗口压力 增加摘要成本,并可能丢失任务信息
AbortSignal 统一传递取消意图 依赖工具配合,不能自动回滚副作用

DeepSeek Harness 的核心价值,是将 Agent 执行中的关键状态显式化:输入有队列,执行有边界,请求有可见历史,工具有调度与审批,失败有独立的处理路径。

这些机制使多轮任务能够被追踪、检查和恢复,同时也明确了框架的能力边界:日志恢复不能替代副作用确认,顺序提交不能替代并发安全,权限审批不能替代环境隔离,上下文压缩也不能保证信息无损。

以上。