犀牛鸟 2026 研究笔记 estelledc.github.io

Ch12: cognee——本体论驱动的认知管线

Part 3: 七大开源项目精读 前置章节:Ch11: Graphiti——时序知识图谱与双时间轴 后续章节:Ch13: supermemory——产品化记忆与多源捕获


1. 一句话定位

cognee 是 ECL(Extract-Cognify-Load)管线引擎,用 Ontology(本体论)组织知识——先定义领域中的概念和关系类型,再把文本映射上去。定制化能力在七个项目中最强,代码量也最大(约 2290 文件),是最接近”知识工程框架”的记忆方案。


2. 设计哲学——图书馆分类法类比

想象两座图书馆。

第一座图书馆没有分类系统。书到馆后,管理员扫一眼封面,写一张摘要卡片,然后按摘要的”语义相似度”把卡片放到最近的位置。找书时,你描述想要什么,管理员在卡片堆里做语义搜索,返回最相关的几张卡片。这是 mem0——扁平事实 + 向量检索。

第二座图书馆使用杜威十进制分类法。每本书先被分类到对应的学科领域(100 哲学、200 宗教、300 社会科学…),再在学科内按子类细分。分类规则是预先定义的——”计算机科学属于 004-006,数据库属于 005.74”。找书时,你先定位学科,再沿子类缩小范围。这是 cognee——Ontology 驱动的知识组织。

两者的核心区别不在”能不能找到书”——语义搜索和分类检索都能找到书。区别在于”能回答什么问题”:

cognee 的 Ontology 就是杜威十进制分类法——它定义了领域中的概念层次和关系类型,然后把文本映射到这个结构上。不是随意放书,而是有系统地归档。

这个类比有一条边界:杜威分类法是通用的(覆盖所有学科),cognee 的 Ontology 是领域特定的。医疗领域的 Ontology 定义”疾病-症状-药物”关系,法律领域的 Ontology 定义”法规-案例-当事人”关系。每个领域需要自己的 Ontology——这是 cognee 定制化能力强的来源,也是它使用门槛高的来源。

2.1 和 Graphiti 的根本区别

cognee 和 Graphiti 都用图结构组织知识,但它们的”图”来自完全不同的途径:

打个比方:Graphiti 像一个画家自由创作——画出来的图案可能很美,但不符合任何建筑规范。cognee 像一个建筑师按蓝图施工——每个构件都有预定义的位置和连接方式,最终产物符合设计规范。

两种方式各有优劣。Graphiti 的自动生成更灵活——不需要领域专家定义 Ontology,任何文本都能直接入库。但生成的图可能包含错误或无用的关系(”小明和咖啡没有关系”也可能被 LLM 错误地创建为一条边)。cognee 的 Ontology 约束更精确——只有预定义的关系类型才会被创建,减少了噪声。但如果 Ontology 定义不完整,重要信息可能被漏掉——如果 Ontology 没有”曾经住过”这个关系类型,用户说的”我以前住在北京”就映射不上去。


3. ECL 管线——Extract-Cognify-Load

cognee 的核心架构是 ECL 管线——三个独立的、可插拔的阶段。这个设计借鉴了数据工程中经典的 ETL(Extract-Transform-Load)模式,但把 Transform 替换为 Cognify(认知化),强调”不只是转换格式,而是理解语义”。

3.1 Extract——数据摄入与分块

Extract 阶段负责把原始数据(文本、PDF、网页、代码等)转化为可处理的单元。

分块策略:cognee 不是简单地按固定 token 数切分(像 RAG 系统的 naive chunking),而是尝试做”语义分块”——在段落边界、章节边界等语义自然断裂处分切。这保证了每个 chunk 内的语义完整性——一个 chunk 不会跨越两个不相关的话题。

数据源支持:cognee 支持 30+ 种数据源格式——纯文本、Markdown、PDF、Word、HTML、代码文件、数据库查询结果等。每种格式有对应的解析器,负责提取文本内容和元数据(标题、作者、创建时间等)。

可插拔性:分块策略和数据源解析器都可以替换。默认的分块策略是按段落分块,但你可以替换为按句子分块、按话题分块、甚至用 LLM 做智能分块。这和 Graphiti 的硬编码实体提取逻辑形成对比——Graphiti 只有一种方式提取实体,没有替换的余地。

3.2 Cognify——关系提取与 Ontology 映射

Cognify 是 cognee 最核心也最复杂的阶段。它做两件事:从文本中提取实体和关系,然后把这些实体和关系映射到 Ontology 预定义的概念和关系类型上。

实体识别(NER):LLM 从每个 chunk 中识别命名实体——人名、地名、组织名、技术术语等。这和 Graphiti 的实体提取类似,但 cognee 多了一步:实体不是直接变成节点,而是先被映射到 Ontology 中预定义的概念类型上。

关系提取:LLM 识别实体之间的关系。比如从”小明在腾讯工作”中提取 (小明, works_at, 腾讯)

Ontology 映射:这是 cognee 独有的步骤。提取出的关系需要匹配到 Ontology 预定义的关系类型上。如果 Ontology 定义了 Person-works_at-Organization 这个关系类型,那么 (小明, works_at, 腾讯) 就映射到这个类型上。如果提取出的关系在 Ontology 中没有对应的类型(比如 Ontology 没有 Person-visited-City 类型),这条关系就会被丢弃或存入”待分类”区。

这个映射过程是 cognee 定制化能力的核心——通过修改 Ontology,你可以精确控制哪些信息被保留、哪些被丢弃、如何组织。但这也是它使用门槛最高的地方——你需要先定义 Ontology,然后才能用 cognee 处理数据。

3.3 Load——存入图/向量双库

Load 阶段把 Cognify 的结果写入两个存储后端:

图数据库:实体和关系以节点和边的形式存入图数据库。支持三种后端——Kuzu(嵌入式,适合本地开发)、Neo4j(企业级,适合生产部署)、FalkorDB(Redis 兼容,适合轻量部署)。每种后端有自己的驱动实现。

向量数据库:实体和关系的文本描述被嵌入为向量,存入向量数据库。向量索引用于语义检索,和图遍历形成互补。

双库的设计意味着:查询时可以同时利用图遍历(结构化查询)和向量检索(语义查询),取长补短。这和 Graphiti 的 Neo4j 向量索引 + Cypher 图遍历有相似的思路,但 cognee 把图数据库和向量数据库分离了——你可以选择不同的向量后端,不受图数据库限制。

3.4 可插拔——每步都可以替换

ECL 管线的每一步都是可插拔的:

这种可插拔性在七个项目中最强。Graphiti 的管线是硬编码的——你只能用 OpenAI 的 LLM 和 Embedding 模型、只能用 Neo4j 图数据库。mem0 只有向量后端可替换,提取和检索逻辑不可替换。cognee 的每一步都可以根据场景定制——这是它作为”知识工程框架”而非”记忆系统”的定位体现。

但可插拔性也有代价:每次替换一个组件,你需要确认它和其他组件的兼容性。换了一个更弱的关系提取模型,Ontology 映射的准确率可能下降。换了一个不支持事务的图数据库,写入一致性可能出问题。可插拔性给了你灵活性,也给了你”自己负责兼容性”的负担。


4. 检索三步管线——Retrieve → Rerank → Filter

cognee 的检索架构也是一个三步管线,和写入的 ECL 管线形成对称。

4.1 Retrieve——候选获取

从图数据库和/或向量数据库中获取候选结果。支持两种检索模式:

向量检索:查询文本被嵌入为向量,在向量数据库中做相似度搜索,返回语义最相关的 top-K 结果。和 mem0 的向量检索没有本质区别。

图检索:从查询中识别关键实体,在图数据库中定位这些实体的节点,然后沿关系边做图遍历,获取和这些实体相关联的其他实体和关系。和 Graphiti 的 Cypher 图遍历类似,但 cognee 的图遍历会利用 Ontology 的类型约束——只沿 Ontology 中定义的关系类型遍历,避免”走偏”。

两种模式可以组合——先向量检索找语义相关的实体,再从这些实体出发做图遍历获取结构化关联。

4.2 Rerank——重排序

对候选结果做重排序。支持两种重排器:

Cross-Encoder 重排:和 Graphiti 的精排阶段相同——把查询和候选拼在一起输入 Cross-Encoder 模型,计算精确的相关性分数。精度高但慢。

LLM 重排:把查询和候选列表一起送给 LLM,让 LLM 判断每个候选和查询的相关性。比 Cross-Encoder 更灵活(LLM 可以理解更复杂的语义),但也更贵(每次重排都消耗 LLM token)。

默认使用 Cross-Encoder 重排,LLM 重排作为可选升级。

4.3 Filter——置信度过滤

根据置信度阈值过滤重排后的结果。相关性分数低于阈值的候选被丢弃,不返回给用户。

这一步是 cognee 独有的——Graphiti 的检索没有显式的过滤步骤(Cross-Encoder 精排后直接返回 top-K),mem0 也没有(向量检索后直接返回 top-K)。过滤步骤的好处是避免返回低质量的记忆——如果所有候选的相关性都不高,宁可返回空结果也不返回噪声。

但过滤阈值的选择是一个微妙的问题。阈值太高,很多有价值的记忆被误杀;阈值太低,过滤形同虚设。最优阈值取决于具体场景——在医疗场景中宁可多返回一些候选(阈值低),在闲聊场景中宁可少返回但精准(阈值高)。cognee 允许动态调整阈值,但需要开发者根据反馈不断校准。

4.4 和 Graphiti 两阶段检索的对比

维度 cognee 三步管线 Graphiti 两阶段
步骤数 3(Retrieve → Rerank → Filter) 2(粗筛 → 精排)
过滤 显式过滤步骤 无显式过滤
重排器 Cross-Encoder 或 LLM(可切换) Cross-Encoder
图检索 Ontology 约束的图遍历 自由 Cypher 图遍历
向量检索 独立向量数据库 Neo4j 内建向量索引
可插拔性 每步可替换 固定管线

cognee 的三步管线比 Graphiti 的两阶段更工程化——多了显式过滤步骤,每个步骤都可以替换。但也更复杂——三个步骤的参数需要协同调优,任何一个步骤的参数变化都可能影响最终效果。


5. 多图后端——工程挑战与隐性成本

cognee 同时支持 Kuzu、Neo4j 和 FalkorDB 三种图数据库。这个”同时支持”听起来很美好,但背后隐藏着巨大的工程挑战。

5.1 三个图数据库的差异

差异维度 Kuzu Neo4j FalkorDB
Schema 强制(建表时定义) 无(运行时定义) 可选
查询语言 Cypher 子集 Cypher 完整版 OpenCypher
事务模型 ACID ACID 最终一致
向量索引 无内建 内建 无内建
部署方式 嵌入式(进程内) 独立服务 独立服务
类型系统 强类型 动态类型 动态类型

这三个数据库在几乎每个维度上都有差异。最典型的问题是 Kuzu 的强类型系统。

5.2 Kuzu 的强类型问题

Kuzu 是强类型图数据库——每种关系类型需要在建表时指定源节点类型和目标节点类型。比如定义 Person-works_at-Organization 关系时,需要先建 Person 节点表和 Organization 节点表,然后建 works_at 关系表,指定源类型为 Person、目标类型为 Organization

当 cognee 的 Ontology 新增一个关系类型(比如 Person-visited-City)时,Kuzu 需要创建一个新的关系表。而 Neo4j 不需要——直接写入就行。

这导致了 cognee 的 Kuzu 适配层有大量”表管理”代码,而 Neo4j 不需要。更麻烦的是:如果 Ontology 后来修改了(”Person 也可以 works_at Person”——比如自由职业者为自己工作),Kuzu 的已有关系表无法修改——需要删表重建。这在生产环境中是不可接受的(删表意味着丢失数据)。Neo4j 完全没有这个问题。

5.3 查询语言的差异

三个数据库都支持 Cypher 的某种变体,但支持程度不同:

这意味着 cognee 的图查询代码需要对每个后端写不同的 Cypher 语句——或者使用一个抽象层把查询翻译成各后端的方言。cognee 选择了后者,但抽象层不完美——某些后端特有的功能无法通过抽象层使用。

5.4 隐性成本总结

“同时支持多后端”的隐性成本包括:

  1. 代码膨胀:每个后端需要自己的驱动实现、查询翻译器、Schema 管理。三个后端意味着三套代码。
  2. 测试组合爆炸:N 个后端 x M 个功能 = N*M 个测试组合。每个 bug 修复都需要确认在所有后端上都生效。
  3. 功能一致性难以保证:新功能先在 Neo4j 上实现,Kuzu 和 FalkorDB 的适配可能延后。用户从一个后端切换到另一个时,可能发现某些功能不可用。
  4. 性能差异:同样的查询在三个后端上的性能特征完全不同。优化一个后端的查询可能对其他后端无效甚至有害。

Graphiti 选择绑定 Neo4j 避免了所有这些成本。cognee 选择支持多后端换来了部署灵活性。这是两种不同的工程策略——没有对错,只有取舍。


6. Ontology——双刃剑

Ontology 是 cognee 最核心的概念,也是它最有争议的设计选择。

6.1 Ontology 是什么

Ontology(本体论)在计算机科学中的定义是:对某个领域中的概念、关系和约束的形式化描述。

用日常语言说:Ontology 就是一张”领域概念地图”——它告诉你这个领域有哪些重要的概念(人、公司、技能、城市),这些概念之间有什么关系(人在公司工作、人拥有技能、人住在城市),以及这些关系的约束(一个公司可以有多个员工,但一个人同一时间只在一个公司工作)。

cognee 的 Ontology 定义了以下内容:

6.2 Ontology 定义不完整——漏信息

Ontology 的第一个风险是”定义不完整”——如果你没有在 Ontology 中定义某个关系类型,相关的事实就会被遗漏。

比如,如果 Ontology 只定义了 Person-works_at-OrganizationPerson-lives_in-City,没有定义 Person-graduated_from-University,那么用户说”我毕业于北京大学”这条信息就无法被映射——cognee 会提取出 (用户, graduated_from, 北京大学) 这个关系,但在 Ontology 中找不到匹配的关系类型,于是丢弃。

这和 Graphiti 的自动生成策略形成鲜明对比——Graphiti 不会遗漏任何信息,因为它没有预定义的约束。LLM 提取出的所有实体和关系都会被创建为节点和边。

6.3 Ontology 定义过宽——降质量

Ontology 的第二个风险是”定义过宽”——如果你定义了太多关系类型,LLM 在映射时容易出错。

比如,如果 Ontology 同时定义了 Person-likes-FoodPerson-prefers-FoodPerson-enjoys-Food 三个关系类型,LLM 在处理”我喜欢吃火锅”时可能随机映射到其中任何一个——三个类型的语义太接近,区分度不够。更糟糕的是,同一条信息在不同对话中可能被映射到不同类型,导致数据不一致。

6.4 Ontology 的维护成本

Ontology 不是”定义一次就完事”的。随着业务发展,领域知识会演变——新的概念类型出现,旧的关系类型不再适用。Ontology 需要持续维护:

这个维护成本在大型项目中不可忽视。Graphiti 没有 Ontology,也就没有维护成本——但它牺牲了知识的结构化程度。

6.5 对比:Ontology vs 自动生成 vs 扁平存储

维度 cognee(Ontology) Graphiti(自动生成) mem0(扁平存储)
知识结构化程度 最高 中等 最低
信息遗漏风险 高(Ontology 不完整时) 低(无约束) 低(全量存储)
信息噪声 最低(Ontology 过滤) 中等(LLM 可能提取无关关系) 最高(所有事实平等存储)
维护成本 高(Ontology 需要持续更新) 中等(图谱需要实体解析) 低(无结构需要维护)
使用门槛 高(需要定义 Ontology) 中等(需要部署 Neo4j) 低(API 即用)
多跳推理能力 强(Ontology 约束的图遍历) 强(自由图遍历) 弱(无图结构)

7. 代码结构精读

cognee 的代码量在七个项目中最大(约 2290 文件),组织结构如下:

cognee/
  cognee/
    api/                       # 顶层 API(add / cognify / search)
      add.py                   # add() 入口,对应 ECL 的 Extract
      cognify.py               # cognify() 入口,对应 ECL 的 Cognify
      search.py                # search() 入口,对应检索三步管线
    tasks/
      ingestion/               # 数据摄入(文档分块、格式解析)
        extract_chunks.py      # 分块逻辑
      graph/                   # 图构建(实体提取、关系映射)
        extract_graph.py       # LLM 驱动的实体和关系提取
        map_ontology.py        # Ontology 映射
      summarization/           # 摘要生成
    modules/
      retrieval/
        base_retriever.py      # 三步管线基类(Retrieve → Rerank → Filter)
        graph_retriever.py     # 图检索实现
        vector_retriever.py    # 向量检索实现
      graph/
        neo4j_driver.py        # Neo4j 后端驱动
        kuzu_driver.py         # Kuzu 后端驱动
        falkordb_driver.py     # FalkorDB 后端驱动
      ontology/                # Ontology 定义与映射
        ontology.py            # Ontology 数据结构
        ontology_mapper.py     # 实体和关系到 Ontology 的映射逻辑
      storage/                 # 向量/关系/Blob 存储抽象
        vector_storage.py      # 向量存储抽象层
        relational_storage.py  # 关系存储抽象层
    infrastructure/            # 底层基础设施(配置、日志、异常处理)
  tests/
  docs/

值得注意的几个关键文件:

api/cognify.py——Cognify 阶段的入口。它编排了完整的认知管线:文档分块 → 实体识别 → 关系提取 → Ontology 映射 → 存入图/向量双库。每一步都是一个可替换的 task。

modules/retrieval/base_retriever.py——检索三步管线的基类。定义了 retrieve()rerank()filter() 的管线接口。具体的检索实现(图检索、向量检索)通过继承这个基类来提供。

modules/graph/——三个图后端的驱动实现。每个驱动需要实现 add_node()add_edge()query() 等接口。由于三个后端的差异,每个驱动内部都有大量后端特化的代码。

modules/ontology/——Ontology 的定义和映射逻辑。这是 cognee 和其他项目最大的区别——其他项目没有这个模块。


8. 局限性——对轻量记忆太重

cognee 的代码量最大(约 2290 文件),复杂度最高。这些复杂度来自三个来源:

8.1 多后端适配的代码膨胀

三个图后端(Kuzu / Neo4j / FalkorDB)的驱动实现、查询翻译、Schema 管理加起来有大量代码。如果只支持一个后端(像 Graphiti 只支持 Neo4j),代码量可以减少 30-40%。

8.2 Ontology 系统的复杂度

Ontology 的定义、映射、维护逻辑本身就很复杂。加上 Ontology 和图后端的交互(Kuzu 的强类型约束需要 Ontology 驱动建表,Neo4j 不需要),复杂度进一步增加。

8.3 管线编排的开销

ECL 三步管线和检索三步管线,每一步都是独立的 task,有输入/输出 schema、错误处理、重试逻辑。管线编排的框架代码(任务调度、状态管理、错误恢复)占据了相当大的比例。

这些复杂度对于”知识工程框架”来说是合理的——企业级知识管理需要这些能力。但对于”轻量 Agent 记忆”来说太重了。如果你只是想让 Agent 记住用户的偏好和历史事件,cognee 的 Ontology 定义、多后端适配、管线编排都是过度工程。


9. 和 DB-Agent-Memory 的对比

9.1 本体论 vs 提炼层次

维度 cognee DB-Agent-Memory
知识组织 Ontology 驱动的领域概念图 四层金字塔(L0-L3 提炼层次)
定制化能力 最强(Ontology 完全可自定义) 中等(层级结构固定,内容自动提炼)
使用门槛 最高(需要定义 Ontology) 最低(零配置自动运行)
适合场景 领域知识密集的企业应用 个人助手 + 轻量记忆

cognee 的 Ontology 和 DB-Agent-Memory 的金字塔代表了两种知识组织哲学:

两种哲学各有适用场景。当领域知识有清晰的概念体系(医疗、法律、金融)时,Ontology 的自上而下更精确。当领域知识边界模糊(个人偏好、日常对话)时,金字塔的自下而上更灵活。

9.2 管线化 vs 集成化

cognee 的 ECL 管线和检索管线是高度模块化的——每一步都可以替换、暂停、重试。DB-Agent-Memory 的 L0→L1→L2→L3 提炼是一个集成化的管道——各层之间紧密耦合,不能单独替换某一层的实现。

管线化的好处是灵活——你可以为特定场景定制每一层。集成化的好处是简单——所有逻辑在一起,不需要处理模块间的接口和兼容性问题。

9.3 代码体量对比

项目 文件数 学习成本
DB-Agent-Memory ~90 最低
Graphiti ~200
cognee ~2290 最高

代码量的差距反映了设计选择的差异。DB-Agent-Memory 的零依赖哲学限制了代码量的上限——没有外部后端适配、没有 Ontology 系统、没有管线编排框架。cognee 的多后端 + Ontology + 管线化设计必然需要更多代码。


10. 竞赛启发——管线化思想可以借鉴

cognee 对 DB-Agent-Memory 的最大启发不是 Ontology(对轻量记忆来说太重),而是管线化思想。

10.1 检索管线的借鉴

DB-Agent-Memory 目前的检索是单步的——BM25 + 向量融合,一步出结果。借鉴 cognee 的三步管线,可以在保持零依赖的前提下提升检索质量:

当前:BM25 + sqlite-vec → 结果
改进:BM25 + sqlite-vec → LLM 重排 → 置信度过滤 → 结果

LLM 重排不需要额外的外部服务——DB-Agent-Memory 已经依赖 LLM 做事实提取,重排只是多一次 LLM 调用。置信度过滤可以通过 LLM 输出的相关性评分来实现。

10.2 写入管线的借鉴

DB-Agent-Memory 的 L0→L1→L2→L3 提炼管线本身就是一个管线,但它目前是紧密集成的。借鉴 cognee 的可插拔设计,可以把每一层的提炼逻辑解耦为独立的 task:

每个 task 有明确的输入/输出接口,可以独立测试、独立替换。这不改变金字塔的层级设计,只改变了代码组织方式——但代码组织方式的改变可以降低贡献门槛,让每个 contributor 只需要理解一个 task 而不是整个管线。

10.3 不需要借鉴的部分

cognee 的 Ontology 对 DB-Agent-Memory 来说是不需要借鉴的。DB-Agent-Memory 的定位是轻量级记忆引擎——引入 Ontology 会大幅增加使用门槛和代码复杂度,违背零依赖的设计哲学。

cognee 的多图后端支持也不需要借鉴。DB-Agent-Memory 选择 SQLite 作为唯一存储后端是有意的——零依赖是核心竞争力之一。如果要加图数据库支持,应该以可选扩展的形式提供,而不是内建。


11. 本章小结

回顾本章的核心要点:

下一章我们看 supermemory——走完全不同的产品化路线,面向终端用户而非开发者。


导读目录:README.md 上一章:Ch11: Graphiti——时序知识图谱与双时间轴 下一章:Ch13: supermemory——产品化记忆与多源捕获