只看单个 Agent,记忆好像只是“把用户说过的话保存下来,下一次搜出来”。一旦进入真实产品,工作量会迅速增加: 数据可能来自聊天、文件、网页、企业云盘、代码仓库;处理可能要排队、抽取、切块、建索引;检索结果要按用户、项目、租户隔离; 过期和删除也要能被解释。Cognee 和 Supermemory 的价值,正在于它们把这些工程责任放到了显式平台层。

场景:你要给一个团队知识助手接长期记忆

输入不再只有聊天:
  会议记录、设计文档、网页、Google Drive、GitHub issue、客服历史

调用方也不止一个:
  聊天 agent、搜索页、工单助手、代码助手、后台同步任务

平台层必须回答:
  这份内容属于谁?
  处理到哪一步了?
  查询时返回画像、证据片段,还是图关系?
  用户撤回数据时删到哪里才算删干净?

这就是 Cognee 和 Supermemory 不应只被当成“大一点的 RAG”的原因。它们提供的是一套共享记忆服务: 数据怎样进入、怎样被加工、查询怎样限制到正确用户或项目,以及多个调用方怎样安全复用同一份知识。

读完后你应该能回答。 读完这一篇,应该能回答三件事:Cognee 的 remember() 为什么还能落回 add()、cognify()、improve() 这条知识图谱管线; Supermemory 的 add、profile、search 和 connectors 为什么更像产品化 context API; 以及在自己的系统里,什么时候该选“知识层”,什么时候该选“上下文平台”。

这篇可以用同一份资料来读:一份“客服升级策略”文档。放进 Cognee 时,问题是怎样把资料加工成可复用知识; 放进 Supermemory 时,问题是怎样让产品通过 API 拿到画像、搜索结果和连接器上下文。下面这张表先把两个生命周期对齐。

阶段 Cognee 路线 Supermemory 路线
写入入口 add / remember 接收文件、文本或 URL。 add 用 content、URL、metadata、containerTag 建立记忆对象。
处理状态 cognify 把 dataset 加工成 graph / vector 可检索层。 API 暴露 queued、processing、done 等状态,托管管线负责抽取与索引。
召回输出 search 更像面向知识层的查询结果。 profile 给稳定画像,search 给当前问题证据。
删除范围 删除数据集时,原始资料和派生的图、向量索引都要处理。 撤回产品对象或连接器时,要清除对应 containerTag 下的数据。

本文依据。 本文使用 topoteretes/cognee、 supermemoryai/supermemory 的公开 README、文档和源码;分别固定于 c0d18c8 与 2415a5c。 Cognee 本地 SDK 与开源服务端代码可直接追踪;Supermemory 的托管服务内部抽取策略不可见,本文只按公开文档、SDK 示例和 API schema 描述合约。

一、为什么最后讨论共享记忆服务

1.1 前面几层的问题,到平台层会同时出现

把 Cognee 和 Supermemory 放在最后,是因为它们需要前面几层作为铺垫。先看过 Mem0 的写入取舍、Letta 的 agent state、 Graphiti 的时间图、LangMem 的执行路径、TencentDB-Agent-Memory 的上下文卸载,再读这两个项目,才容易看见它们合并了哪些责任: 长期记忆最终存在哪里、由谁处理,又通过什么 API 进入这一轮模型上下文。

到平台层以后,记忆不再只是单个 agent 的能力。一个产品可能同时接入聊天记录、文档、网页、邮件、代码仓库和第三方应用; 同一条资料可能属于用户、团队、项目、数据集或租户;一次查询可能要拿用户画像、相似片段、相关文件、图关系和权限过滤后的结果。 所以 Cognee / Supermemory 的问题不是“有没有长期记忆”,而是“长期记忆怎样成为多人、多源、多调用方都能依赖的基础设施”。

1.2 多用户隔离、处理状态和删除缺一不可

平台化以后,记忆至少要承担四类公共责任。第一是 scope:同一套服务要区分用户、项目、数据集、租户; 第二是 process:原始内容进入以后,要经历抽取、切块、嵌入、建图或建索引; 第三是 retrieve:调用方不只要文本相似结果,还可能要图关系、用户画像、相关文档和当前可用上下文; 第四是 delete:当用户、项目或数据源撤回内容时,平台要明确删除哪些原始数据和派生产物。

这四类责任正好对应平台常见故障。scope 不清,会把 A 用户的材料带进 B 用户的上下文;process 不清, 调用方不知道内容是待处理、已切块、已嵌入,还是已经进入图;retrieve 不清,搜索结果会混合画像、证据和文档片段; delete 不清,连接器断开以后旧内容还可能继续影响模型。平台化的价值,是把这些责任从应用 prompt 里搬出来。

Cognee 将资料经 cognify 转为可搜索知识层;Supermemory 的 memory 路径可形成用户画像,superrag 只供检索。
图中分别展开两条资料处理路线:Cognee 将普通资料加工为可搜索知识;Supermemory 的 memory 可形成画像并支持检索,superrag 只用于检索,不更新画像。

二、Cognee:先把资料变成可检索、可推理的知识层

Cognee README 给自己的定位是 open-source AI memory platform:接收任意格式数据,构建 self-hosted knowledge graph, 让 agents 跨会话 recall、connect、act with context。README 还强调领域实体、关系与自定义 ontology,说明它的目标超过向量索引:原始资料要被加工成可连接的知识层。 这些定位可以看 README 的 About Cognee。

2.1 用户看到的是 remember / recall,底下仍是管线

Cognee 的用户接口提供四个常用动作:remember、recall、 forget 和 improve。示例里 remember() 既能永久写入知识图谱, 也能带 session_id 写入 session memory;recall() 则按 query 自动路由。 这一层接口看起来很轻,见 README quickstart。

源码把这个轻接口展开了。remember() 对普通文本与文档的 docstring 明确区分两条路径:没有 session_id 时是 permanent memory,会跑 add() 加 cognify();有 session_id 时先写 session cache,再在默认 self_improvement=True 时把 session 数据桥接到永久图。永久路径实际执行时,内部的 _run() 先 add(...),再 cognify(...),最后按配置调用 improve(...)。

async def remember(data, dataset_name="main_dataset", session_id=None, self_improvement=True):
    if session_id:
        # session memory: fast cache, then optional bridge into permanent graph
        ...

    async def _run():
        await add(data=data, dataset_name=dataset_name, ...)
        cognify_result = await cognify(datasets=[dataset_name], ...)

        if self_improvement:
            await improve(dataset=dataset_name, user=user)

这段调用链让 Cognee 的产品接口变得容易理解:在这条普通资料路径中,remember() 是门面,add() 负责接入, cognify() 负责把资料转成图和向量可检索结构,improve() 负责继续增强。 调用方看到的是一个动词,平台内部保留了明确阶段。

用户动作 平台内部责任 为什么要拆开
remember(data) add 接入原始数据,cognify 生成知识图谱,improve 做后续增强。 调用方只关心“记住”,平台负责数据进入后的多阶段处理。
remember(data, session_id) 先写 session cache,再把值得长期保留的内容写入永久图。 当前会话要快,长期知识层可以异步变好。
recall(query) 按 scope、query type 和 session 信息路由到 session、graph 或 graph context。 同一个问题可能先查短期会话,也可能直接查永久图。

“记住了”的返回时刻还要按调用方式理解。默认的永久写入会等待 add、cognify 与启用的 improve; 设置 run_in_background=True 后,第一次 await 只拿到正在处理的 RememberResult。 再次 await result才等待后台任务结束;后台处理错误保存在 status/error 中,不会像默认阻塞路径那样重新抛出。 所以 done 只表示任务结束,还要检查 status。这段示例明确关闭 improve,便于只观察建图这一步:

result = await cognee.remember(
    "客服升级策略...",
    dataset_name="company_docs",
    run_in_background=True,
    self_improvement=False,
)
await result
if result.status != "completed":
    raise RuntimeError(result.error or f"Pipeline status: {result.status}")

后台路径使用进程内 asyncio task 并保留强引用,避免任务被垃圾回收;这不等于进程重启后可恢复的持久队列。 session 路径又有不同语义:session_stored 只说明会话写入步骤已返回, 后台 improve 失败会记日志,不能据此断言永久图已经同步。生产调用方应分别验证会话可读、建图完成和后续增强,而不是把一个成功状态泛化到所有阶段。

2.2 add 和 cognify 把“资料”变成“知识图”

add() 是第一步,负责接收文本、文件、URL、binary streams 等输入,并把它们放进 dataset,供后续处理。 它的 docstring 也把 workflow 写成 data resolution、content extraction、dataset storage、metadata tracking、 permission assignment。所以 Cognee 的边界从 query 之前就开始:平台先拥有一个明确的数据接入层。

第二步是 cognify()。 这个函数把已接入数据转成 structured knowledge graph:文档分类、文本切块、实体抽取、关系检测、图构建和内容摘要都在它的说明里。 这里的价值在于中间层:平台先把原始材料加工成更稳定的结构,后面不同 agent、不同查询策略都可以复用它。

检索端也延续这个思路。search() 的参数里有 query_type、datasets、top_k、node_name、 neighborhood、references 等控制项;它的说明列出 GRAPH_COMPLETION、RAG_COMPLETION、 CHUNKS、CYPHER、CHUNKS_LEXICAL 等搜索类型。 对调用方来说,请求已经从“给我最相似的 10 段文本”变成了“在这个知识层里选一种回答方式”。

await add(data, dataset_name="company_docs")

await cognify(
    datasets=["company_docs"],
    graph_model=KnowledgeGraph,
    chunker=TextChunker,
)

results = await search(
    query_text="How should we handle customer escalation?",
    query_type=SearchType.GRAPH_COMPLETION,
    datasets=["company_docs"],
    include_references=True,
)
形状示例:同一份文档在 Cognee 里的生命周期

add / remember:
  source = "客服升级策略..."
  dataset = "company_docs"

cognify:
  dataset 被切分、抽取实体和关系,并生成图 / 向量可检索层

search:
  query = "客户投诉升级到谁?"
  结果来自处理后的知识层,并可带回 reference

用这条链看 Cognee,就不会把它误读成单纯“上传文件后向量搜索”。add 只是把资料放入 dataset; cognify 才开始文档分类、切块、实体关系抽取和图构建;search 则让调用方选择图回答、 RAG 回答、chunk 检索或 Cypher 查询等不同视角。

当前 remember 还允许显式指定 content_type="code" 或 "skills", 分别进入代码图或 Skill 摄取路径;它不会仅凭一个路径自动猜出这两类意图。 上面的 add → cognify 骨架适用于普通资料,不应复制成所有输入的唯一处理流程。

2.3 Cognee 把公司资料加工成多个 Agent 可复用的知识

Cognee 最适合的场景,是你有一批资料、业务文档、代码、客户历史或专家经验,希望它们经过处理后成为 可重复使用、可追踪、可自托管的知识层。README 的 use cases 也偏向 company brain、跨会话 Agent 和领域知识这类“领域知识要被多个任务复用”的问题。

代价也来自同一个设计。知识层越重,就越要关心处理延迟、数据集权限、图模型、ontology、搜索类型和运维部署。 如果你的需求只是“这个用户喜欢深色模式,下次回答时记得”,直接上完整知识图谱可能过重; 如果你的需求是“多个 agent 要长期复用一组企业知识,并且要解释关系和来源”,Cognee 这条路线就更自然。

2.4 它更像知识加工层,不是轻量偏好存储

用 Cognee 时最好先问一个问题:这份资料是否值得被加工成中间层。值得的情况通常有三个信号: 第一,资料会被多个 Agent 或多个流程复用;第二,关系和来源比单段文本更重要;第三,团队需要自主部署并控制数据处理过程。 如果这三个信号都不强,Cognee 的 dataset、cognify、graph search 可能会显得笨重。

反过来说,一旦这些信号成立,Cognee 的优势就不是“多一种搜索方式”,而是让资料先离开临时 prompt,进入可重复加工的知识层。 这层可以后续继续 improve、重新 search、按 dataset 管理,也能让不同 agent 面向同一组公司知识工作。

对于值得建图、但尚不清楚处理成本的一批文档,可以先用 remember(..., dry_run=True)。 当前估算范围限于本地普通永久摄取:不入库、不调用 LLM,给出分阶段 token 与粗略成本估计; 不覆盖 improve 的额外模型调用。因此它能帮助比较切块与输入规模,却不能替代一次实际管线的质量和成本测量。

三、Supermemory:把记忆、画像、搜索和连接器包装成 context API

Supermemory README 的定位更产品化:memory and context layer for AI。它把 memory、user profiles、hybrid search、 connectors、multi-modal extractors 放在同一张功能表里,还明确说可以给 AI products 提供 single API。 这条线的重心从手写图谱管线转向可接入的 API 面:内容进入、上下文生成、用户画像、搜索和同步,都由平台包装出来。 这些材料见 README 开头 与 Build with Supermemory。

3.1 写入时先决定:资料只供搜索,还是也形成用户记忆

Supermemory 文档把入口说得很直接:把 conversations、documents、files、URLs 发给 Supermemory,默认的 taskType="memory" 会触发记忆抽取。 Add context 文档 还建议使用 customId 标识 conversation ID 或 document ID,以便更新和去重。 同一页的参数表把 content、customId、containerTag、metadata、 entityContext、dreaming 都列出来;containerTag 用来按用户或项目分组, 也是 user profiles 的必要边界。

公开 API schema 也能看到这一层边界。 MemorySchema 包含 content、metadata、source、status、summary、title、type、url、containerTags、 chunkCount 等字段。这些字段让一份内容在平台里的生命周期变得可见: 它来自哪里、处理到什么状态、属于哪些容器、被切成多少 chunks。

const MemorySchema = z.object({
  customId: z.string().nullable().optional(),
  content: z.string().nullable().optional(),
  metadata: MetadataSchema.nullable().optional(),
  source: z.string().nullable().optional(),
  status: DocumentSchema.shape.status,
  summary: z.string().nullable().optional(),
  containerTags: z.array(z.string()).optional(),
  chunkCount: z.number().default(0),
});

这个 schema 说明 add 的结果不只是“存了一段文本”。customId 负责和业务系统里的文档或会话对齐; containerTags 表达用户、项目或空间分组;应用仍须把调用者身份绑定到允许访问的 tag。status 表示处理进度,chunkCount 只是产物数量,不能单独证明任务完成。

文档里的 processing pipeline 进一步把这件事展开:添加内容后,Supermemory 会 validate request、store and queue、 extract content、chunk into searchable memories、embed、index。这个顺序见 Processing Pipeline。 对产品开发者来说,这意味着“记忆”已经变成后台处理队列和 API 状态机,离开了 prompt 模板里的临时数组。

Supermemory add pipeline:
  validate request
  -> store document and queue processing
  -> extract content (OCR / transcription / web scraping)
  -> chunk into searchable memories
  -> embed
  -> index

Progress:
  GET /v3/documents/{id} -> queued | processing | done
形状示例:同一份文档在 Supermemory 里的状态变化

add:
  content = "客服升级策略..."
  containerTag = "support-team"
  status = "queued"

worker:
  status = "processing"
  派生的可检索产物正在生成

later search:
  status = "done"
  profile / search 可返回当前问题需要的背景和证据

客服升级策略是一份参考文档,并不应该因为客服查阅了它,就变成“这个用户的个人偏好”。 当前写入契约用 taskType 表达这个区别:默认 memory 同时抽取事实、更新画像并建立搜索索引; superrag 只切块、嵌入和索引,不抽取事实、不更新画像。 dreaming 则回答另一件事:dynamic 把相关文档作为逻辑单元处理,instant 按单份文档处理;它不改变资料是否应该成为记忆的用途判断。

await client.add({
  content: "客服升级策略...",
  customId: "support-escalation-policy",
  containerTag: "support-team",
  taskType: "superrag",
});

更新也要区分追加和替换。文档允许相同 customId 追加新内容或提交完整更新内容, 由服务识别新增部分;要彻底替换原文,示例使用 client.documents.update(),并触发重新处理。 只改 metadata 则按文档原地更新,不重建索引。 若抽取时只应参考某类历史,filterByMetadata过滤的是本次写入所参考的既有记忆, 不会替新文档设置 metadata,也不能替代查询时的权限与范围过滤。

3.2 profile 提供稳定用户信息,search 回答当前问题

Supermemory 的一个清晰产品判断,是把 user profile 从普通搜索里拆出来。 User Profiles 文档 把 profile 分成 static 和 dynamic:static 是长期稳定事实,比如职业、偏好、专长;dynamic 是最近上下文和临时状态。 这样做的好处很实际:模型每轮不用靠三五次搜索拼出用户是谁,先拿到一个宽背景,再用 search 补具体细节。

README 里的 API 示例也是这个形状:client.add({ content, containerTag }) 写入对话, client.profile({ containerTag, q }) 一次返回 profile.static、 profile.dynamic 和相关 searchResults。 它把“用户是谁”和“这次问题需要什么证据”分开交给调用方,而不是混成一个结果列表。

await client.add({
  content: conversation,
  customId: "conv_123",
  containerTag: "user_123",
});

const { profile, searchResults } = await client.profile({
  containerTag: "user_123",
  q: "programming style",
});

// profile.static  -> 长期稳定事实
// profile.dynamic -> 最近上下文
// searchResults   -> 和本轮问题相关的证据

搜索端,Searchv4RequestSchema 暴露 containerTag、threshold、filters、include、limit、query、rerank、rewriteQuery 等参数; 返回端的 MemorySearchResult 则包含 memory、similarity、version、parents / children context、documents。这里的 context 不再只是一段文本, 而是带相似分、版本、关系和关联文档的结果对象。

const Searchv4RequestSchema = z.object({
  containerTag: z.string().optional(),
  threshold: z.number().default(0.6),
  filters: SearchFiltersSchema.optional(),
  include: z.object({
    documents: z.boolean().default(false),
    summaries: z.boolean().default(false),
    relatedMemories: z.boolean().default(false),
  }),
  limit: z.number().default(10),
  q: z.string().min(1),
  rerank: z.boolean().default(false),
  rewriteQuery: z.boolean().default(false),
});

const MemorySearchResult = z.object({
  memory: z.string(),
  similarity: z.number(),
  version: z.number().nullable().optional(),
  context: z.object({ parents: z.array(...), children: z.array(...) }).optional(),
  documents: z.array(MemorySearchDocumentSchema).optional(),
});

因此 Supermemory 的检索合约有两个层次:请求侧用 containerTag、filters 和 threshold 控制边界与召回宽度; 返回侧用 similarity、version、parents / children 和 documents 说明“为什么这条 memory 和问题有关”。 这比普通向量库的 top-k chunk 更接近产品 API。

连接器又增加了外部数据同步工作。Supermemory connectors 文档列出 Google Drive、Gmail、Notion、OneDrive、GitHub、 Granola 和 Web Crawler,并说明它们会自动同步外部文档进入知识库。 How Connectors Work 把流程写成创建连接、用户授权、自动建立连接、按连接器能力通过 webhooks、定时任务或手动触发更新。 到这一步,记忆已经接近产品基础设施:它要面对 OAuth、webhook、文件版本、同步状态和用户空间。

同步方式不能从“有 connector”一概推出。当前能力表 中,Granola 仅支持按需手动同步,Web Crawler 支持定期重抓,Google Drive 等则支持 webhook。 应用应按来源定义内容新鲜度,不能把一次授权当作持续实时同步的保证。

3.4 它更像产品上下文 API,不是只给 Agent 用的库

Supermemory 的强项,是把接入成本压到 API 和连接器层。产品可以先把对话、文档、URL 或文件交给平台, 再通过 profile 和 search 得到可用上下文。这样做牺牲了一部分内部可见性: 公开材料能看到请求参数、返回 shape 和处理状态,但托管服务内部怎样抽取、怎样合并、怎样重排,并不都在开源代码里。

部署方式也不再只有托管服务。Supermemory local 文档提供单二进制本地运行入口, 声明可沿用 Memory API,并将 SDK 的 baseURL 指向自己的服务。 但同页也明确把 connectors 与托管 Supermemory MCP 列为平台能力;本地抽取使用自己配置的模型,平台使用其专有抽取模型。 本文核对的是这些公开部署契约,没有运行本地二进制,也不据此声称两种部署的效果相同。 选择时应分别比较 API 接入、数据与模型的部署位置,以及团队是否需要修改知识加工实现。

四、怎么选:知识层、上下文 API,还是前面几条路线

4.1 先说清当前系统缺少什么

Cognee 和 Supermemory 都把记忆做得更“平台”,但它们的入口问题不同。Cognee 先问:我怎样把一批资料加工成可复用的知识层? Supermemory 先问:我怎样让产品用一个 API 得到记忆、画像、搜索、连接器和文件处理?这两问都成立,只是面向的系统阶段不同。

系统压力 更自然的路线 判断方式
想在应用和模型之间加一层长期记忆,重点是写入、去重、时间、衰减和检索排序。 Mem0 先看 memory layer 的写入与检索算法。
记忆本身就是 agent state 的一部分,要和 persona、human profile、tools、messages 一起存在。 Letta 先看 agent state 谁拥有、怎样编译进上下文。
事实会随时间变化,旧事实不能直接删除,还要保留来源和有效期。 Graphiti 先看 temporal graph 和 episode provenance。
记忆写入会影响 agent 当前回答路径,需要决定同步工具还是后台整理。 LangMem / LangGraph 先看 hot path、background manager、store、checkpointer。
公司资料、文档、代码和专家经验要被加工成可复用知识层。 Cognee 先看 add、cognify、search、dataset、graph/vector 配置。
产品想快速接入记忆、画像、搜索、连接器和文件处理。 Supermemory 先看 add、profile、search、connectors、containerTag 边界。

如果把这个系列压成一句话,就是:不要先问“用哪个向量库”。先问这份历史属于谁,是证据、画像、关系、工作流状态、知识层, 还是当前模型上下文。Cognee 和 Supermemory 把问题推进到了平台层,也提醒我们:长期记忆做大以后,难点常常在数据来源、 处理阶段、查询规则和删除范围,而不只是把内容搜出来。

参考资料