本文是「从零搭建私人 RAG 实战」专栏的第一篇,用Markdown持续记录项目的知识。
前言:从"加载全部制度"到"按需获取知识"
在「Claude Code 需求拆解实战:从直觉到推演(SDD 教程)」专栏的《09-不要只让 AI 进入 Plan 模式,要先给 AI 一套工程制度》中,我们已经建立了一个重要前提:使用 Claude Code 开发项目时,不能只把当前需求交给 AI,还要让 AI 了解项目已经形成的技术决策和业务决策。
这能避免 AI 每次都从零猜测项目规则。例如:
- 为什么某类操作需要二次确认;
- 为什么某项业务规则只适用于特定用户;
- 为什么某个技术方案需要遵守明确的使用边界。
但是,当这些决策全部在 Claude Code 冷启动时加载,新的问题也会逐渐出现。
假设项目已经积累了几十条甚至上百条规则,而当前任务只是修改一个按钮样式。此时仍然加载所有业务背景、技术选型和历史方案,会带来三个明显问题:
- 冷启动变慢:AI 需要先读取大量文档,才能开始处理当前任务;
- Token 消耗增加:与任务无关的内容也会进入上下文;
- 有效信息被稀释:真正相关的规则可能淹没在大量背景资料中。
问题并不在于我们记录了太多知识,而在于获取知识的方式仍然是"全部加载"。
这个专栏将使用 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
为什么不允许每个模块分别实现通用校验?
项目中的通用校验规则放在哪里?
模块特有规则也必须放进共享层吗?
如何避免多个模块的校验规则不一致?
Feature、Assessment、Decision 和 Rationale 从不同角度描述同一问题,为 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)"
}
这些元数据主要用于:
- 过滤:只检索某个系统或某类决策;
- 隔离:防止多个项目中语义相似但规则不同的内容相互干扰;
- 溯源:返回原始文件和文档标题;
- 维护:根据稳定标识更新或重建指定文档的索引。
建议至少保存以下字段:
| 字段 | 作用 |
|---|---|
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-A、PROJECT-B、PROJECT-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 知识库的输入层准备:
- 明确了冷启动加载全部工程制度带来的速度和 Token 问题;
- 确定使用 RAG 按任务语义检索相关决策;
- 使用 BADR 记录业务背景、业务规则和约束;
- 使用 FADR 记录技术方案评估、决定和理由;
- 解释了
Feature → Assessment → Decision → Rationale模板对阅读、分块、检索和追溯的作用; - 使用 YAML Frontmatter 保存文档元数据;
- 建立了决策文档写作与自检标准。
我们希望逐步把 Claude Code 的知识获取方式从:
text
每次启动
↓
加载全部技术决策和业务决策
↓
开始处理任务
改造成:
text
收到任务
↓
检索相关决策
↓
只加载必要上下文
↓
根据项目事实完成任务
总结
一个可用的私人 RAG 知识库,不是从向量数据库开始的,而是从认真记录第一条项目决策开始的。
BADR 保存业务规则背后的原因和边界,FADR 保存技术方案的评估过程和使用约束。固定的 Markdown 结构让每条决策都具有相对完整的语义,YAML Frontmatter 则让程序能够对文档进行识别、过滤和溯源。
RAG 不会替我们创造项目知识。它解决的是:
当知识越来越多时,不再把所有内容都塞进 AI 的上下文,而是只找到当前任务真正需要的那一部分。
在继续之前,请先阅读本专栏的第二篇文章《从零搭建私人 RAG 实战:本地开发环境准备》,完成技术栈了解和开发环境配置。
环境准备就绪后,我们将从 Markdown 文档加载和文本分块开始,逐步实现 CorpRAG 的索引链路。