Agent 在聊天平台里问澄清问题时,最理想的交互是按钮。
比如 Telegram 可以直接给 inline buttons:
请选择:
[1] A
[2] B
[3] C
用户点一下,Agent 就知道答案。
但不是所有平台都能稳定支持同样的按钮交互。Mattermost 这类平台上,Hermes 的 clarify multiple-choice 会退化成 numbered text list:把选项按编号发出来,让用户输入 1、2 或选项文字。
NousResearch/hermes-agent issue #25567 的问题就在这里:列表显示出来了,用户也回复了数字,但 Hermes 没把这条消息拦截为 clarify 的答案。Agent 继续卡在 wait_for_response,直到 10 分钟超时,最后拿到空响应。
也就是说:
问题问出去了,答案也发回来了,但中间那层“把答案接回 pending clarify”的逻辑没开。
现象:Mattermost 显示了编号列表,但回复数字没用
复现路径很清楚:
1. 用 Mattermost adapter 跑 Hermes gateway; 2. Agent 调用 clarify,并提供 multiple choices; 3. Mattermost 里正确显示编号列表; 4. 用户回复:
2
或者回复选项文字; 5. Agent 没有继续; 6. 10 分钟后超时:
[user did not respond within 10m]
从用户角度看,这非常迷惑。
因为 UI 已经告诉你“回复一个编号即可”,你也照做了,但系统像没看见一样。
为什么 Telegram 正常,Mattermost 不正常?
issue 里提到:Telegram inline buttons 是可用的。
这说明 clarify 工具本身不是完全坏掉。
差异在交互方式:
Telegram 路径
multiple choices → inline buttons → callback/action 被直接解析为选择
Mattermost fallback 路径
multiple choices → numbered text list → 用户下一条文本消息需要被拦截解析
Mattermost 没有走按钮 callback,而是走“下一条普通消息作为答案”的文本兜底。
问题正好出在这个兜底没有被 pending clarify 查询逻辑识别。
根因:有 choices 时 awaiting_text 被设成 False
issue 指向 tools/clarify_gateway.py。
关键逻辑是:
awaiting_text = not bool(choices)
这行的含义是:
- 如果没有 choices,也就是开放式问题:
awaiting_text = True
- 如果有 choices,也就是多选题:
awaiting_text = False
这对支持按钮的平台可能没问题,因为按钮回调不依赖文本拦截。
但对 Mattermost 这种 numbered-text fallback 来说,多选题虽然有 choices,实际回答方式仍然是文本。
也就是说,它应该等待文本回复。
可旧逻辑把它标成:
awaiting_text = False
于是后续查询 pending clarify 时就查不到它。
第二个关键点:get_pending_for_session 只返回 awaiting_text=True 的条目
issue 里还指出,get_pending_for_session 只返回:
awaiting_text == True
的 pending entries。
于是 Mattermost 用户发来:
2
Gateway 想查“当前 session 有没有 pending clarify 等待文本回复”,但这个 clarify 因为有 choices,被标成了 awaiting_text=False。
所以结果是:
查不到 pending clarify
→ 用户回复没有被当作答案
→ Agent 继续 wait_for_response
→ 10 分钟超时
这就是完整断点。
为什么这是 Gateway/tool 协议问题,不是用户回复格式问题?
用户回复 2 并没有错。
如果 Mattermost 只能展示 numbered text list,那么系统就必须把下一条文本消息纳入解析范围。
这不是用户应该“换一种格式”的问题。
真正的 contract 应该是:
只要平台使用文本 fallback 展示 multiple-choice clarify,下一条文本回复就必须被 pending clarify intercept。
否则 UI 和后端状态机不一致。
正确行为应该是什么?
当 base send_clarify 在 Mattermost 上使用 numbered-text fallback 时,Hermes 应该:
1. 记录一个 pending clarify; 2. 标记它正在等待文本回复; 3. 下一条同 session 用户消息进入时,先检查 pending clarify; 4. 如果用户输入 2,解析为第二个 choice; 5. 如果用户输入完整选项文本,也解析成对应 choice; 6. 结束 wait_for_response,把结果返回给 Agent。
也就是让 Mattermost 的体验和 Telegram 按钮体验在语义上一致。
为什么 10 分钟超时很伤体验?
clarify 的目的,是让 Agent 在不确定时暂停一下,让用户补充选择。
但这个 bug 会让 Agent 卡住:
Agent: 请选择 1/2/3
User: 2
Agent: ...沉默 10 分钟...
Agent: [user did not respond within 10m]
用户明明回答了,却被系统当成没回答。
更糟的是,用户下一条消息可能会被当作新请求,而不是原问题的答案。
于是原任务被打断,上下文也变混乱。
排查时怎么确认?
如果你在 Mattermost 上遇到 clarify 多选题超时,可以这样判断。
1. 看是不是 multiple-choice clarify
如果问题带编号列表:
1. xxx
2. yyy
3. zzz
并要求你回复数字或选项文字,就是 multiple-choice text fallback。
2. 看用户回复是否在 10 分钟内
如果用户很快回复了:
2
但 Agent 仍然超时,说明回复没有被 intercept。
3. 看代码里的 awaiting_text 逻辑
受影响版本里可能有:
awaiting_text = not bool(choices)
同时 pending 查询只返回 awaiting_text=True 的条目。
这两个条件叠加,就会让多选题文本 fallback 永远接不到回复。
修复思路:区分“有按钮”和“需要文本兜底”
问题的关键不应该是:
choices 是否存在
而应该是:
当前平台是否会通过文本消息返回选择
如果平台能发真正的按钮,并且回调会走专门通道,那么 multiple-choice 可以不等待普通文本。
但如果平台退化成 numbered text list,就必须:
awaiting_text = True
也可以理解为:
choices 决定“答案怎么解析”,不应该单独决定“是否等待文本”。
对 DeepAI API 中转站用户的边界说明
如果你的 Hermes Agent 后端接的是 DeepAI API 中转站,链路大概是:
Mattermost → Hermes Gateway → clarify tool pending state → Agent → DeepAI / OpenAI-compatible API
#25567 出错的位置在 clarify tool / Gateway pending response 层。
也就是说:
- 用户的选择没有被接回 Agent;
- Agent 卡在等待用户回复;
- 模型 API 还没继续调用;
- DeepAI Base URL、API Key、模型名都不是根因。
DeepAI 可以提供 OpenAI-compatible 模型入口,例如:
https://api.deepai.wang/v1
但它不能修复 Mattermost adapter 是否正确拦截用户的文字回复。
如果你看到 Agent “明明问了选择题,用户也答了,但还是超时”,要先查 Gateway/tool 状态机,而不是先怀疑模型。
FAQ
为什么 Telegram 可以,Mattermost 不行?
Telegram 使用 inline buttons,选择通过按钮回调进入系统;Mattermost 使用 numbered text fallback,需要拦截下一条文本消息。旧逻辑没有为 multiple-choice fallback 开启文本等待。
用户回复 2 为什么没被识别?
因为有 choices 时 awaiting_text 被设为 False,而 get_pending_for_session 只返回 awaiting_text=True 的 pending clarify。
这是 Mattermost 的问题吗?
不是单纯的平台问题。Mattermost 只是触发了文本 fallback;Hermes 需要在 fallback 路径中正确接住用户文本回复。
临时怎么绕过?
可以尽量改成开放式 clarify,或在支持按钮的平台上操作。但真正修复应在 clarify pending state / text fallback 逻辑里完成。
这是 DeepAI 或模型 API 问题吗?
不是。问题发生在用户选择进入 Agent 之前,模型 API 还没继续调用。
总结
#25567 的核心教训是:
多选题有 choices,不代表一定不需要等待文本。
在 Mattermost 这种 numbered-text fallback 场景下,用户的下一条文本消息就是按钮回调的替代物。
如果 pending clarify 没把它标成 awaiting_text=True,那用户再怎么回复数字,Agent 也只会继续等,直到 10 分钟超时。