# RAG 之外的 AI 记忆：向量、图谱和 Dense-Mem

**Summary:** RAG 不是魔法记忆。本文用实践视角解释 chunk、embedding、向量搜索、图谱支撑的记忆，以及为什么持久 AI 记忆需要来源证据、冲突处理和检索策略。

- Canonical: https://markhuang.ai/zh/blog/ai-memory-beyond-rag
- Language: zh-CN
- Author: [Mark Huang](https://markhuang.ai/about)
- Published: 2026-05-25
- Section: AI 与 LLM
- Tags: AI 记忆, RAG, embedding, 向量数据库, GraphRAG, Dense-Mem
- License: https://creativecommons.org/licenses/by-nc/4.0/

---

![AI 记忆把文档层、向量空间和图谱关系连接到助手核心](https://cdn.markhuang.ai/blog/ai-memory-beyond-rag/hero.webp)

*AI 记忆不只是 RAG，它是证据、状态、关系和更新策略的组合。*

## 先说结论

都 2026 年了，"AI 记忆"早不是一个单一功能了。大家嘴上说的"记忆"，其实往往是下面五层里的某一层。每层都有用，也各有各的坑。

| 层次        | 干嘛的                | 常见翻车                 |
| --------- | ------------------ | -------------------- |
| Prompt 记忆 | 把指令塞进上下文           | 上下文缺东西或者写得含糊，规则就被忽略了 |
| RAG       | 找外部文档，再注入上下文       | 可能捞到过时的、不完整的证据       |
| 向量记忆      | 检索语义相近的内容          | 文本相似不代表答案就对          |
| 图谱记忆      | 存事实、关系、历史和冲突       | 没有验证关卡的话，错误事实会一直赖在里面 |
| 持久记忆      | 把检索、状态、来源、更新策略组合起来 | 缺任何一层，信任就崩了          |

我自己总结的一条实用原则：检索负责找文本，记忆负责判断什么是当前的、可信的、跟这次任务相关的。这也是为什么 prompt 放在哪里依然很重要；更简单的心智模型可以看 [系统 prompt 与用户 prompt](/blog/system-prompt-user-prompt-genai-features)。

这个区分很关键。如果把检索当记忆，系统能找到旧文本，却不知道哪个事实才是当前事实。如果把 embedding 当"理解"，你能做出一个飞快的搜索系统，但它照样会返回错误证据。如果加了图谱却没有清晰关卡，你会把嘈杂的对话变成一套看似自信、其实被污染的记忆。

所以我们把这些部分拆开来看。

## Claude Code 的记忆是上下文，不是数据库

Claude Code 自带的记忆挺好用的，但它不是向量数据库，也不是图谱记忆系统。

按现在 Claude Code 的记忆文档，每个 session 都从全新的上下文窗口开始。跨 session 携带知识靠两个机制：你自己写的 `CLAUDE.md` 文件，以及 Claude 从你的纠正和偏好里自动记下来的笔记。两者都会在对话开始时加载进来。Claude 把它们当上下文，不是强制执行的配置。

最后一句是重点。

`CLAUDE.md` 里可以写：

```markdown
完成代码修改前始终运行 npm test。
优先做小而聚焦的修改。
API 处理器位于 src/api/handlers/。
```

这能让模型行为更一致，因为指令在上下文里看得见。但它并没有创建一个可搜索的语义记忆系统。它不会给每次历史对话做 embedding，不会维护冲突解决，也不会自动知道某个偏好已经被新偏好取代了——除非新事实同样出现在可见上下文里，而且模型真的遵循了它。

自动记忆也是同理。它是持久上下文，不是知识图谱。文档说自动记忆会加载到每个 session，上限是前 200 行或 25KB。存实用指南足够了，但要撑长期、高容量、带证据追踪的记忆就不够了。

所以我更倾向于把 Claude Code 的记忆看成"启动上下文"。它很适合存指令、约定、踩坑后总结的项目笔记。但它不是 AI 记忆的完整答案。

## RAG 不等于"关键词上下文"

很多人脑子里的 RAG 是这样的："搜个关键词，抓它前后 100 个字符，粘进 prompt。"

这种东西可以有，但它不是 RAG 的定义。

RAG 是检索增强生成（Retrieval-Augmented Generation）。系统先检索外部信息，再把这些信息作为额外上下文交给模型去生成答案。检索部分可以是关键词搜索、向量搜索、混合搜索、图谱遍历、SQL 过滤、rerank，也可以是这些方式的组合。

![RAG 流水线：文档被切成片段，片段变成 Embedding，排名靠前的结果被注入提示词](https://cdn.markhuang.ai/blog/ai-memory-beyond-rag/rag-pipeline.zh-CN.webp)

*RAG 流水线*

一个典型的向量 RAG 搭建流程大概是这样：

1. 收集源文档。
2. 把文档切成 chunk。
3. 把每个 chunk 转成向量。
4. 把向量加上元数据存进索引。
5. 查询时，把用户问题也转成向量。
6. 检索最接近的 chunk。
7. 把这些 chunk 注入 prompt。

"chunk"本来就不是固定大小的。它可以是 300 个 token、800 个 token、一个段落、一个 Markdown 小节、一个代码符号，或者一个语义段。有些系统会加重叠区。有些系统会在找到第一个结果后，再拉取相邻的 chunk。还有些系统会在 LLM 看到候选内容之前，用 reranker 重新排序。

所以正确的说法不是"RAG 会抽取关键词附近 100 个字符"。

更准确的说法是：RAG 检索的是你配置好的上下文单位；质量高度依赖你怎么切 chunk、做 embedding、建索引、过滤、rerank，以及最后怎么组装上下文。

## 瓶颈不是 RAG，而是无状态的检索

当答案确实存在于语料里时，RAG 是很强的。

比如我问"这个服务用什么端口？"，RAG 可以找到 README、配置文件或部署笔记。我问"用户上个月说过 Neo4j 什么？"，RAG 可以检索到那段对话片段。

但记忆要面对更难的问题：

```text
3 月 1 日："我偏好用 Postgres 做项目记忆。"
4 月 10 日："其实我想给这个记忆项目用 Neo4j。"
今天："我的记忆项目应该用什么数据库？"
```

纯检索系统可能同时找到 3 月和 4 月那两段话。它可以把两段都交给模型，然后指望模型自己推理出正确答案。有时候没问题。但有时候模型会选中已经过时的事实，把两者混在一起，或者过于自信地给出回答。

这不是向量搜索的问题，而是状态管理的问题。

持久记忆需要知道的不只是"什么文本和问题相似"。它还要知道：

- 说过什么？
- 谁说的？
- 什么时候说的？
- 这是证据、声明，还是已经被接受的事实？
- 它跟现有事实有没有冲突？
- 旧事实是不是已经被取代了？
- 它属于哪个 profile 或项目？
- 这次任务该不该召回它？

![RAG 检索与持久 AI 记忆的对比](https://cdn.markhuang.ai/blog/ai-memory-beyond-rag/rag-vs-memory.zh-CN.webp)

*RAG 检索与持久记忆的区别*

这就是图谱支撑的记忆开始变得重要的地方。不是因为图谱数据库有什么魔法，而是因为记忆本身就是有关系的、有历史的。

## Embedding 到底在做什么

Embedding 模型把文本变成数字。

更准确地说：一段输入文本变成一个向量。一批文本变成一个矩阵，因为多个向量堆在一起了。

![句子 Embedding 可以表示为向量，也可以堆叠成矩阵](https://cdn.markhuang.ai/blog/ai-memory-beyond-rag/vector-matrix.zh-CN.webp)

*句子 Embedding 是向量，也可以组成矩阵*

举个例子：

```text
"用户偏好用 Neo4j 做记忆图谱。"
​
-> [0.12, -0.44, 0.31, ... , 0.08]
```

这些数字不是随机 ID，是模型学出来的坐标。Embedding 模型经过训练后，会让意义相关的文本在向量空间里彼此更接近。

但这些维度不是人类预先定义好的分类。

一个 768 维的向量不是说：

```text
维度 1   = 数据库属性
维度 2   = 项目属性
维度 3   = 偏好属性
...
维度 768 = 记忆属性
```

这种解释很诱人，但太表面了。维度是模型学到的潜在坐标。人类有时候能解读 embedding 空间里的某些方向，但这些坐标并不是一套干净利落的分类体系。

更多维度可以给模型更大的容量来保存信号，但"维度更多"不等于"更准确"。一个弱模型生成的 3,072 维 embedding，可能还不如一个更适配你领域的 768 维 embedding。检索质量取决于 embedding 模型本身、训练数据、语言和领域的适配程度、归一化方式、chunk 质量、元数据过滤，以及评估集。

Embedding 模型之所以重要，是因为它决定了"接近"到底意味着什么。

## 向量数据库搜索到底在搜什么

搜索向量数据库时，你问的不是：

```text
哪篇文档包含这个精确的词？
```

你问的是：

```text
哪些已存的向量跟查询向量最接近？
```

![Embedding 空间里的查询点和最近邻居](https://cdn.markhuang.ai/blog/ai-memory-beyond-rag/embedding-space.zh-CN.webp)

*Embedding 空间与最近邻居*

数据库里存的向量大概长这样：

```json
{
  "id": "fragment-123",
  "text": "用户偏好用 Neo4j 做记忆图谱。",
  "embedding": [0.12, -0.44, 0.31, "..."],
  "metadata": {
    "profile": "mark",
    "source": "chat",
    "created_at": "2026-05-25"
  }
}
```

查询时大概是这样：

```text
查询: "Mark 偏好用什么记忆数据库？"
查询 Embedding: [0.10, -0.40, 0.29, ...]
​
最近的已存向量:
1. "用户偏好用 Neo4j 做记忆图谱。"
2. "记忆服务使用 Neo4j 图谱和向量索引。"
3. "记忆服务器把图谱事实存放在宿主 LLM 外部。"
```

数学上通常用余弦相似度、点积或欧氏距离，取决于数据库和索引配置。很多系统会对向量做归一化，让方向比大小更重要。大数据库会用近似最近邻（ANN）索引，保证数据量上去之后搜索速度还能跟上。

这就是向量数据库的价值所在：它让语义召回变得实用。而且在数据存取层面它是模型无关的——Go 服务、TypeScript 应用、Python notebook、Claude Code 插件或 MCP 服务器都可以存取同一个记忆服务，只要大家约定好 embedding 模型和向量维度就行。

但向量搜索返回的仍然只是候选结果，它不判断真伪。

## 为什么还要加图谱数据库

图谱数据库直接存储关系。

对记忆来说，这比把每条记忆都当成一段纯文本 chunk 要合适得多。

```text
(User)-[:PREFERS]->(Neo4j)
(Neo4j)-[:USED_FOR]->(MemoryProject)
(Fact)-[:SUPPORTED_BY]->(Evidence)
(Fact)-[:SUPERSEDES]->(OldFact)
(Claim)-[:CONFLICTS_WITH]->(Fact)
```

这让你能问一些向量搜索不擅长的问题：

```text
关于这个用户的数据库偏好，当前有哪些活跃事实？
哪条声明取代了更早的 Postgres 偏好？
哪些记忆跟这个项目有关？
哪些事实的证据比较弱？
助手应该主动询问哪些还没解决的矛盾？
```

微软的 GraphRAG 用图谱解决的是一个相关但不同的问题：通过抽取、网络分析、prompt 编排和总结来理解文本数据集。对个人或项目记忆来说，真正有用的启示不是"用图谱替代向量搜索"，而是"当关系和来源成为一等信息时，检索会变得更强"。

向量搜索回答的是："语义上什么接近？"

图谱搜索回答的是："什么是相连的、当前的、有证据支撑的，或者存在冲突的？"

更强的记忆架构会同时用上两者。

## 拿 Dense-Mem 做个小案例

这就是我在 [Dense-Mem](https://github.com/markhuangai/dense-mem) 里实践的思路。

重点不是具体实现，而是边界划分。我不希望每个宿主都发明自己的记忆格式，也不希望 LLM 仅仅因为看到一句看似重要的话，就悄悄改写长期记忆。

![Dense-Mem 记忆流程：对话片段到托管式图谱记忆](https://cdn.markhuang.ai/blog/ai-memory-beyond-rag/dense-mem-flow.zh-CN.webp)

*Dense-Mem 记忆流程*

有用的模式其实很简单：宿主模型负责发现候选记忆，记忆层负责存储、embedding、来源追踪、冲突检查和召回。原始证据不应该直接变成事实。记忆应该先经过关卡；冲突应该触发澄清，而不是被静默覆盖。

这篇文章讲到这里就够了。Dense-Mem 是我目前用来练习这套架构的实验：外部记忆服务、图谱 + 向量召回，以及显式的状态转换。

如果你想跑起来而不只是看概念，可以先看 [Dense-Mem 快速上手：让 Claude Code 和 Codex 共享同一份记忆](/blog/dense-mem-personal-server-claude-code-codex)，里面会走一遍本地 Docker 搭建和 MCP 客户端配置。等你准备好公开 HTTPS 端点了，再看 [用 Vultr 和 Traefik 安全部署 Dense-Mem](/blog/secure-dense-mem-vultr-traefik)。

## 准确率、存储和性能

很容易脱口而出一句"图谱 + 向量记忆比 RAG 更准确、性能更好"。

这样说太笼统了。

诚实一点的说法是这样的：

| 层次           | 改善什么            | 单独解决不了什么                   |
| ------------ | --------------- | -------------------------- |
| Chunking     | 检索精度和上下文质量      | 真伪、时效性、冲突处理                |
| Embedding 模型 | 跨语言、跨领域的语义匹配质量  | 来源、事实确认、用户确认               |
| 向量数据库        | 快速的最近邻检索        | 关系遍历和当前状态策略                |
| 图谱数据库        | 关系、来源、多跳召回、事实取代 | 不配合 embedding 的话，处理不了语义相似度 |
| Reranking    | 更好的最终上下文排序      | 烂的源数据或烂的记忆关卡               |
| 澄清流程         | 记忆冲突时提高正确率      | 完全自动且无需用户参与的记忆             |

图谱数据库如果模型设计得好、索引合理，关系查询可以非常快。向量数据库如果 embedding 一致、索引适配负载，语义搜索也可以非常快。烂的图谱 schema 会慢，烂的向量索引会检索出莫名其妙的结果，塞满检索 chunk 的巨大 prompt 照样会把模型搞糊涂。

没有免费的午餐。这套架构之所以有效，是因为每一层都有自己明确的工作。

## 我信任的设计原则

做 AI 记忆，我逐渐收敛到这条原则：

> 保存原始证据。谨慎提升类型化事实。用向量检索。用图谱推理关系。解决冲突前先问用户。

这样得到的是可以跨宿主、跨语言移植的记忆系统。Claude Code、Codex、web 应用或其他 MCP 客户端都可以跟同一个记忆服务器对话。记忆不会因为聊天窗口重置就消失了，也不依赖一个 prompt 文件无限变长。它还能保留自己为什么相信某件事的理由。

RAG 仍然是系统的一部分，它是召回机制。

但记忆比召回更大。

记忆是你选择保留什么、怎么知道它是真的、怎么更新它，以及什么时候决定把它带回来。

> **Info:**
>
> 来源：[Claude Code memory docs](https://docs.claude.com/en/docs/claude-code/memory)，[OpenAI embeddings docs](https://platform.openai.com/docs/guides/embeddings)，[Pinecone chunking strategies](https://www.pinecone.io/learn/chunking-strategies/)，[Neo4j vector index and search](https://neo4j.com/developer/genai-ecosystem/vector-search/)，[Microsoft GraphRAG](https://www.microsoft.com/en-us/research/project/graphrag/)，以及 [Dense-Mem README](https://github.com/markhuangai/dense-mem)。
