1949 字
10 分钟
- 次浏览
聊天记录别原样塞给模型
读前导览

chatbot-qq 里的消息、文件事件、画像更新和错误日志,会被脚本清洗成证据包,再交给模型读。

正文
1949 字
阅读
10 分钟
结构
8 节
来源线索
GitLaughs/chatbot-qq
chatbot-qq:json-evidence-packet-optimization-plan chatbot-qq:evidence-packet chatbot-qq:raw-json-cleanup

聊天机器人做久了,功能会越写越多,JSON 也会越攒越多。

消息事件是 JSON,文件上传是 JSON,结构化记忆是 JSON,任务结果是 JSON,错误记录也是 JSON。每一条都挺有用,但它们加在一起以后,很快会变成一种诱惑:反正模型能读文本,那就把最近几天的 JSONL 切一段给它,让它自己总结。

我现在越来越不喜欢这种做法。

模型读得懂 JSON,但我不想让它默认直读原始运行层。原始 JSONL 适合回查,不适合每次都进上下文。

JSON 很完整,也很吵#

机器人保存 JSONL 有它的道理。

它保留时间、消息 ID、用户标识、发送者信息、原始消息、标准化文本、路由字段、文件事件和错误字段。以后排查问题时,这些字段很值得记:能追溯一条消息从哪里来,经过哪条路由,最后有没有写入状态。

但模型要做画像更新、长期记忆整理、任务回看时,通常不需要这些完整字段。

它需要知道的是:

  • 用户明确表达了什么偏好。
  • 哪些任务还没完成。
  • 哪些结论已经确认。
  • 最近有哪些失败和重试。
  • 哪些文件进入了工作区。
  • 哪几条上下文能解释当前变化。

这些信息当然可以从 JSON 里来,但不该裹着所有 key、引号、括号、逗号、消息 ID、群 ID、CQ 段和路由元数据一起进上下文。

完整数据适合脚本读,模型更适合读被压缩过的材料。

先由脚本完整扫描#

我更愿意把这件事拆成两层。

第一层是确定性脚本。它可以完整读取原始 JSONL,按时间窗口、文件列表或全量扫描,不偷懒,也不靠模型自己去翻。脚本负责做几件很朴素的事:

  • 丢掉空消息、纯表情、纯命令回显和重复内容。
  • 删除消息 ID、完整用户 ID、临时下载地址和路由噪声。
  • 把时间压短。
  • 把用户显示名压成可区分但不暴露的形式。
  • 把文件事件变成文件名、大小、摘要路径和关联上下文。
  • 把内容分类成偏好、待办、决策、错误、文件、最近上下文。

这一层不需要聪明。它需要稳定、可测、有上限。

脚本处理完以后,才生成给模型看的证据包。

证据包应该是人也能扫读的文本#

给模型的证据包不一定要继续用 JSON。

我更喜欢一种紧凑纯文本:

字段顺序:类别 | 时间 | 用户 | 内容 | 原因
窗口:72小时;扫描1234条;保留80条;文件4个
丢弃:空命令300;噪声120;重复40;过短180
偏好 | 05-24 10:20 | 用户A | 以后默认简短结论 | 明确偏好
待办 | 05-24 11:03 | 用户B | 明天提醒检查部署状态 | 待办请求
决策 | 05-24 12:44 | 用户A | 采用 NapCat / OneBot 路线 | 明确确认
错误 | 05-24 13:15 | 用户C | 图片上传失败 timeout | 报错
文件 | 05-24 14:01 | 用户B | 上传 report.pdf,已提取摘要 | 文件事件

这类文本没有 JSON key,没有重复字段名,也没有路由 ID。它牺牲了一部分机器可逆性,换来更低的 token 成本和更清楚的阅读顺序。

如果需要追溯,脚本可以另存 debug/source-map。默认给模型的输入不该带这些追溯细节。

我的做法是保留追溯入口,但默认输入里不带这些细节。

画像更新不能直接读 raw chat#

画像更新是最需要证据包的入口之一。

如果直接让模型读取最近 72 小时聊天记录,短期很方便,长期会很危险。聊天里有大量临时语气、玩笑、重复、测试命令、机器人回包和失败日志。模型如果把这些都当成画像素材,很容易把“当时说过的一句话”误写成长期偏好。

更稳的流程应该是:

raw chat JSONL
-> evidence packet
-> current profile
-> model update
-> profile diff / run note

模型只看证据包和当前画像。证据不足就写“证据不足”,别回头扫全文补想象。

这会让画像更新慢一点,也会少一点“灵光一现”的总结。不过它更可控。长期记忆系统少记一点还能补,把噪声记成规则才麻烦。

离线维护也要同一套格式#

类似 /dream 这种离线维护任务,也不该默认读原始群记录。

它可以读:

  • 最近上下文证据包。
  • 群画像摘要。
  • 成员画像摘要。
  • 文件索引摘要。
  • 错误和运维提示。

但 raw chat 仍然只作为脚本生成证据包的来源。需要追溯时,维护者回到 source-map 或本地原始文件,不让模型在一次任务里随意深读所有聊天记录。

原因很简单:模型更适合读整理后的材料。

原始层适合保存现场;证据包适合进入推理。

文件事件别带全文#

文件上传也是同样的问题。

机器人收到 PDF、代码、表格或压缩包以后,运行层当然要保留元数据、摘要路径和解析状态。不过模型输入里别默认塞全文,尤其别把 extracted.txt 的大段文本直接拼进上下文。

更合适的是:

文件名 | 相对路径 | 大小 | 解析器 | 摘要路径 | 文本预览 | 关联聊天

如果后续任务真的需要读全文,再由明确动作去读。默认证据包只告诉模型“这里有一个文件,摘要在哪里,和哪个上下文相关”。

这能避免一个很常见的问题:模型本来只是要更新画像,却被某个大文件内容带跑。

证据包也要有生命周期#

证据包不是新的垃圾堆。

它应该按每次运行新建文件,带时间戳,有大小上限,有保留策略。debug/source-map 更应该定期清理,因为那里更接近原始运行现场。

日常 JSONL 也可以做 shard:基准文件超过阈值后写入 name-001.jsonlname-002.jsonl,读取时由脚本自动合并。这样原始层不会无限膨胀,证据包生成器也能稳定扫描。

这个设计看起来很数据工程,但聊天机器人只要认真保存消息、文件、记忆和任务,迟早都会走到这里。

到那时,光有上下文还不够,还得能稳定压缩和复查。

给模型更少、更准的材料#

证据包当然能省 token,但我更看重它把几件事拆开了:

  • 脚本负责完整读取、清洗、去重和限流。
  • 证据包负责稳定顺序、固定格式和可扫读内容。
  • 模型负责判断、归纳和更新。
  • 原始 JSONL 负责追溯,不负责默认推理。

几层混在一起后,排错会很麻烦:模型总结错了,很难判断问题出在原始数据、输入材料、上下文长度,还是画像文件冲突。

证据包生成错,就修脚本;证据包足够清楚但模型判断错,就改输入材料的组织方式;画像更新冲突,就看 profile diff;需要追溯,再回 source-map。

聊天机器人不是没有数据。它往往是数据太多,且太贴近现场。

所以第一步先别让模型读更多,先把原始 JSON 压成一份它应该读的证据包。

聊天记录别原样塞给模型
https://blog.sunmmyapi.xyz/posts/qqbot-evidence-packet-before-raw-json/
作者
Sun
发布于
2026-05-29
许可协议
CC BY-NC-SA 4.0

继续读

相关内容