跳到主要内容

Weixin (WeChat)

通过 WeChat(微信)将 Hermes 接入腾讯的个人消息平台。该适配器使用腾讯的 iLink Bot API,面向的是个人微信账号,与 WeCom(企业微信)不同。消息通过 long-polling 收发,因此不需要公网端点或 webhook。

信息

这个适配器面向的是个人微信账号。如果你需要企业场景,请改看 WeCom adapter

前提条件

  • 一个个人微信账号
  • Python 包:aiohttpcryptography
  • 安装了 messaging extra 后,终端二维码渲染会自动可用
pip install aiohttp cryptography
# Optional: for terminal QR code display
pip install hermes-agent[messaging]

设置

1. 运行设置向导

最简单的接入方式是使用交互式设置:

hermes gateway setup

选择 Weixin。向导会:

  1. 向 iLink Bot API 请求二维码
  2. 在终端显示二维码(或给出 URL)
  3. 等你用微信手机端扫码
  4. 提示你在手机上确认登录
  5. 自动把账户凭据保存到 ~/.hermes/weixin/accounts/

成功后会看到类似:

微信连接成功,account_id=your-account-id

向导会保存 account_idtokenbase_url,因此通常不需要手动配置。

2. 配置环境变量

完成首次扫码后,至少在 ~/.hermes/.env 中设置:

WEIXIN_ACCOUNT_ID=your-account-id

# Optional: override the token (normally auto-saved from QR login)
# WEIXIN_TOKEN=your-bot-token

# Optional: restrict access
WEIXIN_DM_POLICY=open
WEIXIN_ALLOWED_USERS=user_id_1,user_id_2

# Optional: restore legacy multiline splitting behavior
# WEIXIN_SPLIT_MULTILINE_MESSAGES=true

# Optional: home channel for cron/notifications
WEIXIN_HOME_CHANNEL=chat_id
WEIXIN_HOME_CHANNEL_NAME=Home

3. 启动网关

hermes gateway

适配器会恢复已保存的凭据,连接 iLink API,并开始 long-poll 收消息。

功能

  • Long-poll transport:无需公网端点、webhook 或 WebSocket
  • 二维码登录:通过 hermes gateway setup 扫码接入
  • 私聊和群聊消息:支持可配置访问策略
  • 媒体支持:图片、视频、文件和语音消息
  • AES-128-ECB 加密 CDN:自动加解密所有媒体传输
  • context token 持久化:重启后仍能保持回复上下文连续性
  • Markdown 保留:尽量保留标题、表格、代码块等 Markdown 结构
  • 智能分块:未超限时尽量保持为一条消息;超限时按逻辑边界拆分
  • typing indicators:处理时在客户端显示“正在输入”
  • SSRF 防护:下载外部媒体前会校验 URL
  • 消息去重:5 分钟滑动窗口避免重复处理
  • 自动重试:可从瞬时 API 错误中恢复

配置选项

config.yamlplatforms.weixin.extra 下配置:

KeyDefaultDescription
account_idiLink Bot account ID(必需)
tokeniLink Bot token(必需,通常由扫码登录自动保存)
base_urlhttps://ilinkai.weixin.qq.comiLink API 基础 URL
cdn_base_urlhttps://novac2c.cdn.weixin.qq.com/c2c媒体 CDN 基础 URL
dm_policyopen私聊访问策略:openallowlistdisabledpairing
group_policydisabled群访问策略:openallowlistdisabled
allow_from[]私聊 allowlist(当 dm_policy=allowlist 时使用)
group_allow_from[]群 allowlist(当 group_policy=allowlist 时使用)
split_multiline_messagesfalse为 true 时,多行回复会拆成多条消息;为 false 时,未超限的多行回复会尽量保留在一条消息内

访问策略

DM Policy

ValueBehavior
open任何人都可以私聊 bot(默认)
allowlist只有 allow_from 中的用户能私聊
disabled忽略所有私聊
pairing配对模式(用于初始接入)
WEIXIN_DM_POLICY=allowlist
WEIXIN_ALLOWED_USERS=user_id_1,user_id_2

Group Policy

ValueBehavior
open在所有群中响应
allowlist只在 group_allow_from 指定的群中响应
disabled忽略所有群消息(默认)
WEIXIN_GROUP_POLICY=allowlist
WEIXIN_GROUP_ALLOWED_USERS=group_id_1,group_id_2
备注

Weixin 的默认群策略是 disabled(不同于 WeCom 的 open)。这是有意为之,因为个人微信账号通常加入了很多群。

媒体支持

入站(接收)

适配器会接收用户发送的媒体附件,从微信 CDN 下载、解密并缓存到本地供 agent 处理:

TypeHow it's handled
Images下载、AES 解密,并缓存为 JPEG
Video下载、AES 解密,并缓存为 MP4
Files下载、AES 解密并缓存,保留原始文件名
Voice如果平台提供了文本转写,则直接抽取文本;否则下载并缓存 SILK 音频

Quoted messages: 被引用消息中的媒体也会被抽取,方便 agent 理解用户正在回复什么。

AES-128-ECB 加密 CDN

微信媒体通过加密 CDN 传输。适配器会自动处理:

  • 入站:通过 encrypted_query_param URL 下载密文,并使用消息中附带的每文件密钥进行 AES-128-ECB 解密
  • 出站:在本地用随机 AES-128-ECB 密钥加密文件,上传到 CDN,再把加密后的引用写入消息
  • AES 密钥为 16 字节(128 bit),可能以 base64 或 hex 编码形式下发,适配器会自动兼容
  • 这依赖 cryptography Python 包

出站(发送)

MethodWhat it sends
send带 Markdown 格式的文本消息
send_image / send_image_file原生图片消息(通过 CDN 上传)
send_document文件附件(通过 CDN 上传)
send_video视频消息(通过 CDN 上传)

所有出站媒体都会经过加密 CDN 上传流程:

  1. 生成随机 AES-128 密钥
  2. 用 AES-128-ECB + PKCS#7 padding 对文件加密
  3. 向 iLink API 请求上传 URL(getuploadurl
  4. 把密文上传到 CDN
  5. 发送带有加密媒体引用的消息

Context Token Persistence

iLink Bot API 要求同一 peer 的每次出站消息都带上对应的 context_token。适配器会维护一个磁盘持久化的 context token 存储:

  • token 按 account + peer 保存到 ~/.hermes/weixin/accounts/<account_id>.context-tokens.json
  • 启动时会恢复已有 token
  • 每条入站消息都会更新对应发送者的 token
  • 出站消息会自动附带最新 token

这能确保即使网关重启,回复上下文也能保持连续。

Markdown Formatting

通过 iLink Bot API 连接的微信客户端可以直接渲染 Markdown,因此适配器会尽量保留原始 Markdown,而不是改写:

  • 标题会保留为 Markdown heading
  • 表格会保留为 Markdown 表格
  • 代码块会保留为 fenced code block
  • 在非代码块区域,过多空行会被折叠为双换行

Message Chunking

只要消息未超限,适配器就会尽量作为单条聊天消息发送。只有超长消息才会被拆分:

  • 最大消息长度:4000 字符
  • 未超限时,即使包含多个段落或换行,也尽量保持为单条消息
  • 超长时按逻辑边界拆分(段落、空行、代码块)
  • 代码块会尽量保持完整,避免在中间拆开
  • 单个超大块若仍无法容纳,则退回到底层适配器的截断逻辑
  • 分块之间有 0.3 秒延迟,避免触发 WeChat 限流

Typing Indicators

适配器会在微信客户端中显示“正在输入”:

  1. 收到消息后,适配器会通过 getconfig API 获取 typing_ticket
  2. typing ticket 会按用户缓存 10 分钟
  3. send_typing 发送开始输入信号,stop_typing 发送停止输入信号
  4. 网关会在 agent 处理消息时自动触发这些 typing indicators

Long-Poll Connection

适配器通过 HTTP long-poll(而不是 WebSocket)接收消息。

How It Works

  1. Connect:校验凭据并启动 poll loop
  2. Poll:调用 getupdates,超时时间 35 秒;服务端会一直持有该请求,直到消息到来或超时
  3. Dispatch:收到入站消息后,通过 asyncio.create_task 并发分发处理
  4. Sync buffer:把持久化同步游标(get_updates_buf)保存到磁盘,重启后能从正确位置继续

Retry Behavior

遇到 API 错误时,适配器采用简单的重试策略:

ConditionBehavior
瞬时错误(第 1~2 次)2 秒后重试
连续错误(第 3 次及以后)回退 30 秒,然后重置计数
会话过期(errcode=-14暂停 10 分钟(通常需要重新登录)
Timeout立即重新 poll(这是正常 long-poll 行为)

Deduplication

适配器会用 message ID 做 5 分钟窗口去重,防止在网络抖动或重叠 poll 响应场景下重复处理同一条消息。

Token Lock

同一个 Weixin token 在同一时间只能由一个网关实例使用。适配器启动时会获取 scoped lock,关闭时释放。如果另一个网关已经在使用同一个 token,启动会失败并给出明确错误。

All Environment Variables

VariableRequiredDefaultDescription
WEIXIN_ACCOUNT_IDiLink Bot account ID(扫码登录后获得)
WEIXIN_TOKENiLink Bot token(通常由扫码登录自动保存)
WEIXIN_BASE_URLhttps://ilinkai.weixin.qq.comiLink API 基础 URL
WEIXIN_CDN_BASE_URLhttps://novac2c.cdn.weixin.qq.com/c2c媒体 CDN 基础 URL
WEIXIN_DM_POLICYopen私聊访问策略
WEIXIN_GROUP_POLICYdisabled群访问策略
WEIXIN_ALLOWED_USERS(empty)私聊 allowlist(逗号分隔)
WEIXIN_GROUP_ALLOWED_USERS(empty)群 allowlist(逗号分隔)
WEIXIN_HOME_CHANNELcron / 通知输出 chat ID
WEIXIN_HOME_CHANNEL_NAMEHomehome channel 显示名
WEIXIN_ALLOW_ALL_USERSsetup wizard 使用的全开放标志

Troubleshooting

ProblemFix
Weixin startup failed: aiohttp and cryptography are required安装依赖:pip install aiohttp cryptography
Weixin startup failed: WEIXIN_TOKEN is required重新运行 hermes gateway setup 完成扫码登录,或手动设置 WEIXIN_TOKEN
Weixin startup failed: WEIXIN_ACCOUNT_ID is required.env 中设置 WEIXIN_ACCOUNT_ID,或重新运行设置向导
Another local Hermes gateway is already using this Weixin token先停止另一个实例;同一个 token 只能有一个 poller
Session expired (errcode=-14)登录会话过期,请重新运行 hermes gateway setup 扫码
二维码在设置过程中反复过期二维码最多自动刷新 3 次;若仍失败,请检查网络连接
私聊不回复检查 WEIXIN_DM_POLICY;如果是 allowlist,发送者必须在 WEIXIN_ALLOWED_USERS
群消息被忽略Weixin 默认禁用群响应;请设置 WEIXIN_GROUP_POLICY=openallowlist
媒体下载 / 上传失败确认已安装 cryptography,并检查能否访问 novac2c.cdn.weixin.qq.com
Blocked unsafe URL (SSRF protection)出站媒体 URL 指向了私有 / 内网地址,只允许公开地址
语音消息显示为文本如果 WeChat 平台提供了转写,适配器会优先使用文本;这是正常行为
消息似乎重复出现适配器会按 message ID 去重;若仍有重复,请检查是否有多个网关实例在运行
iLink POST ... HTTP 4xx/5xxiLink 服务返回错误。请检查 token 是否有效,以及网络连通性
终端二维码无法渲染重新安装 messaging extra:pip install hermes-agent[messaging];也可直接打开二维码上方打印的 URL