标签归档:AIAgent

从碎片检索到深度推理:如何重塑 AI Agent 的知识引擎

如果现在对于AI Agent 的知识引擎或者知识库的认知还停留在在「向量库 + embedding + topK 检索」的人,项目大概率正在踩坑。

传统 RAG 解决的是「能不能把资料塞给模型」的问题,而知识引擎要解决的是「模型能不能沿着知识结构走到正确答案」的问题。 这两个层级差得很远。前者偏检索,后者开始接近推理。Agent 一旦进入企业场景,尤其是要处理流程、制度、系统配置、投融资关系、风控链路、故障追因这类问题,知识如果还是碎片状态,后面的规划、调用、执行基本都会失真。因为对于模型来说重要的上下文已经是经是不完整的了,自然而然的就开始瞎猜了。

就 AI Agent 知识引擎来说,研发团队常遇到两个坑:

第一,很多团队高估了向量检索的上限。
第二,很多团队又低估了图结构落地的工程代价。

今天我们聊一下AI Agent 的知识引擎如何从碎片检索走到深度推理。

咱们今天主要聊四件事:

  • 为什么传统 RAG 在复杂问题上会持续失手
  • GraphRAG 到底重塑了什么
  • 真正可落地的架构怎么搭
  • 当前有哪些新一些的进展

传统短板

很多人第一次做 RAG,体验都挺好。

文档切块,做向量化,进库。用户一问,召回几段文本,拼 Prompt,模型生成答案。对于 FAQ、制度查询、产品说明、接口文档检索,这一套能迅速出结果,性价比很高。

问题在第二阶段才出现:用户不再问单点事实,而是开始问链式问题。

比如:

  • 「2019 年收购 A 公司的企业,其母公司在 2021 年的主要投资对象是谁?」
  • 「某服务在去年三季度的故障,和两个月前那次容量扩容有没有关联?」
  • 「这份上百篇研报里,哪些公司通过共同供应商暴露了同类风险?」

这类问题有一个共同点:答案不在某一个 chunk 里,而是在多个实体、多个时间点、多个文档之间。

传统 RAG 在这里会出现几种典型失效。

关系丢失

向量检索擅长找「语义相近」。它不擅长找「关系成立」。

文档切块以后,文本里的结构被打散了。原文中可能存在很清楚的逻辑链:

  • 公司 B 在 2019 年收购 A
  • 公司 B 是集团 C 的子公司
  • 集团 C 在 2021 年投资了 D

切完块之后,这三个事实可能散落在不同 chunk、不同文档、不同索引分区里。向量检索能不能一次把它们都召回?经常不能。即便召回了,顺序也未必对,相关性分也未必稳定。

这时候模型只能自己「脑补拼图」。一旦有一块没找到,推理链就断。而且,模型也不会老老实实说「证据不够」,它会倾向于补一个看起来像答案的东西出来。这就是很多人嘴里的幻觉。说白了,很多幻觉并不是生成模型凭空发疯,而是检索层先把它带沟里了。

上下文碎片化

RAG 的 chunk 机制本质上是一个工程妥协。

chunk 太小,语义不完整;chunk 太大,embedding 稀释,召回变钝,窗口成本暴涨。滑窗重叠能缓解一点,但解决不了跨章节、跨文档、跨时间的连续推理。

尤其是企业内部知识库,天然不是一本结构优雅的教材,而是一堆:

  • PRD
  • wiki
  • 飞书文档
  • 邮件摘要
  • 会议纪要
  • Jira 评论
  • 数据库导出
  • 运维记录
  • 外部 PDF

这些东西之间本来就没有自然连续性。你再把它们切成块,信息完整性只会进一步恶化。

实体歧义

只靠向量,相同名字的实体很容易串。

一个公司简称可能对应母公司、子公司、事业部;一个人名可能在不同组织里都出现过;产品代号可能跨年份复用。向量模型对这种歧义问题并不稳定,尤其在中文企业数据里,简称、别名、历史命名混在一起,非常常见。

实际项目里,实体歧义一旦没有解决,后面所有链路都白搭。你在检索层把「A 公司」对齐错了,后面再高级的 Agent 规划、工具调用、答案归因,都是建立在错图上。

时间失真

这个问题很多团队前期根本没建模。

企业知识不是静态的。组织关系会变,制度会改版,接口会下线,股权会变更,设备状态有有效期,药品说明有版本,风控规则按月份生效。

传统 RAG 里,一个 chunk 通常只是一段文本。它不天然携带明确的「生效时间」「失效时间」「版本边界」。所以用户一旦问时间敏感问题,系统就容易把不同年份的事实混成一团。

你会看到模型回答得头头是道,但时间轴已经错了。

多跳能力弱

这个问题其实不是模型不会推理,而是检索不给路。

Agent 场景里,很多问题天然是多跳的。要找到答案,系统需要先定位实体,再沿某种关系扩展,再结合时间、置信度、来源做过滤,最后拿到一条可靠路径。传统向量召回没有这个导航能力,它只会不断找「像不像」。

这就是为什么很多团队把模型从一个升级到另一个,效果只提升一点点。瓶颈不在模型本身,而在知识引擎没有结构。

图谱价值

把知识图谱拉进来,改变了检索目标

传统 RAG 检索的是文本片段。
GraphRAG 检索的是实体、关系、路径、子图,以及这些结构对应的证据。

这个差别很大。

显式关系

知识图谱最核心的价值,是把原来需要模型从文本里隐式猜出来的关系,变成显式结构。

比如:

  • 公司 A — 收购 — 公司 B
  • 公司 C — 母公司 — 公司 A
  • 公司 C — 投资 — 项目 D

一旦这些关系被建出来,系统面对复杂问题时就不需要靠 Prompt 去碰运气,而是可以沿着边去找路径。路径找到后,再把路径上的证据喂给模型组织语言。

这一步非常关键。因为它把「推理」从纯生成问题,变成了「结构检索 + 受约束生成」。

多跳导航

图结构天然支持多跳。

从一个节点出发,系统可以做一跳邻域扩展、两跳路径发现、带类型约束的遍历、带时间过滤的路径搜索。很多复杂问题,其实本质上就是图上的 constrained traversal。

做过故障分析、供应链穿透、组织权限审计的人都知道,真实问题往往不是「找到相似文本」,而是「沿着几类特定关系往外走,直到找到证据链」。

GraphRAG 在这里的优势很直接:它给系统一张路网,而不是一堆散落纸片。

可解释性

只要答案建立在路径之上,归因就容易很多。

传统 RAG 的可解释通常是「我引用了这几段文本」。这不够,因为用户看不出这些片段之间为什么能导出结论。

GraphRAG 则可以给出:

  • 命中的核心实体
  • 扩展的关系类型
  • 经过的中间节点
  • 命中路径对应的来源文档
  • 时间和版本约束

对于金融、医疗、法务、审计这类场景,是准入门槛。没有路径级证据,系统很难真正进入业务闭环。

异构融合

企业知识天生异构。数据库里有结构化字段,文档里有叙事描述,日志里有事件序列,报表里有指标快照,外部网页里有补充事实。

知识图谱很适合做这一层统一承载。节点和边上可以挂:

  • 来源
  • 时间戳
  • 置信度
  • 版本号
  • 原文锚点
  • 权限标签

这样,后面的 Agent 就不需要分别理解十几种数据形态,它只需要围绕图这个统一语义层工作。

幻觉压制

有人说 GraphRAG 「能消灭幻觉」。做工程的人都知道,不存在这种好事。

更准确地说,GraphRAG 能明显降低两类幻觉:

  • 检索证据不足导致的瞎补全
  • 实体关系错配导致的错误推断

图结构把推理空间收窄了。其本质上是一个针对知识的 harness 工程。模型不是面对无边界文本海洋自由发挥,而是在一个有限子图上做组织和归纳。再加上路径归因、时间约束、来源过滤,错误空间会被进一步压缩。

架构重构

GraphRAG 要真正落地,分为三段:建图、检索、生成

建图阶段

图谱质量决定了 GraphRAG 的上限,建图成本决定了它的下限。

如果图是脏的、歧义的、缺时间边界的,后面做再多检索优化都只是补洞。

实体抽取

最基础的是从文本中抽实体、属性、关系。

早期很多方案喜欢直接让 LLM 通读文档抽三元组。这么做效果可以有,但成本极高,而且稳定性不够。文档一长、格式一乱、术语一偏,大模型就开始漂。

建议:实体抽取和关系抽取要拆开看,不要一把梭。

实体层往往更适合用稳定的 IE 管线或者轻量模型先打底,再让 LLM 做补充和歧义修正。原因:

  • 实体是高频基础设施,规模大
  • 实体错一个,影响整片图
  • 实体任务相对更规则,适合工程化优化

尤其在垂直领域,术语标准化比抽取本身更难。医疗、金融、制造、运维,每个领域都有自己的缩写、版本命名、内部代号。这里如果没有术语表和字典体系,后面图融合基本没法看。

关系抽取

关系抽取是建图里最贵、最脆弱的一层。

关系比实体更依赖上下文。它牵涉事件角色、动作方向、时间条件、否定表达、范围限定。比如:

  • 「计划收购」
  • 「已完成收购」
  • 「传闻收购」
  • 「终止收购」

如果你都抽成同一种边,图就废了。

所以关系抽取不能只问「有没有关系」,还要问:

  • 关系类型是什么
  • 方向是什么
  • 是否已生效
  • 时间区间是什么
  • 证据来源在哪里
  • 置信度多少

不建议一上来就追求极全的关系本体。项目初期,关系类型设计得太花哨,抽取和维护成本会迅速失控。更稳一些的做法是围绕业务问题反推关系集合,先保核心链路通。

实体对齐

这是最容易被低估的坑。

同一个实体可能有:

  • 全称
  • 简称
  • 英文名
  • 别名
  • 历史名称
  • 内部代号
  • 不同系统中的主键

如果没有实体对齐,图上会出现大量看似不同、实际相同的节点。结果是路径断裂、统计失真、召回稀释。

实体对齐需要多种信号联合:

  • 名称相似度
  • 类型一致性
  • 属性重合度
  • 上下游关系重合度
  • 来源可信度
  • 时间重叠情况

仅靠名称相似度会出很多事故。尤其在中文企业环境里,简称重复极多。我见过一个项目把两个不同区域的同名子公司合并成一个节点,导致后面整条供应链分析全错。

时间与版本

这部分如果不做,GraphRAG 只是比 RAG 多了一层图壳。

建议节点和边都尽量具备时间语义,至少要能表达:

  • 创建时间
  • 生效时间
  • 失效时间
  • 采集时间
  • 数据版本

这样系统才能回答历史状态问题。比如:

  • 「2021 年时它是否还是子公司」
  • 「这个接口在 3 月版本里是否存在」
  • 「某规则在事故发生当日是否已生效」

如果没有时间建模,系统只会返回一个「混合当前状态和历史状态」的伪答案。

质量控制

建图不是 ETL 一次跑完就结束。它需要持续质控。

至少要有几层控制:

  • 抽取置信度阈值
  • 本体约束校验
  • 规则冲突检测
  • 抽样人工复核
  • 高风险实体白名单校正

比如,公司类型节点不该挂「毕业院校」属性;人员节点不该作为「机房设备」的父实体;时间区间不能出现失效时间早于生效时间。

这些校验就是 harness 工程。

检索阶段

GraphRAG 的检索不是去图库里「搜一下」这么简单。真正有效的检索,至少是一个分层过程。

第一步:实体定位

用户问题进来,先要知道问的是谁、什么对象、哪个版本、哪个时间点。

这一步常见做法是实体链接,必要时混合向量召回。目标是在图里找到核心节点。

这里有两个坑特别常见。

一个是用户表达不规范。简称、口语、错别字、历史名称都可能出现。
另一个是问题里实体不止一个,而且主次关系不清。

所以实体定位通常需要结合:

  • NER
  • 别名字典
  • 向量相似召回
  • 类型约束
  • 上下文消歧

如果这一步错了,后面全错。

第二步:子图扩展

定位到核心节点后,要决定往外扩多少。

这一步不能粗暴按 N 跳展开。图一旦大起来,邻域爆炸是很快的。尤其企业图谱里很多「高连接度节点」会把子图迅速拉成垃圾堆。

建议的做法是带约束扩展:

  • 限关系类型
  • 限最大跳数
  • 限时间区间
  • 限置信度
  • 限来源可信等级
  • 限节点类型

比如查询收购链路,就优先沿「收购」「母公司」「投资」这类关系走,而不是把「合作」「提及」「共现」都混进来。

第三步:路径发现

复杂问题很多时候不只是看邻居,而是找满足条件的路径。

比如:

  • 从 A 出发,找到其收购方,再找到收购方母公司,再找到该母公司在某年的主要投资对象
  • 找到某故障对应组件,再向上找到依赖链,再找变更记录,再对齐时间窗口内的容量调整

这类问题,本质上就是受约束路径搜索。图数据库和图算法在这里比纯向量库更像工具。

但有个问题:路径一多,候选会急剧增加。这个时候不能一股脑全喂给模型。要做路径评分和裁剪。

常见评分信号包括:

  • 路径长度
  • 关系类型优先级
  • 时间匹配度
  • 节点权威性
  • 来源可信度
  • 历史问答反馈

第四步:证据序列化

图拿到了,不能直接丢给模型。大多数模型并不擅长原生理解复杂图结构。

要把子图变成模型好消化的证据形式。常见有两种:

  • 路径序列化
  • 子图摘要

路径序列化适合事实问答。
子图摘要适合全局分析和开放问题。

实际工程里,更推荐结构化证据和原文片段联合输入。因为图给的是骨架,文本给的是细节。只有骨架,没有细节,答案会很硬;只有文本,没有骨架,答案会发散。

生成阶段

GraphRAG 不是检索完就结束。很多项目到这一步依然翻车,因为 Prompt 设计和输出约束没做好。

证据优先

Prompt 里必须明确要求模型优先依据图证据回答,不足时才引用文本补充,证据冲突时按来源等级和时间规则处理。

如果不写这些约束,模型还是会习惯性按语言流畅性组织答案,最后把未经验证的补全混进去。

路径归因

对于高事实性场景,建议回答里至少保留轻量级路径说明。未必要全部展示给用户,但系统内部必须保留。

比如答案背后至少知道:

  • 起点实体
  • 中间关系
  • 终点实体
  • 每一步证据来源
  • 时间过滤条件

有了这层,后面才能做审计、回放、纠错、反馈学习。

模板化输出

在财务、医疗、法务、运维这些高风险领域,自由生成要尽量少。

更稳的方式是字段化输出,例如:

  • 结论
  • 证据路径
  • 证据来源
  • 时间范围
  • 置信度
  • 不确定点

这样做的缺点是可读性略硬,优点是稳定。企业系统最终是要接流程、接审批、接工单、接风控策略的,格式稳定比文采重要得多。

三类路线

GraphRAG 现在大体有三条实现路线。可以工程投入和适用边界来看:

知识驱动

这一类把图谱作为主检索引擎。问题进来后,系统尽量转成图查询、路径搜索或者约束遍历,文本只做辅助。

这条路线适合:

  • 实体和关系相对清晰
  • 业务规则稳定
  • 可解释性要求高
  • 多跳推理占比高

典型场景像金融股权穿透、资产关系分析、故障因果链、药物禁忌关系、组织权限依赖。

优点:精度高、证据链清晰、推理路径稳定。

缺点:图谱不全时脆弱,对建图质量依赖极大。

如果你手里只有一堆杂乱文档,图谱覆盖率还没起来,就直接 all in 知识驱动,项目容易陷进「图不够用,文本又没保留好」的尴尬局面。

索引驱动

这一类不强调图上直接推理,而是把图结构转成索引增强信号。

比如:

  • 给 chunk 挂上关联实体、关系标签
  • 把子图摘要拼进 chunk 再做向量化
  • 用邻居关系信息做 rerank 特征

这条路线改造成本低,适合已有 RAG 系统做增强。很多团队第一阶段其实更适合走这条,而不是直接重做知识底座。

缺点也很实在:图只是辅助特征,没有成为真正的推理空间。所以多跳能力提升有限,可解释性也一般。

混合路线

现在真正跑得稳的,大多是混合型。

简单讲就是双路甚至多路召回:

  • 一路走图
  • 一路走向量
  • 必要时再接关键词或结构化库查询
  • 最后统一重排、裁剪、融合

这条路线最像真实世界。因为企业问题本来就不是纯图问题,也不是纯文本问题。很多时候用户一半在问事实链路,一半在问叙事背景。只用一种召回方式,效果往往不稳。

混合路线的问题在于系统复杂度高。链路变长之后,监控、调参、缓存、权限控制、延迟预算都更难。可它依然是现在最主流、也最靠谱的方案。

轻量建图

微软把 GraphRAG 带火,但慢慢大家发现了一个问题:原版重度 GraphRAG 太贵。

用 LLM 通读全量文档,逐段抽实体、关系、摘要,这种方案到真实数据规模经常直接失控。于是轻量化建图开始流行。

实体图思路

轻量路线的核心变化是:不再执着于完整关系抽取,而是先把实体和文档块稳定连起来。

也就是说,先建一个实体图,或者说 relation-light graph:

  • 节点是实体、文档、chunk
  • 边主要表达提及、共现、引用、归属、锚定等轻关系
  • 更复杂的关系留给后续局部推理或按需补抽

高质量关系抽取太贵、太慢、太脆。那就先把高确定性的部分做出来,把知识骨架做起来。

很多场景下,这么做反而更稳。因为实体图能显著改善召回导航,成本又远低于全量三元组图谱。

成本变化

轻量建图通常能把两个指标打下来:

  • Token 消耗
  • 图谱更新时间

这对中小项目非常关键。一个知识引擎如果更新周期是按天甚至按周算,它对 Agent 的支撑就已经很有限了。现实里的知识是流动的,尤其在运维、客服、风控、投研这些场景,更新速度直接决定答案可信度。

局限

轻量图省成本,但也有边界。

它更适合做「导航增强」和「局部上下文组织」,不适合承担强逻辑推理的全部职责。因为如果关系层太弱,系统最终还是要回文本里做大量补推断。

所以我们一般把轻量图看成一个很好的过渡层,或者作为混合架构中的图底座。

Agent 化检索

GraphRAG 这两年另一个明显趋势,是从固定流水线走向 Agent 化。

传统流程大多是:

用户提问 → 定位实体 → 扩子图 → 生成答案

问题是,复杂问题往往一轮检索拿不全证据。系统需要边查边判断:现在的证据够不够?应该沿哪类边继续走?有没有必要换一个切入实体?要不要回文档补背景?

这时候,GraphRAG 和 Agent 很自然就结合起来了。

查询拆解

复杂问题先拆成子问题,是 Agent 化 GraphRAG 的第一步。

比如:

「2019 年收购 A 公司的企业,其母公司在 2021 年的主要投资对象是谁?」

可以拆成:

  1. 谁在 2019 年收购了 A
  2. 该收购方的母公司是谁
  3. 该母公司在 2021 年的主要投资对象有哪些
  4. 哪个对象符合「主要投资对象」定义

拆解后的每一步都更适合图查询和约束检索。

迭代探索

Agent 可以根据中间结果决定下一步动作:

  • 命中实体不确定,先做消歧
  • 路径证据不足,扩大时间窗口
  • 图证据不全,补文本检索
  • 文本冲突,回图里查来源优先级

这比一条固定链路强很多。因为真实问题不会永远按设计者预想的路径走。

风险

Agent 化一听就很高级,但工程上有几个明显的问题:

  • 延迟增加
  • token 消耗增加
  • 调试复杂度上升
  • 失败路径变多

如果没有严密的步骤预算和停止条件,Agent 会在图里越走越远,最后拖垮时延和成本。

所以 Agent 化 GraphRAG 要有明确的控制策略:

  • 最大迭代轮数
  • 最大扩展跳数
  • 最大证据数
  • 最大 token 预算
  • 终止阈值

没有这些约束,系统会变成一个会自主发散的检索器。

强化学习

2025 到 2026 年,另一个值得关注的方向是把强化学习或者类似 reward-guided 策略引进图检索过程。

这个方向的核心问题是:子图应该扩到哪里停?哪条边值得继续走?

以前这类问题多靠规则和启发式。比如限制两跳、限制 topN 邻居、按关系优先级排序。够用,但不够细。

强化学习的吸引力在于,它试图让检索器学会一件事:在有限上下文预算下,优先收集对答案最有价值的证据。

为什么有价值

GraphRAG 很容易出现一个悖论:

  • 扩得太少,证据不够,答不出来
  • 扩得太多,噪声暴涨,模型反而更容易错

最优点其实是一个动态平衡,而且和问题类型、图谱密度、时间约束都相关。静态规则很难适配所有情况。

落地现实

这个方向我认为在部分场景是一个比较不错的解。

原因很简单。强化学习提升的是「策略细节」,前提是你的图已经比较干净,检索反馈链路也能闭合。如果底层图谱质量一般,奖励模型再聪明也学不到稳定策略。

所以在工程优先级上,我会把它排在:

  1. 实体对齐
  2. 时间建模
  3. 混合召回
  4. 路径裁剪
  5. 再考虑 RL 优化

很多团队喜欢直接追最新论文方向,结果底座没打稳,投入产出很差。

动态建图

静态全量图谱还有一个问题:更新慢。

数据一旦高频变化,整库重建非常重,增量融合也复杂。于是最近很实用的一条路是查询驱动的动态局部建图

思路

先用向量检索或关键词检索,快速圈出和问题强相关的一小批文档或 chunk。然后只针对这部分数据,在内存里临时构建一个局部图,用来做当前问题的推理。

这样做的好处很明显:

  • 不需要维护一个永远完整的全局大图
  • 对高频更新数据更友好
  • 构建成本和时延更可控
  • 对冷门知识不用提前付建图成本

适用场景

这条路线特别适合:

  • 数据更新快
  • 查询分布长尾
  • 很多知识只会被偶尔问到
  • 无法接受全量图谱重构成本

比如运维告警分析、实时舆情、工单流、交易异常调查。

局限

动态建图的问题也很明显:它的全局视角弱。

如果问题本身需要跨全库的大结构理解,比如全局主题分布、长期模式汇总、跨社区关联,局部动态图就不够用了。所以它更像实时推理层,不是全局知识层的完全替代品。

多模态图

GraphRAG 继续演进,节点已经不再局限于文本实体。

很多真实场景里,关键证据来自图像、图表、表格、时序信号。把这些都挂进图里,才可能支撑更完整的 Agent 推理。

例如医疗场景里:

  • 症状描述是文本节点
  • 检查指标是结构化节点
  • 影像特征是图像节点
  • 治疗方案是流程节点

如果这些数据还分散在不同系统里,Agent 再强也只能在局部瞎猜。多模态图的价值,在于把这些证据接成可遍历的语义网络。

不过这块别急着神化。多模态 GraphRAG 落地难度明显高于文本图谱,主要难点有三类:

  • 跨模态对齐难
  • 证据置信度难统一
  • 存储与检索链路更复杂

所以大多数团队短期内更现实的做法,是先把表格和结构化字段接进来,再逐步引入图像等模态。

超图方向

传统的知识图谱仅支持二元关系(即一条边连接两个节点,如 实体A → 关系 → 实体B)。然而在真实复杂场景中,事实往往是多维度的。

比如:

  • 三家公司共同参与某项目
  • 一笔交易涉及买方、卖方、标的、通道、时间、地区
  • 一个法律案件涉及原告、被告、法条、法院、时间节点
  • 超图与超边(Hypergraph & Hyperedges):超图允许单条“超边”连接任意数量的实体/节点。例如在医学场景中,描述“某症状”的发生可能涉及“患者、医生、检查手段、特定药物和治疗结果”。
  • 低阶与高阶关联:通过超图,系统能同时无损存储成对的“低阶关联”与包含多个实体的“高阶关联”,从根本上减少信息压缩带来的损失。

这个方向在金融穿透、案件分析、复杂项目协作里很有吸引力。因为它能更自然地表达群体事件和多方关系。

但说实话,超图现在离大规模工程标配还有距离。核心原因不是理念不对,而是生态、工具链、调试经验都还不够成熟。一般团队现在没必要急着上,除非业务场景确实被二元关系表达卡住了。

落地取舍

那么 团队到底该怎么选?

只做传统 RAG 就够的情况

如果你的问题大多是:

  • 单文档问答
  • FAQ 查询
  • 产品知识助手
  • 代码片段召回
  • 对多跳推理要求不高

那就别急着上图。把 chunk、embedding、rerank、query rewrite、metadata filter、citation 做好,收益通常更高。

应该上轻量 GraphRAG 的情况

如果已经出现这些信号:

  • 同一个问题答案分散在多个文档
  • 实体名称混乱、简称多
  • 用户经常追问上下游关系
  • 向量召回结果看起来相关,答案却老是不对
  • 需要更稳定的引用与归因

那我建议先上轻量图。先做实体层和文档锚定层,不要一口气建满关系宇宙。

应该上重度 GraphRAG 的情况

如果业务本身就是关系驱动的:

  • 金融穿透
  • 医疗知识网络
  • 风控依赖链
  • 故障根因传播
  • 供应链关联追踪
  • 组织权限审计

那重度图谱是值得的。因为这里的问题本质上就是图问题,用纯文本方案长期只能堆补丁。

Agent 化和 RL 什么时候考虑

当且仅当你已经满足这些条件时再往上走:

  • 图谱基础质量稳定
  • 问题复杂度确实需要多轮探索
  • 现有检索链路可观测、可回放
  • 成本和延迟预算允许

否则,先把基础检索做好,比上复杂策略更值。

小结

图结构是检索的 harness 工程。

向量检索还是底座,图结构正在变成高阶能力的分水岭。

没有向量,系统对模糊语义和开放文本会很迟钝。
没有图,系统对关系、路径、时间、一致性会很脆弱。

未来成熟的 Agent 知识引擎,大概率都不是单一路线,而是分层组合:

  • 向量层负责广覆盖召回
  • 图层负责关系组织与多跳导航
  • 结构化查询层负责精确过滤
  • Agent 层负责查询拆解与迭代探索
  • 生成层负责受约束表达与归因输出

说到底,知识引擎这件事,已经从「找资料」变成「构造可推理的证据空间」。

GraphRAG 不是给 RAG 多加一个数据库,也不是给模型多喂一点上下文。它把原来松散、偶然、靠模型自行拼接的知识,重组成了一张可以遍历、可以约束、可以回放的网络。

对 AI Agent 来说,对于 AI Agent 的上下文构建来说,这是关键的一步。

因为 Agent 一旦开始承担任务,它需要的就不再是几个看起来相关的文本块,而是一条能走通、能解释、能复核的知识路径。

以上。

深度拆解 Claude Code 系统提示词中的记忆管理逻辑

最近在做 Agent 相关的工作,研究了 Claude Code 的系统提示词。分享一下看到的东西。

Claude Code 这套逻辑最值得学习的部分,不是它有多少类型,也不是它怎么写文件,而是它把「记忆」从聊天历史里剥离成了一个有边界的系统对象。

其提示词给出了四段闭环:

  • 类型化存储
  • 索引化管理
  • 触发式召回
  • 使用前校验

这套闭环的根本目的只有一个:极度压缩进入大模型上下文的无效 Token

类型化存储

类型化存储解决的是「谁有资格被记住」的问题。

Claude Code 里把记忆分成 user / feedback / project / reference。这一步看上去像分类,实际上是在做准入控制。

很多团队一开始偷懒,做一个统一的 memory 表,字段有 contentcreated_atembedding,剩下全靠检索兜底。前期跑 demo 很爽,后期一团糟。因为「用户偏好」「项目约束」「纠错反馈」「外部入口」这几类东西的生命周期、可信度、更新频率和召回优先级完全不同。你把它们混在一起,后面所有策略都要靠额外条件补救。

Claude Code 这里的好处在于,它先承认记忆不是同质数据。类型不同,保存条件就不同,召回方式也不同。

  • user 影响的是回答风格和交互方式,天然高权重。
  • feedback 代表用户纠正过的内容,这类信息如果不复用,系统会反复踩一个坑。
  • project 带明显时效性,过期不处理就是埋雷。
  • reference 更接近外部入口或指针,重点在可定位,不在长文本本身。

这种分类把后面复杂度最高的事情提前处理了,这就不用在召回阶段临时猜「这一条历史到底算偏好还是事实」,因为写入时已经分流了。

索引化管理

Claude Code 会「先写独立记忆文件,再更新 MEMORY.md 索引」。这里把正文存储和索引存储分开了。

有两个收益。

第一,索引足够轻。MEMORY.md 只存索引,不存正文。这样它天然适合作为一个轻量入口,被优先加载、优先扫描、优先过滤。

第二,正文可以演进。真正的记忆文件有 frontmatter 和正文,这意味着它可以承载更完整的上下文,而不用把所有内容都堆到一个总文件里。总文件一旦既做索引又做正文,后面就很难控制体积,也很难做精细更新。

在写入时,有两条规则。

  • 同主题记忆优先 update,避免重复新增。
  • 用户明确说 forget,就删除对应记忆。

这两条是在控制系统熵增。记忆只要能无限追加,迟早会出现语义重复、事实冲突、时间污染。只做新增,不做更新和删除,系统很快就会进入「候选很多,但没有一条完全可信」的状态。到了那个阶段,召回层再聪明也救不回来。

触发式召回

Claude Code 的建议流程是:

  1. 先判断当前请求是否需要记忆;
  2. 按类型和关键词做少量 Top-K 粗召回;
  3. 再按「任务相关性 > 新鲜度 > 可靠性」精筛;
  4. 只注入必要片段;
  5. 如果和当前事实冲突,以当前事实为准并回写修正。

其逻辑有如下几种:

  1. 强指令触发(显式召回):当用户明确下达指令(如“查一下”、“回想一下”、“你还记得吗”)时,系统被强制(MUST)触发召回链路。
  2. 上下文/语义触发(隐式召回):系统在对话过程中,如果发现当前任务与已有记忆具有强相关性,或者用户提到了“之前的对话/工作”,则隐式触发召回。这要求大模型在理解当前意图时,顺带做一次记忆相关性判定。
  3. 负向门控触发(屏蔽/阻断召回):当用户明确要求“忽略记忆”或“不要用记忆”时,系统必须直接切断召回链路,假装索引文件 MEMORY.md 是空的,防止历史上下文污染当前的新任务。

使用前校验

使用前校验,解决的是「记忆不是事实源」

记忆里如果提到文件、函数、flag,落地前必须重新核验当前状态。

记忆的本质是「过去曾经成立过的信息」。代码仓库、配置开关、函数签名这些东西会变。如果把记忆当事实源,模型越有记忆,出错概率越高。尤其在代码场景里,这种错会放大。因为模型不是只回答一句话,它还会基于过期事实继续生成修改方案、命令、排障路径。

记忆负责缩小搜索空间,当前状态负责给出最终裁决。

做记忆系统时,最警惕的一直是脏记忆。空记忆顶多让模型少一点个性,脏记忆会直接让模型说错话。

以上。

附原始提示词(2.1.86 版本)


## auto memory

You have a persistent, file-based memory system at `/root/.claude/projects/-tmp-claude-history-1774690103689-avi2cu/memory/`. This directory already exists — write to it directly with the Write tool (do not run mkdir or check for its existence).

You should build up this memory system over time so that future conversations can have a complete picture of who the user is, how they'd like to collaborate with you, what behaviors to avoid or repeat, and the context behind the work the user gives you.

If the user explicitly asks you to remember something, save it immediately as whichever type fits best. If they ask you to forget something, find and remove the relevant entry.

### Types of memory

There are several discrete types of memory that you can store in your memory system:

<types>
<type>
    <name>user</name>
    <description>Contain information about the user's role, goals, responsibilities, and knowledge. Great user memories help you tailor your future behavior to the user's preferences and perspective. Your goal in reading and writing these memories is to build up an understanding of who the user is and how you can be most helpful to them specifically. For example, you should collaborate with a senior software engineer differently than a student who is coding for the very first time. Keep in mind, that the aim here is to be helpful to the user. Avoid writing memories about the user that could be viewed as a negative judgement or that are not relevant to the work you're trying to accomplish together.</description>
    <when_to_save>When you learn any details about the user's role, preferences, responsibilities, or knowledge</when_to_save>
    <how_to_use>When your work should be informed by the user's profile or perspective. For example, if the user is asking you to explain a part of the code, you should answer that question in a way that is tailored to the specific details that they will find most valuable or that helps them build their mental model in relation to domain knowledge they already have.</how_to_use>
    <examples>
    user: I'm a data scientist investigating what logging we have in place
    assistant: [saves user memory: user is a data scientist, currently focused on observability/logging]

    user: I've been writing Go for ten years but this is my first time touching the React side of this repo
    assistant: [saves user memory: deep Go expertise, new to React and this project's frontend — frame frontend explanations in terms of backend analogues]
    </examples>
</type>
<type>
    <name>feedback</name>
    <description>Guidance the user has given you about how to approach work — both what to avoid and what to keep doing. These are a very important type of memory to read and write as they allow you to remain coherent and responsive to the way you should approach work in the project. Record from failure AND success: if you only save corrections, you will avoid past mistakes but drift away from approaches the user has already validated, and may grow overly cautious.</description>
    <when_to_save>Any time the user corrects your approach ("no not that", "don't", "stop doing X") OR confirms a non-obvious approach worked ("yes exactly", "perfect, keep doing that", accepting an unusual choice without pushback). Corrections are easy to notice; confirmations are quieter — watch for them. In both cases, save what is applicable to future conversations, especially if surprising or not obvious from the code. Include *why* so you can judge edge cases later.</when_to_save>
    <how_to_use>Let these memories guide your behavior so that the user does not need to offer the same guidance twice.</how_to_use>
    <body_structure>Lead with the rule itself, then a **Why:** line (the reason the user gave — often a past incident or strong preference) and a **How to apply:** line (when/where this guidance kicks in). Knowing *why* lets you judge edge cases instead of blindly following the rule.</body_structure>
    <examples>
    user: don't mock the database in these tests — we got burned last quarter when mocked tests passed but the prod migration failed
    assistant: [saves feedback memory: integration tests must hit a real database, not mocks. Reason: prior incident where mock/prod divergence masked a broken migration]

    user: stop summarizing what you just did at the end of every response, I can read the diff
    assistant: [saves feedback memory: this user wants terse responses with no trailing summaries]

    user: yeah the single bundled PR was the right call here, splitting this one would've just been churn
    assistant: [saves feedback memory: for refactors in this area, user prefers one bundled PR over many small ones. Confirmed after I chose this approach — a validated judgment call, not a correction]
    </examples>
</type>
<type>
    <name>project</name>
    <description>Information that you learn about ongoing work, goals, initiatives, bugs, or incidents within the project that is not otherwise derivable from the code or git history. Project memories help you understand the broader context and motivation behind the work the user is doing within this working directory.</description>
    <when_to_save>When you learn who is doing what, why, or by when. These states change relatively quickly so try to keep your understanding of this up to date. Always convert relative dates in user messages to absolute dates when saving (e.g., "Thursday" → "2026-03-05"), so the memory remains interpretable after time passes.</when_to_save>
    <how_to_use>Use these memories to more fully understand the details and nuance behind the user's request and make better informed suggestions.</how_to_use>
    <body_structure>Lead with the fact or decision, then a **Why:** line (the motivation — often a constraint, deadline, or stakeholder ask) and a **How to apply:** line (how this should shape your suggestions). Project memories decay fast, so the why helps future-you judge whether the memory is still load-bearing.</body_structure>
    <examples>
    user: we're freezing all non-critical merges after Thursday — mobile team is cutting a release branch
    assistant: [saves project memory: merge freeze begins 2026-03-05 for mobile release cut. Flag any non-critical PR work scheduled after that date]

    user: the reason we're ripping out the old auth middleware is that legal flagged it for storing session tokens in a way that doesn't meet the new compliance requirements
    assistant: [saves project memory: auth middleware rewrite is driven by legal/compliance requirements around session token storage, not tech-debt cleanup — scope decisions should favor compliance over ergonomics]
    </examples>
</type>
<type>
    <name>reference</name>
    <description>Stores pointers to where information can be found in external systems. These memories allow you to remember where to look to find up-to-date information outside of the project directory.</description>
    <when_to_save>When you learn about resources in external systems and their purpose. For example, that bugs are tracked in a specific project in Linear or that feedback can be found in a specific Slack channel.</when_to_save>
    <how_to_use>When the user references an external system or information that may be in an external system.</how_to_use>
    <examples>
    user: check the Linear project "INGEST" if you want context on these tickets, that's where we track all pipeline bugs
    assistant: [saves reference memory: pipeline bugs are tracked in Linear project "INGEST"]

    user: the Grafana board at grafana.internal/d/api-latency is what oncall watches — if you're touching request handling, that's the thing that'll page someone
    assistant: [saves reference memory: grafana.internal/d/api-latency is the oncall latency dashboard — check it when editing request-path code]
    </examples>
</type>
</types>

### What NOT to save in memory

- Code patterns, conventions, architecture, file paths, or project structure — these can be derived by reading the current project state.
- Git history, recent changes, or who-changed-what — `git log` / `git blame` are authoritative.
- Debugging solutions or fix recipes — the fix is in the code; the commit message has the context.
- Anything already documented in CLAUDE.md files.
- Ephemeral task details: in-progress work, temporary state, current conversation context.

These exclusions apply even when the user explicitly asks you to save. If they ask you to save a PR list or activity summary, ask what was *surprising* or *non-obvious* about it — that is the part worth keeping.

### How to save memories

Saving a memory is a two-step process:

**Step 1** — write the memory to its own file (e.g., `user_role.md`, `feedback_testing.md`) using this frontmatter format:


---
name: {{memory name}}
description: {{one-line description — used to decide relevance in future conversations, so be specific}}
type: {{user, feedback, project, reference}}
---

{{memory content — for feedback/project types, structure as: rule/fact, then **Why:** and **How to apply:** lines}}


**Step 2** — add a pointer to that file in `MEMORY.md`. `MEMORY.md` is an index, not a memory — each entry should be one line, under ~150 characters: `- [Title](file.md) — one-line hook`. It has no frontmatter. Never write memory content directly into `MEMORY.md`.

- `MEMORY.md` is always loaded into your conversation context — lines after 200 will be truncated, so keep the index concise
- Keep the name, description, and type fields in memory files up-to-date with the content
- Organize memory semantically by topic, not chronologically
- Update or remove memories that turn out to be wrong or outdated
- Do not write duplicate memories. First check if there is an existing memory you can update before writing a new one.

### When to access memories
- When memories seem relevant, or the user references prior-conversation work.
- You MUST access memory when the user explicitly asks you to check, recall, or remember.
- If the user says to *ignore* or *not use* memory: proceed as if MEMORY.md were empty. Do not apply remembered facts, cite, compare against, or mention memory content.
- Memory records can become stale over time. Use memory as context for what was true at a given point in time. Before answering the user or building assumptions based solely on information in memory records, verify that the memory is still correct and up-to-date by reading the current state of the files or resources. If a recalled memory conflicts with current information, trust what you observe now — and update or remove the stale memory rather than acting on it.

### Before recommending from memory

A memory that names a specific function, file, or flag is a claim that it existed *when the memory was written*. It may have been renamed, removed, or never merged. Before recommending it:

- If the memory names a file path: check the file exists.
- If the memory names a function or flag: grep for it.
- If the user is about to act on your recommendation (not just asking about history), verify first.

"The memory says X exists" is not the same as "X exists now."

A memory that summarizes repo state (activity logs, architecture snapshots) is frozen in time. If the user asks about *recent* or *current* state, prefer `git log` or reading the code over recalling the snapshot.

### Memory and other forms of persistence
Memory is one of several persistence mechanisms available to you as you assist the user in a given conversation. The distinction is often that memory can be recalled in future conversations and should not be used for persisting information that is only useful within the scope of the current conversation.
- When to use or update a plan instead of memory: If you are about to start a non-trivial implementation task and would like to reach alignment with the user on your approach you should use a Plan rather than saving this information to memory. Similarly, if you already have a plan within the conversation and you have changed your approach persist that change by updating the plan rather than saving a memory.
- When to use or update tasks instead of memory: When you need to break your work in current conversation into discrete steps or keep track of your progress use tasks instead of saving to memory. Tasks are great for persisting information about the work that needs to be done in the current conversation, but memory should be reserved for information that will be useful in future conversations.

AI Agent 的长期记忆:我在工程落地里踩过的坑、做过的取舍

长期记忆不是「把历史对话存起来」。在生产环境里,它更像一套数据管道和检索系统,目标很具体:

  1. 让 Agent 在跨天、跨周的任务里保持一致性(用户偏好、项目背景、关键决策不丢)。
  2. 让上下文成本可控(Token、TTFT、吞吐量别炸)。
  3. 让错误可被纠正、记忆可被编辑、可被遗忘(不然就是事故制造机)。

三个主要逻辑——记忆捕获、AI 压缩、智能检索

说人话就是:数据结构怎么定、写入怎么做、分层怎么做、检索怎么做、什么时候该忘。

1. 先把「长期记忆」拆成三类

在很多团队里,长期记忆失败不是模型问题,是定义问题:同一个「memory」里混了用户画像、任务状态、项目知识、工具日志,最后检索噪声大到不可用。

我更愿意按「用途」拆,而不是按「存储介质」拆:

1.1 用户长期记忆

用户长期记忆每次都要注入的「稳定事实」。

长期记忆的定义:长期、可编辑的核心记忆,记录稳定属性(姓名、目标、经历、偏好等),并且「每次对话都会强制注入」。

这里我会很强硬地加两条工程规则:

  • 必须可审计:能回答「这条记忆从哪轮对话来的」「谁写入的」「什么时候写入的」。
  • 必须可逆:用户一句「从记忆中删除」要能删干净;内部也要支持 GDPR/合规那种 purge。

更新方式有两种,但优先级不同

  • 显式更新:用户说「记住这个」「删掉这个」这种,优先级最高,直接写。
  • 隐式更新:模型检测到符合标准的事实(如 OpenAI 的标准),默认同意自动添加。
    对于隐式更新,我的态度偏保守:宁可少记,不要乱记。乱记比不记更致命,后面纠错成本很高。

1.2 任务记忆

任务记忆是会过期的「状态」

它属于长期记忆系统,但不属于「永久」。例如:

  • 一个多天的排障进度(已验证什么、下一步计划)
  • 某个 PR 的讨论结论(直到 merge 前都重要,merge 后可降温)

这类记忆如果不做 TTL,很快就把检索污染掉。任务记忆一定要有生命周期

1.3 事件/操作记忆

这也可以叫做过程记忆,这是为检索服务的「轨迹」。

这类通常来自工具调用、文件读写、运行日志。它的价值是:当用户问「你刚才改了哪几个文件」「上周我们为什么选了 A」时,Agent 能把证据拿出来。

它的问题也最大:写入频率极高、噪声极多。这类我默认做分层:热层保最近、冷层做压缩归档,别全塞进同一个向量索引里。

2. 记忆系统的三段式管道

捕获 → 压缩 → 检索(注入)

2.1 记忆捕获

简单来说就是谁来写、写什么、写到哪。

捕获层我建议按「事件源」拆:

  • 对话事件:用户输入、模型输出(或关键片段)、会话元信息(时间、会话 ID、主题)。
  • 工具事件:工具名、参数、返回、影响面(写了哪些文件、改了哪些配置、跑了哪些命令)。
  • 用户显式指令:强制写/删/改的指令,这条要走单独通道,避免被摘要吞掉。

以 Claude-Mem「五大生命周期钩子」为例,是一个比较实用的策略,原因是它把捕获点固化在生命周期上,不靠「模型想起来了」这种玄学。

钩子名称 触发时机 核心作用
context-hook 会话启动时 注入最近记忆作为上下文
new-hook 用户提问时 创建新会话并保存提示词
save-hook 工具执行后 捕获文件读写等操作记录
summary-hook 会话结束时 生成 AI 摘要并持久化存储
cleanup-hook 停止指令时 清理临时数据

我自己的经验:save-hook 和 summary-hook 之间一定要有边界
save-hook 捕获「事实与证据」(做过什么、改过什么)。summary-hook 产出「压缩后的可读结论」(为什么这么做、后续计划)。混在一起,后面做检索融合会很痛。

2.2 AI 压缩

简单来说就是压什么、怎么压、压到什么粒度

压缩不是「把 10 轮对话变 200 字」这么简单。压缩的核心目标只有两个:

  • 降低注入成本:上下文窗口里留给推理的空间要足够。
  • 提高检索可控性:检索返回的 chunk 必须信息密度高、噪声低。

比较典型的做法:每隔 10 轮触发 summary agent,把前 10 轮压成 200 字摘要并替换历史。这里可能会有一个坑:摘要如果不带结构,后面无法做检索约束

我更偏好把摘要拆成固定字段(即使最终还是自然语言):

  • 「目标/约束」
  • 「关键决策 + 理由」
  • 「未决问题」
  • 「下一步」
  • 「证据索引」(指向原始事件/日志的 ID)

这样检索返回摘要时,Agent 能快速判断「这段能不能用」,也能在需要时回溯证据。

2.3 智能检索

别把「能搜到」当成「能用」,这是两回事。

很多记忆系统上线后表现很差,根因是:检索返回了一堆「看似相关」但没有操作价值的片段。工程上我会把检索拆成三段:

  1. 候选召回:向量相似度 / 关键词 / 结构化过滤(用户、项目、时间窗、标签)。
  2. 重排(rerank):结合时间衰减、来源可信度、记忆类型优先级。
  3. 注入策略:怎么塞进 prompt,塞多少,塞哪一层。

「渐进式披露策略」是当前比较流行的注入策略,这比「Top-k 全塞」靠谱太多了:

Level 1: 最近 3 条会话摘要(约 500 tokens)
Level 2: 相关观察记录(用户主动查询)
Level 3: 完整历史检索(mem-search 技能)

Level 1 覆盖 80% 的连续对话场景;Level 2 把「更多细节」交给用户意图;Level 3 才动用重检索,避免每轮都把成本打满。

3. 存储介质怎么选

文件、知识库、数据库都是可以选的。

3.1 文件

最强的可控性,最差的并发与检索体验

文件的优势是「简单到不会出错」:

  • 人可以直接打开改
  • Git 可以审计、回滚
  • 灾备简单

缺点也有:

  • 并发写很麻烦(锁、冲突、合并)
  • 检索靠你自己做索引,否则就是 grep
  • 很难做多租户隔离、权限控制(你当然可以做,但成本会涨)

如 OpenClaw 的设计:每日日志 + MEMORY.md 精选长期存储。它这个方案我很喜欢,原因是它把「噪声」和「精选事实」隔离开了。

一个「看起来保守,但极其工程」的方案:

  • 第一层:每日日志,按日期整理,记录会话发生的事情、决策结果、未来可能相关的信息。
  • 第二层:**MEMORY.md 本身**,作为精选长期存储库,保存应永久保留的信息;也记录对代理错误的修正。

如果捕捉对话每个细节,代理每次加载上下文会消耗更多 Token,杂音会降低响应质量

MEMORY.md 这种「精选」必须有准入机制。靠人手维护能跑,但团队一大就维护不过来。可以整一个「重要性评分系统」,先打分,再决定进不进精选层。

3.2 知识库

适合「稳定知识」,不适合「高频写入」

知识库适合 SOP、产品手册、FAQ、架构决策记录这种相对稳定的内容。它的问题是写入链路通常偏离线:采集、清洗、切分、建索引。你要它承接「每次工具调用写一条」这种场景,很快会把 ingestion 管道压垮。

KB 承接 semantic memory(语义知识),别拿它硬扛 episodic/event memory。

3.3 数据库

能抗并发、能做权限、能做检索,但我们要付出工程代价

数据库我会再分两类:

  • 结构化数据库(关系型/文档型):适合 user memory(key-value、可编辑、可审计)、任务状态、权限控制。
  • 向量数据库:适合 episodic memory 的语义检索,但会带来你参考内容里提到的三个工程问题。

user memory 这种「必须可控」的内容,优先放结构化 DB;event/episode 的检索层再用向量 DB 或混合检索。把所有东西都向量化,后面治理成本会很高。

4. 向量数据库的使用逻辑

向量数据库把记忆从只读变可写后,需要考虑三个具体的工程问题。

4.1 问题一:需要记住什么?

这里最容易走偏。很多团队一开始恨不得「全量记录」,结果两周后发现:

  • 索引膨胀
  • 检索噪声上升
  • 成本上涨
  • 用户抱怨「你记错了」的次数增加

我的判断维度是:时间、频率、类型,这三者会冲突。我会在应用层做一个更硬的分层打分:

  • 硬规则拦截:明显不该记的直接丢
    例如临时文件、一次性下载缓存、明显含敏感信息的内容(看合规策略)。
  • 重要性打分:符合候选条件的打分
    分数来自:任务相关性、用户显式标记、重复出现次数、工具影响面(改了配置文件通常比分割日志重要)。
  • 落层策略:分数决定写入热/冷、决定是否进入精选层(例如 MEMORY.md)。

Milvus 的 TTL 和时间衰减,可以用用,不是核心策略。原因很简单:TTL 只能删时间,删不了噪声。噪声是「内容不该进来」,不是「该不该过期」。

4.2 问题二:怎么分层存储?

按时间切、按访问频率、按用户标注来降冷。「分层」可以,但是:分层的单位别用「向量库的 collection」随便拍脑袋,要用有我们自己的「记忆类型」。

我通常会至少拆三层:

  • 热层:最近 N 天的事件 + 最近几条摘要
    目标是低延迟、写入快、检索稳定。热层可以索引轻一些,宁可召回多一点,再靠重排过滤。
  • 温层:近期项目周期内的关键摘要、关键决策、纠错记录
    读多写少,索引可以更重,提升精度。
  • 冷层:长历史归档
    主要用于追溯,默认不参与每轮检索,只在用户明确追问或系统置信度不足时启用。

这个结构配合「渐进式披露」很顺:默认只碰热层,必要时升级到温层/冷层。成本曲线能压得住。

4.3 问题三:写入频率与速度怎么定?

关键矛盾:agent memory 要实时写入,但向量索引构建需要时间;每条写都重建索引太贵,批量建索引又导致新数据搜不到。

在工程上解这个矛盾的思路:

  • 把「可立即检索」和「可长期高精检索」拆开
    新写入先进入一个轻量的「增量区」(delta store),可以是:

    • 内存缓存 + 简单向量结构(甚至先不建复杂索引)
    • 或者一个专门的「实时 collection」,索引参数偏向写入吞吐
  • 后台异步合并(Compaction)
    到一定量再合并进主索引(main store),这时构建更重的索引结构。
  • 检索时双查
    先查 delta,再查 main,最后融合去重。这样用户刚执行的操作,下一轮一定能搜到,不靠运气。

如果只用一个 collection 硬扛实时写入 + 高精检索,基本会卡在「要么写不动,要么搜不准」之间来回摆。

5. 非向量数据库怎么做长期记忆

我倾向于「结构化事实 + 混合检索」

user memory 和一部分 task memory,用结构化存储更稳,理由:

  • 可编辑(update/delete)是常态操作
  • 需要强一致(至少单用户维度)
  • 需要权限、审计、脱敏、导出、合规删除

向量化适合「相似性召回」,不适合「事实的最终真相」。

这样拆:

5.1 User Memory

KV + 版本 + 来源,每条 user memory 至少需要:

  • key(例如 coding_lang
  • value(例如 Python
  • source(显式指令 / 隐式提取 / 管理后台)
  • updated_at
  • version(解决「多次修改」与「回滚」)
  • confidence / policy tag(是否允许自动注入、是否敏感)

然后注入策略是:每轮只注入白名单 key。别把整个用户画像 dump 进 prompt。

5.2 Event/Log

文档型存证 + 可选向量索引

工具调用日志、文件变更记录,我更愿意先当「证据」存好(文档型或日志系统),向量索引只是加速检索的手段。这样即使向量库挂了,还有可追溯的事实来源。

5.3 混合检索

先过滤,再相似度,再重排

非向量方案想要「像向量检索一样好用」,别上来就全文检索硬搜。最有效的顺序通常是:

  1. 结构化过滤(用户、项目、时间窗、标签、来源可信度)
  2. 再做相似度/全文检索召回
  3. 最后重排(时间衰减 + 类型权重 + 去重)

这套顺序能把噪声压下去,查询也更可解释。

6. 长期记忆的「可用性」核心

注入策略比检索算法更重要

很多人把精力都花在「embedding 模型选哪个」「Top-k 设多少」,上线后发现效果波动很大。

实际可能是:注入策略决定了下限

「渐进式披露」已经是很好的骨架。我补两条我认为必须做的工程约束:

6.1 注入预算必须固定

每轮对话给记忆注入多少 token,要有硬预算。例如:

  • 用户长期记忆:固定 100~300 tokens(只放稳定事实)
  • 最近摘要:固定 300~800 tokens
  • 检索片段:固定 500~1500 tokens(按任务重要性动态)

预算不固定,线上成本就不可控;更糟的是上下文挤压推理空间,模型会「看起来记住了」,实际输出质量下降。

6.2 记忆冲突处理

宁可不注入,也别注入矛盾

最常见的事故是:用户改了偏好(比如语言、格式、技术栈),旧记忆还在注入,Agent 开始精神分裂。

工程上必须有冲突策略:

  • 同一 key 的多版本:只注入最新版
  • 多来源冲突:显式指令 > 管理后台 > 隐式提取
  • 低置信度记忆:默认不注入,只在用户问到时提供候选并求确认

7. 小结

AI Agent 的长期记忆不是「把历史对话都存起来」,而是一套以可控、可维护、可纠错为目标的数据管道与检索系统——先明确记忆类型(用户稳定事实、任务状态、事件证据)并分层治理,再用“捕获 → AI 压缩 → 智能检索/注入”三段式把信息从高频噪声提炼成可用上下文;存储上用结构化数据库承载可编辑的用户/任务事实,用日志/文档留存证据,并按需用向量索引做语义召回与冷热分层,避免写入与索引、噪声与成本之间的失控;

效果上不要迷信 Top‑k,把注入预算、渐进式披露、冲突处理当作系统下限;

运维上把缓存、摘要、显式记忆工具、TTL/衰减与合规删除做成一等能力,并用成本、质量与安全指标持续观测迭代。

最终目标不是「记得更多」,而是让 Agent 在长期任务中更一致、更便宜、更可靠

以上。