OpenClaw 长期记忆与 RAG 深入笔记
面向零基础阅读。目标不是“知道它有记忆”,而是搞懂:它到底把什么叫做记忆、RAG 在哪一层发生、检索结果怎么进入模型、为什么它和常见的“知识库问答”不是同一种东西。
1. 先用一句话说清楚:这个项目的“记忆”到底是什么
OpenClaw 默认的长期记忆,本质上是写在磁盘上的 Markdown 文件,而不是一个神秘的黑盒数据库。
官方概念文档直接写得很明确:
- memory 是 agent workspace 里的纯 Markdown;
- 文件才是 source of truth(真相源);
- 模型只有在内容被写到磁盘以后,下一次才有机会“重新想起来”。
对应代码和文档证据:
docs/concepts/memory.md:11docs/concepts/memory.md:17
这句话非常重要,因为它决定了你理解整个系统的方式:
- 不是“模型脑子里永久存着东西”;
- 不是“聊过一次以后系统就天然永远记得”;
- 而是“模型把该记住的东西写成文件,之后再通过检索把它找回来”。
所以你可以把 OpenClaw 的默认长期记忆理解成:
可被语义检索的个人笔记系统。
2. 什么是 RAG?先把最基础概念讲清楚
很多人第一次接触 RAG,会把它理解成“把文档喂给大模型”。这不算错,但太粗糙。
RAG 的完整名字是 Retrieval-Augmented Generation,中文通常叫:
- 检索增强生成
拆开看有两步:
- Retrieval(检索):先从外部知识源里找相关材料;
- Generation(生成):再把找回来的材料放进模型上下文,让模型基于这些材料回答。
也就是说,RAG 不是“训练模型”,而是:
- 模型本身没有永久学会你的私人知识;
- 只是每次回答前,系统临时把相关资料塞进这次上下文里;
- 所以模型看起来像“记得”,本质上是“查到了”。
这和长期记忆的关系是:
- 长期记忆:你把什么保存到了系统外部;
- RAG:系统如何在需要时把这些记忆找回来并注入当前回答。
因此:
长期记忆是“存”,RAG 是“找回来再用”。
3. OpenClaw 默认的长期记忆长什么样
OpenClaw 默认有两层记忆文件:
3.1 MEMORY.md
这是整理过的长期记忆。
适合放:
- 稳定偏好
- 重要事实
- 长期规则
- 持续性的项目背景
- 人物关系
- 长期决策
比如:
- 用户更喜欢中文回答
- 某个项目的默认部署方式是什么
- 某个人是导师、同事还是客户
3.2 memory/YYYY-MM-DD.md
这是日记型、追加型的每日记忆。
适合放:
- 今天讨论了什么
- 今天做了什么决定
- 临时上下文
- 今天发生的事件
- 可能以后有用的过程信息
文档说明见:
docs/concepts/memory.md:19
你可以把这两层理解成:
MEMORY.md:像“整理后的长期笔记”memory/YYYY-MM-DD.md:像“流水账/实验日志/当天工作记录”
这个设计的好处是:
- 人类可读:你随时能打开看;
- 可人工编辑:你不满意时可以整理;
- 可检索:系统会给它做索引;
- 不锁死在某个数据库里:迁移成本低。
4. 这个系统为什么说“文件是真相源”
这是 OpenClaw 和很多“数据库型记忆系统”最大的差异。
4.1 常见数据库型记忆系统
很多 AI memory 系统会这么做:
- 用户一句话进来;
- 系统判断这句话值得记住;
- 直接抽取成一条结构化 memory entry;
- 写进向量数据库或关系数据库。
这种系统的“真相源”通常是数据库。
4.2 OpenClaw 默认路线
OpenClaw 默认不是这样。
它默认的 memory-core 路线是:
- 先有 Markdown 文件;
- 文件内容才是原始记忆;
- 检索索引只是为了更快更智能地找到这些文件片段。
也就是说:
- 向量索引可以重建;
- SQLite 索引可以重建;
- 但 Markdown 文件本身才是记忆本体。
这就是文档里说的 source of truth。
对应证据:
docs/concepts/memory.md:11docs/reference/memory-config.md:57
这对研究很重要,因为它说明:
OpenClaw 默认更像“知识写作 + 智能检索”,而不是“数据库驱动的记忆黑盒”。
5. 它的 RAG 不是企业知识库 RAG,而是“个人记忆 RAG”
很多人一看到 embeddings、vector search、hybrid search,就会下意识说:“哦,这是个 RAG 系统。”
这句话没错,但还不够精确。
OpenClaw 的默认设计更接近:
5.1 它擅长的场景
- 个人长期偏好
- 个人工作日志
- 每日记录
- 跨会话保留项目背景
- 记住之前讨论过的决定
- 记住谁是谁、某人喜欢什么、你之前答应过什么
5.2 它默认不主打的场景
- 海量 PDF / 网页大规模 ingestion
- 企业知识库权限体系
- 多租户文档检索
- 大规模文档 chunk pipeline 管理
- 复杂文档同步与 ETL
为什么?
因为它默认的数据源就是:
MEMORY.mdmemory/*.md- 可选的 session transcript
- 可选的额外路径
这说明它天然偏向:
围绕一个 agent / 一个人 / 一个工作区形成的长期记忆层。
而不是:
一个通用企业级知识库中台。
6. OpenClaw 默认记忆架构的核心组成
你可以把它拆成 6 层:
- 记忆文件层:Markdown 文件
- 配置解析层:决定是否启用 memory、用哪个 provider、怎样 chunk
- 索引层:把文件切块、做 embedding、建文本索引/向量索引
- 检索层:用户查询来了以后做 recall
- 工具层:
memory_search/memory_get - 聊天运行时集成层:什么时候触发检索、什么时候写 memory flush
下面逐层讲。
7. 第 1 层:记忆文件层
默认情况下,记忆来自这些地方:
MEMORY.mdmemory/**/*.md
文档里明确写了 memory tools 主要面向这些文件:
docs/concepts/memory.md:32
再深入一点,memory_get 的描述也非常直白:
- 从
MEMORY.md或memory/*.md里读指定片段; - 搜到以后再精确读,减少上下文浪费。
对应代码:
extensions/memory-core/src/tools.ts:81
这已经暴露出默认设计哲学:
- 先搜索,再精读
和人类很像:
- 先在脑子里回忆“好像记在哪”;
- 再翻到那一页仔细看。
8. 第 2 层:配置解析层
核心入口在:
src/agents/memory-search.ts:15
这个文件非常关键,因为它定义了几乎整个 memory/RAG 系统的“旋钮”。
8.1 它控制什么
它会解析这些内容:
- memory 是否启用
- 索引哪些 source
- embedding provider 是谁
- 是否本地模型还是远程 API
- store 路径在哪
- chunk 大小和 overlap
- 是否 watch 文件变化
- query 的最大返回条数
- minScore
- hybrid search 配置
- MMR 配置
- temporal decay 配置
- cache 配置
- experimental session memory
从学习角度看,这个文件等于把一个 RAG 系统中最重要的调参项都摆在你眼前了。
8.2 默认值反映了什么设计偏好
你会看到这些默认值:
DEFAULT_CHUNK_TOKENS = 400DEFAULT_CHUNK_OVERLAP = 80DEFAULT_MAX_RESULTS = 6DEFAULT_MIN_SCORE = 0.35- hybrid 默认开启
- sources 默认是
['memory']
对应位置:
src/agents/memory-search.ts:91src/agents/memory-search.ts:107
这些默认值透露了三个设计偏好:
- 偏向轻量个人知识规模:一次只回 6 条;
- 偏向可控上下文:不是把一堆长文全塞进 prompt;
- 偏向混合检索:既要语义相关,也要支持精确关键词。
9. 第 3 层:索引层到底做了什么
索引层最核心的是:
extensions/memory-core/src/memory/manager.ts
里面的 MemoryIndexManager 基本可以看作默认 memory-core 的“大脑”。
9.1 它管理什么
从类的字段看,它管理:
- 当前 agent 的 workspace
- 当前启用的 provider
- SQLite 数据库
- sources 集合
- 向量表 / FTS 表 / embedding cache
- 文件 watcher
- session 监听
- 定时 sync
- dirty 状态
对应位置:
extensions/memory-core/src/memory/manager.ts:77
9.2 它不是“每次搜索时临时现算”
很多零基础读者会误解成:
每次问问题时,系统实时把所有 memory 文件重新扫一遍,再重新做 embedding。
实际上不是。
它会维护一个本地索引数据库,并跟踪 dirty 状态;文件变化时,后续同步会更新索引。
这就是为什么这个类里会有:
dirtysync()- watcher
- interval sync
- session listener
这说明它是持续维护索引,而不是每轮现做。
10. 第 4 层:检索层怎么工作
检索的核心入口是:
extensions/memory-core/src/memory/manager.ts:315
这里的 search() 很值得细读,因为它基本就是 OpenClaw 默认 RAG 的核心算法流程。
10.1 它先做什么
收到 query 后:
- 清洗 query;
- 可能触发 session warm;
- 如果索引脏了,按配置异步 sync;
- 检查是否已有 indexed content;
- 初始化 embedding provider;
- 再开始真正 search。
这说明它不是“纯函数式查库”,而是带有运行时状态管理的检索器。
10.2 它支持三种模式
模式 A:FTS-only
如果没有 embedding provider,但全文检索可用:
- 只做关键词检索
- 还能提取关键词做更适合口语 query 的匹配
对应位置:
extensions/memory-core/src/memory/manager.ts:346
模式 B:Vector-only
如果 embedding 有,但 FTS 不可用或 hybrid 被关掉:
- 只做向量检索
模式 C:Hybrid
默认最重要的模式:
- 关键词检索一份候选
- 向量检索一份候选
- 再按权重合并
对应代码:
extensions/memory-core/src/memory/manager.ts:382extensions/memory-core/src/memory/manager.ts:398
这就是它的默认 RAG 精髓:
不是只靠 embedding 相似度,也不是只靠关键词命中,而是两者混合。
11. 为什么 hybrid search 很重要
零基础读者最容易误解的一点是:
“向量检索已经很智能了,为什么还要全文检索?”
因为两者擅长的事情不一样。
11.1 向量检索擅长
- 语义相近
- 改写表达
- 同义句
- 自然语言模糊回忆
比如:
- “上次说的那个网络配置”
- “我们之前讨论的部署方案”
11.2 关键词检索擅长
- 变量名
- 错误码
- 文件名
- 精确术语
- ID / token / 配置项
比如:
memorySearch.query.hybridSQLITE_READONLYagent:main:discord:
文档对 hybrid 的动机写得非常清楚:
docs/reference/memory-config.md:377
所以 hybrid 的价值是:
你既能“按意思找到”,也能“按关键字钉住”。
12. MMR 和 temporal decay 是什么
这是很多 RAG 系统论文里会出现的增强技巧,OpenClaw 也有。
12.1 MMR(Maximal Marginal Relevance)
MMR 解决的问题是:
- top-k 结果里全是差不多的片段;
- 结果重复度高;
- 看起来召回很多,实际信息量很低。
OpenClaw 文档说明:
- MMR 用来在相关性和多样性之间做平衡;
- 避免连续给模型塞重复内容。
见:
docs/reference/memory-config.md:441
12.2 temporal decay(时间衰减)
时间衰减解决的问题是:
- 很久以前写得很完整的一条旧笔记,容易压过最近的新变化;
- 结果导致模型拿旧信息当最新真相。
OpenClaw 提供了按时间衰减分数的能力:
- 越新的结果,越容易排在前面;
- evergreen 文件(如
MEMORY.md、非日期文件)不一定衰减。
见:
docs/reference/memory-config.md:494
所以从研究角度看,OpenClaw 的默认 memory 不是一个“只有 embedding 的幼年版 RAG”,而是一个已经考虑到:
- 相关性
- 精确匹配
- 去重
- 时间性
这些真实使用问题的系统。
13. 工具层:memory_search 和 memory_get
这是模型真正“用记忆”的接口。
核心文件:
extensions/memory-core/src/tools.ts
13.1 memory_search
它的描述非常有意思:
- 在回答“之前的工作、决定、日期、人物、偏好、todo”这类问题前,必须先做 recall。
对应位置:
extensions/memory-core/src/tools.ts:24
这句话几乎相当于在系统里写了一个原则:
涉及过去信息时,不要凭空猜你还记得,先查 memory。
这是非常典型的 RAG 思想。
13.2 memory_get
它不是用来“搜索”的,而是用来:
- 已经知道某个 path 了;
- 再读具体行;
- 精准拉取需要的片段。
对应位置:
extensions/memory-core/src/tools.ts:81
这说明 OpenClaw 默认工具层是两阶段设计:
memory_search:粗召回memory_get:精读取
这比“一步直接把整份文件塞进 prompt”更省上下文。
14. 聊天时记忆到底在什么时候进入系统
你可以把默认聊天里的记忆接入理解成下面这条链:
- 用户发消息
- 运行时判断当前 agent 的 memory 配置
- 获取 active memory runtime / manager
- 在需要 recall 的地方调用
memory_search - 返回 snippet、path、line range、score 等结果
- 模型再决定是否进一步
memory_get - 检索出来的内容进入本轮上下文
运行时桥接入口:
src/plugins/memory-runtime.ts:16
这里能看到:
- 先确保 memory runtime 存在;
- 再调用
getMemorySearchManager()。
这说明 memory 不是写死在聊天主循环里的某一个 if,而是通过插件 runtime 暴露出来的。
14.1 网关启动时会做什么
src/gateway/server-startup-memory.ts 说明:
- 启动时会遍历 agent;
- 看 memory config 是否存在;
- 若是 QMD 后端则提前把 manager arm 起来。
对应位置:
src/gateway/server-startup-memory.ts:9
这说明 startup 阶段就会考虑 memory backend,但不是每个 backend 都一样处理。
15. memory flush 是什么,为什么非常关键
这是理解 OpenClaw 长期记忆时最容易忽视、但非常关键的一层。
15.1 先理解 compaction
大模型上下文窗口有限,聊久了以后,旧对话不能无限堆下去,所以系统会:
- 压缩(compact)
- 总结旧内容
- 避免超过上下文上限
问题来了:
如果马上要 compact,哪些真正值得永久保留的信息,应该在压缩前先写入长期记忆?
15.2 OpenClaw 的做法
它会在“接近 compaction”时,触发一个静默的、agentic 的额外回合,提醒模型:
- 现在快 compact 了;
- 如果有值得长期保留的内容,赶紧写进 memory 文件;
- 一般情况回复
NO_REPLY,用户不会看到。
文档说得很清楚:
docs/concepts/memory.md:53
15.3 它不是数据库 flush
这里非常容易误解。
“memory flush” 这个名字听起来像:
- 把内存缓存刷进数据库。
但在 OpenClaw 的默认语境里,它更接近:
在上下文快要丢失前,提醒模型把重要信息写成长期记忆文件。
也就是“认知层的 flush”,不是“存储引擎层的 flush”。
15.4 代码上怎么判断是否该 flush
核心逻辑在:
src/auto-reply/reply/memory-flush.ts:53
它会根据:
- 当前会话 token 数
- context window 大小
- reserveTokensFloor
- softThresholdTokens
来判断是否触发。
并且:
- 同一轮 compaction 周期只触发一次;
- 读写受 workspace 可写性影响。
这说明它不是“每轮都写 memory”,而是接近风险点时才提醒。
16. session memory 是什么,为什么默认不是它
OpenClaw 还支持一个容易和长期记忆混淆的东西:
- session memory
文档和配置里能看到:
memorySearch.experimental.sessionMemorysources: ["memory", "sessions"]
对应:
src/agents/memory-search.ts:33docs/reference/memory-config.md:613
16.1 它和默认 memory 的区别
默认长期记忆:
- 来源是 Markdown memory 文件;
- 比较“人为整理”;
- 更 durable。
session memory:
- 来源是 session transcript;
- 更接近“历史对话日志检索”;
- 更新异步;
- 默认是实验性的、可选的。
16.2 为什么这点重要
因为你研究一个系统时,必须分清:
- 它是在检索“整理过的长期记忆”
- 还是在检索“原始聊天记录”
这两者虽然都叫 memory,但质量和用途不同。
17. memory-lancedb 为什么和默认路线不一样
OpenClaw 仓库里还有一条很容易让人误会的路线:
extensions/memory-lancedb/index.ts
这个插件和默认 memory-core 是两种不同思路。
17.1 memory-core 思路
- 文件为真相源
- 先写 Markdown
- 再建立索引和检索层
- memory_search / memory_get 服务于这些文件
17.2 memory-lancedb 思路
- 记忆直接作为 entry 存进 LanceDB
- 提供
memory_store/memory_recall/memory_forget - 支持 auto-recall 和 auto-capture 生命周期钩子
对应位置:
extensions/memory-lancedb/index.ts:282extensions/memory-lancedb/index.ts:304extensions/memory-lancedb/index.ts:536extensions/memory-lancedb/index.ts:564
17.3 它更像什么
它更像很多常见 AI assistant memory 插件:
- 每条记忆是数据库记录
- 每次回忆是 semantic recall
- 每次存储是结构化 entry
所以如果你要比较两种“长期记忆”范式:
| 路线 | 真相源 | 更像什么 |
|---|---|---|
memory-core | Markdown 文件 | 可读可编的个人知识库 |
memory-lancedb | 向量数据库 entry | 数据库型 AI 记忆插件 |
18. 为什么默认路线更适合“个人聊天 AI”
如果你想做自己的聊天 AI,而不是做企业级 RAG 平台,OpenClaw 默认路线其实很有吸引力。
18.1 优点
优点 1:可解释性强
你知道系统“记住了什么”。
因为都在:
MEMORY.mdmemory/*.md
里。
优点 2:人能干预
你可以:
- 人工修改
- 删除错误记忆
- 合并重复内容
- 重写长期总结
这对长期陪伴型 AI 很重要,因为自动抽取难免出错。
优点 3:适合小而持续的知识积累
个人使用时,很多有价值的信息其实不是海量文档,而是:
- 最近在做什么
- 你的偏好
- 你和谁在合作
- 你上次做了什么决定
这种信息用 Markdown 管理非常合适。
18.2 代价
代价 1:它不是现成的企业知识库平台
如果你要的是:
- 百万文档导入
- 复杂权限控制
- 多租户知识隔离
- 大规模 ingestion pipeline
那默认路线不是为这个而生的。
代价 2:需要模型“会写记忆”
因为默认路线依赖:
- 该写的时候真的写
- 写得像人能看懂的笔记
- 该升级到
MEMORY.md时能够整理
也就是说,它对 agent 行为质量有要求。
19. 零基础最容易误解的 10 件事
误解 1:有 memory 就等于模型永久记住了
错。永久存在的是磁盘文件,不是模型脑子。
误解 2:RAG = 向量数据库
错。向量数据库只是 retrieval 的一种基础设施。RAG 的本质是“先检索,再生成”。
误解 3:OpenClaw 默认 memory 就是 LanceDB
错。默认是 memory-core,memory-lancedb 是另一条插件路线。
误解 4:memory flush 是数据库刷盘
错。这里更接近“在上下文快丢前,提醒模型把重要信息写入长期记忆文件”。
误解 5:只要开启 embeddings,效果就一定好
不一定。召回质量还受:
- chunking
- query 改写
- BM25
- MMR
- 时间衰减
- 记忆文件写法
影响。
误解 6:session memory 和长期记忆是一回事
不是。一个偏 transcript 检索,一个偏长期文件知识。
误解 7:RAG 检索到的就是最终答案
不是。检索结果只是材料,真正回答仍然是模型生成。
误解 8:检索系统和记忆写入系统是一回事
不是。一个负责找,一个负责存;OpenClaw 把这两件事拆得很清楚。
误解 9:文件路线太原始,不算现代 RAG
不对。文件是真相源并不妨碍上面构建现代检索层。很多真实系统恰恰喜欢“原始数据可读、索引可重建”。
误解 10:只要 recall 了,模型就会严格服从记忆
也不一定。模型只是“看到了材料”,不是“机械执行数据库事实”。提示词、工具策略、上下文拥挤程度都影响最终表现。
20. 如果你想深入研究,建议按什么顺序读
第一遍:先建立大局观
先读文档:
docs/concepts/memory.mddocs/reference/memory-config.md
目标:
- 分清 memory 文件、memory tools、memory flush、hybrid search、session memory。
第二遍:看配置层
再读:
src/agents/memory-search.ts
目标:
- 理解一个 memory/RAG 系统有哪些核心参数;
- 看默认值反映了什么产品选择。
第三遍:看工具层
extensions/memory-core/src/tools.ts
目标:
- 理解模型怎么调用 memory_search / memory_get;
- 看检索结果以什么格式回给模型。
第四遍:看检索与索引核心
extensions/memory-core/src/memory/manager.ts
目标:
- 理解 search / sync / watcher / cache / fallback;
- 真正进入 RAG 引擎内部。
第五遍:看运行时接入
src/plugins/memory-runtime.tssrc/gateway/server-startup-memory.tssrc/auto-reply/reply/memory-flush.tssrc/auto-reply/reply/agent-runner-memory.ts
目标:
- 理解 memory 何时接入聊天;
- 理解 compaction 附近的 memory flush。
第六遍:看另一种设计路线
extensions/memory-lancedb/index.ts
目标:
- 对比文件型 memory 和数据库型 memory。
21. 你可以把它类比成什么系统
如果你想快速形成直觉,可以这样类比:
类比 1:Obsidian + 向量检索 + AI 助手
- Markdown 是笔记
- 索引层是搜索增强
- 模型通过工具查笔记
类比 2:程序员版的“可写可查的第二大脑”
- 日志写在每日文件
- 稳定知识沉淀到
MEMORY.md - 回答时按语义和关键词召回
类比 3:带长期外部记忆的 agent
- 模型本身会忘
- 外部磁盘不忘
- 回答前先去外部记忆里取材料
22. 一个最重要的研究视角:OpenClaw 在解决什么问题
我觉得它真正想解决的不是“让大模型无所不知”,而是:
让一个长期运行的 agent,能够把值得保留的东西沉淀下来,并在以后真正用得上。
这背后至少有 4 个现实约束:
- 上下文窗口有限:聊久了必须 compact;
- 模型本身不会跨会话永久记住:必须写到外部;
- 纯关键词检索不够自然:需要 embedding / hybrid;
- 纯数据库黑盒不可控:人需要看得见、改得动。
所以你会发现 OpenClaw 默认路线是一个相当务实的折中:
- 存储层:人类可读
- 检索层:现代 RAG
- 集成层:agent 工具调用
- 生命周期层:compaction 前静默写记忆
这个组合很有“工程味”。
23. 最后给你一个简洁总结
用最简单的话总结 OpenClaw 默认长期记忆
OpenClaw 默认不是“把记忆藏进数据库里”,而是:
- 把重要信息写成 Markdown;
- 给这些 Markdown 建索引;
- 回答问题前通过
memory_search找相关片段; - 必要时再
memory_get精读; - 在上下文快压缩前,通过 memory flush 提醒模型把该保留的内容写下来。
用更学术一点的话总结
它是一个:
以文件为真相源、以混合检索为召回机制、以 agent 工具调用为接口、以 compaction 前记忆沉淀为生命周期补偿机制的长期记忆系统。
为什么值得研究
因为它不是一个“玩具向量搜索 demo”,也不是一个“只会喊 embeddings 的营销系统”。
它把真实长期运行 agent 会遇到的几个难点都摆上桌面了:
- 记忆写在哪里
- 哪些记忆值得保留
- 怎么在聊天里取回来
- 怎么避免结果重复或过时
- 怎么在上下文即将溢出前做记忆沉淀
这就是它最有研究价值的地方。
24. 关键代码与文档索引(建议收藏)
- 概念总览:
docs/concepts/memory.md - 配置总览:
docs/reference/memory-config.md - 配置解析:
src/agents/memory-search.ts - memory runtime 桥接:
src/plugins/memory-runtime.ts - gateway 启动 memory:
src/gateway/server-startup-memory.ts - 默认 memory 工具:
extensions/memory-core/src/tools.ts - 默认索引/检索核心:
extensions/memory-core/src/memory/manager.ts - memory flush 触发条件:
src/auto-reply/reply/memory-flush.ts - memory flush 运行时集成:
src/auto-reply/reply/agent-runner-memory.ts - transcript 注入:
src/gateway/server-methods/chat-transcript-inject.ts - LanceDB 路线:
extensions/memory-lancedb/index.ts
25. 你下一步最值得继续深挖的 5 个问题
- memory_search 返回结果的格式具体是什么?
- sync 什么时候触发,dirty 状态如何消除?
- chunking 和 overlap 对召回质量的影响是什么?
- MMR / temporal decay 在真实长期使用里能改善多少?
- 如果把它改造成“更像我的私人聊天 AI”,最该改写入策略还是检索策略?
如果你愿意,下一轮可以继续看第二篇:
《OpenClaw memory-core 源码精读:从 query 到 recall result 到 prompt 的完整链路》
这一篇会按函数级别,一段一段带你读。