记忆、Session 与对话压缩
AI-04: 记忆、Session 与对话压缩
学习目标
- 掌握 SessionABC 四方法协议
- 理解 SQLiteSession / OpenAIConversationsSession 两种持久化方案
- 理解 OpenAIResponsesCompactionSession 自动对话压缩
- 理解 RunState 序列化与 HITL 恢复流程
前置知识
本章涉及记忆系统的通用原理,建议先阅读:
下文直接聚焦 OpenAI Agents SDK 的具体实现。
项目实践
SessionABC 四方法协议
SDK 定义了统一的 Session 协议,两种实现方式(Session Protocol 和 SessionABC):
| 方法 | 作用 |
|---|---|
get_items(limit) | 获取对话历史(限制时取最新 N 条,按时间顺序返回) |
add_items(items) | 追加新 items 到历史 |
pop_item() | 原子删除并返回最新 item |
clear_session() | 删除该 session_id 的所有 items |
三种 Session 实现对比
SQLiteSession:
- 两种模式:内存(
:memory:)或文件(Path) - 线程安全:文件级
RLock(引用计数,跨实例共享同文件锁) get_items带 limit 时:ORDER BY id DESC LIMIT n然后反转pop_item:DELETE ... RETURNING原子操作- 无效 JSON 行被静默跳过
OpenAIConversationsSession:
session_id首次 API 调用前为None(懒初始化)- 存储完全在 OpenAI 服务端
clear_session调用conversations.delete()并重置session_id- 不能被 compaction session 包装(服务端管理历史)
OpenAIResponsesCompactionSession:自动对话压缩
这是一个装饰器模式,包装任何本地 Session(除 OpenAIConversationsSession):
session = OpenAIResponsesCompactionSession( underlying_session=SQLiteSession(path="...", session_id="conv-001"), model="gpt-4.1", # 用于压缩的模型)触发条件:默认当候选 items ≥ 10 时触发(可自定义 should_trigger_compaction)。
压缩流程:
- 每个 turn 结束后,
save_result_to_session()检查 session 是否支持压缩 - 如有本地工具结果待处理 → 延迟压缩;否则立即执行
- 调用
client.responses.compact()生成压缩摘要 clear()+add_items()替换底层 session 内容为压缩结果- 更新内部缓存
_compaction_candidate_items和_session_items
安全机制:如果 clear-and-replace 失败,尝试恢复之前 items。用户消息和已有 compaction items 被排除在候选集外。
Session 集成到 Runner.run()
与 Server-Managed Conversation 的互斥:当 OpenAIServerConversationTracker 活跃时(conversation_id/previous_response_id),session 持久化被完全禁用。
问题与规避
| 问题 | 规避方案 |
|---|---|
| 长时间对话未压缩导致 token 超限 | 使用 OpenAIResponsesCompactionSession,设置合理的触发阈值 |
| OpenAIConversationsSession 被 compaction 包装导致异常 | 检查 session 类型,OpenAIConversationsSession 不使用 compaction |
| Session 恢复时 corrupted JSON 行 | pop_item 自动丢弃无效行并尝试前一条 |
| 多进程并发写同一 SQLite 文件 | 使用文件级 RLock,或切换为 OpenAIConversationsSession |
设计取舍
为什么 Session 持久化与 Server-Managed Conversation 互斥
当使用 OpenAI 服务端对话管理时,OpenAI 服务端已维护完整的对话历史和上下文窗口。此时 SDK 侧再维护一份 session history 会导致双重存储和状态不一致。因此 SDK 明确禁用 session 持久化。
为什么 compaction 使用 clear-and-replace 而非增量更新
responses.compact 返回的是一条压缩摘要消息。用这条消息替换全部历史(clear + re-add)比增量替换更简单、更不易出错。
风险:如果 replace 失败,可能丢失原始 items。SDK 通过尝试恢复来缓解此风险。