@akshay_pachaar: https://x.com/akshay_pachaar/status/2058976178908885210
摘要
解释了如何通过使用Pydantic模式定义本体来修复代理记忆,实现结构化提取到知识图谱中以进行多跳推理,并提供了一个开源解决方案(Zep)。
查看缓存全文
缓存时间: 2026/05/25 18:42
Pydantic 修复了我的 Agent 记忆
你的智能体记了一切,却什么都不懂。
智能体记忆最初基于向量数据库。将事实切分成块,通过相似性检索。
在需要跨块连接事实的查询出现之前,这套方法都工作得很好。一旦出现这种查询,它就崩了。问题不在于相似性,而在于结构。
知识图谱是解决方案。实体作为节点,关系作为边,遍历代替匹配。
但大多数团队遇到了另一堵墙。
当你给智能体一个知识图谱作为记忆时,默认行为是:负责提取的 LLM 自行决定结构。
它自己挑选实体类型、关系标签和属性。
结果是通用的。
举个例子,你在构建一个客户支持智能体。你喂给它 50 条支持对话,涉及客户、工单、功能和升级历史。
你问:“哪些企业客户有未解决的 sev-1 工单?”
图谱里有数据。但每条支持工单都被存成了“主题”节点。每个客户都是“对象”。每条关系都是“关联到”。
无法按类型、严重程度或套餐等级进行筛选。查询返回的是噪音。
智能体没有忘记任何东西。只是没人告诉它该关注什么。
解决方案很直接:预先定义 schema。 告诉提取模型你的领域中存在哪些类型的实体、哪些关系是有效的、每个实体携带哪些属性。
这个组织蓝图被称为 ontology(本体)。你可以把它看作 智能体大脑的 schema。
让我们来看看为什么这很重要、没有它会出什么问题,以及如何用一个 100% 开源的方案 来实现它。
为什么平面检索在多跳推理中会失效
基于向量的记忆将事实存为文本块,并通过语义相似性检索。这仅在查询不需要连接不同块中的事实时有效。
考虑一个项目相关的三个事实。
-
Alice 管理 Project Atlas
-
Project Atlas 运行在 PostgreSQL 上
-
PostgreSQL 集群在周二宕机了
一个查询如“Alice 的项目是否受到了周二宕机的影响”需要用到全部三个事实。
向量检索只会检索到事实 1 和 3,因为它们都包含了相关术语。事实 2 是通过 Project Atlas 连接 Alice 和 PostgreSQL 的桥梁,但它既没提到 Alice 也没提到周二。相似性搜索错过了它。
知识图谱将实体存为节点,关系存为边。它不是匹配文本,而是遍历连接。
这条链(Alice → 管理 → Project Atlas → 运行于 → PostgreSQL)正是多跳推理生效的关键,而它在平面向量检索中是看不见的。
记忆流水线与提取步骤的位置
每个基于图的智能体记忆系统都遵循一个通用的流水线:
-
摄取: 原始数据进入(对话消息、文档、JSON 业务数据)
-
提取: LLM 读取原始数据,决定存在哪些实体、哪些关系连接它们、哪些属性重要
-
存储: 提取出的实体变成节点,关系变成边,全部持久化到图中
-
检索: 查询时,系统搜索图并组装相关事实
-
交付: 检索到的事实被格式化为上下文块,注入到智能体的 prompt 中
提取步骤决定了所有事情。它决定了你的图里有什么、如何结构化,以及下游可以查询什么。
问题来了。在大多数框架中,这一步是个黑盒。你传入文本,LLM 提取出“实体”和“关系”,你得到节点和边。LLM 自行决定类型、标签和属性。
你无法控制它分类的方式。
让我们看看如何修复。
用 Pydantic 定义 Schema
修复方法跟 AI 栈中其他地方使用的模式一样。
-
FastAPI 端点有 Pydantic 响应模型。
-
函数调用工具有 Pydantic schema。
-
在 Zep 中,智能体记忆也是同样的方式。
使用 EntityModel(Pydantic 的 BaseModel 的子类)定义自定义实体类型,配合 EntityText 字段和引导提取模型的描述。
from zep_cloud.external_clients.ontology import EntityModel, EntityText
from pydantic import Field
class Project(EntityModel):
"""
Represents a specific software project, application,
or codebase that the user is building or contributing to.
"""
project_status: EntityText = Field(
description="Current status: active, completed, paused, or archived.",
)
project_type: EntityText = Field(
description="Type of project: web app, mobile app, API, CLI tool, etc.",
)
这里的文档字符串和字段描述非常重要,因为带有具体示例的良好描述能给提取器足够的信号来准确分类。
上面的 Pydantic 描述不只是分类指令。它们教会提取器它原本不认识的词汇。
一个 Technology 实体遵循同样的模式。
class Technology(EntityModel):
"""
Represents a programming language, framework, library,
database, or tool that the user works with.
"""
tech_category: EntityText = Field(
description="Category: programming language, framework, database, etc.",
)
边类型使用 EdgeModel,并携带自己的属性。
from zep_cloud.external_clients.ontology import EdgeModel
class WorksOn(EdgeModel):
"""The user is currently working on, building, or contributing to a project."""
role: EntityText = Field(
description="User's role: lead developer, contributor, maintainer, etc.",
)
class UsesTechnology(EdgeModel):
"""The user actively uses or works with a specific technology."""
proficiency: EntityText = Field(
description="Proficiency level: beginner, intermediate, advanced, or expert.",
)
最后,通过 EntityEdgeSourceTarget 将这些与源/目标约束一起接入图,它定义了哪些实体类型可以通过哪些边类型连接:
from zep_cloud import EntityEdgeSourceTarget
client.graph.set_ontology(
entities={"Project": Project, "Technology": Technology},
edges={
"WORKS_ON": (
WorksOn,
[EntityEdgeSourceTarget(source="User", target="Project")],
),
"USES_TECHNOLOGY": (
UsesTechnology,
[EntityEdgeSourceTarget(source="User", target="Technology")],
),
},
)
这段代码强制规定:
-
WORKS_ON只能连接 User 到 Project -
USES_TECHNOLOGY只能连接 User 到 Technology -
任何不符合这些约束的关系都不会产生类型化边。
总结一下,到目前为止我们得到了:
底层发生了什么
当对话在 schema 激活状态下被摄取时,Zep 的提取管道运行五个步骤:
-
实体提取 识别文本中的命名实体
-
实体解析 合并重复项(“Nexus” 和 “Nexus project” 变成一个节点)
-
事实提取 识别关系并将其输出为类型化边
-
事实解析 检测矛盾并使过时的事实失效(保留历史)
-
时间提取 解析时间引用并将其映射到每条边的有效窗口
(插入 GIF)
你的 Pydantic schema 指导步骤 1 和 3。实体类型告诉提取器要寻找什么。带有约束的边类型告诉它要分类哪些关系。解析和时间处理会自动进行。
实际示例演示效果
我们摄取一段对话,其中一位名叫 Alex 的开发者在讨论他的工作(一个名为 Nexus 的活跃 Web 应用、他的技术栈、熟练度):
查询 Project 节点会返回 Nexus,其中填充了 project_status 和 project_type 属性。
这个节点不是一个泛泛的“主题”或“对象”。它是一个 Project,带有 schema 中定义的、结构化的字段。
边也是类型化的。
-
WORKS_ON携带role: lead developer -
USES_TECHNOLOGY对 Python 和 Docker 携带proficiency: advanced,对 TypeScript 携带proficiency: intermediate。
现在可以按状态筛选项目、按类别筛选技术,并能够准确回答“哪些活跃项目使用了 PostgreSQL”这样的问题。
上下文模板
最后一部分是上下文模板,它将类型化的事实组装成一个可用于 prompt 的块。
你可以定义要包含的边类型和实体类型,Zep 会将其与时间注解一起格式化为一个字符串,注入到智能体的 prompt 中。
client.context.create_context_template(
template_id="dev-context",
template="""# PROJECTS
%{edges types=[WORKS_ON] limit=5}
# TECH STACK
%{edges types=[USES_TECHNOLOGY] limit=10}
# PROJECT DETAILS
%{entities types=[Project] limit=5}
# TECHNOLOGIES
%{entities types=[Technology] limit=10}""",
)
效果如下:
生成的上下文块中的每一条都是类型化的、带有时间注解的,并且携带定义的属性。模板只需保存一次,在智能体调用中通过 ID 引用即可。
10/10/10 约束与 Schema 作为推理边界
Zep 强制限制了 10 个自定义实体类型、10 个自定义边类型和每个类型 10 个字段。
这是有意为之,迫使开发者思考领域中的重点,而不是建模所有东西。
源/目标约束也充当了智能体允许记住什么的护栏。如果 schema 中没有包含连接 Project 到 Competitor 的边类型,即使对话中同时提到了两者,提取模型也不会创建该关系。
Schema 定义了有效记忆的空间。
这与类型化函数调用背后的原理相同——我们约束 LLM 的输出空间,使其不能生成无效参数。记忆 schema 将同样的约束应用于智能体存储的内容。
从 3-4 个实体类型和 3-4 个边类型开始,覆盖你 80% 的领域逻辑,然后逐步增加复杂度。
没有 schema 纪律的智能体记忆,就是一个行为像向量存储的图。
某种意义上,你付了构建图的成本,却没有得到结构化检索的好处。
Schema 就是让你找回这个好处的关键,而且由于它是 Pydantic,你没有任何新东西需要学。
对于特定领域的应用尤其如此。LLM 提取在通用知识上做得还可以,但一旦你的领域有内部术语、与常见词冲突的产品名称,或训练数据中缺失的行话,无引导的提取就会产生无意义的结果。Schema 弥补了这个差距。它将领域词汇直接带入提取步骤,LLM 不需要事先见过你的术语。它只需要你写的定义。
你可以在这里找到 Zep 的 GitHub 仓库 → (别忘了给星 🌟)
感谢阅读!
相似文章
@akshay_pachaar: 你的智能体记性很好,但理解力为零。大多数智能体记忆系统都在优化回忆能力。但更难的问题是……
一则关于智能体记忆中模式(Schema)约束重要性的讨论,介绍了 Zep AI 的开源 Graphiti 库,该库用于构建受约束实体和关系类型的时序知识图谱。
Zep:一种用于智能体记忆的时序知识图谱架构
本文介绍了 Zep,这是一种用于智能体(agent)记忆的时间知识图谱架构,在 DMR 和 LongMemEval 等基准测试中表现优于 MemGPT。文章强调了 Zep 在企业级用例中处理动态知识融合和时间推理的能力。
@akshay_pachaar: https://x.com/akshay_pachaar/status/2054564519280804028
Nous Research 推出的 Hermes Agent 综合指南,重点介绍其技能自进化、三层记忆架构以及用于构建持久化 AI 智能体的 GEPA 优化能力。
@akshay_pachaar: https://x.com/akshay_pachaar/status/2064051835636498924
Opik 是一个用于AI代理可观测性的开源平台,它不仅限于追踪,还能自动诊断故障、提出修复方案并进行验证,从而在没有人工干预的情况下关闭调试循环。
@ByteMohit: https://x.com/ByteMohit/status/2063493300884246598
一篇关于构建AgentForge的详细技术文章,AgentForge是一个基于Python的开源agent框架,涵盖了会话运行时、工具合约、审批层和持久化等组件,强调agent由其运行时定义,而不仅仅是模型。