聊天记忆
ChatMemory 功能使 AI 智能体能够存储对话历史记录,并在多次运行中检索它。
安装后,智能体会在每次运行开始时自动加载之前的消息,并在运行完成时存储更新后的对话,从而实现自然的多轮聊天。
核心能力:
- 按会话 ID 自动加载和存储对话历史记录
- 通过
ChatHistoryProvider提供可插拔的存储后端 - 内置预处理器以限制历史记录大小和过滤消息
- 支持自定义预处理器以实现任意消息转换
添加依赖项
聊天记忆是一个可选的功能,在 Koog 中默认不可用。
要为你的 Koog 智能体实现聊天记忆,请添加 ai.koog:agents-features-memory 的依赖项:
Note
ChatMemory 功能从 Koog 版本 0.7.0 开始可用。
启用聊天记忆
在创建智能体时,使用 install() 方法安装 ChatMemory:
默认情况下,它使用一个无预处理器的内存聊天历史记录提供程序。
配置 ChatMemory 功能以使用自定义的聊天历史记录提供程序和预处理器,例如:
AIAgent<String, String> agent = AIAgent.builder()
.promptExecutor(executor)
.llmModel(OpenAIModels.Chat.GPT4oMini)
.install(ChatMemory.Feature, config -> config
.chatHistoryProvider(new MyDatabaseChatHistoryProvider())
.windowSize(20)
.filterMessages(msg -> msg instanceof Message.User || msg instanceof Message.Assistant))
.build();
会话 ID
将会话 ID 作为第二个参数提供给 agent.run()。
ChatMemory 使用此 ID 来存储和加载对话:
// First run - the agent saves the chat history at the end
agent.run("What is the capital of France?", "session-1")
// Second run — the agent loads the previous exchange
agent.run("And what about Germany?", "session-1")
不同的会话ID会产生完全隔离的历史记录。
历史记录提供器
默认的 InMemoryChatHistoryProvider 是线程安全的,但不具备持久性(重启后历史记录会丢失)。
在生产环境中,请实现您自己的 ChatHistoryProvider,以持久化存储消息。
class MyDatabaseChatHistoryProvider(private val db: Database) : ChatHistoryProvider {
override suspend fun store(conversationId: String, messages: List<Message>) {
db.saveMessages(conversationId, messages)
}
override suspend fun load(conversationId: String): List<Message> {
return db.loadMessages(conversationId) ?: emptyList()
}
}
预处理器
预处理器在加载时(代理看到消息列表之前)和存储时(保存之前)对消息列表进行转换。
它们按照您添加到 ChatMemory 功能配置中的顺序依次运行。
内置预处理器
| 配置方法 | 预处理器类 | 行为 |
|---|---|---|
windowSize(n) |
WindowSizePreProcessor |
仅保留最后 n 条消息 |
filterMessages { ... } |
FilterMessagesPreProcessor |
保留符合谓词条件的消息 |
预处理器的顺序
预处理器按顺序运行,每个预处理的输出作为下一个的输入。 这意味着顺序很重要。
// Effect: keep last 10 messages, then filter short ones from those 10
windowSize(10)
filterMessages { it.content.length <= 100 }
// Effect: filter short messages first, then keep last 10 of the survivors
filterMessages { it.content.length <= 100 }
windowSize(10)
自定义预处理器
要创建自定义预处理器,请实现 ChatMemoryPreProcessor 接口:
class RedactEmailsPreProcessor : ChatMemoryPreProcessor {
override fun preprocess(messages: List<Message>): List<Message> {
return messages.map { message ->
// Replace email addresses in message content
Message.User(message.content.replace(Regex("[\\w.]+@[\\w.]+"), "[REDACTED]"))
}
}
}
然后将其添加到配置中:
聊天记忆与代理持久化
ChatMemory 将每次 agent.run() 调用视为一个原子性的、自包含的循环。
代理在运行前加载聊天历史记录,并在成功运行后存储它。
如果代理在运行期间崩溃,它不会存储当前的聊天消息,
这意味着聊天历史记录将保持运行前的状态。
持久化 在运行期间捕获代理的内部执行状态 (图节点、消息历史记录、输入和输出)作为检查点。 如果代理崩溃,它可以从最后一个检查点恢复。
| 聊天记忆 | 持久化 | |
|---|---|---|
| 保存内容 | 对话消息 | 执行状态 |
| 保存时机 | agent.run() 完成后 |
每个图节点之后或在运行期间手动定义的检查点 |
| 崩溃行为 | 进行中的运行会丢失;之前的历史记录保持不变 | 可以从最后一个检查点恢复 |
| 典型用途 | 多轮对话连续性 | 需要崩溃恢复的长时运行代理 |
如果您的代理执行长时运行任务,且中途崩溃会造成较大损失,请考虑 同时安装这两个功能:
val agent = AIAgent(
promptExecutor = executor,
llmModel = OpenAIModels.Chat.GPT4oMini,
systemPrompt = "You are a helpful assistant.",
) {
install(ChatMemory) {
chatHistoryProvider = MyDatabaseProvider()
windowSize(50)
}
install(Persistence) {
storage = MyPersistenceStorageProvider()
enableAutomaticPersistence = true
}
}
最佳实践
- 始终设置窗口大小,以防止对话无限增长。
- 仔细安排预处理器顺序,因为先过滤后窗口化与先窗口化后过滤会产生不同的结果。
- 使用有意义的会话ID 进行历史记录隔离:用户ID、聊天线程ID或UUID都是不错的选择。
- 在生产环境中实现持久化提供器,因为默认的
InMemoryChatHistoryProvider在重启时会丢失历史记录。
后续步骤- 学习如何构建一个带有记忆的简单 CLI 聊天循环
- 查看一个带有记忆的聊天端点示例