日期:2026-02-13
目标:沉淀 Telegram ↔ OpenClaw ↔ OpenCode 联调中的高价值发现,提供可复用的实现与排障参考。
本文聚焦三类问题:
- 会话卡住(Queue 不前进)
invalid api key与 provider 线路错配- Question Tool 远程回答的“无 hack”官方实现
核心产出:
- 明确了 Queue 卡住的可复现根因链。
- 明确了 MiniMax 双线路配置边界(
minimaxi.comvsminimax.io)。 - 实测验证并落地了 OpenCode 官方
/questionAPI 回传闭环。
- OpenCode:
1.1.53 - OpenClaw: Hermes 网关模式(Telegram 群组接入)
- 关键目录:
- 仓库:
/Users/napstablook/.config/opencode/HERMES - 运行插件:
/Users/napstablook/.config/opencode/plugins - OpenClaw 运行映射:
/Users/napstablook/.config/opencode/workspace-hermes
- 仓库:
现象:
- Telegram 消息可成功转发到 OpenCode。
- OpenCode 会话停在 Queue/不继续。
证据:
- 报错:
ProviderModelNotFoundError - 报错参数:
providerID: "opencode",modelID: "minimax-m2.1-free"
结论:
- 会话阻塞由“配置的 provider/model 组合无效”触发。
- 这类错误在 UI 上可能表现为“队列卡住”,但本质是模型解析失败。
工程建议:
- 启动后第一步先执行
opencode models <provider>校验模型名。 - 统一用完整 provider/model 字符串,避免历史别名遗留。
发现:
- OpenCode 同时存在两条 MiniMax Coding Plan provider:
minimax-cn-coding-plan(minimaxi.com)minimax-coding-plan(minimax.io)
- 两者都使用
MINIMAX_API_KEY,但 key 与线路必须匹配。
结论:
invalid api key在该场景下常见根因是“线路选对了模型,但 key 来源/作用域不匹配”。
工程建议:
- 显式固定 provider(如国区使用
minimax-cn-coding-plan)。 - 用
opencode auth list同时检查 Credentials 与 Environment 生效来源。 - 排查 shell 环境中旧
MINIMAX_API_KEY覆盖本地 auth 的情况。
现象:
opencode --port 4096打印插件初始化日志后似乎无响应。
根因:
- 旧进程已监听
127.0.0.1:4096,新进程无法正常接管。
工程建议:
- 启动前固定检查端口占用(
lsof -iTCP:4096 -sTCP:LISTEN)。 - 管理脚本加入“单实例守护”或启动前清理策略。
现象:
- 日志出现
Non-fatal unhandled rejection: TypeError: fetch failed。
结论:
- 当前实现下该异常未直接中断主链路,但属于稳定性预警信号。
工程建议:
- 对外部 HTTP 调用补充超时与重试策略。
- 把该异常频率纳入监控,超过阈值触发告警。
现象:
- 用户已点击
✏️ 自定义回答,但后续文本输入后界面仍停在QUEUED。
根因:
- 文本发给了业务机器人(如
@Napsta...),被当作普通任务转发。 - 没有 reply Permission Bot 的自定义输入提示消息,Listener 无法做强绑定匹配。
修复:
- 点击
qcustom后,Listener 发送force_reply提示。 - 用户 reply 该提示消息;Listener 通过
reply_to_message_id精确识别回答并回传。
收益:
- 明显降低群聊并发下的误匹配概率。
- 与用户交互心智一致,排障成本更低。
GET /questionPOST /question/{requestID}/replyPOST /question/{requestID}/reject
-
GET /question返回包含:id(requestID)sessionIDtool.callIDquestions[]
-
replypayload 要求:
{
"answers": [["A"]]
}补充:
answers为二维字符串数组。["A"]与["问题文本","A"]均可被接受,但输出语义略有不同。
发现:
- 若在
tool.execute.before中阻塞 question 执行(例如轮询等待用户输入),GET /question会长期返回空列表。
结论:
- 这会“抑制官方 question 生命周期”,导致外部 API 无法拿到 requestID。
hermes-hook.js只负责转发问题到 Telegram,不阻塞、不注入异常。permission-listener.js负责回传答案到/question/{requestID}/reply。- requestID 通过
callID + sessionID匹配获取。
- 先
GET /question?directory=<project_dir>。 - 优先
tool.callID精确匹配。 - 回退到同
sessionID的唯一 question。 - 成功后调用
reply,并清理 pending。 - 过期条目调用
reject做关闭清理。
发现:
- 不带
directory时可能查不到当前会话的 question。
策略:
- 在 pending 中持久化
directory,所有 question API 调用统一携带。
建议按以下顺序排查:
- 模型字符串是否有效:
opencode models <provider> - provider 与 key 是否同线路:
opencode auth list - 端口是否被旧实例占用:
lsof -iTCP:<port> -sTCP:LISTEN - Question API 是否可见:
GET /question?directory=<cwd> - Question 回传格式是否正确:
{"answers":[["A"]]} - 自定义回答是否 reply 了 Permission Bot 提示消息(而非
@Napsta...) - Listener 日志是否完整:
callback_query(qcustom)→收到 message→question 已回传
- 给出了
Question Tool官方回传在v1.1.53的可运行证据链。 - 明确了
tool.execute.before阻塞会抑制/question的机制性问题。 - 给出了可维护、低耦合的生产实现路径(分层职责 + requestID 匹配 + 过期 reject)。
- 给出了 MiniMax 双线路在真实项目中的配置边界与踩坑防护。
- 明确了 Telegram 群聊下自定义回答的稳定交互范式(
force_reply + reply_to绑定)。
/Users/napstablook/.config/opencode/HERMES/docs/技术文档_QuestionTool官方API回传_2026-02-13.md/Users/napstablook/.config/opencode/HERMES/docs/问题排查_OpenCode队列阻塞与InvalidApiKey_2026-02-13.md/Users/napstablook/.config/opencode/HERMES/docs/question-tool-api-exploration_2026-02-11.md