ARTICLE / 开源项目
补充after精准续写命令特性的研究
PR 地址:NousResearch/hermes-agent#47044 · /after 命令实现对话历史的精准续写定位
研究摘要
长会话中想"接着某个特定位置继续说"是高频但笨重的需求:agent 连续多轮输出后,用户想针对第 N 轮回复之后的位置续写,要么把相关上下文完整重述一遍(费 token 又容易走样),要么在消息里模糊描述"就是刚才那段"(agent 靠猜)。本研究向 Hermes 上游补充了 /after 精准续写命令:发送 /after N <内容> 后,内容被注入 agent 上下文提示中"第 N 条 AI 回复之后"的位置,N 每轮递减、到点生效——用户只需指位置、写内容,无需重复任何历史上下文。命令注册在 hermes_cli/commands.py(含 af 别名),核心逻辑在 gateway/run.py,配套 98 行完整文档。改动规模 +146/-0,3 个文件,全新命令、无配置变更。
一、问题背景:续写的两个笨办法
1.1 重述上下文:贵且容易走样
跨过多轮输出续写时,最朴素的办法是把相关历史重新描述一遍。代价是双重的:上下文预算被重复内容消耗——本来长会话的上下文就紧张,重述等于把预算烧在已经存在过的内容上;且用户转述的历史往往与真实记录有出入——转述本身就是有损压缩,遗漏细节、改换措辞、顺序颠倒,都可能让续写指向错误的位置。对话越长,转述越长,走样的概率越大。
重述还有第三重代价:转述内容可能被 agent 当作新事实吸收。用户说“你说过 X”,agent 可能把转述的 X 直接当作历史事实——即使转述与真实记录有出入,历史被“二次解释”后就不再可靠。显式定位从源头避免了对历史的再解释。
1.2 模糊指位:agent 靠猜
另一种做法是自然语言指位:“接着你刚才说的那个方案往下说”。agent 需要从多轮输出里猜"那个方案"是哪一轮、哪一段,猜测错误时续写落点就错了,且用户无法预先验证指位是否准确——回复出来才知道对不对,错了再纠,一来一回又是几轮。定位错误的代价还随轮次累积:猜错一次,修正所需的新一轮澄清又带来新的转述与猜测空间,误差在对话中滚雪球。两个笨办法的共同根源是:缺少一种显式的、按轮次计数的定位原语。位置如果能用数字表达,指位就从"语义猜测"变成"精确计算"。
长会话里“位置”本质上是相对的:用户记住的是“那次说方案的时候”,agent 记录的是消息 ID 与轮次。命令的价值是把用户的模糊位置翻译成精确轮次,让双方在同一坐标系里沟通——用户说 N,agent 执行第 N 轮之后的注入,中间没有解释空间。
二、特性设计:按 AI 回复轮次计数的注入机制
2.1 命令注册与别名
hermes_cli/commands.py 在 COMMAND_REGISTRY 中新增 CommandDef(after, aliases=(af,)),/after 与 /af 等价。命令面保持极简:两个参数,N 与内容——N 指位置,内容指要续写的话。注册方式与既有 slash 命令一致,不引入新的解析框架,命令的发现、补全、帮助等既有机制自动生效。
选择 slash 命令形态而非特殊语法,是为了让续写定位与其他命令操作共享同一入口:用户在输入框里以 /af 2 继续 的方式表达,语义自明,不需要学习新语法。
2.2 热路径存储、冷路径回落
gateway/run.py 的 GatewayRunner 初始化时新增 _after_queue 字典。处理分两条路径:
- 热路径:收到
/after时,先把条目存入_after_queue,不打断当前正在进行的处理——命令是"预约"而非"中断",会话忙到一半时/after不会抢断正在生成的回复; - 冷路径:无
/after预约时,回落正常消息处理流程,命令的存在对普通路径零开销——不参与匹配、不增加判断分支。
存储与处理分离的设计,让 /after 在会话繁忙时也能被可靠接收,不会因为时序错过;同时保证了普通消息路径的性能不受命令存在的影响。
热路径的存储选择字典(_after_queue)而非列表,暗示了按会话或按上下文字段索引的潜力——同一会话的多个 /after 预约可以独立管理、互不覆盖(存储键设计为推断,PR body 未详述)。
2.3 按轮递减的计数器
每个 /after 条目携带一个 N 计数:agent 每完成一轮回复,N 递减一次;递减到零的那一轮,内容被注入该轮的上下文提示(_handle_message_with_agent 中完成注入)。“第 N 条 AI 回复之后"被翻译成可执行的计数语义:N 指定的是相对位置,计数随对话推进自动推进,用户无需关心具体消息 ID 或时间戳。
计数器的设计有几个值得展开的细节:按回复轮次而非消息条数计数,是因为用户的锚点天然是"agent 的第几轮输出”;递减发生在每轮完成之后,意味着 /after 0 语义上即"下一轮就注入";注入发生在上下文提示组装环节,内容以显式段落进入模型视野,而非混入用户消息流。
注入的显式性是设计的关键:内容以独立段落入模型视野,与用户消息区分,模型能感知“这是用户指定位置注入的内容”而非普通对话——这为指令遵循提供了清晰边界,也避免了注入内容被当作用户新消息而触发新一轮对话。
2.4 文档先行
docs/after-command.md(+98 行)提供完整文档:用法示例覆盖典型场景(针对第 N 轮输出续写、跨轮指位、与追问组合使用),并记录设计理由——为什么按回复轮次计数、为什么不要求消息 ID(消息 ID 对用户不可见、不可记忆,轮次计数是用户唯一可感知的锚点)。文档与实现同 PR 落地,命令的上手成本被压到最低,也把设计约束固化成了文字,避免后续维护者误改语义。
从改动构成看,文档(+98 行)与实现(gateway/run.py +46 行、commands.py +2 行)的比例接近 2:1,文档被当作交付物的一部分而非事后补充——使用契约与实现同 PR 落地,降低了命令被误用的概率。
三、实测结果
PR body 记录的回归测试数据:
| 测试套件 | 结果 |
|---|---|
| tests/test_hermes_state.py | 267 passed |
| tests/test_lazy_session_regressions.py + tests/honcho_plugin/test_session.py | 135 passed |
| 合计 | 402 passed |
回归测试确认命令注册与注入逻辑未破坏既有网关行为。N 递减与注入时机的端到端表现未在 PR body 中记录独立验证数据,命令的实际使用体验依赖真实会话验证。
回归测试覆盖命令注册与网关主路径,但 /after 计数递减、到点注入的专门测试未被 PR body 记录——该核心语义依赖人工验证,未来改动计数器逻辑时缺少直接回归锚点。
四、能力边界
- N 按 AI 回复轮次计数,与用户感知的"第几句"可能存在偏差——agent 一轮回复可能包含多句,用户需要按轮而非按句指位,初次使用需要建立"轮"的心智模型;——一轮多句时,用户想要“第 3 句之后”需换算成“第 2 轮之后”,换算错误会让注入位置偏移;
- 内容注入发生在上下文提示组装环节,注入位置对模型的实际影响取决于提示组装顺序,跨版本演进时注入行为可能漂移;
- 命令定义在网关层,CLI 交互式会话与网关会话的注入行为是否一致未被 PR body 记录,两种入口的体验差异需要实测确认;
- 计数器只在会话持续推进时递减:会话闲置期间到达的 /after 不会凭空消耗 N,但长时间闲置后 N 的语义(基于哪一轮为起点)可能模糊;
- 无配置变更意味着也没有配置层面的开关——命令对所有网关用户立即可用,依赖用户了解命令存在;滥用 /after 造成的上下文污染没有内置护栏。
- N 的边界语义未定义:N 大于已发生轮次、N 为负数、内容为空等输入的行为未被记录;
- 与追问路由(#47043)的组合场景——/after 注入后用户又回复 bot 消息——两条路径的交互未被记录;
- CLI 与网关两种入口的注入一致性未验证,跨入口使用时 N 的计数基准可能不一致。
- /after 与普通消息混用的时序语义(预约期间用户又发普通消息)未被记录,注入内容与普通消息的相对顺序可能影响模型理解;
- 内容注入后,该轮回复结束时 /after 条目即消耗,用户如需在更远的位置续写需重新预约;
- N 的计数起点基于 /after 到达时已完成的回复轮次,不同时刻到达的多个预约的计数基准可能不同,多预约场景需要用户自行换算。
五、PR 信息
- PR 地址:https://github.com/NousResearch/hermes-agent/pull/47044
- 改动规模:+146 / -0,3 个文件(hermes_cli/commands.py +2,gateway/run.py +46,docs/after-command.md +98)
- 状态:closed
- 提交时间:2026-06-16
本文记录 x7peeps 向 Hermes Agent 上游贡献的特性研究,所有数据来自 PR 实测记录。