用Cognee把群聊记录变成可溯源问答
作者为Cognee开发WhatsApp连接器,将239天内的3320条群消息转成可查询记忆,回答附带发言人与日期。方案使用聊天导出文件,避开个人及群聊历史缺少读取API的限制。
AI 深度解读
164 人的住宅小区 WhatsApp 群,239 天积累了 3,320 条消息。停水通知、维修安排、会议决定都在里面,但要找一条具体信息,依然得翻聊天记录。作者用 Cognee 做了一个连接器,把导出的群聊变成可以提问、能交代日期和发言人的记忆。
这个项目值得看的地方,是它怎样处理重复导入、删除同步和手机号。尤其是一个容易踩坑的问题:新导出里少了旧消息,究竟是消息被删了,还是导出数量超限?
为什么先按天整理,再交给 AI
作者选择读取导出文件。按原文说明,WhatsApp 没有读取个人或群聊历史的 API;WhatsApp Business Platform 面向企业与客户之间的消息业务,也不适合读取居民群。
Android 导出得到 .txt,iPhone 导出得到包含 _chat.txt 的 .zip。连接器只读取指定文件,不连接聊天账号。
文件拿到后,第一道选择是切分粒度。整群存成一份文档,新增消息容易触发整份重处理,查某天的通知也不方便;每条消息单独存,又会让「可以,周五」这样的回复丢失上下文。
作者取了中间值:一个群每天生成一份文档,并在开头写明 Chat:、Date:、People:。日期和参与人直接进入正文,后面的消息按时间排列。媒体只保留 [media]、[image]、[attachment: Society rules.pdf] 等标记及说明文字;系统事件用 · 保留,已删除消息被丢弃,编辑标记被移除。
Cognee 的流程是添加数据、用 cognify 构建知识图谱,再搜索。连接器可以直接交给 cognee.remember,完成前两步:
import cognee
from cognee_community_connector_whatsapp import whatsapp_source
await cognee.remember(
whatsapp_source(["exports/WhatsApp Chat with Residents Block 5.txt"]),
dataset_name="whatsapp",
primary_key="id",
write_disposition="merge", # 默认的 "replace" 会丢掉未变化的日期
max_rows_per_table=0, # 不限制行数,让删除检查覆盖全部数据
)
要留意 write_disposition="merge":后续导入只提交变化的日期,默认的 replace 会让没被提交的旧日期丢失。
每个群还有独立的节点集合,名称为 whatsapp:<chat name>,查询时可以限定范围:
from cognee.modules.engine.models.node_set import NodeSet
answer = await cognee.search(
query_text="When is the water supply off?",
query_type=cognee.SearchType.GRAPH_COMPLETION,
datasets=["whatsapp"],
node_type=NodeSet,
node_name=["whatsapp:Residents Block 5"],
)
关键是 node_name,它把问题限制在指定群内。作者得到过带日期的供水回答,例如「8 月 8 日供水一小时」,但没有提供完整的问答准确率评测。
增量导入好做,安全地删除更难
连接器把每天的文本计算成哈希,指纹保存在 dlt 的 resource state 中。相同导出再次导入,不产生新数据;较新的导出只提交新增或文本变化的日期。
作者用真实 Android 导出、Cognee 默认 OpenAI 模型运行:首次同步约 14 分钟,第二次约 15 秒。不过,第二次没有内容变化,这组数字反映的是跳过重复处理的收益,不能直接代表有新增消息时的速度。
真正棘手的是删除。WhatsApp 导出存在数量上限:不含媒体最多保留最新 40,000 条消息,包含媒体最多 10,000 条。旧日期可能只是被截掉了。
新快照里没有的数据,不一定已经被删除。
连接器因此只在新导出实际覆盖的时间窗口内判断删除:
- 早于新导出首条消息的日期保留,防止误删被数量上限截掉的历史。
- 晚于新导出末条消息的日期保留,防止导入旧文件时覆盖较新的记忆。
- 导出达到 10,000 条消息时,最早一天可能不完整,该日不参与更新或删除。
- 文件读取失败时跳过并计数,不据此删除数据。
窗口内某天确实消失,连接器才发出 _deleted 行,由 Cognee 的 orphan cleanup 清理图谱。作者在真实导出的副本中删光一天的消息,观察到一条 _deleted,文档数从 239 降到 238。
这里的 10,000 条判断是保守策略:它宁可暂时保留最早一天,也不拿可能残缺的数据覆盖已有记录。
真实聊天文件,比格式说明更复杂
作者先用文档和合成文件开发解析器,跑真实导出后才补上不少边界情况。
Android 的 <Media omitted> 后面可能同一行紧跟说明;附件写成 <file name> (file attached) <caption>,文件名还可能包含空格、括号。消息正文以换行开头时,第一行只有 Name:。阅后即焚语音则可能显示为 <View once voice message omitted>。
还有更隐蔽的问题:ZIP 被改名为 WhatsApp.zip 后,应从内部文本文件获取群名;窄不换行空格、不换行空格和从左到右标记,都需要先清理。
日期也不能只看一条。03/04/26 可能是 3 月 4 日,也可能是 4 月 3 日。解析器扫描全部日期,利用大于 12 的数值判断日月顺序。作者的月在前、24 小时制导出被成功识别,但全文日期都存在歧义时如何处理,原文没有说清。
作者为这些情况补了测试,共 34 个,仓库只放合成聊天。test_whatsapp_forget.py 使用真实 Cognee 流水线,但模拟 LLM 和 embeddings,验证窗口内删除与窗口外保留。CI 覆盖 Python 3.11 至 3.13、ruff 和 wheel 构建。这能支持同步逻辑的可信度,不能证明真实模型回答一定正确。
我的判断
我更认可它对数据同步边界的处理。相比反复把整份聊天交给模型,按天切分、计算指纹、限定删除窗口,都是可以检查和测试的工程决策。
但按天切分仍是经验选择。讨论可能跨午夜延续,同一天也可能混着多个话题。原文没有和固定长度分块、按话题切分、普通全文检索做对照,不能据此认定知识图谱效果最好。
适用范围也有限:媒体文件不被读取;iPhone 只有合成测试;日期跟随导出手机时钟,跨时区文件无法自然对齐。群名就是身份,改名需要用 chat_ids={"Old name": "my-group", "New name": "my-group"} 映射。系统事件和占位符只匹配英文,其他语言会保留原文标记。
隐私处理有实际价值。164 名发送者中有 161 名显示为手机号,连接器默认把发送人、正文和 @ 提及中的国际号码遮到只剩末四位,金额和订单号不动;mask_phone_numbers=False 可以关闭。不过,号码遮蔽并不等于聊天匿名化,正文仍可能暴露身份和住址。
按原文状态,PR topoteretes/cognee-community#471 尚待审核,对应 issue 为 topoteretes/cognee#5836。pip install cognee-community-connector-whatsapp 是合并发布后的安装方式,不能当作已经可用的发行包。
能拿来做什么
- 为有成员同意的小区群或协作群做通知问答时,可以先验证「日期、发言人、决定内容」能否被稳定找回,并把跨天讨论列入验收样例。
- 在 Node 或其他数据同步任务里导入不完整快照时,可以借用覆盖窗口规则,把「缺失」与「删除」分开处理,避免误清历史数据。
- 验证聊天解析器时,可以保留真实文件的换行、附件名称和不可见字符形态,再替换人名、号码和正文,减少测试夹具泄露隐私的风险。
- 试用该连接器时,应单独建立 dataset 并限制读取权限;原文说明可用
cognee.forget移除该数据集,同时应记住默认 OpenAI 模型也参与处理,手机号遮蔽不能替代成员同意。