Agent长期记忆实现原理 1. 核心结论长期记忆不是简单保存历史对话全文也不是只依赖传统 RAG 向量检索。更合理的实现方式是将长期记忆组织成一个个独立、完整、可复用的知识点每个知识点保存一份 canonical 原文并派生出向量索引、关键词/全文索引、结构化元数据索引和规则索引查询时通过混合召回、去重、rerank 和上下文压缩最终进入 Agent 当前状态。在 Agent 架构中长期记忆通常服务于上下文管理模块例如 SAMState / 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通常可选MySQLPostgreSQLSQLiteMongoDB本地 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 向量库向量库负责语义召回常见选择MilvusQdrantpgvectorFAISSOpenSearch 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 TABLErerank对候选记忆重新排序和适用性判断判断当前任务最相关的记忆9.2 rerank 的核心目标rerank 的目标不是单纯选择“语义最像”或“关键词命中最多”的记忆而是判断哪些候选记忆对当前 Agent 决策最有用、最适用、最安全。它通常会综合考虑当前用户任务。当前对话和工具调用摘要。候选记忆正文。候选记忆元数据例如category、project、module、importance、updated_at。召回来源例如向量召回、关键词召回、规则召回。是否涉及安全、权限、数据库、生产环境等高风险场景。例如当前任务是MCP 启动时报 Zod 校验错误候选记忆为mem_001MCP CLI port字段类型需转number mem_002OpenSearch port配置说明 mem_003Web服务端口冲突排查 mem_004Zod 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也可以使用 RRFReciprocal Rank Fusionscore(d) Σ 1 / (k rank_i(d))多路召回都排名靠前的记忆会获得更高最终分数。9.3.2 Cross-Encoder rerankerCross-Encoder 是典型的独立 reranker 模型。它通常输入(query, candidate_memory)输出relevance_score它比单纯 embedding 相似度更细因为它会同时阅读 query 和候选记忆判断二者之间的细粒度相关性。9.3.3 LLM rerankLLM 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 token50 × 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 操作 记忆3partial_init 场景必须创建目标环境账号 记忆4MySQL 全局权限不可用于数据库级 GRANT 记忆5db-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 命令统一输出 JSONexit 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 做出更稳定、更符合规范的决策。