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%',通常难以走索引,数据量变大后容易退化成全表扫描。MySQL FULLTEXT 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_003

9.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 rerank

9.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 rerank
9.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_at

14.2 向量索引表或向量库记录

memory_embeddings
  - memory_id
  - embedding
  - embedding_model
  - dimension
  - content_hash
  - updated_at

14.3 规则索引表

memory_rules
  - trigger_type
  - trigger_value
  - memory_id
  - priority
  - status

14.4 关键词/全文索引层索引文档(以 OpenSearch 为例)

memory_index
  - memory_id
  - title
  - content
  - keywords
  - usage_scenario
  - category
  - project
  - module
  - importance
  - status
  - version
  - content_hash
  - updated_at

15. 关键设计原则

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 做出更稳定、更符合规范的决策。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐