1. 核心结论
长期记忆不是简单保存历史对话全文,也不是只依赖传统 RAG 向量检索。更合理的实现方式是:
将长期记忆组织成一个个独立、完整、可复用的知识点;每个知识点保存一份 canonical 原文,并派生出向量索引、关键词/全文索引、结构化元数据索引和规则索引;查询时通过混合召回、去重、rerank 和上下文压缩,最终进入 Agent 当前状态。
在 Agent 架构中,长期记忆通常服务于上下文管理模块,例如 SAM(State / Semantic / Selective Attention Memory)。SAM 根据当前任务输入、历史轨迹和长期记忆,动态构造 Agent 下一步决策所需的状态:
s_t = SAM(τ_t, x, M)其中:
s_t:当前时刻 Agent 的状态。τ_t:到当前时刻为止的历史轨迹,包括对话、动作、工具结果等。x:当前用户输入或任务目标。M:长期记忆集合。SAM:负责选择、压缩、召回、组织上下文的模块。
2. 长期记忆的知识点模型
长期记忆可以理解为多个 Memory Object 的集合。每个 Memory Object 是一个相对独立、可复用、可单独召回的知识点,而不是一整篇大文档。
一条记忆通常包含:
{"id":"mem_001","title":"MCP CLI port字段类型需转number","content":"MCP CLI 读取配置时,port 字段如果来自 JSON 或环境变量,需要显式转成 number,否则 Zod 校验会失败。","category":"common_pitfalls_experience","keywords":["MCP","CLI","port","Zod","number"],"usage_scenario":["调试 MCP 启动参数校验失败","修改 CLI 配置解析逻辑"],"project":"xxx","module":"mcp","importance":0.8,"status":"active","version":12,"content_hash":"abc123","created_at":"2026-07-09T00:00:00Z","updated_at":"2026-07-09T00:00:00Z"}字段职责:
id:记忆唯一标识,多路索引都回指该 id。title:知识点标题,用于展示和高权重检索。content:知识正文,是最终进入上下文的主要内容。category:记忆分类,例如项目规范、常见坑点、环境配置。keywords:关键词,用于精确召回和规则命中。usage_scenario:适用场景,帮助判断是否应进入当前上下文。project/module:项目和模块范围,用于结构化过滤。importance:重要性,用于排序和上下文预算分配。status:状态,例如 active、deprecated、deleted。version/content_hash:一致性校验与索引重建依据。
3. 一份内容,多种索引
长期记忆的核心存储原则是:
一份 canonical memory 原文 + 多种派生索引也就是说,系统中应有一份权威的原始记忆记录,其他索引都是从它派生出来的检索结构。
Memory 原文记录 mem_001 │ ├── 向量索引:embedding(mem_001) ├── 关键词/全文索引:由倒排索引层基于 title/content/keywords/usage_scenario 构建 ├── 结构化元数据索引:category/project/module/status/time └── 规则索引:任务类型/安全约束/强制加载规则3.1 原文数据
原文数据用于最终进入上下文,给模型阅读、总结或执行约束。它是长期记忆的事实来源。
3.2 向量索引
向量索引用于语义召回。系统会对记忆内容生成 embedding:
{"memory_id":"mem_001","embedding":[0.012,-0.83,0.41,"..."],"embedding_model":"text-embedding-model","content_hash":"abc123"}向量召回适合处理语义相近但关键词不完全一致的场景。例如用户问“参数校验错误怎么办”,可能召回“port 字段类型需转 number 避免 Zod 校验失败”。
3.3 关键词/全文索引
关键词/全文索引是一种检索能力,用于根据用户输入中的精确词项、短语、字段名、错误码等内容召回相关记忆。它通常由倒排索引层提供,OpenSearch 是这类倒排索引层的一种工程实现。
三者关系可以理解为:
关键词/全文索引能力 ↓ 依赖 倒排索引数据结构 ↓ 可由以下组件实现 OpenSearch / Elasticsearch / Lucene / SQLite FTS5 / PostgreSQL full-text search它适合工程场景中的明确字段:
port Zod DROP TABLE exit code 2 db-config.json createdByAgent这些内容用向量召回可能不稳定,但关键词召回非常直接。后文提到的“倒排索引层”和“OpenSearch”,就是在解释这类关键词/全文索引能力如何被存储和实现。
3.4 结构化元数据索引
结构化元数据索引用于过滤和范围约束:
category = common_pitfalls_experience project = xxx module = mcp status = active importance > 0.7它可以先缩小候选范围,再做语义、关键词或规则匹配。
3.5 规则索引
规则索引用于强制加载关键约束,例如:
- 当前任务涉及数据库 DDL,则强制加载“禁止 DROP TABLE / DROP COLUMN”相关规则。
- 当前任务涉及 CLI 命令输出,则强制加载“CLI 输出 JSON 与 exit code 规范”。
- 当前任务涉及安全敏感操作,则强制加载安全约束。
这类记忆不能只依赖相似度召回,因为它们属于必须遵守的规则。
4. 存储分层架构
长期记忆系统通常采用分层存储:
主存储 保存 canonical memory 原文和结构化元数据 关键词/全文索引层(倒排索引层,例如 OpenSearch / Elasticsearch / Lucene) 保存面向检索优化的索引文档,负责关键词/全文检索 向量库 保存 embedding,负责语义召回 规则/元数据索引 负责任务类型匹配、权限过滤、强制加载4.1 主存储
主存储保存完整 memory record,通常可选:
- MySQL
- PostgreSQL
- SQLite
- MongoDB
- 本地 JSON / 文件系统
它的职责是:
- 保存 canonical 原文。
- 保存结构化元数据。
- 提供写入一致性、版本管理和审计能力。
- 支持从事实源重建关键词/全文索引层(如 OpenSearch)和向量索引。
4.2 关键词/全文索引层(倒排索引层,以 OpenSearch 为例)
关键词/全文索引层是关键词召回和全文检索能力的实现层。它背后的核心数据结构通常是倒排索引:先对title、content、keywords、usage_scenario等字段分词,再建立“词项 -> 记忆列表”的映射。
OpenSearch 不是“倒排索引”本身,而是一个提供倒排索引、全文检索和相关性排序能力的搜索引擎实现。类似实现还包括 Elasticsearch、Lucene、SQLite FTS5、PostgreSQL full-text search 等。
关键词/全文索引层负责:
- 倒排索引构建。
- 全文检索。
- 关键词匹配。
- BM25 相关性排序。
- 多字段权重检索。
- 元数据过滤组合查询。
以 OpenSearch 为例,关键词/全文索引层中保存的是面向检索优化的索引文档。它可以包含来自主存储的冗余字段,例如title、content、keywords、category、project、module等。
如果一致性保障充分,采用 OpenSearch 实现的关键词/全文索引层可以作为可靠读模型直接返回内容;如果一致性无法保证,则更适合作为候选召回层,通过memory_id回查主存储。
4.3 向量库
向量库负责语义召回,常见选择:
- Milvus
- Qdrant
- pgvector
- FAISS
- OpenSearch k-NN
如果长期记忆规模不大,可以使用 OpenSearch 同时承载全文检索和 k-NN 向量检索;如果语义召回规模较大或性能要求较高,建议将向量检索交给专业向量库。
5. 关键词/全文索引的数据结构(以 OpenSearch 为例)
以 OpenSearch 为例,关键词/全文索引层写入的是 JSON 索引文档。它不是简单的数据库行拷贝,而是面向检索优化的 read model。
示例:
{"memory_id":"mem_001","title":"MCP CLI port字段类型需转number","content":"MCP CLI 读取配置时,port 字段如果来自 JSON 或环境变量,需要显式转成 number,否则 Zod 校验会失败。","keywords":["MCP","CLI","port","Zod","number"],"usage_scenario":["调试 MCP 启动参数校验失败","修改 CLI 配置解析逻辑"],"category":"common_pitfalls_experience","project":"xxx","module":"mcp","importance":0.8,"status":"active","version":12,"updated_at":"2026-07-09T00:00:00Z","content_hash":"abc123"}字段分工:
memory_id:用于回指主存储。title/content/usage_scenario:用于分词和全文检索。keywords:用于精确关键词匹配,也可参与加权检索。category/project/module/status:用于过滤和权限范围控制。importance:用于排序或 rerank 特征。version/content_hash:用于判断索引是否与主存储一致。
5.1 OpenSearch mapping 示例
下面用 OpenSearch mapping 举例说明关键词/全文索引层如何区分全文检索字段和精确过滤字段:
{"mappings":{"properties":{"memory_id":{"type":"keyword"},"title":{"type":"text","analyzer":"standard"},"content":{"type":"text","analyzer":"standard"},"keywords":{"type":"keyword"},"usage_scenario":{"type":"text","analyzer":"standard"},"category":{"type":"keyword"},"project":{"type":"keyword"},"module":{"type":"keyword"},"status":{"type":"keyword"},"importance":{"type":"float"},"version":{"type":"long"},"updated_at":{"type":"date"},"content_hash":{"type":"keyword"}}}}text字段会被分词,用于全文检索;keyword字段不分词,用于精确匹配、过滤和聚合。
5.2 内部倒排结构
以 OpenSearch 为例,写入的是 JSON 文档,但搜索引擎内部会基于文本字段生成倒排索引:
MCP -> mem_001, mem_008, mem_021 Zod -> mem_001, mem_009 port -> mem_001, mem_003 校验 -> mem_001, mem_020 失败 -> mem_001, mem_020因此,关键词/全文索引层的作用不是简单“存数据”,而是用倒排索引把文本字段转换成可快速检索和排序的结构。OpenSearch 只是这种能力的一种工程实现。
5.3 为什么不用 MySQL 普通索引承担全文检索
MySQL 普通索引通常是 B-Tree,适合:
WHERE id = 'mem_001' WHERE category = 'common_pitfalls_experience' WHERE project = 'xxx' WHERE created_at > '2026-01-01'但长期记忆的关键词召回经常是:
MCP Zod port 配置校验失败这类检索需要:
- 中文/英文分词。
- 倒排索引。
- 多关键词交集与并集。
- BM25 / TF-IDF 相关性排序。
- 标题、正文、关键词等字段权重。
- 短语匹配、模糊匹配、同义词扩展。
- 大文本字段检索优化。
如果用 MySQL 普通索引配合LIKE '%keyword%',通常难以走索引,数据量变大后容易退化成全表扫描。MySQLFULLTEXT INDEX可以满足轻量全文检索,但在中文分词、复杂相关性调优、多字段权重、搜索扩展能力上通常不如 OpenSearch / Elasticsearch / Lucene 这类专门关键词/全文索引引擎。
6. 关键词/全文索引层是索引副本还是读模型
以 OpenSearch 为例,关键词/全文索引层通常会冗余存储一份来自主存储的检索字段,因此它看起来“已经包含了 MySQL 的内容”。关键区别不在于它有没有数据,而在于系统是否承诺这份索引文档永远最新、完整、可审计、可恢复。
可以按一致性能力划分三种读策略。
6.1 只作为候选召回层
适用条件:
- 关键词/全文索引层(如 OpenSearch)与主存储最终一致,但可能存在短暂延迟。
- 任务对最新性、安全性要求较高。
- 需要强权限校验或审计。
读取路径:
关键词/全文索引层召回 topK memory_id ↓ 主存储 batch get 最新 memory ↓ version/content_hash 校验 ↓ 进入上下文这种方案最稳,但多一次主存储查询。
6.2 作为主要读模型
适用条件:
- 关键词/全文索引层(如 OpenSearch)与主存储的一致性得到可靠保障。
- 索引文档包含完整可用的
title/content/metadata。 - 业务允许准实时或最终一致。
- 权限、状态、删除字段同步可靠。
读取路径:
关键词/全文索引层检索 ↓ 直接返回 title/content/metadata ↓ 进入上下文这种方案性能更好,也更简单。此时采用 OpenSearch 实现的关键词/全文索引层可以被视为 Search Read Model。
6.3 混合读策略
推荐采用混合策略:
普通记忆:直接使用关键词/全文索引层返回内容 关键规则:关键词/全文索引层召回 + 主存储校验 安全/权限场景:强制回查主存储或规则源 索引异常:回退主存储关键规则包括:
- 安全红线。
- 权限规则。
- 数据库禁止操作。
- 最新项目约束。
- 会直接影响 Agent 执行动作的规则。
7. 一致性对查询准确度的影响
如果关键词/全文索引层与主存储不一致,会直接影响长期记忆召回准确度。下面以 OpenSearch 作为实现举例:
| 不一致类型 | 表现 | 影响 |
|---|---|---|
| 主存储有,关键词/全文索引层没有 | 新增记忆未同步 | 漏召回 |
| 主存储已更新,关键词/全文索引层仍是旧内容 | 召回过期规则 | 错误上下文 |
| 主存储已删除,关键词/全文索引层仍存在 | 废弃记忆被召回 | 脏召回 |
| 元数据不一致 | project/module/status 错误 | 过滤或权限错误 |
| version/hash 不一致 | 索引副本过期 | 需要回查或重建 |
因此,如果要直接使用关键词/全文索引层返回的数据,必须把索引一致性作为核心能力设计。
8. 索引同步与一致性保障
推荐写入流程:
写入 memory_records ↓ 生成 version / content_hash ↓ 写入 outbox 或变更事件 ↓ 索引服务消费事件 ↓ 写入关键词/全文索引层索引文档(如 OpenSearch document) ↓ 生成并写入向量索引 ↓ 记录索引同步状态更新或删除记忆时,需要同步处理:
- 原文变更后重新生成关键词/全文索引文档和 embedding。
content_hash不一致时触发重建索引。- 删除建议使用
status = deleted/deprecated软删除,并在查询时过滤status = active。 - 异步同步需要支持重试、幂等和失败告警。
- 大规模 mapping 变更时使用 alias + reindex 平滑切换。
- 定期对比主存储和关键词/全文索引层的数量、version、content_hash,发现异常后重建索引。
推荐原则:
主存储是可审计、可恢复的事实源;关键词/全文索引层是可重建的检索读模型。若一致性得到保障,采用 OpenSearch 等实现的关键词/全文索引层可以作为主要读源;若一致性不可验证,则应回查主存储。
9. 混合召回机制
长期记忆召回通常不是单一路径,而是混合检索:
用户任务 x ↓ 生成多个检索信号 ↓ 并行召回候选记忆 ↓ 合并去重 ↓ 规则过滤 / 相关性排序 / rerank ↓ 压缩并放入当前上下文可以表示为:
候选记忆 = VectorRecall(x) ∪ KeywordRecall(x) ∪ MetadataFilter(x) ∪ RuleMatch(x)之后执行:
selected_memory = RerankAndSelect(候选记忆, τ_t, token_budget)最终进入状态:
s_t = BuildContext(x, τ_t, selected_memory)9.1 各召回方式分工
| 召回方式 | 适合场景 | 示例 |
|---|---|---|
| 向量召回 | 语义相似但关键词不一致 | “参数校验错误”召回“Zod port 类型问题” |
| 关键词召回 | 精确字段、错误码、函数名、配置项 | createdByAgent、exit code 2 |
| 元数据过滤 | 控制项目、模块、分类、状态范围 | project = xxx |
| 规则召回 | 安全红线、强制约束、项目协议 | 禁止 DROP TABLE |
| rerank | 对候选记忆重新排序和适用性判断 | 判断当前任务最相关的记忆 |
9.2 rerank 的核心目标
rerank 的目标不是单纯选择“语义最像”或“关键词命中最多”的记忆,而是判断:
哪些候选记忆对当前 Agent 决策最有用、最适用、最安全。
它通常会综合考虑:
- 当前用户任务。
- 当前对话和工具调用摘要。
- 候选记忆正文。
- 候选记忆元数据,例如
category、project、module、importance、updated_at。 - 召回来源,例如向量召回、关键词召回、规则召回。
- 是否涉及安全、权限、数据库、生产环境等高风险场景。
例如当前任务是:
MCP 启动时报 Zod 校验错误候选记忆为:
mem_001:MCP CLI port字段类型需转number mem_002:OpenSearch port配置说明 mem_003:Web服务端口冲突排查 mem_004:Zod schema 校验规范rerank 应该判断:
mem_001 最相关,因为同时命中 MCP、CLI、port、Zod、校验失败。 mem_004 次相关,因为它讲 Zod,但不一定是 MCP CLI 场景。 mem_002 较弱,因为只是 port 字面匹配。 mem_003 更弱,因为是 Web 服务端口冲突。最终排序可能是:
mem_001 > mem_004 > mem_002 > mem_0039.3 rerank 的实现方式
rerank 可以由多种方式实现,不一定必须是一个独立大模型。
9.3.1 分数融合
最轻量的方式是融合多路召回分数:
final_score = 0.4 * vector_score + 0.3 * keyword_score + 0.1 * metadata_score + 0.1 * importance_score + 0.1 * rule_score也可以使用 RRF(Reciprocal Rank Fusion):
score(d) = Σ 1 / (k + rank_i(d))多路召回都排名靠前的记忆会获得更高最终分数。
9.3.2 Cross-Encoder reranker
Cross-Encoder 是典型的独立 reranker 模型。它通常输入:
(query, candidate_memory)输出:
relevance_score它比单纯 embedding 相似度更细,因为它会同时阅读 query 和候选记忆,判断二者之间的细粒度相关性。
9.3.3 LLM rerank
LLM rerank 是把当前任务上下文和候选记忆交给 LLM,让模型判断哪些记忆更适合当前场景。
输入通常包括:
1. 当前用户任务 2. 当前对话/工具调用摘要 3. 候选记忆列表 4. 每条记忆的元数据 5. 选择标准输出可以是:
[{"memory_id":"mem_001","score":0.95,"decision":"keep","reason":"直接命中 MCP、Zod、port 类型转换问题"},{"memory_id":"mem_004","score":0.72,"decision":"keep","reason":"Zod 校验相关,但不一定是 MCP CLI 场景"},{"memory_id":"mem_002","score":0.21,"decision":"drop","reason":"只是 port 字面匹配,语义偏 OpenSearch 配置"}]LLM rerank 可以由当前交互 Agent 自己完成,也可以由独立的 rerank 子模块完成。
当前 Agent 自己判断 - 实现简单 - 上下文完整 - 适合候选较少的场景 独立 rerank 模块判断 - 职责清晰 - 输出格式更稳定 - 便于评估和替换 - 可替换为 Cross-Encoder 或规则融合关键点是:LLM rerank 通常只处理已经召回的候选 topN,而不是处理全部长期记忆。
不是:所有记忆 → LLM 判断 而是:召回候选 topK → LLM rerank9.4 rerank 的 token 与计算开销
独立 reranker 也有额外开销,只是不同实现方式的成本形态不同。
9.4.1 LLM rerank 的开销
如果 reranker 是 LLM,就会有明确的 token 开销:
当前任务摘要 + 候选记忆 topN + 排序规则 + 输出排序结果例如召回 50 条候选记忆,每条 300 token:
50 × 300 = 15000 token如果全部交给 LLM rerank,会产生较明显的输入成本和延迟。但 rerank 后可能只把 top5 放入主 Agent 上下文:
5 × 300 = 1500 token因此 LLM rerank 的价值在于:用中间成本换取更少的最终上下文、更低噪声和更高适用性。
9.4.2 专门 reranker 模型的开销
如果使用 Cross-Encoder 等专门排序模型,也会有 tokenization 和推理成本,但它不占用主交互 Agent 的上下文窗口。
它通常逐对处理:
(query, candidate_memory) -> score相比 LLM rerank,它的特点是:
- 不占主 Agent 上下文。
- 输出通常只是分数。
- 模型通常比大 LLM 小。
- 成本通常低于 LLM rerank。
- 可以批处理并行优化。
所以它不是“没有 token 成本”,而是成本更可控、工程化程度更高。
9.5 rerank 成本控制策略
为了避免 rerank 成为性能和成本瓶颈,可以采用分层策略。
9.5.1 先粗排再 rerank
不要把所有候选都交给 reranker:
召回 200 条 ↓ 规则过滤 + 分数融合筛到 30 条 ↓ reranker 排序 ↓ top5 进入上下文9.5.2 只传摘要,不传全文
候选记忆可以分成短摘要和完整正文:
short_content:用于 rerank full_content:最终入上下文时使用reranker 先看:
title + keywords + usage_scenario + summary命中后再取完整内容。
9.5.3 分层 rerank
推荐分层处理:
第一层:BM25 / vector score / metadata 规则融合 第二层:轻量 reranker 模型 第三层:高风险或复杂任务才用 LLM rerank9.5.4 缓存 rerank 结果
对高频相似 query,可以缓存:
query_signature + candidate_ids -> rerank_result例如项目规范、常见错误排查、固定模块问题,通常适合缓存。
9.5.5 规则强制保留
安全规则和硬约束不一定参与复杂 rerank,可以在任务类型命中时直接保留:
涉及数据库删除操作 ↓ 直接强制加载禁止删除规则这样既降低模型判断成本,也避免安全规则被模型误删。
10. SAM 与长期记忆的关系
RAG / 检索只负责“找候选资料”,SAM 负责“管理上下文”。
当前任务 x 历史轨迹 τ_t 长期记忆 M ↓ 混合召回:向量 + 关键词 + 元数据 + 规则 ↓ 候选记忆集合 ↓ SAM 选择 / 压缩 / 排序 / 去重 ↓ 当前状态 s_t ↓ Agent 决策下一步动作因此,长期记忆召回是 SAM 的子能力之一。SAM 的核心职责包括:
- 根据任务决定是否需要召回长期记忆。
- 判断候选记忆是否适用于当前上下文。
- 在 token 预算内选择最重要的信息。
- 对长记忆做摘要压缩。
- 将记忆、历史轨迹、工具结果合并成当前状态。
11. 知识点粒度控制
长期记忆最好不是一整篇大文档,而是拆成多个相对完整的知识点。
11.1 不推荐的大粒度
Qoder 插件开发完整说明 数据库子 Agent 完整规范 MCP 工具全部问题总结这些内容太大,容易导致:
- 召回语义模糊。
- 关键词污染。
- 上下文浪费。
- 局部更新困难。
11.2 推荐的知识点粒度
Qoder 插件实际加载路径 Qoder MCP transport type 规范 CLI 输出与错误码规范 MCP CLI port 字段类型需转 number MySQL 全局权限不可用于数据库级 GRANT 禁止 DROP TABLE / DROP COLUMN 操作每条记忆只表达一个规则、经验、决策或坑点,并带上必要的适用条件。
11.3 判断是否过大
如果一条记忆可以拆成多个明确标题,就说明它太大。
例如:
数据库子 Agent 规范可以拆成:
记忆1:数据库 Agent 禁止删除操作 记忆2:禁止 MODIFY COLUMN 操作 记忆3:partial_init 场景必须创建目标环境账号 记忆4:MySQL 全局权限不可用于数据库级 GRANT 记忆5:db-config.json 账号字段扁平化规范拆分后,每条记忆都可以独立召回、独立更新、独立进入上下文。
12. 文档知识与长期记忆的区别
文档类知识偏向原文切片:
大文档 ↓ 切分 chunks ↓ 每个 chunk 生成 embedding ↓ 召回 chunk ↓ 必要时回溯 parent_doc_id 或相邻 chunk长期记忆偏向知识点沉淀:
大经验 / 大规范 ↓ 提炼独立知识点 ↓ 每个知识点成为 Memory Object ↓ 分别建立索引 ↓ 按需召回简单说:
- 文档库更适合“保留原文结构”。
- 长期记忆更适合“提炼可复用知识点”。
13. 一次完整召回示例
用户输入:
MCP 启动时报 Zod 校验错误,帮我看看是不是配置问题。系统可能执行:
1. 关键词召回 - OpenSearch 命中:MCP、Zod、port、配置 2. 向量召回 - 向量库命中:MCP CLI port 字段类型需转 number 3. 元数据过滤 - project = xxx - module = mcp - status = active 4. 规则召回 - 加载 MCP transport type 兼容性要求 - 加载 CLI 输出与错误码规范 5. 合并去重 - 多路召回可能命中同一个 memory_id,需要去重 6. 一致性判断 - 普通记忆可直接使用 OpenSearch 内容 - 关键规则或 hash 异常时回查主存储 7. rerank - 根据当前任务判断最相关的是 port 字段类型问题 8. 上下文构造 - 将相关记忆摘要放入当前状态 s_t最终进入上下文的可能是:
当前任务涉及 MCP 启动配置校验。 历史经验:MCP CLI 的 port 字段如果来自 JSON 或环境变量,需要显式转成 number,否则 Zod 校验会失败。 相关约束:CLI 命令统一输出 JSON,exit code 0/1/2 分别表示成功、业务失败、系统错误。14. 建议数据模型
14.1 主存储表
memory_records - id - title - content - category - keywords - usage_scenario - project - module - importance - status - version - content_hash - created_at - updated_at14.2 向量索引表或向量库记录
memory_embeddings - memory_id - embedding - embedding_model - dimension - content_hash - updated_at14.3 规则索引表
memory_rules - trigger_type - trigger_value - memory_id - priority - status14.4 关键词/全文索引层索引文档(以 OpenSearch 为例)
memory_index - memory_id - title - content - keywords - usage_scenario - category - project - module - importance - status - version - content_hash - updated_at15. 关键设计原则
15.1 原文是事实源
向量、关键词、元数据索引都是派生数据,不能替代 canonical memory 原文。
15.2 关键词/全文索引层可以作为读模型
如果一致性、权限字段、删除状态和版本校验都可靠,关键词/全文索引层可以直接返回记忆内容并进入上下文。OpenSearch 是实现该读模型的一种常见选择。
15.3 关键场景需要事实源兜底
安全规则、权限判断、最新状态、审计追溯等场景,建议回查主存储或规则源。
15.4 所有索引都回指同一个 memory id
多路召回的结果最终都应该回到统一的memory_id,便于合并去重、统一排序、权限控制和一致性校验。
15.5 规则记忆不能只靠相似度
安全规范、强制约束、项目协议必须通过规则命中加载,不能只依赖向量或关键词相似度。
15.6 召回之后还需要选择
召回只是得到候选记忆,还要经过:
- 去重。
- rerank。
- 适用性判断。
- 一致性校验。
- token 预算控制。
- 摘要压缩。
- 与当前轨迹合并。
15.7 记忆需要持续维护
长期记忆不是越多越好,需要维护:
- 过期记忆要更新或删除。
- 冲突记忆要合并或标记优先级。
- 大记忆要拆分。
- 太碎的记忆要合并。
- 高频使用记忆可以提高重要性。
- 索引异常要可检测、可重建。
16. 总结
长期记忆的实现可以概括为:
长期记忆 = 独立知识点集合 每个知识点 = canonical 原文 + 元数据 + 多种派生索引 关键词/全文索引层 = 基于倒排索引实现关键词/全文检索读模型,OpenSearch 是一种实现示例;一致性可靠时可直接返回内容 向量库 = 语义召回能力 混合召回 = 向量 RAG + 关键词 grep + 结构化过滤 + 规则命中 + rerank 上下文管理 = SAM 根据当前任务、历史轨迹和 token 预算选择并压缩记忆最终目标不是“存下所有历史”,而是:
在正确的任务时机,召回正确的知识点,并以合适的粒度放入当前上下文,帮助 Agent 做出更稳定、更符合规范的决策。