跳到主要内容

微信(Weixin)

将 Hermes 连接到微信,腾讯的个人消息平台。适配器使用腾讯的 iLink 机器人 API 针对个人微信账号 —— 这与企业微信不同。消息通过长轮询方式投递,无需公网端点或 Webhook。

信息

此适配器适用于个人微信账号(微信)。如需企业/公司微信,请参阅 企业微信适配器

iLink 机器人身份 —— 普通微信群可能无法使用

二维码登录将 Hermes 连接到 iLink 机器人身份(例如 a5ace6fd482e@im.bot),而非完全可脚本化的普通个人微信账号。影响如下:

  • iLink 机器人身份通常无法像普通联系人一样被邀请加入普通微信群
  • 对于大多数机器人类型账号,iLink 通常不会将普通微信群事件(包括对个人账号的 @ 提及)传递给网关。
  • 对用于扫描二维码的个人微信账号的 @ 提及与对 iLink 机器人的 @ 提及不同 —— 机器人是独立的身份。
  • 以下 WEIXIN_GROUP_POLICY / WEIXIN_GROUP_ALLOWED_USERS 设置仅在 iLink 实际为您的账号类型返回群事件时才生效。如果不返回,无论策略如何,群消息都不会到达 Hermes。

实际上,大多数部署只能可靠地让私聊到 iLink 机器人正常工作。如果配置后群聊投递仍无效,限制在于 iLink 端,而非 Hermes。网关在启动时,当 WEIXIN_GROUP_POLICY 设置为 disabled 以外的值时,会记录 WARNING

前置条件

  • 一个个人微信账号
  • Python 包:aiohttpcryptography
  • 使用 messaging 扩展安装 Hermes 时已包含终端二维码渲染支持

安装所需依赖:

pip install aiohttp cryptography
# 可选:用于终端二维码显示
pip install hermes-agent[messaging]

配置步骤

1. 运行配置向导

连接微信账号最简便的方式是通过交互式配置:

hermes gateway setup

出现提示时选择微信。向导将:

  1. 从 iLink 机器人 API 请求二维码
  2. 在终端显示二维码(或提供 URL)
  3. 等待您用微信手机 App 扫描二维码
  4. 提示您在手机上确认登录
  5. 自动将账号凭据保存到 ~/.hermes/weixin/accounts/

确认后,您将看到如下消息:

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

向导会存储 account_idtokenbase_url,无需手动配置。

2. 配置环境变量

初始二维码登录后,在 ~/.hermes/.env 中至少设置账号 ID:

WEIXIN_ACCOUNT_ID=your-account-id

# 可选:覆盖 Token(通常从二维码登录自动保存)
# WEIXIN_TOKEN=your-bot-token

# 可选:限制访问
WEIXIN_DM_POLICY=open
WEIXIN_ALLOWED_USERS=user_id_1,user_id_2

# 可选:恢复传统多行拆分行为
# WEIXIN_SPLIT_MULTILINE_MESSAGES=true

# 可选:定时任务/通知的主频道
WEIXIN_HOME_CHANNEL=chat_id
WEIXIN_HOME_CHANNEL_NAME=Home

3. 启动网关

hermes gateway

适配器将恢复已保存的凭据,连接到 iLink API,并开始长轮询消息。

功能特性

  • 长轮询传输 —— 无需公网端点、Webhook 或 WebSocket
  • 二维码登录 —— 通过 hermes gateway setup 扫码连接
  • 私聊消息 —— 可配置的访问策略;群聊消息取决于 iLink 是否实际为所连身份投递群事件(对于 iLink 机器人账号通常无效 —— 见上方警告)
  • 媒体支持 —— 图片、视频、文件和语音消息
  • AES-128-ECB 加密 CDN —— 所有媒体传输的自动加密/解密
  • 上下文 Token 持久化 —— 跨重启的磁盘级回复连续性
  • Markdown 格式化 —— 保留 Markdown,包括标题、表格和代码块,支持 Markdown 的微信客户端可原生渲染
  • 智能消息分块 —— 消息在限制内保持为单个气泡;仅超大消息在逻辑边界拆分
  • 输入状态指示 —— Agent 处理时在微信客户端显示"输入中..."状态
  • SSRF 防护 —— 出站媒体 URL 在下载前经过验证
  • 消息去重 —— 5 分钟滑动窗口防止重复处理
  • 自动退避重试 —— 从瞬时 API 错误中恢复

配置选项

config.yamlplatforms.weixin.extra 下设置:

默认值说明
account_idiLink 机器人账号 ID(必填)
tokeniLink 机器人 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[]允许私聊的用户 ID(当 dm_policy=allowlist 时)
group_allow_from[]允许的群组 ID(当 group_policy=allowlist 时)
split_multiline_messagesfalsetrue 时,将多行回复拆分为多条聊天消息(传统行为)。为 false 时,除非超过长度限制,否则将多行回复保持为一条消息。

访问策略

私聊策略

控制谁可以向机器人发送私聊消息:

行为
open任何人都可以私聊机器人(默认)
allowlistallow_from 中的用户 ID 可以私聊
disabled所有私聊均被忽略
pairing配对模式(用于初始配置)
WEIXIN_DM_POLICY=allowlist
WEIXIN_ALLOWED_USERS=user_id_1,user_id_2

群聊策略

控制机器人在iLink 为所连身份投递群事件时响应哪些群组。对于通过二维码登录的 iLink 机器人身份(例如 ...@im.bot),群事件通常根本不会被投递,因此此策略可能无效 —— 见页面顶部的 iLink 机器人限制警告。

行为
open机器人在所有群组中响应(如果事件被投递)
allowlist机器人仅在 group_allow_from 中列出的群组 ID 中响应(如果事件被投递)
disabled所有群组消息均被忽略(默认)
WEIXIN_GROUP_POLICY=allowlist
# 注意:这是逗号分隔的群聊 ID 列表,而非成员用户 ID,
# 尽管变量名包含 "USERS"。配置时请注意这一点。
WEIXIN_GROUP_ALLOWED_USERS=group_id_1,group_id_2
备注

微信的默认群聊策略是 disabled(不同于企业微信默认为 open)。这是有意为之 —— 个人微信账号可能在很多群组中,且 iLink 机器人身份通常根本无法接收普通微信群消息。如果您将 WEIXIN_GROUP_POLICY 设置为 disabled 以外的值,网关启动时会记录 WARNING

媒体支持

入站(接收)

适配器从用户处接收媒体附件,从微信 CDN 下载、解密并在本地缓存,供 Agent 处理:

类型处理方式
图片下载、AES 解密,并以 JPEG 缓存。
视频下载、AES 解密,并以 MP4 缓存。
文件下载、AES 解密并缓存。保留原始文件名。
语音如果有文字转录,则提取文字。否则下载并缓存音频(SILK 格式)。

引用消息: 引用(回复)消息中的媒体也会被提取,Agent 因此可以了解用户正在回复的内容。

AES-128-ECB 加密 CDN

微信媒体文件通过加密 CDN 传输。适配器透明处理:

  • 入站: 加密媒体从 CDN 下载(使用 encrypted_query_param URL),然后使用消息有效负载中提供的每文件密钥通过 AES-128-ECB 解密。
  • 出站: 文件使用随机 AES-128-ECB 密钥在本地加密,上传到 CDN,加密引用包含在出站消息中。
  • AES 密钥为 16 字节(128 位)。密钥可以是原始 base64 或十六进制编码 —— 适配器处理两种格式。
  • 这需要 cryptography Python 包。

无需任何配置 —— 加密和解密自动进行。

出站(发送)

方法发送内容
send带 Markdown 格式的文本消息
send_image / send_image_file原生图片消息(通过 CDN 上传)
send_document文件附件(通过 CDN 上传)
send_video视频消息(通过 CDN 上传)

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

  1. 生成随机 AES-128 密钥
  2. 使用 AES-128-ECB + PKCS#7 填充加密文件
  3. 从 iLink API 请求上传 URL(getuploadurl
  4. 将密文上传到 CDN
  5. 使用加密媒体引用发送消息

上下文 Token 持久化

iLink 机器人 API 要求在每条针对特定联系人的出站消息中回显 context_token。适配器维护磁盘级上下文 Token 存储:

  • Token 按账号+联系人保存到 ~/.hermes/weixin/accounts/<account_id>.context-tokens.json
  • 启动时恢复之前保存的 Token
  • 每条入站消息更新该发送者的已存储 Token
  • 出站消息自动包含最新的上下文 Token

这确保了即使网关重启后回复仍然连续。

Markdown 格式化

通过 iLink 机器人 API 连接的微信客户端可以直接渲染 Markdown,因此适配器保留 Markdown 而非重写:

  • 标题 保留为 Markdown 标题(###、...)
  • 表格 保留为 Markdown 表格
  • 代码围栏 保留为围栏代码块
  • 多余的空行 在围栏代码块外折叠为双换行

消息分块

消息在平台限制内以单条聊天消息投递。仅超大消息才会拆分投递:

  • 最大消息长度:4000 字符
  • 限制内的消息保持完整,即使包含多段或换行
  • 超大消息在逻辑边界(段落、空行、代码围栏)处拆分
  • 代码围栏尽可能保持完整(除非围栏本身超过限制,否则不在中间拆分)
  • 超大的单个块回退到基础适配器的截断逻辑
  • 发送多个块时,0.3 秒的块间延迟防止触发微信频率限制

输入状态指示

适配器在微信客户端中显示输入状态:

  1. 消息到达时,适配器通过 getconfig API 获取 typing_ticket
  2. 输入 Ticket 按用户缓存 10 分钟
  3. send_typing 发送输入开始信号;stop_typing 发送输入停止信号
  4. Agent 处理消息时,网关自动触发输入状态指示

长轮询连接

适配器使用 HTTP 长轮询(非 WebSocket)接收消息:

工作原理

  1. 连接: 验证凭据并启动轮询循环
  2. 轮询: 调用 getupdates,超时 35 秒;服务器保持请求直到消息到达或超时
  3. 分发: 入站消息通过 asyncio.create_task 并发分发
  4. 同步缓冲区: 持久同步游标(get_updates_buf)保存到磁盘,适配器重启后从正确位置恢复

重试行为

发生 API 错误时,适配器使用简单的重试策略:

条件行为
瞬时错误(第 1-2 次)2 秒后重试
重复错误(3 次及以上)退避 30 秒,然后重置计数器
会话过期(errcode=-14暂停 10 分钟(可能需要重新登录)
超时立即重新轮询(正常长轮询行为)

消息去重

入站消息使用消息 ID 进行去重,窗口为 5 分钟。这防止在网络抖动或轮询响应重叠期间重复处理。

Token 锁

每次只有一个微信网关实例可以使用给定 Token。适配器在启动时获取作用域锁,在关闭时释放。如果另一个网关已经在使用相同 Token,启动时会失败并提供详细错误信息。

所有环境变量

变量必填默认值说明
WEIXIN_ACCOUNT_IDiLink 机器人账号 ID(来自二维码登录)
WEIXIN_TOKENiLink 机器人 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私聊访问策略:openallowlistdisabledpairing
WEIXIN_GROUP_POLICYdisabled群聊访问策略:openallowlistdisabled
WEIXIN_ALLOWED_USERS(空)私聊允许列表的逗号分隔用户 ID
WEIXIN_GROUP_ALLOWED_USERS(空)逗号分隔的群聊 ID(非成员用户 ID)用于群聊允许列表。变量名是历史遗留 —— 它期望群组 ID,而非用户 ID。
WEIXIN_HOME_CHANNEL定时任务/通知输出的聊天 ID
WEIXIN_HOME_CHANNEL_NAMEHome主频道显示名称
WEIXIN_ALLOW_ALL_USERS网关级别标志,允许所有用户(配置向导使用)

故障排查

问题解决方案
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,或运行 hermes gateway setup
Another local Hermes gateway is already using this Weixin token先停止另一个网关实例 —— 每个 Token 只允许一个轮询器
会话过期(errcode=-14登录会话已过期。重新运行 hermes gateway setup 扫描新二维码
配置期间二维码过期二维码最多自动刷新 3 次。如果持续过期,请检查网络连接
机器人不响应私聊检查 WEIXIN_DM_POLICY —— 如果设置为 allowlist,发送者必须在 WEIXIN_ALLOWED_USERS
机器人忽略群组消息群聊策略默认为 disabled。设置 WEIXIN_GROUP_POLICY=openallowlist —— 但请注意,二维码登录的 iLink 机器人身份(...@im.bot)通常根本无法接收普通微信群消息。如果网关日志中没有群消息的原始入站事件,限制在于 iLink 端,而非 Hermes。
媒体下载/上传失败确保已安装 cryptography。检查到 novac2c.cdn.weixin.qq.com 的网络访问
Blocked unsafe URL (SSRF protection)出站媒体 URL 指向私有/内部地址。只允许公网 URL
语音消息显示为文本如果微信提供了转录,适配器使用文本。这是预期行为
消息出现重复适配器按消息 ID 去重。如果看到重复,检查是否有多个网关实例在运行
iLink POST ... HTTP 4xx/5xxiLink 服务的 API 错误。检查 Token 有效性和网络连通性
终端二维码无法渲染使用 messaging 扩展重新安装:pip install hermes-agent[messaging]。或者,打开二维码上方打印的 URL