微信 4.0 更新后,大量界面元素被移除或重构,传统的元素定位式自动回复 很多已经失效。本期换一种思路:用 RPA 读消息、用 Agent 生成回复,难点集中在两件事——如何稳定拿到微信新消息,以及 如何把 Agent 的回复发回微信。
本次技术栈为 Hermes + 影刀 RPA;换成 OpenClaw、其他 Agent 或 RPA 工具,整体链路同样适用。
1. 从消息列表入手
虽然 4.0 改了很多控件,但 会话消息列表 仍可通过 RPA 获取。做法是对列表做循环,逐条输出当前项,一般能拿到:

- 用户名(或群名 / 会话名)
- 未读条数(有未读才显示;没有新消息则通常没有条数)
对循环结果做 数据清洗 后,只保留 有新消息的会话 及其 未读数量,作为后续触发 Agent 的输入。
将清洗后的 未读条数 作为本次要拉取的消息数量,再进入对应会话按条读取,就能稳定拿到 未读消息内容(而不是整段历史聊天记录)。
2. 调用本地 Agent 生成回复
拿到未读消息后,交给 本机 Agent 处理。这样做的好处是:知识库、人设、工具链(harness)都在你自己环境里,不依赖云端默认配置,回复风格和业务规则可控。
在影刀流程里增加 「调用 Python」 模块即可:把 RPA 抓到的消息文本传入 Python,由 Python 调用本地大模型 / 本地 Agent,拿到回复后再交回 RPA 发微信。
链路可以概括为:
消息列表(未读数) → 按未读数读取消息正文 → Python 模块 → 本地 Agent / 大模型 → 回复文本 → RPA 发送
调用Hermes Agent 代码如下:
import os
import platform
import re
import shlex
import subprocess
import sys
import time
DEFAULT_TIMEOUT = int(os.environ.get("HERMES_CHAT_TIMEOUT", "120"))
QUERY_PATTERN = re.compile(r"^(\d+)/(\d+)/(\d+)(\s+(\d+))?\D*$", re.UNICODE)
def _ensure_utf8_stdio() -> None:
if hasattr(sys.stdout, "reconfigure"):
try:
sys.stdout.reconfigure(encoding="utf-8")
sys.stderr.reconfigure(encoding="utf-8")
except Exception:
pass
def _normalize_query(message: str) -> str:
s = message.strip().replace("\ufeff", "").strip("\u200b\u200c\u200d\ufeff")
s = re.sub(r"^@\S+\s*", "", s, count=1).strip()
return s
def call_hermes_chat(message: str, *, timeout: int = DEFAULT_TIMEOUT) -> str:
"""完整 Agent,根据自己Hermes agent存放地址填写"""
hermes = "/root/hermes-agent/venv/bin/hermes"
shell_cmd = (
f"{hermes} --profile doudou chat -q {shlex.quote(message)} -Q "
f"--max-turns {os.environ.get('HERMES_MAX_TURNS', '8')}"
)
if platform.system() == "Windows":
cmd = ["wsl", "-e", "bash", "-lc", shell_cmd]
else:
cmd = ["bash", "-lc", shell_cmd]
result = subprocess.run(
cmd,
capture_output=True,
text=True,
encoding="utf-8",
errors="replace",
timeout=timeout,
check=True,
)
return (result.stdout or "").strip()
def call_hermes(message: str, timeout: int = DEFAULT_TIMEOUT) -> str:
os.environ.get("HERMES_FORCE_CHAT", "").strip() not in ("1", "true", "yes")
return call_hermes_chat(message, timeout=timeout)
完整流程
1. 无限循环,等待图像出现
微信 4.0 之后,左侧会话列表的控件结构变了不少,但「未读小红点」相对稳定,所以第一层仍然用图像识别做触发,而不是一上来就死循环扫列表。
外层套一个无限循环,里面判断「小红点图像是否出现在整个屏幕」。没有新消息时就空转等待,有红点再往下走。这样能避免列表一直被遍历,CPU 常年拉满;也比「每隔几秒扫一遍列表」更省资源、更贴近真实有新消息才处理的节奏。
图像元素继续用之前那套「消息小红点」,不用改。
2. 小红点出现后,再扫会话列表
红点只是说明「有人找你了」,具体是谁、几条未读,还得回到列表里拿。
流程里用「循环相似元素」遍历窗口中的「联系人提示」——也就是左侧每一条带未读角标的会话。每轮循环把当前项存成 当前桌面元素,再「获取窗口元素信息」,把会话名等属性读到 win_元素属性值。
光有名字还不够,未读条数在括号里,例如 张三(3)。这里加了一步「执行 Python」,调用 extract_bracket_text,从元素文案里拆出:会话名、未读数量、后续要用的片段,统一放进 脚本输出。后面所有判断都基于这个变量,别在影刀里硬抠字符串,维护会轻松很多。
3. 过滤不该自动回的会话
不是每条未读都值得 Agent 回。微信支付、服务号、微信运动、公众号这类系统消息,一旦自动回复,既打扰用户,也容易把流程带偏。
所以加了一层「多条件」判断,同时满足才继续:
脚本输出不为空(确实解析到了未读信息)- 会话名里不包含:微信支付、服务号、微信运动、公众号
通过后再「点击窗口元素」,进入该会话;通不过的直接跳过,处理列表里的下一条。
4. 只读未读,不扫整屏历史
进聊天窗口后,再用「循环相似元素」抓「消息」控件,变量记为 当前桌面元素1。
关键在这一句:win_index < 脚本输出[1]。
脚本输出[1] 是前面 Python 解析出来的未读条数,循环索引小于它,才收集这条消息。这样读的是最近 N 条新消息,而不是把半年前的聊天记录全捞一遍——Agent 输入干净,token 也省。
每条符合条件的消息,把 脚本输出[2](消息正文)追加进列表 任意值列表。如果一个会话里连续来了好几条,就先攒列表,再一次性交给 Agent,比「一条一条问、一条一条回」更像真人客服。
5. 拼成一段话,交给本地 Hermes
内层消息循环结束后,用「列表转文本」把 任意值列表 合成 拼接后文本。
建议打一条日志:用户:{脚本输出[0]} 消息:{拼接后文本}。跑久了全靠这条日志排错——到底是没抓到消息,还是 Agent 回歪了,一眼能看出来。
然后「执行 Python」,调 call_hermes。这里不走微信 iLink Bot 通道,Hermes 只干一件事:根据本地知识库和人设生成回复。RPA 负责看微信、发微信;Agent 负责想,两边各干各的,这也是为什么前面说 Hermes 接不了普通微信代回,但在这条链路里仍然好用。
返回值写入 agent回复。
6. 回消息,再回到列表
生成回复之后,流程就简单了:
- 点击「输入框」
- 键盘输入
agent回复,并发送回车 - 再点一下「当前桌面元素」,回到会话列表
第三步是为了从聊天页退出来,继续处理下一个带未读的会话。如果一个红点对应多条未读会话,会在这个大循环里依次点完、回完。
7. 外层继续等下一个红点
当前这批未读处理完后,不会停。流程回到最外层的无限循环,继续等屏幕上的小红点再次出现。
整条链路可以概括成:
等红点 → 扫会话 → 过滤系统号 → 按未读条数读消息 → Hermes 想回复 → RPA 发出去 → 回列表接着等
和「元素定位式一键回复」相比,这套方案多做了三件事:图像触发降 CPU、未读数限定读取范围、Python 桥接本地 Agent。微信 4.0 聊天区控件再变,只要列表项和输入框还能拾取,主流程就不用推倒重来。
&spm=1001.2101.3001.5002&articleId=162097007&d=1&t=3&u=36e8f733b68c42acb87a5589b8db2724)
380

被折叠的 条评论
为什么被折叠?



