DeepSeek Harness Agent Loop 2026:驱动每一轮对话的可替换插件内部解析
DeepSeek Harness Agent Loop 2026:驱动每一轮对话的可替换插件内部解析
核心要点 (TL;DR)
- DeepSeek Harness agent loop 本身就是一个可替换插件:
packages/core/agent-loop中的ReactLoopAgent是默认实现,它实现core/agent定义的Agent接口——其他插件只依赖接口、不依赖循环,因此整个循环可以整体换掉。 - DeepSeek Harness agent loop 在两层上运作:turn(轮)(零或多个 step,收到第一条输入时打开,模型不再欠回应时关闭)与 step(步)(一次模型请求 + 它调用的工具)。模型可见的历史从不单独存储,而是通过
deriveMessages()从追记式会话日志投影而来。 - 驱动器由状态机(
idle | maintenance | running)门控,turn/start、step/start、user/message、assistant/chunk、tool/call、tool/result、step/end、turn/end等所有边界都写入持久化会话日志——fork、resume、replay、telemetry 全部从同一条事件流派生。 - DeepSeek Harness agent loop 暴露四个无需改源码即可拦截的扩展点:
agent/pre-step(waterfall)、agent/request(waterfall)、agent/request-error(waterfall)、agent/turn-stopping(serial)。工具执行走三段流水线:tools/pre-execute → tools/execute → tools/post-execute。 - 为 DeepSeek Harness agent loop 开发插件,DSH Plugins 目录是发现、收录、分享插件的最佳去处。
目录
- 什么是 DeepSeek Harness Agent Loop?
- 两个层次:turn 与 step
- 状态机
- 完整流程分步解析
- 工具执行:三段流水线
- 持久化事件 vs 扩展点
- 为什么可替换的 Agent Loop 很重要
- DSH Plugins 生态
- 常见问题解答
- 总结
什么是 DeepSeek Harness Agent Loop?
DeepSeek Harness agent loop 是驱动 Agent 持续工作的引擎:它读取输入、请求模型、执行工具,并判断 Agent 是否还欠模型一次请求。关于它最值得深挖的事实是:循环本身就是一个插件。
在 packages/core/agent-loop 中,默认实现是 ReactLoopAgent,它实现 core/agent 定义的 Agent 接口。其他插件只依赖这个接口,从不依赖具体的循环类。这一架构决策意味着 DeepSeek Harness agent loop 可以被整体替换:挂载一个实现 Agent 接口的其他插件,所有消费者照常工作。接口是契约,循环是实现,Harness 是组合。
这与 DeepSeek Harness「一切皆插件」的整体哲学一脉相承。Agent loop 不是特权核心组件——它是众多插件中的一个,与其它插件共享同一套事件系统。
专业提示: 读代码时,先从
core/agent的Agent接口入手,再读ReactLoopAgent。循环做的一切都是对该契约的响应,而接口正是你自己的循环插件必须满足的东西。
两个层次:turn 与 step
DeepSeek Harness agent loop 把工作组织成两个嵌套层次:turn 与 step。
- turn(轮) 是零或多个 step。它在收到第一条输入时打开,在「不再欠模型任何东西」时关闭——即没有挂起输入、也没有需要另一次模型请求的工具结果。
- step(步) 是一次模型请求加上它调用的工具。每个 step 是经过模型的一次往返,随后执行模型要求的工具调用。
模型可见的历史从不单独存储,而是通过 deriveMessages() 从追记式会话日志投影而来。模型能看到的一切都必须能从日志重建——这是 docs/architecture.md 明文记载的架构不变量。实际后果是:会话日志是唯一事实来源,模型的上下文窗口永远是它的派生视图。
| 层次 | 定义 | 边界事件 |
|---|---|---|
| turn | 零或多个 step;首条输入打开,模型不再欠回应时关闭 | turn/start、turn/end |
| step | 一次模型请求 + 它调用的工具 | step/start、step/end |
| message | step 内的用户或助手消息 | user/message、assistant/message |
状态机
DeepSeek Harness agent loop 的驱动器由一个小型状态机门控:Phase = idle | maintenance | running。
idle— 没有驱动器在跑,Agent 等待输入。maintenance— 驱动器拆除或准备期间的过渡状态。running— 驱动器在跨越多个连续 turn 的整个排空区间内活跃。
状态切换发出 agent/status 事件,任何插件都能在不触碰循环的情况下观察其生命周期。这是 DeepSeek Harness agent loop 反复出现的模式:状态变化是事件,事件就是扩展面。
完整流程分步解析
以下是 DeepSeek Harness agent loop 的完整流程,对应 packages/core/agent-loop/src/agent.ts 的实际代码。
第 0 步:唤醒与驱动循环
输入通过 send / followup / steer / inject 进入,把消息塞进 Inbox 的两条有序队列(next-turn / next-step)。followup 和 steer 还会唤醒驱动器。
wakeDriver()— idle 时占一个runningphase(新 AbortController,turn=上一轮,step=0),在ctx.agents.withInitiator(this, …)里跑kick()。kick()—while (await this.turn()) {}:只要有挂起输入就一直开新 turn。
第 1 步:开 turn
turn() 先 session.append('turn/start', { turn })——持久化开轮边界。然后循环处理每一步。
第 2 步:pre-step——认领输入 + 拦截决策
preStep() 做三件事:
inbox.claim(target, turn)— 认领本步的输入批次(全部next-step消息,轮边界时再带一条next-turn)。认领是纯删除式 splice,每条消息发agent/inbox/claimed。ctx.systemPrompt.assemble(...)— 组装 prompt 段与工具 schema。dispatch.waterfall('agent/pre-step', …)— 第一个扩展点。监听器可以reject(不开步,turn 直接以blocked结束)或enter并改写消息批次;默认enter用认领的消息。返回{ reject }或{ enter, messages, assembly }。
特例: 第一步被改写为空 → turn 仍占边界但不花一次模型调用,以
completed结束。
第 3 步:开 step + 落 user 消息
session.append('step/start', { turn, step })— 持久化。- 对
decision.messages逐条session.append('user/message', …)— 持久化。认领进来的输入此刻才落成 durable 用户消息。
第 4 步:构建请求 → 流式 → 组装消息 → 工具
step() 内部循环(支持重试):
4a. 构建请求 buildRequest():
dispatch.waterfall('agent/request', …)— 第二个扩展点。监听器可替换冻结的调用配置(provider/model/reasoningEffort/maxTokens);默认用 agent 选项或已记录的 header。ctx.llm.prepareCall(config, signal)— 绑定到具体 adapter,物化 exact-model 默认值。session.append('request/header', …)与request/context(变化时才记)— 持久化,保证日志可重建请求。- 组装冻结的
request(config +deriveMessages()历史 + system + tools + sessionId + signal)。
4b. 流式:
preparedCall.stream(request) ?? ctx.llm.stream(request)— 发请求、读流。for await chunk— 每片session.append('assistant/chunk', …)(持久化,保原始流保真,供 replay/UI)+ 推进BlockAssembler。
4c. finish 分流 assembler.finish:
error/aborted→dispatch.waterfall('agent/request-error', …)— 第三个扩展点。监听器返回{ kind: 'retry' }(不调next)则重试本步;默认undefined让失败终止(抛LlmError)。max-tokens→ 追加assistant/message,返回{ kind: 'max-tokens' }。- 正常 → 追加
assistant/message({ turn, step, message, usage },sourceEventSeqs引用对应 chunk)— 持久化。
4d. 工具调用:
- 从 assistant 消息滤出
tool-call块。没有 → 返回{ kind: 'completed' }(本步结束,模型不欠回应)。 - 有 →
executeToolCalls(...)(tool-calls.ts)。
第 5 步:关 step / 判定是否再开步
finally: session.append('step/end', { turn, step })— 持久化。- 若本步产生终止结果(
turnEnds非 null)且 inbox 没有next-step输入: dispatch.serial('agent/turn-stopping', { turn, signal })— 第四个扩展点(serial,无 next)。监听器若反对,调agent.steer(...)塞 steering,机器重读 inbox → 再开一步;没人反对 → 关 turn。- 有
next-step输入 →target='next-step',回第 2 步再开一个 step(工具延续)。
第 6 步:关 turn / 判定是否再开轮
finally: session.append('turn/end', { turn, reason: turnEnds })— 持久化。reason是completed/max-tokens/blocked/aborted/error。- 若 inbox 还有挂起输入 → 重置 AbortController、
step=0,返回 true(开新 turn);否则回idle。
工具执行:三段流水线
DeepSeek Harness agent loop 的工具调度按 execution mode 分组:互斥调用是 barrier,并行调用用有界滚动池(maxParallelToolCalls)。每个调用走三段流水线,事件挂在 ctx.tools 的 scheduler 上——这是策略/超时/观测的挂载点:
tools/pre-execute → tools/execute → tools/post-execute
prepare(pre-execute)可能短路成直接结果。dispatch(execute)跑工具。finalize/finish(post-execute)收尾。
两个细节值得强调。第一,结果按模型顺序提交(commitReady 跨连续槽推进),而不是按完成顺序——模型的世界观保持一致。第二,tool/call 在派发前追加(持久化),tool/result 在 post-execute 后追加(持久化,引用对应 call seq)。结果的 additionalContexts 进 next-step inbox,成为下个 step 边界的上下文;concludesTurn 的结果提前结束本 turn。
返回值是 { concluded }:concluded → { kind: 'completed' };否则返回 null,表示工具还欠一次模型请求——回到 4a 再跑一轮。
持久化事件 vs 扩展点
DeepSeek Harness agent loop 的整个设计可以浓缩成一张图:持久化事件(记录)与扩展点(接缝)。
turn/start (durable)认领 inbox + 组装 prompt─ agent/pre-step (waterfall) reject | enter(messages) ─step/start (durable)user/message* (durable)─ agent/request (waterfall) 换配置 ─llm/stream → assistant/chunk* (durable) → assistant/message (durable)tool/call* (durable) → tools/pre-execute → tools/execute → tools/post-execute → tool/result* (durable)─ agent/request-error (waterfall) 失败时 retry ─step/end (durable)工具还欠请求 or 有 next-step 输入 → 再开一步─ agent/turn-stopping (serial) 没延续就关轮 ─
turn/end (durable)
| 扩展点 | 类型 | 插件能做什么 |
|---|---|---|
agent/pre-step |
waterfall | 拒绝本步,或 enter 并改写消息批次 |
agent/request |
waterfall | 替换冻结的调用配置(provider/model/effort/tokens) |
agent/request-error |
waterfall | 返回 { kind: 'retry' } 重试失败的 step |
agent/turn-stopping |
serial | 通过 steering 反对关轮;循环重读 inbox 再开一步 |
带 ─ ─ 的是插件可挂的扩展点(waterfall 必须 next() 才向下传);其余带 (durable) 的是写进会话日志的持久化事件——fork/resume/转写/telemetry 全从这条流派生。这套设计的意义就是:换掉某个 adapter、加策略、拦截请求/工具/turn,都是挂事件或换 provider,不需要改 loop 本身。
为什么可替换的 Agent Loop 很重要
DeepSeek Harness agent loop 是插件这件事不是实现细节,而是产品本身。三个推论:
- 无需 fork。 需要不同循环行为(不同重试策略、不同工具调度策略、不同关轮启发式)的团队,写一个实现
Agent的插件即可,而不是维护一个 Harness 的 fork。 - 策略住在接缝上。 超时、审批门、限流、观测都挂在
ctx.toolsscheduler 和四个扩展点上,而不是循环代码内部。 - 日志即契约。 因为每个模型可见的产物都能从追记式会话日志重建,任何循环实现——默认或自定义——都能用同一套工具审计、回放、恢复。
✅ 最佳实践: 写自定义循环之前,先确认你的需求是不是一个可以挂在现有扩展点上的策略。DeepSeek Harness agent loop 的设计目标就是让大多数定制根本不碰循环。
DSH Plugins 生态
DeepSeek Harness agent loop 与其它所有能力都是插件,因此围绕它的生态与核心同样重要。DSH Plugins 目录是发现和收录 DeepSeek Harness 插件的社区枢纽——包括循环替换、工具适配器、模型提供商、策略插件。为 DeepSeek Harness agent loop 开发时,把插件收录进去能让社区发现它;动手前先浏览,也能避免重复造轮子。
常见问题解答
Q: DeepSeek Harness agent loop 真的是插件吗?
A: 是的。packages/core/agent-loop 提供 ReactLoopAgent 作为 core/agent 的 Agent 接口的默认实现。其他插件只依赖接口,因此挂载另一个插件即可整体替换循环。
Q: turn 和 step 有什么区别?
A: turn 是零或多个 step,首条输入到达时打开,模型不再欠回应时关闭。step 是一次模型请求加上它调用的工具。两者在会话日志中都有持久化的开始/结束事件。
Q: 模型的历史从哪里来?
A: 不单独存储。DeepSeek Harness agent loop 通过 deriveMessages() 从追记式会话日志投影模型可见的历史。模型能看到的一切都必须能从日志重建。
Q: DeepSeek Harness agent loop 有哪些扩展点?
A: 四个:agent/pre-step(waterfall——拒绝或改写输入批次)、agent/request(waterfall——替换调用配置)、agent/request-error(waterfall——失败重试)、agent/turn-stopping(serial——通过 steering 反对关轮)。
Q: 工具结果按什么顺序提交?
A: 按模型顺序提交(commitReady 跨连续槽推进),而不是按完成顺序,保证模型的世界观一致。
Q: 在哪里可以找到 DeepSeek Harness agent loop 的插件?
A: DSH Plugins 目录收录了 DeepSeek Harness 的社区插件,包括循环替换、工具适配器、策略插件等。
总结
DeepSeek Harness agent loop 是可替换架构的教科书。通过定义 Agent 接口、把 ReactLoopAgent 作为众多实现之一、把每个边界持久化到追记式日志、并暴露四个 waterfall/serial 扩展点,循环把 agent harness 中最容易僵化的部分变成了最灵活的部分。
对开发者的实际启示:不理解循环也能用它,但理解了才能打开接缝。在 agent/request 拦截请求、在 agent/request-error 重试失败、在 agent/turn-stopping 门控关轮,或者用你自己的 Agent 实现整体替换循环。当你做出值得分享的东西,DSH Plugins 目录就是生态发现它的地方。循环是 DeepSeek Harness 的心脏——而且是一颗可以移植的心脏。
