Daily Technical Tracking

每周科技追踪 · 10月第3周

2026年10月第3周 · 技术 / 产品 / 公司

每日追踪
← 返回首页

Koog 如何压缩 Agent 会话记忆

JVM 上的 Kotlin Agent 框架 Koog 把状态拆成三类,并强调三者不能互相替代:聊过什么由 ChatMemory 保存,任务做到哪里由 Persistence 的 checkpoint 记录,业务字段放节点输入输出或 AIAgentStorage,以后需要按相关性检索的知识则交给 LongTermMemory。搞混这三类是多轮 Agent 最常见的工程错误——把进度塞进聊天历史,重启后只能指望模型从对话里「回忆」到第几步。

ChatMemory 的提交边界很明确:strategyStarting 阶段按 sessionId 载入历史,strategyCompleted 阶段写回,也就是一轮对话成功结束才提交,中间失败则整批新消息丢弃、旧记录不动。存储抽象是 ChatHistoryProvider,只有 store 与 load 两个方法,保存的是 Message 列表,温度、工具选择等 LLMParams 不进记忆;默认实现是进程内存版,退出即丢。一个 Agent 只装一份 ChatMemory、一个 Provider 实例,不同 sessionId 在内部按 conversationId 分桶隔离;想让多个 Agent 共享历史,必须传入同一个 Provider 实例或指向同一持久后端。另一个易踩的点是 sessionId:run 时不传就给你一个随机 UUID,每轮都是全新会话,不会自动续聊,而且它只是会话标识,不是权限凭据。

历史压缩和窗口裁剪不是一回事。windowSize(n) 按消息条数裁剪、原消息保留,压缩则是让模型生成 TLDR 摘要替换原文,有损且可能写错,不能当无限上下文用。策略有五种:WholeHistory、FromLastNMessages(n)、FromTimestamp(t)、Chunked(size),以及按 Concept 与 FactType 抽取事实的 FactRetrieval。合成时会固定保留全部 system 消息、首个含文本的 user 消息、传入的 memory 消息与末尾未完成的工具调用,preserveMemory 保留的也只是带记忆标记的那几条,不是全部历史。自定义策略必须显式把结果写回 llmSession.prompt,否则压缩算完不生效。

Persistence 存的是消息历史、最后成功执行的节点及其输出、模型与工具选择、可序列化 storage 数据以及版本时间戳,不含协程堆栈与外部世界。恢复只依赖存储位置加 runId 或 sessionId,但策略、子图、节点路径与输出类型必须兼容;正常跑完会写 tombstone,同一 session 再跑就从头开始。序列化有两档降级:storage 字段无法序列化时只跳过该字段,节点输出无法序列化时整个 checkpoint 被跳过、方法返回 null 并打警告。回滚语义也有差别,rollbackToCheckpoint 会按逆序触发工具补偿,rollbackToLatestCheckpoint 和启动时的自动恢复都不触发,所以补偿不能当分布式事务用,外部副作用仍需幂等键兜底。