← 文章 / Writing

Agent10 min编者 nabunana ↗

聊天摘要 Agent:消息拉全以后,才轮得到模型

记录聊天摘要从能运行到可以放心使用,中间补过的分页、游标、证据和隐私边界。
#Python#Agent#SQLite#Privacy#容错设计

这个项目最早的想法很直接:把 QQ 和微信消息收进来,让模型整理一份摘要。

Demo 很快能做出来。读一段文本,拼 Prompt,拿回 Markdown。真正接上日常聊天后,问题全变了:私信一页装不下,微信消息散在多个数据库里,模型会超时,也会引用输入里根本不存在的消息。更麻烦的是,程序看起来正常运行,并不能证明消息真的收全了。

我后来花的时间,大多不在 Prompt 上。

采集和摘要分成两个进程

现在的数据流大致是这样:

QQ / 微信数据源 → 采集进程 → SQLite(WAL) ← MCP 查询进程 ← 本地 Agent

采集进程负责标准化和入库,MCP 进程负责查询、分页和组织摘要。模型调用即使卡住,采集也可以继续;MCP 重启后,已经写进 SQLite 的消息和检查点还在。

这里有一条不能放松的约束:消息和下一检查点必须放在同一个事务里提交。

如果先推进检查点,随后写消息失败,这一段会永久漏掉。反过来,消息写成了但检查点没动,恢复后只会重放。消息表有稳定唯一键,重复写入会被忽略,所以重放可以接受,漏消息不行。

微信桌面数据库还可能有多个消息分片。查询时要分别取候选消息,再按 (sort_seq, local_id) 做全局排序和交界去重。只打开第一个数据库最危险的地方在于它通常不会报错,只会安静地少一截内容。

私信要明确告诉调用者“还没拉完”

摘要拆成了两步:

prepare_digest          本地整理私信
prepare_group_analysis  生成群聊分析包

prepare_digest 会返回 direct_completenext_direct_cursor。只要 direct_complete 还是 false,调用者就得继续翻页。第一页看起来已经有不少消息,也不能据此说“今天的私信都总结完了”。

分开两个入口还有隐私上的好处。私信留在本机,由本地 Agent 阅读;只有明确选中的群聊,才会进入配置的外部模型分析链路。这个边界写进调用方式后,比依赖一段“请勿上传私信”的说明可靠得多。

微信数据源需要维护只读解密缓存,同一个工作目录只允许一个采集器占用。第二个实例拿不到锁就直接退出。两个进程同时碰这些中间文件,未必马上报错,反而可能互相覆盖,留下很难复现的损坏。

模型给的内容先当成待校验数据

模型生成的每个摘要条目都要带 evidence_message_ids。本地服务会检查这些 ID 是否属于本批输入。

某条证据不合法,只丢掉或降级对应条目,不牵连同批其他有效结果。整批无法解析时则记为失败,未读游标不动。生成了一段语句通顺的摘要,和成功处理完这一批消息,是两回事。

未读游标也不会在预览时推进。只有摘要成功,并且用户明确选择非预览模式,才会写入新的位置。调试和查看历史范围用的是只读查询,不应该顺手改变下次的未读起点。

长时间范围允许部分完成

全天消息可能横跨很多群。系统会按群数和消息数分批,限制并发、单批等待时间和输出 token。模型因长度上限截断时不自动重试,已经成功的批次也不会因为另一批超时而作废。

返回结果会写清楚成功、部分成功和失败的批次数,还会标出输出截断、证据修正以及实际送入模型的群数。跨批结果在本地精确去重并稳定排序,不再多调一次模型做“大总结”。少一次调用,也少一个把已有结果全部卡住的地方。相似但写法不同的话题可能保留成两条,这个代价目前可以接受。

日志只记耗时、批次数和输入字节量,不记群名、正文、证据值或密钥。异常在写日志和返回 MCP 之前统一脱敏,因为泄漏往往来自第三方库的异常对象,不一定是业务代码主动打印。

现在这个 Agent 仍会遇到数据源离线和模型超时,只是失败的位置能看见,也能从已经提交的检查点继续。对聊天摘要来说,这比偶尔生成一份很漂亮、却说不清漏了多少消息的报告更有用。

今聴いている / NOW LISTENINGYorushika

读完之后,留一点安静给音乐。这是一则状态记录,不是播放器。

站内搜索

AgentVibe的东西 真的看懂了吗Notes · 工程 反思 AI 能力 · 攒了一堆能跑的项目,能力却没长进。问题不在代码谁写的,在于我有没有真正想明白。谈谈多Agent并发与OpenclawNotes · AI 工作流 OpenClaw 工程取舍 · 把一个写文案的活拆成五六个 Agent 开会,听着爽,实际是 Token 火葬场。真正把事做完的,是单 Agent 加工具,加上几个克制的角色。一次 GitHub Actions 自动发布复盘Engineering · Astro GitHub Actions Nginx SSH CI/CD 部署 · 从旧 Hexo GitHub Pages、Astro 新站到服务器:怎样处理分支切换、SSH 信任、未入库的 94 首音乐,以及可回滚的原子发布.更新静态博客时,我不直接覆盖 /var/www/blogEngineering · Astro Nginx SFTP 部署 静态网站 · 这个 Astro 博客的发布过程:先在旁边准备好完整站点,核对资源,再一次替换并保留回滚。Java 没有断,WebSocket 为什么一直重连Engineering · Java WebSocket Nginx Vue 部署 · 一次公网部署故障复盘:REST 正常、进程没重启,WebSocket 却持续 403,子路由刷新也跟着 404。做“今天吃什么”,我只想让页面给一家店Engineering · Java Vue uni-app 推荐系统 产品设计 · ELMA 目前的项目思路:少问几个问题,一次给一家店,再从反馈和行为里慢慢学。做一个能按真实尺寸打印的卡牌 PDF 工具Projects · Python Pillow PDF Tkinter Desktop · 卡牌图片放进 A4 不难,麻烦的是毫米、DPI、图片比例和桌面程序里的几个小坑。三体模拟器跑起来以后,四条链路开始互相拖后腿Engineering · Java Vue WebSocket Canvas 性能优化 · 一次实时模拟项目里的拆分:积分照常跑,网络只发该发的,画布和归档各自守住上限。聊天摘要 Agent:消息拉全以后,才轮得到模型Agent · Python Agent SQLite Privacy 容错设计 · 记录聊天摘要从能运行到可以放心使用,中间补过的分页、游标、证据和隐私边界。拿一个 8KB 数组写个 mallocSystems · C Memory Data Structure 嵌入式 · MiniMalloc 里真正需要想明白的几件事:块头放哪、空闲块怎么找,以及 free 后怎样把内存拼回去。我为什么还在用 RSSNotes · RSS Reading Web · 不猜兴趣,也不替我调整顺序。想看的来源,自己订阅就够了。这个夏天看过的动画ACG · Anime ACG Life · 没做排名,只记下几段过了一阵子还会想起来的内容。ProjectsPage · 正在构建的项目与实验MusicPage · Yorushika、n-buna、Vocaloid 与听歌记录AboutPage · 关于这个数字空间和它的主人