cmdc_memory_pg
CMDC PostgreSQL backend — Checkpoint + EpisodicMemory 持久化。
让 cmdc Agent 在 BEAM 节点重启 / 跨设备 / 跨进程的场景下,完整保留对话上下文 + 情景记忆。
v0.1 范围(严控二件套)
| 模块 | 实现 behaviour | 用途 |
|---|---|---|
CMDCMemoryPg.CheckpointBackend | CMDC.Checkpoint.Backend |
Agent 会话快照持久化(CMDC.checkpoint!/2 后端) |
CMDCMemoryPg.EpisodicMemoryBackend | CMDC.Memory |
情景记忆 few-shot 持久化(与 Plugin.Builtin.EpisodicMemory 对接) |
v0.1 明示不含
-
❌ pgvector 真语义检索 —
similarity_search/3降级为 ILIKE 文本匹配(与 ETS backend 同行为) - ❌ 3-tier Memory(Working / Semantic / Procedural)— 留 v0.2
-
❌ Composite 路由 backend — 见 cmdc 主库
CMDC.Backend.Composite - ❌ KV jsonb backend — 留 v0.2
-
❌ Cloak encryption 强制集成 — 给集成方留
CMDC.Checkpoint.Snapshot.redact/2hook, 按需在 wrapper 层接 Cloak / KMS(详见CheckpointBackendmoduledoc)
安装
defp deps do
[
{:cmdc, "~> 0.5"},
{:cmdc_memory_pg, "~> 0.1"}
]
end配置
# config/runtime.exs
config :cmdc_memory_pg, CMDCMemoryPg.Repo,
database: "cmdc_prod",
username: System.fetch_env!("PGUSER"),
password: System.fetch_env!("PGPASSWORD"),
hostname: System.get_env("PGHOST", "localhost"),
port: String.to_integer(System.get_env("PGPORT", "5432")),
pool_size: 10
# 设为 CMDC.Checkpoint 默认 backend
config :cmdc, :checkpoint_backend, CMDCMemoryPg.CheckpointBackend启动
defmodule MyApp.Application do
def start(_type, _args) do
children = [
CMDCMemoryPg.Repo,
# ... 其他子进程
]
Supervisor.start_link(children, strategy: :one_for_one)
end
endMigration
$ mix ecto.create
$ mix ecto.migratemigration 创建 2 张表:
| 表 | 用途 |
|---|---|
cmdc_checkpoints |
Snapshot bytea 存储(:erlang.term_to_binary([:compressed]))+ 索引 (session_id, checkpoint_id) |
cmdc_episodic_memories |
情景记忆(按 user_id namespace 隔离)+ 索引 (user_id, episode_id) |
使用
1. Checkpoint 持久化
# 抓快照
{:ok, snap} = CMDC.checkpoint!(session)
# 跨 BEAM 恢复
{:ok, snap} = CMDC.Checkpoint.load("sess-prod-001")
{:ok, new_session} = CMDC.resume_session!(snap)
无需指定 backend — 配置 :cmdc, :checkpoint_backend 后默认走 PG。
2. 情景记忆 few-shot
# 配置 EpisodicMemory Plugin 用 PG backend
{:ok, session} =
CMDC.create_agent(
model: "anthropic:claude-sonnet-4-5",
user_data: %{user_id: "alice"},
plugins: [
{CMDC.Plugin.Builtin.EpisodicMemory,
memory_store: :ignored,
memory_module: CMDCMemoryPg.EpisodicMemoryBackend}
]
)
# 成功对话自动写入;下次同用户类似 query 自动 few-shot 加载与 Cloak 集成(可选 encryption at rest)
cmdc 主库提供 CMDC.Checkpoint.Snapshot.redact/2 helper — 集成方在 wrapper backend 层接 Cloak:
defmodule MyApp.EncryptedCheckpointBackend do
@behaviour CMDC.Checkpoint.Backend
@impl true
def save(sid, snap, opts) do
sanitized = CMDC.Checkpoint.Snapshot.redact(snap, &MyApp.Vault.encrypt/1)
CMDCMemoryPg.CheckpointBackend.save(sid, sanitized, opts)
end
@impl true
def load(sid, opts) do
case CMDCMemoryPg.CheckpointBackend.load(sid, opts) do
{:ok, snap} ->
decrypted = CMDC.Checkpoint.Snapshot.redact(snap, &MyApp.Vault.decrypt/1)
{:ok, decrypted}
other -> other
end
end
# list / delete 透传
defdelegate list(sid, opts), to: CMDCMemoryPg.CheckpointBackend
defdelegate delete(sid, opts), to: CMDCMemoryPg.CheckpointBackend
end测试
测试套件分两层:
- 单元测试 — 验证 backend 逻辑 / 序列化 / 路径解析等(不依赖真实 PG)
- 集成测试 — 真实 PG,需 docker:
$ docker compose up -d
$ mix ecto.setup
$ mix test --include pg
不带 --include pg 时跳过 PG 集成测,便于纯单元 CI。
v0.2 路线图
| 项 | 优先级 | 说明 |
|---|---|---|
| pgvector embedding 检索 | P0 |
替换 ILIKE,让 similarity_search/3 真实语义匹配 |
| Working Memory backend | P1 | session 短期 KV 存储(区别于 Episodic 长期) |
| Composite 路由配方 | P1 |
在 cmdc 主库 Backend.Composite 之上提供推荐配置模板 |
| Cloak ecto_field encryption | P2 | 字段级加密预设(不强制) |
License
Apache 2.0