摘要连续性这个需求,是我自己用出来的。我经常在笔记本上跑灵桥,但下班 / 出差时会直接关机,几天后再开。问题来了:主动推送和滚动摘要都是按「现在」滚动窗口算的,关机的那几天在系统眼里等于「从没存在过」——周二周三没跑,周四打开,近七天视图里周二周三就是空的,推送也只从周四现在开始。我的聊天记录明明有内容,摘要却缺了一块,很别扭。
核心思路:记录「上次运行时间」,启动时反推停机窗口
解法不复杂,但有几个坑得想清楚。我在主库加了一张 meta 表,记下 last_run_at。每次启动先读它,和「现在」一减,就知道停了多久:
- 停机超过一天,就把遗漏段切成若干个自然日窗口;
- 对每一天,查当天落在该窗口内的消息,按会话分组,现场调 LLM 生成当日摘要;
- 写回
conversation_summaries——这一步是关键,它和正常周期完全同源,Web 摘要页和主动推送不用改一行代码,自然就把停机期补上了。
「近七天」不用单独处理:补跑写回的摘要归属时刻标成「当日 23:59:59」,近七天视图(回溯 7 天)一拉就把它们全带上,主人端看到的就是连续的。
两个真正花时间的坑
坑一:时区。 消息时间戳在库里是混着的——有的存本地 naive,有的带 Z 是 UTC。要是用 SQL 字符串直接比较窗口,跨时区会漏补。我把 SQL 只当粗筛,真正的切日放在 Python 里:先把每条时间戳统一归到本地时区,再分自然日。一个 _parse_ts helper 把 Z / +00:00 / 纯本地串都消化掉,稳。
坑二:别刷爆额度、别拖垮主链路。 停机几十天如果全补,免费 LLM 额度瞬间就没了。所以加了 max_backfill_days(默认 14)上限,只补最近 N 天;背靠既有 llm_throttle 限速,多会话 / 多天也不会轰炸。补跑跑在独立后台线程,DB 或 LLM 哪怕崩了也只是记条日志,绝对不碰主回复链路。单会话当天消息不足 3 条的也跳过,避免噪声。
一个设计上的小取巧
补跑的「当日摘要」和正常滚动摘要用的是同一个 summarize_conversation 函数和同一套 prompt,所以格式天然一致——都以「【对话摘要】」开头,结构对齐。我刻意没为补跑另写一套模板,否则哪天正常摘要改了格式,补跑的就成了「异类」。复用既有的,才是最省心的连续性。
升级注意:新增 summary_backfill 配置段,默认开启;停机超过 14 天只补最近 14 天。改动需重启服务生效。Web 摘要页的「今日 / 昨日 / 近七天」三个窗口会在补跑完成后自动补齐,无需任何手动操作。
完整变更见 CHANGELOG,代码在 GitHub。下一个想动的方向是 RAG 分块按语义边界切分,max_len 改成软上限——避免在句子中间硬截断导致检索召回率下降。