DeepSeek Harness 源码解析 · 第 9 讲
DeepSeek Harness 源码解析系列 第 9 讲:Agent Loop 核心循环 基于 DeepSeek Harness 源码 · 2026-08-24 一、本节概述 前几讲我们逐步拆解了 Harness 的整体架构、事件系统、Turn flow、依赖注入、Session 管理和 Agent 注册表。本节进入整个框架的 核心引擎 —— ReactLoopAgent 驱动循环。这是整个 Harness 中唯一包含具体循环逻辑的包,其他所有行为都通过插件和事件扩展点注入。 源码位置: packages/core/agent-loop/ ,共 6 个核心文件: agent-loop 包文件结构 🔹 index.ts (713 行)— AgentLoop 工厂服务,负责创建、恢复、销毁 Agent 🔹 agent.ts (496 行)— ReactLoopAgent 核心驱动,turn/step 循环 🔹 tool-calls.ts (289 行)— 工具调用调度器,串行/并行分组执行 🔹 runtime-context.ts (76 行)— 运行时上下文投影,动态上下文快照 🔹 constants.ts (6 行)— 默认并行工具调用数(10) 🔹 invariant.ts (63 行)— 运行时不变量检查,请求一致性校验 二、ReactLoopAgent:三阶段状态机 ReactLoopAgent 是整个循环的心脏。它维护一个内部 Phase 状态机,管理 Agent 从空闲到运行再到空闲的完整生命周期。先看它的三种状态定义: 📄 packages/core/agent-loop/src/agent.ts(第 38-46 行) type Phase = | { kind: 'idle'; lastTurn: number } // ↑ 空闲状态:记录最后一个 turn 编号,用于下一个 turn 从 lastTurn+1 开始计数 | { kind: 'maintenance' // ↑ 维护状态:允许后台任务(如压缩、清理)在不打开 turn 的情况下执行 abort: AbortController // ↑ 维护任务也有独立的取消信号,可以中止正在进行的维护操作 lastTurn: number // ↑ 维护期间 turn 计数不前进,保留 lastTurn 供恢复后使用 wakeRequested: boolean // ↑ 维护期间如果有唤醒请求,标记为 true,维护结束后自动回放 } | { kind: 'running'; abort: AbortController; turn: number; step: number; wakeRequested: boolean } // ↑ 运行状态:包含当前 turn 和 step 编号,独立的取消信号,以及挂起的唤醒标记 注意 maintenance 状态的设计意图。它让压缩(compaction)、清理等后台任务可以在 Agent 空闲时执行,但不打开新的 turn。如果维护期间收到用户消息, wakeRequested 标记确保维护结束后自动触发新的 turn。 三、kick() → turn() → step():三层循环 驱动循环的核心是 kick() 方法。它启动一个 while 循环,每轮调用 turn() ,每个 turn 内部有多个 step() 。这是典型的三层嵌套结构: 📄 packages/core/agent-loop/src/agent.ts(第 210-223 行) private async kick(): Promise<void> { try { while (await this.turn()) {} // ↑ kick 是一个无限 while 循环,每次调用 turn()。turn() 返回 true 表示还有 // 待处理消息(inbox.hasPending),返回 false 表示本轮对话结束,循环退出 } catch (_error) { // ↑ 所有异常在这里被捕获——失败和取消在驱动边界被包含,不会向外传播 // 错误已经在 throwError() 中通过 agent/error 事件报告了 } finally…