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 负责驱动模型与工具之间的循环。主要执行路径包括:
- 准备模型请求,解析配置并选择适配器。
- 投影系统提示词,提交本次需要进入历史的用户输入。
- 从会话构造请求,接收并累积流式响应。
- 将 assistant 消息写入 Session。
- 执行模型提出的工具调用,并将结果交给后续模型请求。
- 在没有工具调用或满足结束条件时,结束本轮执行。
理解这条路径,需要分清三个层次:
- 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 等白名单字段。执行函数、超时设置和并发属性保留在运行时。
这将模型侧的工具描述与运行时的执行控制分开:模型负责提出调用,框架负责判断调用是否可执行,以及如何执行。
一次工具调用依次经过:
- 解析模型生成的参数。
- 查找当前 Agent 可见的工具。
- 通过
tools/pre-execute 进行执行前检查,允许或拒绝调用。
- 进入
tools/execute 包装链。
- 通过
tools/post-execute 处理结果。
- 将结果写回 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 执行中的关键状态显式化:输入有队列,执行有边界,请求有可见历史,工具有调度与审批,失败有独立的处理路径。
这些机制使多轮任务能够被追踪、检查和恢复,同时也明确了框架的能力边界:日志恢复不能替代副作用确认,顺序提交不能替代并发安全,权限审批不能替代环境隔离,上下文压缩也不能保证信息无损。
以上是本次对 DeepSeek Harness 架构的分析。类似源码层面的讨论,也可以在 云栈社区 找到。