Files
deepseek-harness/docs/rfc/implemented/simplification/2026-06-20-public-agent-stop-surface.zh.md
Ziya 2565133af3 docs(i18n): RFC tree batch — 146 bilingual pairs via the committed pipeline
implemented(除 4 篇超长文档随后补)、proposed、rejected 全树配对;
同一流水线 + 二遍校验(paraphrase-back + 仓库上下文一致性)产出。
docs/rfc/implemented/AGENTS.md 与其 CLAUDE.md 符号链接列入排除
(agent 指令文件,与根 AGENTS.md 同策略)。
2026-07-15 23:25:06 -07:00

4.4 KiB
Raw Blame History

RFC:保留单一公开停止原语

English | 中文

Status: implemented

实现说明: 仅移除了 abort()。whenIdle() 予以保留,因为它是公开的静默信号,能安全处理等待者结算与替换轮次竞态;消费方不应从状态转换中自行重建该行为。

问题

公开的 Agent 句柄暴露了两种重叠的方式来停止进行中的工作:abort(reason?) 与 cancel(reason?)。abort() 仅终止当前正在执行的步骤,不影响队列中的工作;cancel() 清除队列中的工作与 steering(中途引导),终止正在运行的步骤,并处理步骤前竞态。在生产环境中,ACP(Agent Client Protocol)使用 cancel() 实现 session/cancel,而生命周期所有者通过 AgentHandle.dispose() 拆除 agent。没有生产调用方需要裸 abort()。

abort() 与 cancel() 的区别是真实存在的:abort() 保留队列中的提示词和 steering,而 cancel() 丢弃它们。但没有已发布的代码调用过公开的 abort() 动词。agent loop(智能体循环)自身的停止路径(cancel() 与 dispose)直接终止当前 AbortController,而非经由 Agent.abort() 路由。大多数调用 abort() 的测试实际上中断的是空队列,可以改用 cancel(reason);那个刻意依赖队列保留的 steering 重投递测试则直接驱动进行中的 AbortController,因为 cancel() 会丢弃它试图证明在步骤终止后仍存活的队列 steering。无参 abort() 的默认原因('aborted')随动词一起删除,而非意外保留;cancel() 保留自己的默认值 'cancelled'。

多余的公开接口面使 agent loop 不得不承载一个本质上是拆除内部机制的公开动词:abort() 必须被文档描述为与队列感知的取消不同,尽管 UI 取消几乎总是需要更广义的操作。

决策

cancel() 是 Agent 上唯一的公开停止原语。生命周期所有者使用 AgentHandle.dispose() 停止并注销 agent;非所有者使用 cancel() 放弃当前与队列中的工作。实现内部保留一个私有 abort controller,但它不属于面向插件的 Agent 契约。

whenIdle() 作为公开的静默观测原语予以保留(agent 脱离 running 状态后 resolve;已处于 idle 时立即 resolve;dispose 后等待循环退出)。它不是停止动词;它是非所有者观测停止完成而无需 dispose agent 的方式。它的活跃消费方是 ACP 和通过此公开 seam 等待结算的 agent 测试(packages/ui/acp/tests、packages/core/agent-loop/tests);生产环境的 ACP 桥接层拥有其 agent 并通过 AgentHandle.dispose() 拆除它们,因此 packages/ui/acp/src 本身没有 whenIdle() 调用。

公开的 abort() 被删除,连同将其作为独立 API 测试的用例以及将步骤级终止描述为嵌入特性的文档。空队列终止测试迁移到 cancel(reason),仍然验证取消行为;测试对象为 agent loop 内部 AbortController 的测试通过包内类型转换直接驱动该 controller 的私有字段;仅固定已移除的无参 abort() 默认值的测试随方法一起删除。disposer 仍为异步,仍等待循环停止。

曾考虑的替代方案

同时移除 whenIdle():最初提案的形态,在对照代码验证前提后被推翻(上方的实现说明记录了完整过程):它是承重的静默原语,强迫消费方手动观测 running→idle 转换正是防御性模式所警告的脆弱路径。

验证

Agent 不再暴露公开的 abort(),而 cancel()、whenIdle() 与 steer() 保留;ACP 取消调用 cancel();拆除通过 handle disposal 等待静默,whenIdle() 为非所有者观测者在静默时 resolve;测试套件覆盖取消与 disposal 作为两条受支持的停止路径。

后果

未来的插件无法通过公开接口仅终止当前模型/工具步骤而保留队列中的提示词。如果该用例变为现实需求,它应当带着一个具名消费方和更窄的契约重新引入。目前它只是把一个私有循环机制暴露为公开接口的潜在泛化。

相关

本 RFC 仅移除冗余的停止动词。中途 steering 仍是有意保留的消息路径;静默观测仍通过 whenIdle() 提供。最终的公开接口面为 send()、steer()、inject()、cancel()、whenIdle()、status、options、session 与 identity。