实现存储适配器#
把生命周期集成留在基类扩展中#
不要在新的数据库扩展里重复实现上下文恢复、Raw Log 事件或压缩的记账逻辑。请实现 SessionStorage 契约,并把它传给 BaseSessionPersistenceExtension。基类已经知道何时加载、追加、生成快照,以及清理请求级的记账数据。
运行时的三个存储操作#
方法 |
返回值 |
不变量 |
|---|---|---|
load |
SessionView:可选的检查点,以及有序的原始消息尾部 |
一致的视图;未知会话为空。 |
append |
分配的每会话序列号 |
原始消息不可变;追加会原子地推进序列号。 |
snapshot |
带新版本的 ContextSnapshot |
检查 expected_version 以及不断推进的 Raw Log 边界;绝不改写原始消息。 |
会话标题的 CRUD 以及分页的界面历史记录由具体的存储实现提供,例如 SQLiteSessionStorage。它与运行时持久化扩展要求的三个操作相互独立。
完整的适配器示例#
这个适配器在 SQLite 之上加了审计追踪。它有意把实际事务交给经过测试的 SQLite 实现去处理;这并不表示它实现了一个不同的数据库。PostgreSQL 或远程适配器会替换这些实现体,同时保持相同的顺序/版本保证。
class AuditedStorage:
"""A transparent adapter with an audit trail around an existing SQLite store."""
def __init__(self, database: SQLiteSessionStorage) -> None:
self.database = database
self.operations: list[str] = []
async def load(self, session_id: str) -> SessionView:
self.operations.append("load")
return await self.database.load(session_id)
async def append(
self,
session_id: str,
request_id: str,
message: AnyMessage,
timing: MessageTiming | None = None,
parent_session_id: str | None = None,
title: str | None = None,
agent_name: str | None = None,
metadata: Mapping[str, JsonValue] | None = None,
tags: Mapping[str, JsonValue] | None = None,
usage: ModelUsage | None = None,
) -> int:
self.operations.append(f"append:{message.role.value}")
return await self.database.append(
session_id, request_id, message, timing, parent_session_id, title, agent_name, metadata, tags, usage
)
async def snapshot(
self,
session_id: str,
compacted_message: CompactedMessage,
compacted_through_sequence: int,
expected_version: int,
) -> ContextSnapshot:
self.operations.append("snapshot")
return await self.database.snapshot(session_id, compacted_message, compacted_through_sequence, expected_version)
扩展本身保持精简:
class AuditedPersistence(BaseSessionPersistenceExtension[AuditedStorage]):
"""Only construct the adapter; the base owns all lifecycle integration."""
def __init__(self, database: SQLiteSessionStorage) -> None:
super().__init__(AuditedStorage(database))
运行 完整程序来验证一次请求会产生 load, append:system, append:user, append:assistant,并且不会做不必要的快照。
快照一致性检查清单#
要一致地读取检查点及其尾部消息;不要混合不同的数据库版本。
把 compacted_through_sequence 视为包含端点的已表示边界。
重放的尾部消息严格从该边界之后开始。
过期的 expected_version 应当被拒绝,而不是静默覆盖检查点。
不要每成功得到一次响应就存一个检查点。
要保留消息角色、属性、重放块、时间信息、元数据、标签和用量。
不要把 request_id 当作自动的幂等键。
为空会话、两消息轮次、工具往返、压缩之后又有新尾部、冲突、未知会话以及数据库重新打开等情况分别编写独立的测试。使用临时数据库,并释放所有连接池。
配套的检查点类型#
快照契约使用来自其定义模块的这个类:
from zett_agent.extensions.compaction import CompactedMessage
checkpoint = CompactedMessage(content="The user prefers Python.")
CompactedMessage 是一种特殊的系统消息,用来标记上下文检查点。它的 content保存摘要;继承来的 attributes保存可选的消息注解。它不会从包的根模块导出。请把这个检查点与原始 Raw Log 分开存储,并连同上面描述的、包含端点的序列边界一起保存。
相关 API:SessionStorage、BaseSessionPersistenceExtension、SQLiteSessionStorage、SessionView。