wechat-ai-bot
这是什么
一个驻守在微信群里的 AI 助手:被 @bot 时应答,带着近期群聊上下文回答问题,能解析图片、文件(PPT/Word/Excel)、转发的聊天记录和链接,也能通过无头浏览器抓取动态页面。
实际用途是群运营:每天发公告、同步信息、做群管理(@全体、成员操作)。这类事本身不难,难的是它们零散、需要按时、且措辞每次都要重写——正好是模型擅长而人不愿意做的部分。
消息链路由第三方微信网关承接,AI 侧用 Claude Agent SDK 驱动,每次 @bot 触发一次全新的 Agent 查询,运行在带 Read/Write/Bash/WebFetch 工具的沙箱容器里。
消息处理时序
值得注意的是应答之后那三件事是并行异步做的:追加问答历史、提取用户新事实更新画像、历史超长时压缩为摘要——都不阻塞回复本身。用户等待的只有”理解问题 + 生成回答”这一段。
记忆设计:这个项目真正的工程量
群机器人和一次性对话的根本差别在于它要活很久。两级持久记忆分开设计,因为它们的读写模式完全不同:
| 群历史 | 用户画像 | |
|---|---|---|
| 存什么 | 原始问答日志(history.jsonl) | 自由文本的个人偏好(users/{wxid}.md) |
| 怎么写 | 自动全量追加,不做判断 | 要判断值不值得记 |
| 增长 | 只增,超过一定条数后压缩成摘要;摘要本身过长时再次压缩 | 小而精,反复改写同一份 |
| 用途 | 回答”刚才谁说了什么” | 跨会话记住”这个人是谁” |
这个划分不是当时照着什么框架做的,但事后回看,它正好落在记忆分层那套分类上——群历史是情景记忆,用户画像是语义记忆。两者读写模式差得远,放进同一个存储会互相干扰:情景自动全量、语义提炼精简,混在一起检索时,有用的结论会被大量聊天细节淹没。
一个关键决定:不指望模型自觉去写记忆
系统提示里会告诉模型”学到值得记的事情时,更新那份用户画像文件”。但只靠这一句是不可靠的——模型在专注回答问题时,很容易把这件附带的事忘掉。
所以应答之后强制再发起一次提取调用,专门问”这轮对话里有什么值得写进画像的”。
这一步的意义比它看起来大:它把”记什么”从模型的自觉,变成了流程的一个固定环节。 我在 Agent 工程系列里后来写到”谁来判断这条值得记”时,结论是这类跨轮次的动作应该由外层程序负责——而这个 bot 里那次强制提取,就是那个结论的一个具体实现。
能力边界
它有 Bash 工具、能联网、能读写文件,所以权限收窄是必须的,不是可选项:
- 群白名单:只在配置的群里响应,空配置才是全部群
- webhook 路径带随机串:避免入口被扫到
- 凭据全部走环境变量,不进代码也不进镜像
- 运行在沙箱容器里:被搞坏可以直接重建
顺带一提,这套约束和我后来在注入那篇里写的”致命三角”是一个思路——群消息是不可信内容,而它同时能对外通信,所以能做的就是把权限收到最窄。
部署
Docker Compose 本地跑;生产在 K8s 上,关键是记忆目录必须挂持久卷——否则 Pod 一重建,群历史和所有用户画像全部归零。这是”有状态”这件事在部署层面唯一但也是致命的要求。