Discord 里做 Agent 机器人,最怕一种体验:你只是想在一个频道里随手聊天,结果每发一句话,机器人都开一个新 thread。
NousResearch/hermes-agent issue #25310 说的正是这个问题:DISCORD_FREE_RESPONSE_CHANNELS 本来是为了把某个 Discord 频道变成轻量聊天入口——用户不用 @mention,每条消息都能得到机器人回复,而且按文档应该直接在频道内 inline reply。
但旧逻辑里,free-response channel 仍然触发 auto-thread。
结果是:
一条消息 → 一个新 thread
再一条消息 → 又一个新 thread
频道很快变成 thread 垃圾场。
文档承诺:free-response channels 会跳过 auto-threading
issue 引用了当前文档中 discord.free_response_channels 的描述。
核心意思是:
Free-response channels also skip auto-threading — the bot replies inline rather than spinning off a new thread per message.
这说明它不是产品设计争议。
文档已经明确:
- free-response channel 是轻量聊天 surface;
- 不需要
@mention; - 不应该每条消息开新 thread;
- bot 应该在频道里 inline reply。
所以用户期待是合理的。
实际行为:free-response 免 @ 成功,但仍然开 thread
复现配置大概是:
discord:
require_mention: true
auto_thread: true
free_response_channels:
- "你的频道 ID"
然后在该频道发一条普通消息,不 @mention bot。
预期:
bot 在频道内直接回复
实际:
bot 创建一个以消息命名的新 thread,然后在线程内回复
再发一条,又开一个新 thread。
这让 free-response channel 失去了意义。
因为用户想要的是:
频道内连续聊天
而不是:
每句话都被拆成独立工单
根因:auto-thread gate 只看 no_thread_channels,没看 is_free_channel
issue 指向 gateway/platforms/discord.py 的 _handle_message。
旧逻辑中,auto-thread block 里计算:
skip_thread = bool(channel_ids & no_thread_channels)
也就是说,只要频道在:
DISCORD_NO_THREAD_CHANNELS
里,就跳过 thread。
但它没有把:
DISCORD_FREE_RESPONSE_CHANNELS
纳入 skip 条件。
更微妙的是,函数前面已经计算了:
is_free_channel
它知道这个频道是 free-response channel。
只是这个状态没有传到 auto-thread gate。
所以才会出现这种割裂:
免 @ 判断:知道这是 free-response channel
是否开 thread:忘了这是 free-response channel
一行修复:free-response 频道也应该 skip_thread
issue 给出的修复非常小:
skip_thread = bool(channel_ids & no_thread_channels) or is_free_channel
这行表达了文档里已有的语义:
no_thread_channels:显式不要 thread;is_free_channel:轻量 inline chat,也不要 thread。
这样 free-response channel 才会真正成为:
无需 @mention 的内联聊天频道
而不是自动开 thread 的入口。
为什么这会影响上下文连续性?
对 Agent 来说,消息位置就是会话边界的一部分。
如果每条消息都开一个新 thread,用户以为自己在继续同一个话题:
第一句:帮我看这个报错
第二句:这是日志
第三句:我刚才又试了一次
但系统可能看到的是:
Thread A:帮我看这个报错
Thread B:这是日志
Thread C:我刚才又试了一次
这就破坏了连续对话。
用户体验上也很糟:
- 频道里全是 thread;
- 每个 thread 只有一两条消息;
- 用户不知道应该继续在哪个 thread 里补充;
- bot 的上下文可能被拆散;
- 管理员很难清理。
所以它不是一个小 UI bug,而是 Gateway 会话分发层的问题。
和 DISCORD_NO_THREAD_CHANNELS 有什么区别?
可以这样理解。
DISCORD_NO_THREAD_CHANNELS
这是显式告诉 bot:
这个频道不要自动开 thread。
但它不一定改变 mention 行为。
DISCORD_FREE_RESPONSE_CHANNELS
这是告诉 bot:
这个频道可以直接响应普通消息,不要求 @mention。
按文档,它还隐含:
这个频道也应该跳过 auto-threading。
所以 free-response 是更强的“轻量聊天频道”语义。
如果实现只做了“免 @”,没做“跳过 thread”,就只实现了一半。
排查时怎么看是不是这个问题?
如果你配置了 free-response channel,但出现:
每条消息都创建一个新 Discord thread
可以按下面查。
1. 确认频道在 free_response_channels 里
看配置:
discord:
free_response_channels:
- "channel-id"
2. 确认 auto_thread 是否开启
discord:
auto_thread: true
如果 auto-thread 开着,而 free-response 没被 skip,就会触发这个问题。
3. 搜代码里的 skip_thread
在受影响版本中,可能只有:
skip_thread = bool(channel_ids & no_thread_channels)
如果没有:
or is_free_channel
那就是 issue 描述的代码路径。
临时绕过方案
如果暂时不能升级,可以先把同一个频道也加入 no-thread 列表。
也就是同时配置:
discord:
free_response_channels:
- "channel-id"
no_thread_channels:
- "channel-id"
这样让现有逻辑通过 no_thread_channels 跳过 auto-thread。
但长期看,free-response channel 本身就应该自动跳过 thread,不应该让用户配置两遍。
对 DeepAI API 中转站用户的边界说明
如果你的 Hermes Discord bot 最终把用户消息发给 DeepAI API 中转站,链路大概是:
Discord channel → Hermes Gateway → session/thread routing → Agent → DeepAI / OpenAI-compatible API
#25310 发生在 Gateway 的 Discord 会话路由层。
也就是说:
- 消息能不能被分到正确会话;
- 是否 inline reply;
- 是否新开 Discord thread;
- 上下文是否被拆散;
这些都发生在模型 API 调用之前。
DeepAI 可以作为后端 OpenAI-compatible 模型入口,比如:
https://api.deepai.wang/v1
但它不能决定 Discord adapter 是否开 thread。
所以如果你看到“上下文像断了一样”,不要只查模型记忆或 API provider,也要看 Discord Gateway 是否把每条消息拆成了新 thread。
FAQ
free-response channel 是什么?
它是一个 Discord 频道模式:用户不需要 @mention bot,普通消息也会触发回复,适合轻量聊天入口。
为什么每条消息都新开 thread?
旧代码的 auto-thread gate 只检查 DISCORD_NO_THREAD_CHANNELS,没有把 DISCORD_FREE_RESPONSE_CHANNELS 也视为 skip-thread 条件。
最小修复是什么?
skip_thread = bool(channel_ids & no_thread_channels) or is_free_channel
临时怎么绕过?
把同一个频道同时加入 free_response_channels 和 no_thread_channels。
这是 DeepAI 或模型上下文问题吗?
不是。它发生在 Discord Gateway 会话路由层,在模型 API 调用之前。
总结
#25310 的核心是一个代码和文档不一致的问题。
文档说 free-response channel 是轻量 inline chat surface;代码却只让它免 @,没有让它跳过 auto-thread。
一行:
or is_free_channel
就能让实现回到文档承诺的语义:
free-response channel = 不用 @mention,也不自动开 thread。