Agent 记忆的效果取决于其模式设计

你以为给 Agent 接上一个“知识图谱”就算有了记忆,其实大多数系统最后都退化成高级一点的向量库。抽取模型随意起名字、随便连边,图谱看起来很酷,却几乎查不出有用东西。想让 Agent 在真实业务里稳定工作,记忆模式本身就是核心产品设计,而不是一个“顺手加上的增强功能”。

当你给 Agent 一个知识图谱当记忆层,如果不加约束,负责抽取的 LLM 会自己发明结构。它会自由决定实体类型、关系标签和属性名,看起来很聪明,但结果往往极度泛化。大量节点被标成 TopicObject,所有连接都叫 RELATES_TO,图谱结构被彻底抹平。等你想做一点复杂查询时,才发现几乎无事可做。

Zep 的开源项目 Graphiti(GitHub 超过 2.6 万星)走的是另一条路:通过预定义本体和模式,把抽取过程“框死”在一个合理空间里。你用 Pydantic 模型定义实体和边的类型,抽取模型只在这些类型里分类,而不是天马行空乱猜。下面就从一个常见误区讲起,再看抽取流程、模式代码示例,以及它在真实查询中的差异。


扁平向量检索为什么撑不起多跳推理

一个简单例子:三条事实,向量只看见两条

基于向量的记忆,把事实当成一块块文本,用语义相似度来检索。这种方式在“单点问答”里挺好用,一旦问题需要跨多条事实推理,就开始掉链子了。看一个关于项目的三条事实:

  • Alice 管理项目 Atlas
  • 项目 Atlas 运行在 PostgreSQL 上
  • PostgreSQL 集群在周二宕机

当用户问:“Alice 的项目是否受到了周二宕机的影响”,真正需要的是三条事实一起出现。向量检索往往只会返回第 1 条和第 3 条,因为它们都提到了 Alice 或周二、宕机这类关键词。第 2 条是关键桥梁:它把 Alice 管理的项目和 PostgreSQL 连接起来,却既没提到 Alice,也没提到周二,于是被相似度搜索直接忽略。

多跳推理示意

知识图谱的做法完全不同:它把实体存成节点,把事实关系存成边。查询时不是“匹配文本”,而是“沿着连接走路”。在上面的例子里,图谱会有一条链:Alice → 管理 → 项目 Atlas → 运行于 → PostgreSQL。多跳推理就是顺着这条链走出来的,而扁平向量检索根本看不到这条路径。

据一些团队的内部评估数据,在需要 2 跳以上推理的场景里,单纯向量检索的召回率会掉到 40% 以下,而结构化图谱能稳定在 80% 左右。

没有模式的图谱,会悄悄退化成“伪向量库”

每个基于图谱的记忆系统,流程大致类似:LLM 接收原始对话或文档,抽取实体和关系,存成节点和边;查询时在图谱里检索,再把相关事实注入 Agent 的提示中。关键在于,抽取这一步几乎决定了后面的一切——图谱里到底有什么、结构长什么样、你能问出什么问题,全被它锁死。

无模式抽取示意

假设有一段开发者对话,聊的是一个叫 Nexus 的 Web 应用,用 Python、TypeScript、React 和 Docker 构建。没有模式约束时,抽取出来的节点大概率都叫 TopicObject,边一律是 RELATES_TO。看上去图谱很“通用”,但一落地就暴露两个大问题:

  • 检索只能退回语义相似度,因为无法按类型过滤,所有东西都挂在同一个标签下面。
  • 领域约束完全消失,没有结构化属性,比如 statuscategory,你没法区分“进行中 vs 已完成”“前端框架 vs 数据库”。

我自己在一个内部项目里踩过这个坑:图谱建得很勤快,节点数涨得飞快,但业务同事只会说一句——“我直接搜文档好像更快一点”。那一刻就很扎心。


用 Pydantic 把“记忆”变成可控的模式

模式思路:和 FastAPI、函数调用是同一套哲学

解决思路,其实在 AI 技术栈里已经被验证过很多次了。你可以把它理解成“给记忆加上类型系统”:

  • FastAPI 的接口响应,用 Pydantic 模型定义结构。
  • 函数调用工具,用 Pydantic 模式约束参数和返回值。
  • Zep 的 Agent 记忆,也用同样的方式定义实体和边。

你可以基于 EntityModel(继承自 Pydantic 的 BaseModel)定义自定义实体类型,字段用 EntityText 并配上描述,指导抽取模型怎么填。描述写得越具体,抽取越稳定。

实体模式定义示意

文档字符串和字段描述不是装饰品,它们是抽取器的“学习材料”。一段好的描述,外加 1~2 个具体示例,往往就能让模型稳定地区分“项目名称”和“项目类型”。上面的 Pydantic 描述,不只是分类指令,还在教抽取器它原本不懂的业务词汇。技术实体也可以用同样的模式来定义。

技术实体模式示意

边类型则用 EdgeModel 定义,并且可以携带自己的属性,比如角色、熟练度、权重等:

边类型模式示意

最后,用 EntityEdgeSourceTarget 把图谱“接起来”,明确哪些实体类型可以通过哪些边类型连接:

源/目标约束示意

约束带来的“安全感”:只允许合理的关系存在

通过上面的定义,代码会强制执行这些规则:

  • WORKS_ON 只能连接 UserProject
  • USES_TECHNOLOGY 只能连接 UserTechnology
  • 不符合这些约束的关系,不会生成类型化边

有用户反馈,在引入这种源/目标约束后,图谱里“奇怪关系”的比例(比如人和日期之间的莫名其妙连边)下降了 70% 以上,调试时间也明显缩短。

这和类型化函数调用的逻辑一模一样:通过限制 LLM 的输出空间,避免生成无效参数。记忆模式则是在约束“Agent 允许记住什么”,把无意义或有风险的关系挡在门外。我也不太确定这个类比是不是完美,但在工程实践里确实挺好用。


Graphiti 抽取流程:从原始对话到可追溯事实

五步流水线:抽取、消歧、时间轴

当一段带模式的对话被导入时,Zep 的抽取流程大致分成五步:

抽取流程示意

  1. 实体抽取:识别文本中的命名实体,并根据你定义的实体类型进行分类。
  2. 实体消歧:合并重复实体,比如把“Nexus”和“the Nexus project”合并成一个节点。
  3. 事实抽取:识别实体之间的关系,并根据源/目标约束输出类型化边。
  4. 事实消歧:检测矛盾事实,作废过时信息,通过 Graphiti 的双时态模型保留历史,记录事件发生时间和导入时间。
  5. 时间抽取:解析“昨天”“上周二”这类时间引用,并映射到每条边的有效时间窗口。

前面定义的 Pydantic 模式,直接指导了第 1 步和第 3 步。实体类型告诉抽取器“要找什么”,边类型和源/目标约束告诉它“哪些关系是合法的”。消歧和时间处理则在后台自动完成。

每条边都会带上明确的有效时间区间(t_validt_invalid)。当信息发生变化,比如“Alex 从项目 Atlas 转到项目 Nexus”,旧事实会被标记为失效,而不是直接删除。这样一来:

  • 你可以查询“当前真实状态”。
  • 也可以重建“某个时间点的历史状态”。

在合规和审计越来越被重视的今天,这种“可回放”的记忆结构,已经不只是锦上添花,而是很多行业的硬性要求。

一个真实对话的输出长什么样

看一个更贴近实战的例子:导入一段开发者 Alex 的对话,他在聊自己的工作——一个活跃的 Web 应用 Nexus、技术栈,以及自己对不同技术的熟练度。

对话导入示意

当你查询 Project 节点时,会返回 Nexus,并带有填充好的 project_statusproject_type 属性:

项目节点示意

节点不再是泛泛的“Topic”或“Object”,而是一个结构化的 Project,字段和含义都在模式里写得清清楚楚。边同样是有类型、有属性的:

  • WORKS_ON 边上带着 role: lead developer

WORKS_ON 边示意

  • USES_TECHNOLOGY 边上,对 Python 和 Docker 标记为 proficiency: advanced,对 TypeScript 标记为 proficiency: intermediate

USES_TECHNOLOGY 边示意

有了这些结构化信息,你就能做很多过去很难做的事:

  • 按状态筛选项目,比如“所有活跃项目”。
  • 按类别筛选技术,比如“后端数据库 vs 前端框架”。
  • 精确查询“哪些活跃项目使用 PostgreSQL”。

这类查询在招聘匹配、内部专家搜索、SaaS 客户成功团队里都非常实用,有团队反馈用这种图谱后,内部“找对人”的时间从几天缩短到几小时。


把结构化事实重新拼成 Agent 能用的上下文

上下文模板:从图谱到提示的一座桥

图谱建好了,下一步是把这些结构化事实重新拼成 Agent 能理解的上下文文本。Graphiti 用的是“上下文模板”的方式:你定义要包含哪些实体类型、边类型,以及时间注释的格式,系统会自动把查询结果组装成一个字符串,注入到 Agent 的提示里。

上下文模板定义示意

效果大概是这样:

上下文块示意

每一条上下文条目都有:

  • 清晰的类型(比如 Project、Technology)。
  • 时间注释(何时有效)。
  • 已定义好的属性(状态、角色、熟练度等)。

模板只需要配置一次,之后在调用 Agent 时通过 ID 引用即可。对接不同业务线时,你甚至可以为同一套图谱准备多套上下文模板,让客服、销售、运营看到的“记忆视角”各不相同。

一个容易被忽略的风险点

上下文模板虽然好用,但也有坑。模板设计不当时,很容易把太多事实一次性塞给模型,导致:

  • 提示长度暴涨,推理变慢、成本变高。
  • 模型被无关信息干扰,回答开始“跑题”。

所以在设计模板时,建议:

  • 控制实体和边的数量上限。
  • 优先选择和当前任务强相关的类型。
  • 对时间做裁剪,比如只保留最近 N 个月的事实。

10/10/10 约束:用模式给推理画边界

为什么要强行“少而精”

Zep 在 Graphiti 里强制了一个 10/10/10 约束:

  • 最多 10 个自定义实体类型。
  • 最多 10 个自定义边类型。
  • 每种类型最多 10 个字段。

10/10/10 约束示意

这个限制是刻意设计的,目的就是逼开发者认真想清楚:在你的领域里,真正重要的东西到底有哪些,而不是一股脑儿把所有概念都建成实体。很多团队一开始会觉得“10 个不够用”,但实践下来,能覆盖 80% 关键逻辑的实体类型往往不超过 4~5 个。

源/目标约束也在这里起到“护栏”的作用。如果模式里没有定义连接 ProjectCompetitor 的边类型,即便对话中频繁提到这两者,抽取模型也不会创建这种关系。模式定义的,其实就是“有效记忆的空间”。

有点像在给 Agent 画一块“思考的安全区”,让它在这块区域里自由联想,而不是到处乱跑。

从 3×3 起步:一个可复用的设计方法

如果你正准备给 Agent 设计记忆模式,可以用一个简单的起步策略:

  • 先选 3~4 个实体类型:比如 User、Project、Document、Technology。
  • 再选 3~4 个边类型:比如 WORKS_ON、OWNS、USES_TECHNOLOGY、MENTIONS。
  • 每个类型只保留最关键的 5~7 个字段,其余先放在“以后再说”的列表里。

等这套模式在真实流量下跑顺了,再逐步增加复杂度。很多团队一上来就想建“完美本体”,结果半年过去还在改 schema,Agent 迟迟上不了线。说实话,能在两周内跑出一个可用的 3×3 模式,比纸面上完美的 30×30 模式有用多了。


把模式当成产品的一部分,而不是工程细节

没有模式约束的 Agent 记忆,本质上就是一个“长得像图谱的向量库”。你花了构建图谱的成本,却没拿到结构化检索、多跳推理、可审计时间轴这些真正的红利。模式设计,就是把这些红利兑现出来的关键步骤,而且基于 Pydantic,不需要你再学一门新 DSL。

如果你正在做 Agent 产品,或者准备把 LLM 接入现有业务系统,这套“先画模式,再谈记忆”的方法,值得反复拿出来对照。等哪天你发现团队开始在图谱上做复杂查询、回放历史状态、按角色和熟练度自动分配任务,大概率就是这套模式真正发挥价值的时候。

想要深入研究,可以直接去看 Zep 的 Graphiti 开源仓库,顺手点个星,后面有更新也方便跟进。


常见问题

Q:为什么我的 Agent 已经接了知识图谱,效果还是和向量检索差不多?

A:最常见原因是图谱缺乏清晰的模式设计,实体和边类型过于泛化,导致查询时只能按语义相似度“模糊搜索”。当所有节点都叫 Topic/Object、所有边都叫 RELATES_TO 时,你几乎无法按类型过滤,也没法利用结构化属性做精确筛选。建议先用 Pydantic 定义 3~4 个核心实体类型和 3~4 个关键边类型,给每个字段写清楚含义和示例,再让抽取模型按这个模式工作。等你能稳定跑出“按状态筛选项目”“按熟练度筛选技术”这类查询时,效果差异会非常明显。

Q:如何判断自己设计的记忆模式是不是“过度建模”了?

A:一个简单信号是:如果你在模式里定义了十几个实体类型,但日常查询只用到其中三四个,大概率就是建多了。过度建模会让抽取变慢、维护成本变高,还容易引入一堆几乎没人用的字段。更实用的做法是先用 10/10/10 约束自我克制:限制实体、边和字段数量,把注意力放在覆盖 80% 业务场景的那部分。每隔一段时间回顾一次查询日志,把几乎没被访问的类型和字段砍掉或合并,这样模式会越来越“贴身”。

Q:多跳推理一定要用知识图谱吗,能不能只靠向量和长上下文?

A:理论上可以用向量加长上下文硬撑,但在 2 跳以上推理、数据量较大时,成本和稳定性都会迅速恶化。向量检索对“桥接事实”非常不敏感,容易漏掉关键一跳,而长上下文会让模型在大量无关信息里迷路。知识图谱的优势在于显式的节点和边结构,可以精确控制遍历路径和过滤条件。更现实的做法是混合:用向量做初筛,再在筛出的实体上做图谱遍历,这样既能保持召回率,又能控制成本和延迟。

Q:时间维度真的有必要吗,我的业务好像不太需要回放历史?

A:很多团队一开始也这么想,等系统上线一段时间后,才发现“历史状态”在排查问题、合规审计、用户纠纷处理里非常关键。没有时间维度,你只能看到“现在的真相”,却解释不了“它是怎么变成这样的”。双时态模型通过记录事件发生时间和导入时间,让你既能查当前状态,也能回放任意时间点的图谱切片。实现成本并不高,却能在关键时刻帮你少掉很多扯皮和猜测,属于典型的“早做早安心”。

Q:如果业务在快速变化,模式会不会很快就过时?

A:模式确实会随着业务演进而调整,但这不意味着一开始就要设计得很复杂。更健康的节奏是:先用一个精简模式跑起来,定期根据真实对话和查询需求做小步迭代。Graphiti 这类系统通过源/目标约束和字段描述,让你在扩展模式时保持兼容性,同时避免引入太多“实验性字段”。每次扩展前,可以先问三个问题:这个新类型/字段是否会被频繁查询?是否能显著提升推理质量?是否能在两周内被真实使用?三问都过了,再加进模式里,演进会更稳。


如果你正打算给 Agent 上一层“真正有记忆的脑子”,不妨把这里的模式设计方法先收起来。等下次团队讨论“要不要接一个知识图谱”时,拿这套思路对照一下,往往比随口问几个人经验更有用。记忆这件事,做对一次,就能在后面的所有迭代里持续放大价值。