CMDC.Memory behaviour (cmdc v0.6.0)

Copy Markdown View Source

Memory behaviour 接口 — 可插拔的语义记忆存储。

定义 Agent 经验记忆的通用接口,支持多种存储后端:

  • CMDC.Memory.ETS — 内存存储(开发/测试)
  • CMDCMemoryPg.EpisodicMemoryBackend — PostgreSQL 持久化(生产推荐)

Entry 结构

存入的 entry 为 map,必填字段

  • :id :: String.t() — 调用方指定的唯一标识符
  • :content :: String.t() — 主体文本(用于搜索匹配)
  • :inserted_at :: DateTime.t() — 插入时间戳(backend 自动注入)
  • :tags :: [String.t()] — 标签列表,v0.6+ 一等公民字段,默认 []

其余字段自由扩展(如 :task:outcome:user_id:source)。 Backend 实现需在 store/3 时自动注入缺省字段(:id / :inserted_at / :tags)。

Tags 字段(v0.6+ 一等公民)

:tags 是后端一等公民索引列——backend 实现应为该字段建专用索引(PG GIN / ETS 二级索引),让 search/3:tags_any / :tags_all filter 高效执行。 老 entry 写入时省略 :tags 字段,backend 应自动落 [],搜索时 tags_* filter 对其等同于"无任何 tag"。

典型用例:

  • Anthropic Agent Skills 标准 frontmatter 字段对接(tags: [data-analysis, sql]
  • 多领域记忆隔离(tags: ["domain:finance", "year:2026"]
  • 检索时按场景筛选(filters: [{:tags_any, ["domain:finance"]}]

Schema Version(v0.6+)

Backend 可选实现 schema_version/0 返回正整数,标识其 entry shape 版本。 Backend 引入新必填字段 / 改变字段语义时应升版本号。集成方可通过 resolve_schema_version/1 安全读取(未实现 callback 的老 backend 默认返 1)。

Filter 操作符(v0.6+)

search_opts.filters 支持两种形式(同一调用混用合法,按 AND 组合):

形式语义示例
{key :: atom(), value}entry[key] == value(v0.5 行为,永久保留){:user_id, "u-1"}
{:tags_any, [tag1, tag2, ...]}entry.tags 与列表有任一交集{:tags_any, ["sql"]}
{:tags_all, [tag1, tag2, ...]}entry.tags 全部包含列表元素{:tags_all, ["sql", "v1"]}

{:tags_any, []} / {:tags_all, []} 视为跳过该过滤(所有 entry 通过), 方便业务条件构造("如果有 tag 选项就传入,没有就不过滤")。

Summary

Types

记忆 entry 标准形态。

search_opts.filters 支持的过滤项。

Callbacks

按 id 删除一条记忆 entry,id 不存在时也返回 :ok。

返回所有记忆 entry,按插入时间降序排列。

返回 backend 的 entry shape 版本号(v0.6+)。

按文本关键词搜索记忆 entry(大小写不敏感)。

按语义相似度检索(ETS 降级为关键词匹配)。

存储一条记忆 entry。

Functions

安全读取 backend 的 schema_version,未实现的老 backend 默认返 1

Types

entry()

@type entry() :: %{
  :id => entry_id(),
  :content => String.t(),
  :inserted_at => DateTime.t(),
  :tags => [tag()],
  optional(atom()) => term()
}

记忆 entry 标准形态。

v0.6+ 起 :tags 是一等公民字段(默认 []),与 :id / :content / :inserted_at 同等地位。其他字段自由扩展。

entry_id()

@type entry_id() :: String.t()

filter()

@type filter() :: {:tags_any, [tag()]} | {:tags_all, [tag()]} | {atom(), term()}

search_opts.filters 支持的过滤项。

  • {key :: atom(), value} — 等值匹配 entry[key] == value(v0.5 行为)
  • {:tags_any, [...]} — entry.tags 与列表有任一交集(v0.6+)
  • {:tags_all, [...]} — entry.tags 全部包含列表元素(v0.6+)

search_opts()

@type search_opts() :: [
  limit: pos_integer(),
  order: :desc | :asc,
  filters: [filter()]
]

store()

@type store() :: term()

tag()

@type tag() :: String.t()

Callbacks

delete(store, entry_id)

@callback delete(store(), entry_id()) :: :ok | {:error, term()}

按 id 删除一条记忆 entry,id 不存在时也返回 :ok。

list(store)

@callback list(store()) :: {:ok, [entry()]} | {:error, term()}

返回所有记忆 entry,按插入时间降序排列。

schema_version()

(optional)
@callback schema_version() :: pos_integer()

返回 backend 的 entry shape 版本号(v0.6+)。

当 backend 引入新必填字段 / 改变字段语义时应升版本号, 让集成方在 schema migration 期间能据此判断是否需要 ETL。

实现为可选 callback;未实现的老 backend 默认认为是 1。 调用方应通过 CMDC.Memory.resolve_schema_version/1 安全读取。

search(store, t, search_opts)

@callback search(store(), String.t(), search_opts()) ::
  {:ok, [entry()]} | {:error, term()}

按文本关键词搜索记忆 entry(大小写不敏感)。

similarity_search(store, t, search_opts)

@callback similarity_search(store(), String.t(), search_opts()) ::
  {:ok, [map()]} | {:error, term()}

按语义相似度检索(ETS 降级为关键词匹配)。

store(store, entry_id, map)

@callback store(store(), entry_id(), map()) :: :ok | {:error, term()}

存储一条记忆 entry。

id 为调用方指定的唯一标识符,data 必须包含 :content 字段。 Backend 实现应在缺省时自动注入 :tags(默认 [])和 :inserted_at

Functions

resolve_schema_version(backend)

@spec resolve_schema_version(module()) :: pos_integer()

安全读取 backend 的 schema_version,未实现的老 backend 默认返 1

示例

iex> CMDC.Memory.resolve_schema_version(CMDC.Memory.ETS)
1