Files
deepseek-harness/docs/rfc/implemented/feature/2026-07-19-fresh-agent-ralph-workflow-tool.zh.md
T
2026-07-20 00:51:19 +08:00

7.8 KiB
Raw Blame History

RFC:全新 agent Ralph 工作流工具

Status: implemented

English | 中文

问题

同会话目标会保留对话,让一个 agent 持续完成持久目标;通用工作流工具则让模型编写扇出编排脚本。两者都不是 Ralph 模式:把同一目标反复交给完全全新的工作者,以共享工作区作为长期记忆,并且在各轮之间只传递一份小型显式交接,直到工作完成或触及限制。

如果把 Ralph 行为加入 dsh-agent-loop、目标驱动器或面向模型的公开工作流语言,就会让一项策略与无关的执行机制耦合。让每个子 agent 继承父对话也会破坏上下文重置,并让重放依赖不断增长的隐式前缀。此功能需要一项由现有插件原语组合而成的固定、可评审策略,同时具备取消静止性、有界跨轮数据、宽裕且可配置的上限,并且不引入新颖的面向人类目标状态。

决策

在 packages/workflow/ 下新增独立消费者包 @deepseek-ai/dsh-tool-ralph。它注册 ralph({ objective, maxRounds? }),拥有固定工作流脚本,并且只依赖 ctx.tools、ctx.systemPrompt、ctx.workflows 和 ctx.subagents。Ralph 运行不是会话目标,不会创建目标状态,也不要求在具体 agent loop 中增加分支。

该工具仅以前台方式运行。调用 agent 作为每个子 agent 的父级以提供 cwd 和谱系,父工具调用等待整次运行结束,父步骤的中止信号会取消工作流。每条路径都会等待 run.dispose(),因此调用返回前,取消会经过工作流引擎的有界收敛并达到子 agent 静止状态。

每次运行的工作流 provider 路由

WorkflowStartRequest 新增可选的 subagentProvider。工作线程引擎先解析这个显式的每次运行值,再回退到引擎配置的 provider,并把结果用于该运行中的每次 agent() 调用。脚本无法观察或替换此路由。普通 workflow 工具不设置该字段,也不暴露新的模型参数,因此通用工作流行为和 provider 策略保持不变。

Ralph 插件的 subagentProvider 默认为 spawn。每次调用前,它要求具名 provider 已存在、支持结构化输出且报告 inheritsParentContext: false;类似 fork 或能力不足的 provider 会在工作流启动前响亮失败。provider 查找保留在调用期,因为效果作用域内的 provider 注册可能随 HMR 改变。

Ralph 轮次与交接

层级为 Ralph 运行 → Ralph 轮次 → 全新子 agent 回合 → 步骤。每个 Ralph 轮次恰好通过所选 provider 创建一个子 agent。Spawn 给该子 agent 一个没有种子的独立会话,同时保留父级 cwd,因此共享工作树是持久权威,父对话和先前子 agent 历史都不会进入请求。

固定提示只传递不可变目标、当前轮次与上限、以工作区为权威的指令,以及上一份结构化报告。RalphRoundReport 包含 status: continue | complete | blocked、summary、evidence、nextSteps 和 blocker。字符串必须规范化;continue 要求存在下一步且没有阻塞项,complete 要求存在证据且没有下一步或阻塞项,blocked 要求具体阻塞项。报告成为下一次交接前,脚本会验证语义与序列化大小;消费者还会跨工作流接缝再次验证实体化的终止值。

maxRounds 默认为 256,同时也是调用覆盖值的部署上限。maxHandoffChars 默认为 16384。两者都是正安全整数配置值;过大的报告会失败,而不会被静默截断。最后一个允许轮次报告 continue 后,固定脚本返回 budget-limited;complete 和 blocked 会立即返回最终报告与已启动轮次数。

模型与 UI 表面

模型只能提供 objective 和可选的 maxRounds;provider 选择、报告 schema、交接上限和脚本都由部署拥有。固定提示区段说明,只有直接人类明确要求 Ralph 或全新 agent 迭代时才使用 ralph,并将其与同会话目标、有界委派和通用扇出工作流区分开。这是指导,而不是新的目标 UX 状态机。

ACP 和终端展示使用通用 ralph 卡片,并把目标作为原始输入。父转录只保留原始工具调用和一份有界终止报告,不包含中间子 agent 消息。发布的无头、REPL/TUI 与 ACP 组合会在现有工作流引擎旁加载该插件;JSON-RPC 保持不变,因为其默认组合不暴露工作流。

测试

单元测试覆盖配置与调用上限解析、provider 能力拒绝、固定启动请求路由、三种终止结果、畸形及过大边界值、中止时序、处置、渲染意图、提示生命周期和命名空间插件形状,并达到逐文件 100% 覆盖率。工作流引擎测试证明,每次运行的 provider 覆盖会选择每个子 agent 且不改变配置默认值,其中包括普通 Node 下构建后的 lib/worker.cjs。

一项无密钥真实栈集成测试通过实际工作线程引擎、spawn provider、结构化输出运行时和 agent loop 驱动固定脚本。它证明子 agent 标识不同、没有 seedLength、继承 cwd、两个子请求都不含父历史标记、上一份报告只精确出现在下一轮交接中、终止完成以及两个子 agent 都被处置。工具测试固定通用调用/结果展示,而 ACP 重放请求头快照固定发布的 schema 与提示指导转录表面。

考虑过的替代方案

  • 把 Ralph 放进同会话目标驱动器 — 拒绝,因为目标轮次有意保留同一段对话,而 Ralph 的定义性属性是每轮使用全新上下文;合并两者会让目标生命周期与子 agent 编排无法分离。
  • 在通用工作流工具上暴露 fresh 或循环标志 — 拒绝,因为模型编写的脚本表面应保持通用且与 provider 无关;Ralph 的固定报告协议和停止策略值得拥有一个可评审消费者。
  • 为了方便重放而使用 subagent_fork — 拒绝,因为继承的已完成回合是隐式、不断增长的交接状态,并违反全新上下文契约。工作区加一份结构化报告即可重放,无需插入人为取消记录。
  • 让工具直接调用 subagent 接缝 — 拒绝,因为现有工作流引擎已经拥有前台编排、结构化子 agent、取消传播、工作线程终止、事件和静止处置。复用它可以展示插件组合,而不是构建第二个循环运行时。
  • 静默截断大型报告 — 拒绝,因为截断可能删除状态证据或下一步,却仍看似权威交接。生产者必须在配置边界内发出有效报告。

后果

  • 全新 agent 迭代成为一项一等模型工具,并完全以现有接缝之上的可移除插件实现。
  • 目标轮次与 Ralph 轮次保持不同概念:前者是一次同会话续行回合,后者是前台工作流中的一个全新子 agent。
  • 工作区成为权威跨轮记忆,因此工作者必须检查和验证工作区,而不能信任叙事性交接。
  • 宽裕的轮次上限允许大量自治工作,而部署配置仍会限制子 agent 数量,并且每次交接始终受大小约束。
  • provider 路由成为显式的工作流启动关注点,但不扩展脚本或普通工作流工具表面。

已知限制与推迟工作

  • 完成与阻塞状态由工作者自行声明。独立 evaluator、evaluator 驱动的反馈轮次、完成证书或对抗式 verifier 被有意推迟。
  • 运行位于前台且只存在于进程内。后台收集、持久化/恢复、调度和重启恢复均不存在。
  • 轮次数是唯一聚合预算。token、货币、耗时和 provider 用量预算仍属于未来的独立策略。
  • 每轮创建一个子 agent。轮内扇出、evaluator/工作者角色分离、动态 provider 或模型选择,以及跨运行日志均被推迟。
  • 提示指导模型不要递归调用 Ralph;结构化的子 agent 工具限制需要另行设计工作流子策略表面。