从零实现 GeekAgent —— Day5 历史压缩

💡 原文中文,约8300字,阅读约需20分钟。
📝

内容提要

本文介绍GeekAgent开发第5天实现历史压缩功能。当对话历史超过4000字符阈值时,自动将旧消息摘要为一条系统消息,保留最近6条完整消息,防止上下文溢出且不丢失关键信息。提供自动触发和手动/compact命令两种入口,压缩过程显示黄色进度提示。通过实测验证,压缩后模型仍能准确回答旧对话中的事实。

🔎

延伸解读

为什么用字符数而非 token 数

文章选择用 JSON.stringify 后的字符数作为历史长度的近似度量,而非精确的 token 数。原因是 token 计数需要依赖具体模型的分词器,维护成本高;字符数虽不精确,但足以作为触发压缩的阈值。这种取舍在原型开发中常见,但需注意不同模型对 token 的计费与窗口限制不同,字符数估算可能偏差较大,后续若需精确控制成本或避免超限,仍需引入 token 计数。

压缩策略的权衡:保留最近 6 条

压缩时保留最近 6 条完整消息,其余旧消息摘要为一条 system 消息。这一设计兼顾了短期上下文与长期记忆:最近几轮对话是当前任务的直接上下文,保留原样可避免信息损失;更早的对话则通过摘要保留关键事实。但保留条数固定为 6,可能不适用于某些场景,例如工具调用密集时,6 条可能不足以覆盖完整上下文,需根据实际使用调整。

摘要可能被二次压缩的隐患

文章明确提到,摘要消息本身也是普通消息,在后续压缩中可能再次被摘要,导致信息层层折损。这是当前实现的一个已知局限,没有设置“摘要只压一次”的保护。对于长期对话,多次压缩可能使早期关键细节逐渐丢失。读者在使用时需留意,若对话极长,可能需要定期手动检查或调整压缩策略。

Q&A

GeekAgent的历史压缩功能是什么?

GeekAgent的历史压缩功能是在对话历史超过一定字符数(默认4000字符)时,自动将旧消息摘要为一条系统消息,保留最近6条完整消息,以防止上下文溢出且不丢失关键信息。

GeekAgent历史压缩的触发条件是什么?

触发条件是history序列化后的字符数超过阈值,默认阈值为4000字符,可通过环境变量GEEKAGENT_MAX_HISTORY调整。在每轮对话开始前检查,超过阈值则自动压缩。

GeekAgent历史压缩时保留多少条最近消息?

保留最近6条完整消息,更早的消息会被摘要成一条系统消息。

GeekAgent历史压缩的两种入口是什么?

两种入口:自动触发(每轮对话前检查字符数,超过阈值自动压缩)和手动触发(用户输入/compact命令主动压缩)。两者共用同一套压缩逻辑。

GeekAgent历史压缩时终端会显示什么提示?

压缩发生时,终端会显示黄色进度行,格式为:[历史压缩:N 条旧消息合并为 1 条摘要]。

GeekAgent历史压缩后模型还能记住旧对话中的事实吗?

能。通过实测验证,压缩后模型仍能准确回答旧对话中的事实,因为摘要保留了关键信息。

GeekAgent历史压缩的阈值如何调整?

可以通过设置环境变量GEEKAGENT_MAX_HISTORY来调整阈值,例如GEEKAGENT_MAX_HISTORY=600 npm run dev -- day5/index.ts,将阈值临时调小以便观察触发过程。

GeekAgent历史压缩的摘要指令是什么?

摘要指令是COMPRESS_SYSTEM,它告诉模型将旧对话压缩成简洁的中文要点,保留用户目标、决定、文件路径、shell命令、工具调用、关键结论和未完成事项。

🏷️

标签

➡️

继续阅读