从零搭建私人 RAG 实战:用 Markdown 沉淀技术决策与业务决策

本文是「从零搭建私人 RAG 实战」专栏的第一篇,用Markdown持续记录项目的知识。

前言:从"加载全部制度"到"按需获取知识"

在「Claude Code 需求拆解实战:从直觉到推演(SDD 教程)」专栏的《09-不要只让 AI 进入 Plan 模式,要先给 AI 一套工程制度》中,我们已经建立了一个重要前提:使用 Claude Code 开发项目时,不能只把当前需求交给 AI,还要让 AI 了解项目已经形成的技术决策和业务决策。

这能避免 AI 每次都从零猜测项目规则。例如:

  • 为什么某类操作需要二次确认;
  • 为什么某项业务规则只适用于特定用户;
  • 为什么某个技术方案需要遵守明确的使用边界。

但是,当这些决策全部在 Claude Code 冷启动时加载,新的问题也会逐渐出现。

假设项目已经积累了几十条甚至上百条规则,而当前任务只是修改一个按钮样式。此时仍然加载所有业务背景、技术选型和历史方案,会带来三个明显问题:

  1. 冷启动变慢:AI 需要先读取大量文档,才能开始处理当前任务;
  2. Token 消耗增加:与任务无关的内容也会进入上下文;
  3. 有效信息被稀释:真正相关的规则可能淹没在大量背景资料中。

问题并不在于我们记录了太多知识,而在于获取知识的方式仍然是"全部加载"。

这个专栏将使用 RAG(Retrieval-Augmented Generation,检索增强生成)改造这套工作流:

text 复制代码
用户提出任务
    ↓
根据任务语义检索相关决策
    ↓
只返回当前任务需要的知识片段
    ↓
Claude Code 根据项目事实分析和实现

工程制度仍然负责沉淀知识,RAG 则负责在正确的时间找到正确的知识。

本篇暂时不急着编写向量检索代码,而是先完成 RAG 最基础的输入层:使用 Markdown 编写结构清晰、可以独立理解、适合后续分块和检索的技术决策与业务决策文档。

关于技术栈介绍和本地开发环境配置,请参考本专栏的第二篇文章《从零搭建私人 RAG 实战:本地开发环境准备》。


一、为什么先写文档,而不是先选向量数据库?

RAG 的检索质量首先取决于原始知识的质量。

如果文档只有零散结论:

md 复制代码
项目统一使用方案 A。

那么即使检索到了这句话,AI 仍然无法判断:

  • 方案 A 用于解决什么问题?
  • 它适用于哪些模块?
  • 为什么没有选择方案 B?
  • 这是建议还是必须遵守的规则?
  • 这条决策当前是否仍然有效?

更适合进入知识库的写法应该同时包含决策点、候选方案、最终决定和选择原因:

md 复制代码
## FADR #001 - 示例技术方案

### Feature(决策点)
示例项目应该如何解决目标问题?

### Assessment(评估)
对比方案 A 与方案 B 的实现成本、维护难度和适用范围。

### Decision(决定)
示例项目采用方案 A,并将其使用范围限制在指定模块。

### Rationale(理由)
方案 A 更符合当前约束;明确使用边界可以避免其他模块产生不必要的耦合。

两段内容都提到了方案 A,但后一段才是一条完整、可执行、可追溯的项目知识。

因此,建设私人 RAG 知识库的第一步不是把所有 Markdown 写入向量数据库,而是先保证每条知识脱离整份文档后仍然能够被理解。


二、项目使用 BADR 和 FADR 记录什么?

CorpRAG 将项目决策分成两类:

类型 全称 主要记录内容
BADR Business Architecture Decision Record 业务流程、业务边界、产品分类、合规规则等业务决策
FADR Feature Assessment Decision Record 框架、状态管理、组件设计、网络层等技术决策

二者都不是普通的项目说明文档。

普通说明文档经常只描述"现在是什么样",而决策记录还要解释"为什么变成这样"以及"以后修改时不能破坏什么"。

1. BADR:保存容易被遗忘的业务原因

BADR 关注业务问题,例如:

  • 为什么某类操作需要额外审核;
  • 为什么不同用户群体适用不同规则;
  • 某项业务规则适用于哪些场景,以及有哪些例外。

BADR 的价值在于保存代码背后的业务原因。代码可以告诉我们"系统做了什么",但通常无法完整解释"业务为什么要求这样做"。

2. FADR:保存技术选型的评估过程

FADR 关注技术问题,例如:

  • 为什么选择方案 A 而不是方案 B;
  • 某个基础能力应该在哪一层实现;
  • 不同模块之间通过什么方式通信;
  • 一项技术能力的使用范围和边界是什么;
  • 在性能、维护成本和开发效率之间如何取舍。

技术决策不能只留下最终依赖名称。框架会升级,成员会变化,如果没有记录候选方案和选择原因,后续开发者很容易在不了解约束的情况下重新做一遍选型,甚至破坏原有架构。


三、BADR 业务决策文档结构模板

BADR 与 FADR 使用相同的 Feature → Assessment → Decision → Rationale 结构,区别只在于记录的决策类型:BADR 面向业务问题,FADR 面向技术问题。

md 复制代码
---
title: 项目名称业务架构决策记录(BADR)
slug: project-name-badr
date: 2026-07-26
tags: [BADR, 业务架构]
---

# 业务架构决策记录(BADR)

## 项目背景

- 项目名称:
- 业务类型:
- 目标用户:
- 核心业务域:

---

## BADR #001 - 决策标题

### Feature(决策点)
这次需要解决什么业务问题?

### Assessment(评估)
| 方案 | 优点 | 缺点 |
| --- | --- | --- |
| 方案 A |  |  |
| 方案 B |  |  |

### Decision(决定)
最终采用什么业务方案?需要遵守哪些明确规则、适用范围和边界?

### Rationale(理由)
为什么选择该方案?为什么没有选择其他方案?

BADR 各部分的作用

部分 需要回答的问题
Feature 这次需要解决什么业务问题?
Assessment 评估过哪些业务方案?各自有什么优缺点?
Decision 最终必须遵守的业务规则、适用范围和边界是什么?
Rationale 为什么选择这个方案,而没有选择其他方案?

并不是每条 BADR 都必须写得很长,但"决策点、评估、决定、理由"最好能够独立成立。


四、FADR 技术决策文档结构模板

一条基础 FADR 使用 Feature → Assessment → Decision → Rationale 结构:

md 复制代码
---
title: 项目名称技术决策方案(FADR)
slug: project-name-fadr
date: 2026-07-26
tags: [FADR, 技术架构]
---

# 技术决策方案(FADR)

## 项目背景

- 项目类型:
- 目标平台:
- 核心约束:

---

## FADR #001 - 决策标题

### Feature(决策点)
这次需要解决什么技术问题?

### Assessment(评估)
| 方案 | 优点 | 缺点 |
| --- | --- | --- |
| 方案 A |  |  |
| 方案 B |  |  |

### Decision(决定)
最终采用什么方案?需要遵守哪些明确规则?

### Rationale(理由)
为什么选择该方案?为什么没有选择其他方案?

FADR 案例:基础能力实现位置

下面用一个不绑定具体框架的示例说明如何记录技术决策:

md 复制代码
## FADR #001 - 通用校验能力的实现位置

### Feature(决策点)
多个模块都会使用的输入校验能力应该在哪里实现?

### Assessment(评估)
| 方案 | 优点 | 缺点 |
| --- | --- | --- |
| 每个模块分别实现 | 修改灵活、互不影响 | 规则容易重复和不一致 |
| 在共享基础层统一实现 | 规则集中、便于复用和测试 | 需要定义稳定的调用接口 |

### Decision(决定)
通用校验规则由共享基础层统一提供;模块特有规则仍保留在各自模块中。

### Rationale(理由)
统一通用规则可以减少重复实现,同时保留模块特有规则的独立性,避免共享层承担过多业务职责。

如果 AI 只读到"校验能力统一实现",它仍然无法判断哪些规则应该共享。完整的 FADR 会同时说明统一范围、保留边界和选择理由。

FADR 案例:模块通信边界

技术决策不仅要选择通信方式,还要明确使用边界:

md 复制代码
## FADR #002 - 模块间通信方式

### Feature(决策点)
模块之间应该如何交换数据并触发操作?

### Assessment(评估)
| 方案 | 优点 | 缺点 |
| --- | --- | --- |
| 模块直接访问彼此内部状态 | 实现直接 | 耦合较高,内部变化容易影响调用方 |
| 通过公开接口通信 | 边界清晰、便于测试 | 需要提前设计接口 |
| 使用全局共享对象 | 接入简单 | 数据来源和修改路径难以追踪 |

### Decision(决定)
模块之间只通过公开接口通信,不直接访问其他模块的内部状态。

### Rationale(理由)
明确通信边界可以降低模块耦合,使内部实现能够独立调整,并提高测试和维护的可控性。

这类记录比只写"使用某种通信方案"更有价值,因为真正影响代码质量的是方案的适用范围和使用边界。


五、Feature → Assessment → Decision → Rationale 格式模板有什么作用?

Markdown 中的固定格式不是为了让文档显得正式,而是为了同时服务开发者、检索系统和 AI。

1. Feature:确定当前片段讨论的问题

Feature 用一句话定义决策点。

md 复制代码
### Feature(决策点)
多个模块共用的输入校验能力应该在哪里实现?

它让检索系统知道这条记录在回答什么问题,也让开发者避免在同一条记录中混入多个无关主题。

2. Assessment:保留被否决的方案

Assessment 记录候选方案及其优缺点。

只记录最终结果会丢失大量背景。例如"统一实现校验能力"并不能解释为什么不允许各模块分别实现。保留评估过程后,未来重新选型时可以判断原来的限制是否仍然存在,而不必重复讨论已经验证过的问题。

3. Decision:形成可以执行的项目规则

Decision 必须明确,尽量避免"建议""可以考虑"等模糊表达。

md 复制代码
### Decision(决定)
通用校验规则由共享基础层统一提供,模块特有规则保留在各自模块中。

这一部分通常是 AI 在编码前最需要获取的内容。

4. Rationale:让 AI 理解规则背后的目标

Rationale 解释选择原因。

当新需求与旧规则发生冲突时,AI 不能只机械地重复结论,还需要根据原始目标判断应该继续遵守、调整还是重新评估。理由正是完成这种判断所需的上下文。

5. 为 Markdown 分块提供自然边界

后续索引程序会把长文档切成多个 Chunk。稳定的二级、三级标题可以作为天然边界:

text 复制代码
## FADR #001 - 通用校验能力的实现位置
├── ### Feature
├── ### Assessment
├── ### Decision
└── ### Rationale

理想的检索单元不是随机的 500 个字符,而是一条语义相对完整的决策。固定模板能够降低两条决策被混入同一片段,或者决定与理由被完全拆开的概率。

6. 提高不同问法的召回概率

用户可能用不同方式询问同一条规则:

text 复制代码
为什么不允许每个模块分别实现通用校验?
项目中的通用校验规则放在哪里?
模块特有规则也必须放进共享层吗?
如何避免多个模块的校验规则不一致?

FeatureAssessmentDecisionRationale 从不同角度描述同一问题,为 Embedding 模型提供了更丰富的语义信息,从而提高相关问题的命中概率。

7. 让检索结果可以追溯和复核

每条记录具有稳定的类型、编号和来源,检索结果可以返回:

text 复制代码
结论:模块之间只通过公开接口通信。
来源:data/PROJECT-FADR.md
决策:FADR #002

AI 的回答不再只是一个无法验证的结论,开发者可以根据来源回到原文复核完整背景。


六、使用 YAML Frontmatter 保存文档元数据

除了正文结构,每份 Markdown 顶部还可以使用 YAML Frontmatter 描述文档:

yaml 复制代码
---
title: 项目名称技术决策方案(FADR)
slug: project-name-fadr
date: 2026-07-26
tags: [FADR, 技术架构]
---

正文负责保存给人阅读的知识,Frontmatter 负责保存给程序处理的元数据。

项目后续可以使用 gray-matter 解析这些字段,并将它们转换成 LangChain Document 的 metadata:

ts 复制代码
{
  system: "project",
  docType: "FADR",
  source: "data/PROJECT-FADR.md",
  title: "项目名称技术决策方案(FADR)"
}

这些元数据主要用于:

  1. 过滤:只检索某个系统或某类决策;
  2. 隔离:防止多个项目中语义相似但规则不同的内容相互干扰;
  3. 溯源:返回原始文件和文档标题;
  4. 维护:根据稳定标识更新或重建指定文档的索引。

建议至少保存以下字段:

字段 作用
title 描述文档主题
slug 提供稳定、可读的文档标识
date 记录创建或更新日期
tags 标记决策类型、系统和业务领域

七、如何组织项目中的决策文档?

CorpRAG 当前按照"系统 + 决策类型"组织 Markdown:

text 复制代码
data/
├── PROJECT-A-BADR.md
├── PROJECT-A-FADR.md
├── PROJECT-B-BADR.md
├── PROJECT-B-FADR.md
├── PROJECT-C-BADR.md
└── PROJECT-C-FADR.md

其中:

  • PROJECT-APROJECT-BPROJECT-C 代表不同项目或业务系统;
  • BADR 代表业务决策;
  • FADR 代表技术决策。

这种命名方式可以直接推导出文档元数据:

ts 复制代码
{
  system: "project-a",
  docType: "BADR",
  source: "data/PROJECT-A-BADR.md"
}

新增系统时,可以先创建:

text 复制代码
data/NEW-BADR.md
data/NEW-FADR.md

然后分别记录业务决策和技术决策。

一条决策只解决一个核心问题

不要把状态管理、路由、请求层和组件规范全部写进同一个 FADR。主题过多会降低检索精度,也不利于后续独立更新。

标题中保留稳定编号

md 复制代码
## FADR #003 - 移动端适配方案

稳定编号便于引用、检索、评审和追踪。标题可以调整,但编号不应随意复用。

不要只复制代码

代码示例可以作为证据,但不能代替决策说明。代码会变化,而决策记录需要说明约束和目标。

记录规则的适用范围

同一条规则可能只适用于某个系统、产品线或渠道。缺少范围说明,会让 AI 把局部方案误认为整个公司的统一规范。


八、写完一条决策后如何自检?

把决策放入知识库前,可以使用下面的检查清单:

内容检查

  • 这条记录属于 BADR 还是 FADR?
  • 标题是否能准确描述一个核心问题?
  • 是否说明了触发决策的背景?
  • 是否记录了评估过的候选方案?
  • Decision 是否明确、可执行?
  • 是否解释了选择理由?
  • 是否写清适用范围、例外和边界?

检索检查

  • 脱离整份文档后,这一条记录是否仍然容易理解?
  • 标题和正文是否包含用户可能使用的关键词?
  • 决策与理由是否可能被合理地分到同一个 Chunk?
  • 是否能够返回系统、类型、编号和来源?
  • 是否混入了与当前决策无关的其他主题?

维护检查

  • 文档是否有 Frontmatter?
  • 决策编号是否唯一?
  • 规则变化时,是更新原决策还是新增一条替代决策?
  • 已失效规则是否明确标记状态,而不是直接删除历史原因?

九、本章完成了什么?

这一篇完成了私人 RAG 知识库的输入层准备:

  1. 明确了冷启动加载全部工程制度带来的速度和 Token 问题;
  2. 确定使用 RAG 按任务语义检索相关决策;
  3. 使用 BADR 记录业务背景、业务规则和约束;
  4. 使用 FADR 记录技术方案评估、决定和理由;
  5. 解释了 Feature → Assessment → Decision → Rationale 模板对阅读、分块、检索和追溯的作用;
  6. 使用 YAML Frontmatter 保存文档元数据;
  7. 建立了决策文档写作与自检标准。

我们希望逐步把 Claude Code 的知识获取方式从:

text 复制代码
每次启动
  ↓
加载全部技术决策和业务决策
  ↓
开始处理任务

改造成:

text 复制代码
收到任务
  ↓
检索相关决策
  ↓
只加载必要上下文
  ↓
根据项目事实完成任务

总结

一个可用的私人 RAG 知识库,不是从向量数据库开始的,而是从认真记录第一条项目决策开始的。

BADR 保存业务规则背后的原因和边界,FADR 保存技术方案的评估过程和使用约束。固定的 Markdown 结构让每条决策都具有相对完整的语义,YAML Frontmatter 则让程序能够对文档进行识别、过滤和溯源。

RAG 不会替我们创造项目知识。它解决的是:

当知识越来越多时,不再把所有内容都塞进 AI 的上下文,而是只找到当前任务真正需要的那一部分。

在继续之前,请先阅读本专栏的第二篇文章《从零搭建私人 RAG 实战:本地开发环境准备》,完成技术栈了解和开发环境配置。

环境准备就绪后,我们将从 Markdown 文档加载和文本分块开始,逐步实现 CorpRAG 的索引链路。

相关推荐
用户37899822121281 小时前
别再「凭感觉」写代码了:我用 Qoder 花一天时间,从 0 到 1 真正掌握了 Vibe Coding(附完整踩坑实录)
后端
触底反弹1 小时前
💡 React 父子组件通信:一个进度条教会我的 5 件事
前端·react.js·面试
咩咩啃树皮1 小时前
第44篇:Vue3侦听器完全精讲——watch与watchEffect区别、深度监听、异步业务落地
前端·javascript·vue.js
码农进化录1 小时前
Java 程序员的 AI 进化论 | AI 加 Postman 跑接口测试,省了三天活
java·后端·openai
Python私教1 小时前
前端转 AI 全栈:别只做聊天框,SSE 与审批流才是分水岭
前端·人工智能
怕浪猫2 小时前
第8章 前端交互与可视化:构建Agent的用户界面
aigc·openai·ai编程
旺仔学长 哈哈2 小时前
基于SpringBoot的在线招聘测评系统的设计与实现----附源码35253+数据库文档
数据库·spring boot·后端·在线招聘
晓得迷路了2 小时前
栗子前端技术周刊第 139 期 - Nuxt 4.5、Vue 3.6 RC、Angular 发布节奏...
前端·javascript·vue.js
Ivanqhz2 小时前
Rust #[derive(Serialize)]浅析
开发语言·后端·rust