DeepAI Paper Hermes Agent 教程 Discord 免 @ 频道变成 thread 工厂:Hermes free-response 为什么每条消息都新开线程

Discord 免 @ 频道变成 thread 工厂:Hermes free-response 为什么每条消息都新开线程

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_channelsno_thread_channels

这是 DeepAI 或模型上下文问题吗?

不是。它发生在 Discord Gateway 会话路由层,在模型 API 调用之前。


总结

#25310 的核心是一个代码和文档不一致的问题。

文档说 free-response channel 是轻量 inline chat surface;代码却只让它免 @,没有让它跳过 auto-thread。

一行:

or is_free_channel

就能让实现回到文档承诺的语义:

free-response channel = 不用 @mention,也不自动开 thread。

Related Post

Hermes Agent 报 UnicodeEncodeError?ASCII locale、API Key 和 reasoning_content 排错指南Hermes Agent 报 UnicodeEncodeError?ASCII locale、API Key 和 reasoning_content 排错指南

Hermes Agent 报 UnicodeEncodeError: ascii codec can't encode character?本文从 LANG=C、API Key 混入 Unicode 近似字符、OpenAI SDK Authorization header、tool schema、api_messages 和 reasoning_content 角度给出完整排错清单。

Hermes Agent 报 No module named agent.transports?pip / Nix 源码安装缺包排错指南Hermes Agent 报 No module named agent.transports?pip / Nix 源码安装缺包排错指南

Hermes Agent 源码安装后报 ModuleNotFoundError: No module named agent.transports?这通常不是 Anthropic API Key 或模型名问题,而是 setuptools include 缺少 agent.*,导致 pip / Nix 安装产物漏掉 agent/transports 子包。

Docker 重新 build 突然 invalid file request:Hermes 的 runtime data 为什么混进了构建上下文Docker 重新 build 突然 invalid file request:Hermes 的 runtime data 为什么混进了构建上下文

Hermes Docker compose 第一次 build 成功,容器运行后下一次却报 invalid file request?根因可能是 bind-mounted ./data 作为 /opt/data 写入 runtime symlink,而 .dockerignore 没排除 data/。本文复盘 #13925。