DeepSeek Harness 05:Agent Loop——控制平面与状态机推进

把 Agent 的核心调度逻辑写成几行代码非常简单:

1
2
3
4
5
6
7
# 简单的玩具代码:隐藏了所有的真实工程隐患
while not is_done:
response = call_llm(history)
if response.tool_calls:
execute_tools(response.tool_calls)
else:
break

这种朴素的循环完全无法抵御真实生产环境的风暴:

  • 如果用户在第二轮工具执行到一半时忽然插话:“别跑测试了,赶紧回滚代码”,循环如何打断?
  • 如果进程在第 15 步由于系统 OOM 异常退出,重启后凭什么恢复到刚刚执行完的状态?
  • 如果模型反复陷入逻辑死循环,如何从外部强制接管?

DeepSeek Harness 的设计把 Agent 推进过程抽象为一个具备严谨状态机语义的控制平面(Control Plane)。


一、控制平面与执行平面的物理分离

在 DSH 中,系统被清晰划分为两个彼此隔离的平面:

  • 控制平面(Control Plane):由大语言模型和 Agent Loop 组成。它的核心职责是“提出下一步假说”,它本身绝对不能直接触碰任何现实副作用。
  • 执行平面(Execution Plane):由受控的沙箱、文件系统驱动和网络通信管道组成。它负责执行具体命令,并将产出的确定性结果反哺给事实源。

在这个闭环中,所有的驱动都以**事实写入日志(Session Log)**为唯一分界线。控制流的任何决策,都不能依赖主进程内存里悄悄维护的临时变量,一切状态变更必须先落盘为不可篡改的事实。


二、三级执行生命周期:Turn、Step 与 Request

为了精准治理不同时间跨度的问题,DSH 将生命周期严格切分为三个层级:

生命周期层次 时间与逻辑跨度 核心治理与拦截职责
Turn(轮次) 从用户提交一条意图开始,直到任务宣告彻底完成或被用户中止 绑定用户原始目标、全局 Token 预算上限、最终状态结算
Step(步进) 一次模型推理与其所触发的一批工具调用的完整执行闭环 局部上下文切片、工具并发批次调度、单步取消与重试
Request(物理请求) 向特定大模型供应商发起的一次具体 HTTP 网络通信 供应商路由选择、传输超时控制、网络级重试与退避

[!NOTE]
一个关键细节:没有 Step 的 Turn
如果用户的输入在刚进入系统时就被安全策略或预检查拦截(例如输入命中非法敏感词),系统依然会生成一个完整的 Turn 记录,但其中包含 0 个 Step。系统必须如实记录“尝试进入执行但被策略拦截”这一客观事实,绝不能假装这次交互从未发生过。


三、铁律:模型可见历史必须能从日志严格派生

在一些非严谨的 Agent 框架中,存在一种常见的“捷径做法”:在将请求发给模型的前一秒,通过某个拦截钩子悄悄往 messages 数组里硬塞一段临时提示词(例如动态注入当前系统时间或某段上下文)。

DSH 在架构层完全封死了这种偷跑逻辑:

  1. 模型每一次调用所看到的完整历史,必须严格通过 deriveMessages() 从 Session Log 中确定性派生出来。
  2. Loop 构造完成的请求体,在进入模型适配器前会通过 Object.freeze() 进行深度递归冻结。
  3. 运行时配备了严格的不变量检查器(Invariants Checker):在请求发出前后,校验日志中是否存在当前 Step 记录、请求消息是否与日志投影严格一致。

这一铁律彻底杜绝了“幽灵状态”:在任何时候需要审计、Fork 或恢复任务时,回放系统都能精准重现模型做出决策时所面对的世界。


四、拦截点责任分层机制

为了实现高度的可扩展性,Agent Loop 在不同节点暴露了细粒度的拦截点,但每个拦截点都有其严格的职责红线:

拦截接缝 拥有的决策权 坚决禁止的行为
agent/pre-step 准入控制:允许、改写或拒绝刚刚领取的输入 直接触发任何具有系统副作用的工具执行
agent/request 决定本次请求所使用的 Provider、模型配置与降级策略 绕过日志私自修改已派生的消息历史数组
llm/stream 横切传输包裹:流式性能统计、测试 Mock、内容实时审计 破坏 StreamChunk 流式协议的不变量结构
agent/request-error 供应商错误处理:退避重试、路由切换或宣告终止 将发生的错误从日志事实源中抹掉
tools/* 工具权限校验、沙箱执行与展示投影转换 将模型的推测选择直接等同为用户授权
session/event 只读观察:异步持久化、指标上报、外部监控 试图逆向修改已经写入日志的历史事实

五、取消与恢复的真实边界

在真实工程中,用户经常需要通过界面打断执行。Agent Loop 采用基于 AbortSignal 的协作式取消机制。

但我们必须清醒地认识到:取消是协作式的终止,绝不是现实副作用的时光倒流!

  • 如果模型发起的 rm -rf 命令已经提交给了执行平面,即使上层发送了取消信号,已经删除的文件也无法自动复原。
  • 当任务被取消后,系统会如实将 aborted 状态记入日志。当用户下一次唤醒任务时,Agent Loop 会根据已经落盘的事实重新评估:哪些副作用已经落地,哪些未完成,进而规划后续的补偿或重跑策略。

[!WARNING]
评测视角的警示:模型声明完成 $\neq$ 任务真正完成
在第三方对抗评测(如 coding-agent-harness-eval)中发现:尽管 DSH 的状态机完备度很高,但依然存在“模型在 turn/end 主观声明任务已解决,而实际后端代码根本没有跑通测试”的现象。模型是概率系统,turn-stopping 只是它的自我评估。生产系统的最终验收,必须依赖确定的外部自动化测试(如 pytest / npm test)或人工审批进行二次强制校验。