接入存储适配器#

包装一个已有的存储后端,以记录持久化操作,同时让持久化扩展管理对话历史记录。

运行#

在仓库根目录下执行:

uv run python docs/_examples/storage_adapter.py

输出包含:

load, append:system, append:user, append:assistant

这个程序在需要时使用确定性的离线模型和临时存储。它不需要 API 密钥,并由文档测试套件执行。

适配存储,而不是智能体循环#

AuditedStorage 把读写委托给 SQLite,同时记录它们的顺序。扩展只需要构造这个适配器:

class AuditedPersistence(BaseSessionPersistenceExtension[AuditedStorage]):
    """Only construct the adapter; the base owns all lifecycle integration."""

    def __init__(self, database: SQLiteSessionStorage) -> None:
        super().__init__(AuditedStorage(database))

完整源码实现了加载历史记录、追加消息和保存压缩检查点所需的存储方法。如果你用其他数据库替换 SQLite,请保留顺序、事务和版本校验;这些保证在安全恢复对话时很重要。

完整源码#

下载 storage_adapter.py。

  1"""Adapt a storage implementation without reimplementing persistence hooks."""
  2
  3import asyncio
  4from collections.abc import AsyncIterator, Mapping
  5from pathlib import Path
  6from tempfile import TemporaryDirectory
  7
  8from zett_agent.agent import AgentRunConfig
  9from zett_agent.client import create_agent
 10from zett_agent.extensions.compaction import CompactedMessage
 11from zett_agent.extensions.events import MessageTiming
 12from zett_agent.extensions.persistence import (
 13    BaseSessionPersistenceExtension,
 14    ContextSnapshot,
 15    SessionView,
 16)
 17from zett_agent.json_types import JsonValue
 18from zett_agent.messages import (
 19    AnyMessage,
 20    AssistantMessage,
 21)
 22from zett_agent.model import (
 23    ModelEvent,
 24    ModelRequest,
 25    ModelResponse,
 26    ModelUsage,
 27    RetryOptions,
 28)
 29from zett_agent.storage import SQLiteSessionStorage
 30
 31
 32class AuditedStorage:
 33    """A transparent adapter with an audit trail around an existing SQLite store."""
 34
 35    def __init__(self, database: SQLiteSessionStorage) -> None:
 36        self.database = database
 37        self.operations: list[str] = []
 38
 39    async def load(self, session_id: str) -> SessionView:
 40        self.operations.append("load")
 41        return await self.database.load(session_id)
 42
 43    async def append(
 44        self,
 45        session_id: str,
 46        request_id: str,
 47        message: AnyMessage,
 48        timing: MessageTiming | None = None,
 49        parent_session_id: str | None = None,
 50        title: str | None = None,
 51        agent_name: str | None = None,
 52        metadata: Mapping[str, JsonValue] | None = None,
 53        tags: Mapping[str, JsonValue] | None = None,
 54        usage: ModelUsage | None = None,
 55    ) -> int:
 56        self.operations.append(f"append:{message.role.value}")
 57        return await self.database.append(
 58            session_id, request_id, message, timing, parent_session_id, title, agent_name, metadata, tags, usage
 59        )
 60
 61    async def snapshot(
 62        self,
 63        session_id: str,
 64        compacted_message: CompactedMessage,
 65        compacted_through_sequence: int,
 66        expected_version: int,
 67    ) -> ContextSnapshot:
 68        self.operations.append("snapshot")
 69        return await self.database.snapshot(session_id, compacted_message, compacted_through_sequence, expected_version)
 70
 71
 72class AuditedPersistence(BaseSessionPersistenceExtension[AuditedStorage]):
 73    """Only construct the adapter; the base owns all lifecycle integration."""
 74
 75    def __init__(self, database: SQLiteSessionStorage) -> None:
 76        super().__init__(AuditedStorage(database))
 77
 78
 79class FixedModel:
 80    retry = RetryOptions(max_retries=0)
 81
 82    async def stream(self, request: ModelRequest) -> AsyncIterator[ModelEvent]:
 83        yield ModelEvent.completed(ModelResponse(AssistantMessage(content="Saved.")))
 84
 85
 86async def main() -> None:
 87    with TemporaryDirectory(prefix="zett-storage-adapter-") as directory:
 88        database = SQLiteSessionStorage(Path(directory) / "sessions.sqlite")
 89        try:
 90            extension = AuditedPersistence(database)
 91            client = await create_agent(FixedModel(), config=AgentRunConfig(session_id="audit"), extensions=[extension])
 92            await client.run("Remember this")
 93            assert extension.storage.operations == ["load", "append:system", "append:user", "append:assistant"]
 94            print(", ".join(extension.storage.operations))
 95            assert len(await database.list_raw_messages("audit")) == 3
 96        finally:
 97            await database.close()
 98
 99
100if __name__ == "__main__":
101    asyncio.run(main())

下一步:存储适配器的约定见实现存储适配器;如果 SQLite 已经满足需求,请看保存并恢复对话。