Files
deepseek-harness/.agents/notes/implemented/feature/2026-07-19-plugin-command-registration.zh.md
2026-07-24 01:40:25 +08:00

7.2 KiB
Raw Blame History

Agent Note: 插件拥有的人类命令注册

Status: implemented

English | 中文

问题

TUI 拥有斜杠命令。如果命令名、帮助文本、自动补全、分派和取消都留在适配器内部,每个新命令都需要修改 TUI可选插件也无法贡献命令。把斜杠输入当作普通模型提示同样不安全用户可见的直接操作可能意外消耗 token或让模型重新解释未知命令。

共享机制必须仍是 UI 关注点,而不是模型工具或智能体循环分支。它还需要精确的逐智能体可见性、可安全 HMR 移除、直接结果渲染和请求作用域取消,同时不会自动把命令文本或输出加入模型历史。

决策

位于 packages/ui/commands/@deepseek-ai/dsh-commands 是产品命令注册表。TUI 应用 bundle组合包把它挂载在消费该服务的前端旁仅面向自动化的 ACPAgent Client Protocol应用和无执行器、无 UI 的智能体 spine主干都省略该服务。TUI 注入该服务,命令生产者只依赖注册表及其操作的领域。

注册表契约

CommandDefinition 包含不带 / 的小写名称、非空描述、可选的非结构化输入提示,以及可取消处理器。注册会校验并分离元数据、冻结有效定义,并返回准确的 Cordis effect disposer副作用释放器。同一层中的重复名称会失败。每个消费方都能看到所有有效定义若命令插件无法在某种部署中运行它就不在该部署中注册而不是把消费方身份编码进共享领域。

list(agent) 在作用域遮蔽后返回不可变、按名称排序的描述符。find(agent, name) 解析有效定义。execute(agent, line, signal) 解析并运行已知定义,返回分离后的 successerror 结果;无效语法和未知名称返回 undefined,由适配器拥有直接错误文本。

parseCommand(line) 要求 / 位于第零字节,后接由字母、数字、_- 组成的小写 ASCII 名称,并以空白或输入末尾结束。它把适配器交付的完整后缀保留为 rawInput,包括分隔空白。每个命令插件自行拥有后续语法决策。

作用域与生命周期

无作用域注册是全局注册。挂载在智能体上下文之下并注入 commands 的插件会继承该智能体的作用域键与生命周期,因此其定义仅为该准确智能体遮蔽同名全局定义。子插件自行声明 commands 注入,因为 agent.ctx 有意只继承核心智能体循环的依赖界面;仅为了实现作用域注册而让循环依赖 UI 服务会倒置依赖图。

注册和移除会发出未过滤、不可否决的 commands/change 注册表通知。适配器重新计算每个实时智能体的有效视图,而不尝试推断某次变更影响哪些会话。注册表会分别隔离并记录每个观察者失败,因此损坏的 UI 刷新无法回滚另一插件的变更也无法阻止后续观察者。Cordis 所有权会在生产者、UI 实例或智能体作用域卸载时移除定义,因此 HMR 不会留下陈旧的发现项或处理器。

直接分派与取消

命令在仅面向人类的命令平面中运行。注册表不会把输入转成 user/message,输出不会成为会话事件,两者都不会隐式发送给模型。处理器接收准确的目标智能体、原始输入和请求拥有的 AbortSignal;生产者可以通过该智能体显式调度单独的模型可见工作,随后由生产者负责其日志记录和生命周期契约。信号中止时,注册表不再等待不合作的处理器;处理器仍负责停止已经启动的外部副作用。

预期的处理器失败返回 CommandResult.error。抛出的异常或格式错误的结果仍是适配器可见的命令失败,而不是模型消息。该边界有意分离 UI 输出与持久领域变更:例如目标命令可以改变 ctx.goals,但持久状态由目标服务拥有。

TUI 映射

TUI 把内置斜杠命令注册为智能体作用域命令定义,不再对字符串执行 switch。自动补全与帮助视图读取实时目录因此插件命令会随其副作用出现和消失。任何以 / 开头的提交行都留在命令平面;未知输入产生终端警告,不会落入 Agent.send()Agent.steer()

每个提交的命令拥有一个 AbortController。TUI 释放会中止未完成的分派、移除本地定义,并等待命令生产者 fiber纤程后再完成清理。

测试

注册表测试覆盖语法边界、不可变规范化、运行时元数据校验、确定性排序、全局与作用域遮蔽、重复拒绝、准确释放、变更通知失败隔离、直接调用、预期和格式错误结果、同步与异步失败,以及每种中止时序边沿;该源文件达到逐文件 100% 语句、分支、函数和行覆盖率。

TUI 测试覆盖全部迁移后的内置命令、实时插件发现、帮助与自动补全刷新、直接结果、未知命令拒绝、原始输入交付、定义移除、启动回滚和释放取消。无密钥终端快照固定渲染后的帮助、错误与命令结果形态。

考虑过的替代方案

  • 保留适配器本地 switch——不予采纳,因为可选插件无法贡献发现与行为,除非修改 TUI。
  • 把人类命令表示为模型工具——不予采纳,因为发现与直接调用属于人类 UI 行为经由模型路由会增加延迟、token 成本和重新解释。
  • 把注册表放入核心智能体主干——不予采纳,因为无 UI 前端不消费它,而 TUI 可以显式组合它。
  • dsh-agent-loop 注入 commands——不予采纳,因为循环不执行也不发现人类命令。智能体作用域生产者改为在子插件中声明 UI 依赖。
  • 为每个定义附加适配器掩码——不予采纳,因为支持能力是组合事实,而不是命令领域状态。每个已组合适配器都暴露已注册命令;不兼容插件不会在该部署中注册。
  • 把未知斜杠输入发送给模型——不予采纳,因为输入错误或不可用的直接操作必须可预测地失败,而不能改变执行平面。
  • 持久化通用命令输入与输出——不予采纳,因为适配器提示不是模型可见状态。改变持久行为的处理器会调用拥有该状态的领域 API由后者记录自己的事件。

后果

  • 命令生产者是普通的可移除插件TUI 消费其经过校验的目录与分派契约。
  • 智能体特定定义保留现有扁平作用域与遮蔽语义,不引入核心到 UI 的依赖。
  • 未知斜杠输入与命令输出是确定性 UI 行为,直接模型 token 成本为零。
  • 直接命令取消与模型轮次取消彼此隔离。

已知限制与延期工作

  • 输入元数据仅限非结构化文本提示。类型化表单、参数模式和补全提供器仍由命令拥有,或需要后续注册表或消费方扩展。
  • 通用命令输出仅实时存在TUI 重启后不会重建。
  • 注册表取消会立即停止等待,但外部工作只有在处理器配合信号时才会停止。
  • ACP 自动化服务器、无头 CLI 与 JSON-RPC SDK 前端不暴露命令平面;只有 TUI 消费它。