资讯动态

DeepSeek Harness Web 队列插话机制:严格 Steering Action 的架构设计与实现解析

发布时间:2026/9/20 23:48:53 来源:尧图企业网站定制
DeepSeek Harness Web 队列插话机制严格 Steering Action 的架构设计与实现解析【免费下载链接】deepseek-harnessDeepSeek Harness: Everything is a Plugin.项目地址: https://gitcode.com/gh_mirrors/de/deepseek-harness导读本文基于 DeepSeek Harness 的 Agent Note《Steer a queued Web message into the active turn》记录于.agents/notes/implemented/feature/2026-07-30-web-queue-steer-action.md深入解析 Web 端插话发送Steer into active turn这一核心交互的实现。你将理解为什么 Web 在 Agent 运行期间把 Enter 提交的消息排入 Queue、又如何通过一条严格的steerAction 将队列中的消息插入当前回合以及QueueAction、placement、steer-unavailable等关键契约背后的 AgentLoop、Host 快照与客户端渲染的完整协作链路。一、背景Web 消息队列与插话问题的由来1.1 QueueDockAgent 运行期间的待发送消息队列在 DeepSeek Harness 的 Web 端当 Agent模型回合正在运行时用户在 composer输入框里按下 Enter 提交的新消息并不会直接打断当前回合而是进入一个名为QueueDock的待发送队列。该队列有以下特性每条 pending 消息都拥有一个可寻址的行addressable row可被单独编辑、删除或提升队列行的内容由 Session 控制队列control queue承载并通过session/queue快照同步到客户端。与此同时会话的**持久化记录durable transcript**已经具备把已消费的 steer 事件渲染成用户风格气泡的能力。换句话说仓库中已经存在两块表面surface——QueueDock 行与 durable 用户气泡——但Web 端缺少一个把两者连接起来的行级操作也缺少一个在 composer 中直接选择插入当前回合的手势。1.2 朴素方案的原子性问题在引入正式机制之前一个直观但危险的实现是客户端先删除队列行再调用session.prompt(mode: steer)。原文档明确指出这种方案存在三个并发风险见原文档 Problem 一节驱动权driver claim竞态删除与 steer 是两次独立 RPC两次调用之间驱动可能已认领claim该消息删除后 steer 失败行已被移除但消息未能插入当前回合用户意图丢失best-effort 回退副作用现有的agent.steer()是尽力而为语义若下一个回合窗口已关闭它会静默地重新追加一条新的 Queue 项导致用户看到原行消失后又冒出新的队列项。因此立即发送操作必须区分当前回合插话与队列提升两种语义并且在插话不可行时保留原始队列行。这正是本次实现要解决的核心设计问题。二、产品契约什么是插话发送2.1 QueueDock 行的向上箭头产品定义QueueDock 中每一条非编辑态、普通会话的行都会暴露一个向上箭头操作即插话发送。其启用规则与行为如下场景行为会话报告 Agent 正在运行running bit箭头启用可触发插话会话空闲无运行中的 Agent箭头禁用Tooltip 提示不可用混合内容消息mixed-content如文本 附件仍然可用——steer 转发的是完整的不可变UserMessage而非行的文本投影被寻址的子代理addressed subagent的 Queue 行只读因为其续跑传输continuation transport不暴露队列变更能力实现上这个箭头在 QueueDock.tsx 中渲染按钮的aria-label绑定本地化文案queue.steer插话发送仅在running为真时可用点击后派发{ kind: steer }操作失败时展示queue.steerFailed提示。禁用状态下 title 提示为queue.steer.unavailable。2.2 触发后的端到端行为激活箭头会请求对该确切InboxItemId进行严格的当前回合插话成功路径通过权威的 Host 快照移除该 Queue 行并立即在 Deep diving...运行中状态行之后投影同一条 pending steering 气泡该气泡只有Copy操作没有Fork——因为此刻消息还没有 durable 事件序列投递路径一旦 AgentLoop 消费drain该插话既有的 durableuser/message事件接管同一个用户风格气泡并恢复其时钟clock、Copy 与 Fork无需单独的 durable 展示路径失败路径窗口已关闭若同步变更边界上的acceptsNextStep已为假操作保留原 Queue 行不变返回类型化错误steer-unavailable原始等待消息继续按 Queue 顺序投递失败路径已被驱动认领返回既有错误queue-item-not-found此时独立回合投递已在路上。关键设计running bit 只是交互提示真正的裁决者是 AgentLoop 在同步变更边界的acceptsNextStep值。UI 对上述两种语义竞态race都视作已收敛到 Queue 投递不弹失败提示但传输层错误与未知错误仍然会如实呈现。2.3 composer 的尽力而为契约对于新键入的输入composer 走另一套独立的契约会话状态EnterCmd/CtrlEnterShiftEnter被寻址会话空闲Queue 发送Queue 发送换行主会话运行中默认设置Queue 发送Steer 发送换行主会话运行中偏好设为 SteerSteer 发送Queue 发送换行被寻址子代理两者均为 Queue 续跑传输两者均为 Queue 续跑传输换行偏好由General Settings保存持久化在 Host settings 文档中跨共享同一 DSH home 的 Web 源生效它只影响可 steer 的忙碌状态手势对steer-capable busy-state gesture pair。若 composer 的直接 Steer 错过了当前 next-step 窗口AgentLoop 会自动把它接纳为下一个唤醒的 Queue 回合Web 端不报失败——这正是新输入无既有队列行可保留因此采用 best-effort的语义差异详见下文 Alternatives 分析。三、Agent 与生命周期边界InboxAction的严格 steer3.1 新的 Action 类型InboxAction在既有的edit与remove之外新增了消费者驱动的{ kind: steer }操作。其类型定义位于 types.ts/** One client-requested mutation of a still-pending queue item. */ export type QueueAction | { readonly kind: edit; readonly content: readonly ContentBlock[] } | { readonly kind: remove } | { readonly kind: steer }Agent.updateInbox()只在定位到该 queued occurrence 并证明acceptsNextStep为真之后才处理该操作它绝不委托给 best-effort 的agent.steer()别名。这是严格语义的关键行操作要么在当前回合窗口内成功插话要么明确拒绝并保留原行。3.2 生命周期事实一个 occurrence 结束一个 steering occurrence 开始应用一次 steer Action 后生命周期发生如下转换原 queued occurrence 结束同一个不可变UserMessage被接纳为一条新的 steering occurrence并获得新的InboxItemId与真实的placement: steering消息本身保留其MessageId、内容、来源以及任何待处理的SteeringReceipt投递控制器。从源码的类型定义types.ts可见快照中的每条 occurrence 都带placement标签readonly placement: queued | steering | context事件发布顺序不可颠倒AgentLoop 先安装新的 outbox 条目发布其 enqueue然后才发布旧 occurrence 的 discard。这样可重入的取消re-entrant cancellation不会观察到或回收一条未宣告unannounced的条目。因此既有的收件箱守恒不变量inbox conservation invariant继续成立——每个 occurrence 必须恰好有一次 enqueue 和一次终态 dequeue 或 discard。3.3 不做什么该 Action不运行agent/prompt-submit选择插话意味着把投递方式从独立准入的回合改为当前回合的下一步输入它既不取消当前工作也不重排剩余 Queue 的顺序。四、Host 与客户端边界快照、镜像与顺序保证4.1session.updateQueue与类型化错误session.updateQueue承载steer操作并把两种负面结果映射为类型化 RPC 错误steer-unavailable携带itemId见 types.tsnext-step 窗口已关闭原行保留queue-item-not-found携带itemId驱动已认领独立回合投递已在进行。整个转换是一次同步的 Agent 操作Host 永远不会用remove prompt 调用的组合来重建它——这正是对朴素方案原子性缺陷的根本修复。4.2queuedMirror唯一的瞬态收件箱权威Host 侧既有的queuedMirror实现于 queue-mirror.ts仍是唯一的瞬态收件箱权威但语义扩展为placement-awaresession/queue快照携带每一条活 occurrence含placement: queued | steeringQueueDock 只渲染 queued 行ChatView 在会话尾部Deep diving... 运行状态行之后渲染 pending steering提供 Copy 但无 Fork、编辑、删除操作重连reconnect会重放同一快照因此这套可见性不需要客户端乐观更新也不需要第二个注册表registry。4.3 pending → durable 的有序交接当 AgentLoop 认领 pending steering 时顺序至关重要AgentLoop先发出agent/inbox/dequeue紧接着同步追加durable 的user/messageHost 在**下一个微任务microtask**中退休retire那条 steering 行让 durable 会话事件先进入线性 mux 流客户端 Session 在已接受的 live 事件上先退休第一个匹配的当前 steering occurrence再发布快照——历史重放不会消费后来复用同一MessageId的 occurrence。由此 ChatView 做到任一时刻只渲染一个权威无需扫描 durable 历史durable 投影则依据其日志时间与序列恢复时钟、Copy 与 Fork。即便追加失败被认领的行也会被退休避免悬挂。4.4 composer 的显式模式路由既有的session.prompt(mode: steer)契约对新输入保持 best-effort在 next-step 窗口之外它退化为唤醒后的后续消息。composer 在调用该契约前会通过**斜杠裁决slash adjudication与引用序列化reference serialization**携带显式的queue | steer模式对应 types.ts 的readonly mode: queue | steer。职责划分如下浏览器提交策略browser submission policy拥有忙碌时 Enter偏好的运行时裁决权仅在可 steer 的会话上把普通 Enter 与加速 Enter 解析为互补手势Host settings 服务拥有持久化所有权Settings 行与 InputBar 共享同一策略实例不复制存储也不重复拥有投递窗口的裁决权。唯一严格的是 Queue 行操作——因为它的两种负面结果都会收敛回原始 Queue occurrence。五、验证体系契约测试、端到端与快照原文档的 Verification 一节勾勒了覆盖矩阵仓库中可交叉印证AgentLoop 契约测试保持 prompt admission 窗口开放、转换恰好一条 queued occurrence、证明替换后的 steering occurrence 保留消息值与投递回执、以user/message形式排空且绝不启动其原独立回合同时钉死窗口关闭时的行保留、已认领地址的拒绝以及可重入取消的生命周期守恒。Host schema 与代理测试如 commands-queue-attachment.host.spec.ts覆盖新 Action、两种类型化错误、placement-aware 快照与重连重放以及 durable 先于 retirement 的排序。客户端测试覆盖两种语义竞态的静默收敛、真实错误上报、子代理行的只读与子代理手势的 Queue-only。运行时与 ChatView 测试覆盖 occurrence 感知的 pending → durable 交接包括重复MessageId值的场景。Web ARIA 快照覆盖运行状态行之后仅带 Copy 的 pending steering以及带时钟、Copy、Fork 的 durable 节点。Web e2e 场景steering.e2e.ts通过真实 composer 在首个响应流式输出时排队一条消息激活行箭头再用ask_user_question作为稳定的 pending-steering 屏障验证Host 支撑的 pending 气泡在准入前出现、应答后交接为恰好一条 durable 插话、并影响下一个模型请求。组装型 composer 场景还证明默认模式下 CmdEnter 走 pending/durable 路径而不创建 Queue 行而 Steer 模式下 CmdEnter 会创建 Queue 行。Settings 与提交策略覆盖默认值、持久化、忙碌态作用域与互补手势映射既有 Queue 编辑/删除场景则继续证明这些操作不受影响。六、被否决的方案与取舍原文档的 Alternatives 一节值得逐条保留它们是理解最终设计的钥匙候选方案否决理由删除行后再调用session.prompt(mode: steer)两次 RPC 无法使删除与插话原子化失败与驱动认领竞态会丢失或重复用户消息在向上箭头下恢复Queue 提升移到队首移到队首仍会产生一个独立准入的回合该控件承诺的是当前回合插话而非 Queue 内优先级对 Queue 行复用 best-effortagent.steer()窗口关闭时会新建一条 queued occurrence可能位于不同位置且身份不同严格拒绝才能保留原 occurrence使 UI 将其视作同一条已接受的 Queue 投递。新键入的 composer 输入没有既有队列行可保留故有意使用 best-effort让agent.steer()对所有调用者都变严格TUI 与插件调用者依赖其安全的后续消息回退来处理新提交的输入而队列行有可恢复状态它们没有保持同一InboxItemId仅改 placementInboxItemId标识一次 FIFO 接纳placement记录该接纳的最终投递方式结束一个 queued occurrence 并接纳一个 steering occurrence才能保持生命周期事实真实、守恒不变量不变为 pending steering 增加专用投影与客户端 storequeued 与 steering occurrence 本就共享同一个 Agent 收件箱生命周期与同一个 Host 镜像第二套投影会复制重连状态与排序权威。一个 placement 标签即可让各客户端表面选取自己的行且无需拓宽 Queue 变更语义取消当前回合并运行所选 Queue 项会销毁无关的在途工作且开启的是新回合而非插话当前回合七、后果与消费方注意事项原文档的 Consequences 一节指出了三个对下游最重要的影响session/queue语义扩展它现在是placement-aware 的瞬态收件箱快照而不再是纯 Queue 列表——每个消费者都必须按 placement 过滤pending steering 的持久性边界它跨重连存活并立即出现但在 durableuser/message提交前是非持久的running bit 的短暂失真严格 next-step 窗口关闭后running bit 可能短暂保持为真因此一个已启用的箭头内部可能返回steer-unavailable而产品仍通过 Queue 继续投递、不报告失败。此外还有两点架构级结论显式 Action 把投递方式从独立准入的回合改为当前回合插话因此prompt-admission 插件不会处理被转换的消息而enqueue-before-discard 的生命周期发布顺序是重入取消安全性的硬性要求需要专门的回归测试保护原文档明确要求 focused regression coverage 保护该顺序。结语插话发送并非简单地把 Queue 行提前而是通过一条新的严格InboxAction、placement-aware 的收件箱快照以及严格界定的 enqueue/dequeue/retire 顺序在保证消息不丢失、不重复、不重排的前提下把一条已排队消息安全地注入当前 Agent 回合。它同时证明了 DeepSeek Harness 的一个核心设计哲学Everything is a Plugin——即使是 UI 上的一个箭头其语义边界、生命周期不变量与并发安全也被提升到了架构级契约的高度。如需继续深入可分别阅读 QueueDock 行实现、Session 控制队列类型、queuedMirror 交接逻辑与 Web 端到端场景。【免费下载链接】deepseek-harnessDeepSeek Harness: Everything is a Plugin.项目地址: https://gitcode.com/gh_mirrors/de/deepseek-harness创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价