- T6 (Issue6): RerankConfig(enabled/model=BAAI/bge-reranker-v2-m3/device)
- rag-layer-design §6.3 Rerank 精排:窗口=RRF top-10、候选≤top_k 跳过、
故障降级 RRF 原序;原 §6.3-6.6 顺延 6.4-6.7
- config-design §5 新增 rerank 段;design §5.5 检索策略加 rerank
- T11 (OV2): EmbeddingConfig.model 默认 bge-small-zh-v1.5 → BAAI/bge-m3(日文语料)
- rag-layer-design 选型表/依赖表/manifest/流程图同步 + 新增 §2.3 日文样本验证
- design.md / implementation-plan 4.3 / config-design embedding 同步
- 新增 test_rag_design_consistency.py 一致性门禁(6 用例防文档漂移)
- TDD: RED(默认模型仍旧 + rerank 字段不存在)→ GREEN → 全量 198 passed / 100.00%(996 stmts/252 br)
25 KiB
RAG 基础设施层详细设计
版本: v1.0 | 日期: 2026-07-30 | 状态: 初版
本文档是
docs/design.md第5章(RAG 基础设施层)的详细展开。
目录
1. 定位与职责
RAG 不是独立 Agent,而是 Parser 与 Writer/Impact/QA 之间的基础设施层。它负责:
- 接收 Parser 解析后的规则文档,构建规则手册(持久化存储)
- 为 Writer / Impact / QA 提供统一规则检索入口
- 管理规则手册的版本生命周期(更新、回退、追溯)
1.1 存储内容分类
RAG 层存储三类内容,分类在 Parser 解析时完成:
| 类型 | 内容 | 检索方 |
|---|---|---|
| Type A: 写入规则 | 记入规则、图表规则、字体/格式规范 | Writer Agent(每章生成时) |
| Type B: 设计规则 | 架构约束、安全要求、设计方针 | Impact Agent(关联推理时)+ Writer |
| Type C: 参考设计文档(可选) | 过往概要设计书、设计决策记录 | Impact Agent(改修场景参考) |
说明:Type C 为可选增强,初期聚焦 Type A + Type B。Type C 文档若纳入,由用户显式上传到「参考文档」分类。
2. 技术选型
| 选型点 | 决策 | 说明 |
|---|---|---|
| 向量数据库 | Chroma(v1 唯一) | 初期用 Chroma(轻量本地,嵌入进程,Docker 部署最简);通过 Storage Adapter 抽象保留接口,Qdrant 切换为 v2 预留(Scope 缩减裁定,T5 整改) |
| Embedding 模型 | bge-m3(本地运行) | 多语言模型(中/日/英),适配实际日文语料(OV2/T11);统一配置 config/rag.yaml 的 embedding.model(见 docs/config-design.md §5);稠密向量 1024 维,CPU 可跑 |
| Rerank 模型 | bge-reranker-v2-m3(v1 引入,I6/T6) | 精排阶段使用;统一配置 config/rag.yaml 的 rerank.model;默认启用,可通过 rerank.enabled=false 关闭降级为纯 RRF 结果 |
| 实现方式 | 手写实现(不引入 LangChain/LlamaIndex) | chromadb + rank_bm25 + sentence-transformers 直接实现;依赖轻、可控性强、与现有轻量技术栈一致 |
2.1 为什么手写而非框架
| 维度 | 分析 |
|---|---|
| 检索效果 | 相同(Embedding + BM25 + RRF 为标准算法,框架也调用同样的库) |
| 分割质量 | 本项目需要「按格式适配分割」(Word标题层级/Excel规则块/PPT页),框架无现成方案,仍需自写 |
| 可追溯性 | 手写可完美对接 Provenance 体系(source_uri / headings) |
| 依赖负担 | 轻(3 个库);框架引入 langchain 全家桶 ~100+ 传递依赖 |
| 调试维护 | 手写直接可控;框架封装层级深,出问题难定位 |
| 代码量 | 向量+BM25+RRF 约 100~200 行,不值得为此引入重框架 |
2.2 关键依赖
chromadb # 向量存储(Chroma)
sentence-transformers # Embedding 编码(bge-m3)+ Rerank 精排(bge-reranker-v2-m3)
rank_bm25 # BM25 关键词检索
pydantic # 数据模型(RuleChunk 等)
2.3 多语言与日文样本验证(OV2/T11)
实际规则语料为日文(要件定义/概要设计/记入规则),因此:
- Embedding 采用多语言模型 bge-m3(支持中/日/英,稠密向量 1024 维)
- Rerank 采用多语言精排模型 bge-reranker-v2-m3(Cross-Encoder,query×chunk 打分)
- 日文样本验证(Phase4 实施时执行):用真实日文规则片段(如「見出しは「1.1」形式で記述する」)构造检索用例,断言:
- 语义近义日文 query 能召回对应规则(top-5 命中)
- rerank 后相关片段排位不低于 RRF 原始排位(不劣化)
- 日英混合/片假名术语 query 不空召回
3. 文档分割策略
3.1 按格式适配分割(业界最佳实践)
规则文档为多格式(Word / Excel / PPT),分割器需感知格式:
规则文档(多格式)
│
├── 📄 Word (.docx) — 标题结构最清晰
│ 分割单位: Heading 1/2/3 层级 → 每个小节一个 chunk
│ 表格处理: 表格整体作为一个 chunk(标题 + 表格内容)
│ 列表处理: 列表项随所属小节
│
├── 📊 Excel (.xlsx) — 表格型规则
│ 分割单位: 每个 Sheet 的「规则块」为 chunk
│ (规则块 = 表头 + 数据行组,按语义分组切分)
│ 例: 记入规则的「表头行 + 5条规则行」作为一个 chunk
│
└── 📽 PPT (.pptx) — 幻灯片要点型
分割单位: 每 1~2 页幻灯片作为一个 chunk
(单页内容少时合并,保证 chunk 有意义)
标题+正文分别作为 chunk 文本的一部分
3.2 统一 Chunk 结构
@dataclass
class RuleChunk:
chunk_id: str # 形如 rules-write_v3_001
doc_id: str # 源文档名
doc_type: str # "word" | "excel" | "ppt"
content: str # 纯文本内容(含结构标记)
headings: list[str] # 所属标题路径,如 ["3. 記入規則", "3.2 表の書き方"]
source_uri: str # 可追溯来源
position: int # 文档内顺序
token_count: int # token 数(用于后续优化)
3.3 分割器实现
Chunking Pipeline
├── WordChunker # 遍历 python-docx 段落,按 Heading 样式切分
├── ExcelChunker # 遍历 openpyxl 行,按「表头+数据行组」切分
└── PPTChunker # 遍历 pptx 幻灯片,1~2页合并为一个 chunk
分割规则:
- chunk 上限:默认 max_tokens=512,超出时按语义段落追加切分
- chunk 下限:内容过短(< 30 tokens)时与相邻 chunk 合并(PPT 场景)
- 保留结构:headings 记录标题路径,供检索后的上下文组装
4. 存储架构
4.1 目录结构
/data/shared/rules-handbook/ # 规则手册(全用户共享)
├── chroma/ # Chroma 持久化数据目录
│ ├── v1/
│ │ ├── rules-write/ # Collection: 写入规则 v1
│ │ ├── rules-design/ # Collection: 设计规则 v1
│ │ └── ref-docs/ # Collection: 参考设计文档 v1(可选)
│ └── v2/
│ ├── rules-write/
│ ├── rules-design/
│ └── ref-docs/
├── chunks/ # 分割后的文本(JSON,可追溯)
│ ├── v1/
│ │ ├── rules-write/
│ │ │ ├── chunk_001.json
│ │ │ └── ...
│ │ └── rules-design/
│ └── v2/
└── manifests/ # 版本清单(元数据)
├── v1.json
└── v2.json
4.2 Collection 命名规范
格式: {content_type}-v{version}
例:
rules-write-v3 # 写入规则 v3
rules-design-v3 # 设计规则 v3
ref-docs-v3 # 参考设计文档 v3(可选)
4.3 版本清单 manifest.json
{
"version": "v3",
"created_at": "2026-07-30",
"active": true,
"source_files": [
{"name": "记入规则.docx", "hash": "sha256:...", "type": "write", "built": "reuse_from_v2"},
{"name": "图表规则.xlsx", "hash": "sha256:...", "type": "write", "built": "rebuilt"},
{"name": "设计方针.docx", "hash": "sha256:...", "type": "design", "built": "reuse_from_v2"}
],
"collections": {
"rules-write": {"chunk_count": 130, "embedding_model": "bge-m3", "rerank_model": "bge-reranker-v2-m3"},
"rules-design": {"chunk_count": 45, "embedding_model": "bge-m3", "rerank_model": "bge-reranker-v2-m3"}
},
"active_previous": "v2"
}
说明:
collections[*].embedding_model为版本清单的记录字段,记录该版本实际使用的模型名;其值来源于统一配置config/rag.yaml的embedding.model(见 docs/config-design.md),两者保持一致,不再单独配置。
4.4 Chunk 持久化(JSON)
说明:示例 content 为模拟日文规则文档的原始文本(技术必要保留)。
{
"chunk_id": "rules-write_v3_001",
"doc_id": "记入规则.docx",
"doc_type": "word",
"content": "見出しは「1.1」形式…",
"headings": ["3. 記入規則", "3.2 見出しの書き方"],
"source_uri": "记入规则.docx#見出し!3.2",
"position": 15,
"token_count": 240
}
5. 版本管理
5.1 文档级增量 + 版本组合
规则手册 = 多个文档(记入规则、图表规则、设计方针…)。当只更改其中一个文档时,避免全量重建。
更新时流程:
1. 计算所有上传文档的内容 hash(sha256)
2. 与当前激活版本的 manifest 对比
3. 识别出「变化/新增/删除」的文档
4. 只对变化的文档重新解析 → 分割 → embedding
5. 未变化的文档直接复用上一版本的 chunks + embeddings
6. 生成新版本 Collection
5.2 示例场景
用户上传 3 个规则文档,只修改了「图表规则.xlsx」:
更新前(handbook-v2):
记入规则.docx hash=A → 100 chunks
图表规则.xlsx hash=B → 30 chunks
设计方针.docx hash=C → 45 chunks
用户更新「图表规则.xlsx」→ hash=B'(内容变了)
更新后(handbook-v3):
记入规则.docx hash=A → 100 chunks 【复用 v2,不重新 embedding】
图表规则.xlsx hash=B' → 32 chunks 【重新构建】
设计方针.docx hash=C → 45 chunks 【复用 v2,不重新 embedding】
5.3 变化类型处理
| 场景 | 处理方式 |
|---|---|
| 文档内容变化 | 只重建该文档 |
| 新增文档 | 只构建新增文档 |
| 删除文档 | 新版本不含该文档,旧版本仍保留 |
| 所有文档未变化 | 不创建新版本(提示用户「无变化」) |
5.4 会话锁版本
- 会话开始(用户上传要件定义时)锁定当时激活的规则版本
- 整个生成过程固定用该版本,避免生成中途规则更新导致前后不一致
- 设计书元数据记录「使用的规则版本号 + 冲突决策列表」,可完全追溯
5.5 回退策略
回退到 v2:
manifest v2 的 active 字段 → true
会话锁版本逻辑不变(新会话锁 v2)
v3 保留不删除(可再回退)
5.6 存储冗余说明
v3 会复制未变化文档的 embeddings。对规则文档几 MB 的量级,冗余可接受,且换来逻辑简单与完全可追溯。
6. 检索引擎
6.1 检索请求流程
调用方(Writer/Impact/QA)
│ 传入: 检索意图 + 查询文本 + 约束
▼
┌──────────────────────────────────────────┐
│ Retrieval Engine │
│ │
│ Intent Router(意图路由) │
│ ├── Writer → 检索 rules-write Collection
│ ├── Impact → 检索 rules-design Collection
│ └── QA → 双 Collection 都检索
│ │
│ 查询构造: │
│ ├── 查询文本 = 用户提供的 query 文本 │
│ ├── 上下文增强 = 当前章节/要素信息 │
│ └── 过滤条件 = version + category │
│ │
│ 双通道检索: │
│ ├── VectorRetriever(向量语义检索 top-10) │
│ │ 使用 bge-m3 编码查询 │
│ └── BM25Retriever(关键词检索 top-10) │
│ │
│ RRF Fusion(结果融合) │
│ └── 合并两通道结果,按 RRF 公式排序 │
│ │
│ Rerank 精排(v1 新增,I6/T6) │
│ └── bge-reranker-v2-m3 对候选重打分 │
│ └── 输出 top-N 个 RuleChunk │
└──────────────────────────────────────────┘
6.2 RRF 融合
RRF_score(d) = Σ 1 / (k + rank(d)) # k=60 常用值
示例(k=60):
向量通道排名第2: 1/(60+2) = 0.0161
BM25通道排名第5: 1/(60+5) = 0.0154
总分 = 0.0315(两通道都命中的文档分数更高)
参数:
通道 top-k = 10(各自取 top-10,扩大召回窗口)
融合后 top-k = 调用方传参(默认 5)
6.3 Rerank 精排(v1 新增,I6/T6)
RRF 融合后、注入 LLM prompt 前,对候选做 Cross-Encoder 精排(2026 主流实践:向量→rerank→精排):
RRF 候选(默认 top-10 窗口)
│
▼
bge-reranker-v2-m3(Cross-Encoder)
│ 对每对 (query, chunk) 打分,输出相关性分数
▼
按分数降序重排 → 截取最终 top_k(默认 5)
| 项目 | 决策 |
|---|---|
| 模型 | BAAI/bge-reranker-v2-m3(多语言 Cross-Encoder,与日文语料匹配) |
| 开关 | rerank.enabled(默认 true);false 时跳过精排,直接取 RRF 前 top_k |
| 打分窗口 | RRF 融合后 top-10 候选(与通道 top-k 一致,避免重排成本随全文增长) |
| 候选过少 | 候选 ≤ top_k 时跳过 rerank(无重排必要,节省推理) |
| 故障降级 | rerank 推理异常 → 降级为 RRF 原始顺序,不阻断检索(同 §10 容错策略) |
6.4 上下文增强(Contextual Enrichment)
检索时不仅用 query 文本,还拼入当前上下文:
| 调用方 | 查询上下文增强 |
|---|---|
| Writer | 「第3章 機能一覧」的生成 → query + 章名 + 相关要素ID |
| Impact | 「要素 F001 与 TB001 的关系」推理 → query + 要素类型 + 要素描述 |
| QA | 「校验某段内容的规则遵守」→ query + 待校验段落 |
6.5 各 Agent 查询构造规范
| 调用方 | query 构造方式 |
|---|---|
| Writer | query = 章目标题 + 相关要素 ID + 生成意图描述 |
| Impact | query = 要素类型 + 要素描述 + 待推论的关联方向 |
| QA | query = 待校验段落内容 + 所属章 |
6.6 top_k 配置
top_k 由调用方传参,允许按章差异配置:
| 章节类型 | 建议 top_k |
|---|---|
| 概要章 | 3 |
| DB 设计章 | 8 |
| 其余章 | 5 |
6.7 检索结果上下文组装
检索出的 chunk 注入 LLM prompt 时,保留结构信息:
说明:以下示例模拟日文规则文档的原始内容(规则文档本身为日文,技术必要保留)。
--- 适用的记入规则(来自规则手册 v3)---
【3.2 見出しの書き方】
見出しは「1.1」「1.2」形式で記述する。
(来源: 记入规则.docx#見出し!3.2)
【5.1 表の書き方】
表のヘッダー行は太字で記載する。
(来源: 记入规则.docx#表!5.1)
7. 规则冲突处理
7.1 冲突检测时机
在检索引擎返回结果后、注入 LLM prompt 前执行:
检索返回 top-N chunks
│
▼
┌────────────────────────────────┐
│ Conflict Detector(冲突检测器)│
│ 1. 按「内容主题」对 chunks 分组 │
│ 2. 同一主题下,检测规则约束是否矛盾│
│ └ 对比: 格式(字体/字号/边距) │
│ 结构(标题层级/编号) │
│ 内容(必填项/禁止项) │
│ 3. 发现矛盾 → 标记为冲突组 │
└────────────────────────────────┘
│
▼
有冲突? ──否──→ 正常注入 prompt,继续生成
│
是
▼
用户确认介入(不阻塞整章,只阻塞该章)
7.2 冲突检测示例
说明:以下示例模拟日文规则文档的原始规则文本(技术必要保留)。
主题: 「表のヘッダー行」格式
[记入规则.docx #5.1] 表ヘッダーは太字+下線で記載する
[图表规则.xlsx #2.3] 表ヘッダーは太字のみ(下線なし)
→ 检测到矛盾:下線の有無
7.3 用户确认界面
─────────────────────────────────────────
⚠ 检测到规则冲突(第3章 機能一覧)
主题: 表ヘッダー行のフォーマット
┌────────────┬────────────┬──────────────┐
│ 来源文档 │ 规则内容 │ 章节出处 │
├────────────┼────────────┼──────────────┤
│ 记入规则.docx│ 太字+下線 │ 5.1 │
│ 图表规则.xlsx│ 太字のみ │ 2.3 │
└────────────┴────────────┴──────────────┘
[采用「记入规则」] [采用「图表规则」] [两规则都标注给人工]
─────────────────────────────────────────
7.4 冲突决策记录
{
"conflict_id": "c-001",
"session_id": "genesis-xxx",
"chapter": "機能一覧",
"topic": "表ヘッダー行のフォーマット",
"conflicting_chunks": [
{"chunk_id": "rules-write_v3_012", "doc": "记入规则.docx", "rule": "太字+下線"},
{"chunk_id": "rules-write_v3_045", "doc": "图表规则.xlsx", "rule": "太字のみ"}
],
"user_decision": "adopt_记入规则",
"decided_at": "2026-07-30 12:00",
"resolution_uri": "generated_doc#章3"
}
7.5 冲突决策的后续影响
- 决策结果存入会话(session),同会话内相同主题冲突不再重复询问
- 决策记录写入设计书元数据(可追溯「本设计书如何处理了规则冲突」)
- QA 校验时,以已决策的规则为准,不再对已决冲突告警
8. 集成接口
8.1 各 Agent 的调用场景
Parser ──→ RAG: 构建规则手册(首次上传 / 规则更新)
调用: build_handbook(file_list, category)
返回: version_id
Writer ──→ RAG: 每章生成前检索该章相关写入规则
调用: search(session_id, query, category="write", top_k=章配置)
返回: list[RuleChunk](含冲突标记)
Impact ──→ RAG: 关联推理时参考设计规则
调用: search(session_id, query, category="design", top_k=5)
返回: list[RuleChunk]
QA ──────→ RAG: 校验时检查规则遵守(双 Collection)
调用: search(session_id, query, category="write"|"design", top_k=5)
返回: list[RuleChunk]
Orchestrator ──→ RAG: 创建会话时锁定版本
调用: lock_version(session_id)
get_latest_version()
8.2 RAG Service 公开 API 一览
# 构建与版本管理
class RuleHandbookManager:
def build_handbook(self, files: list[UploadedFile], categories: dict) -> str
# 增量构建,返回新版本号(无变化则返回 None)
def get_latest_version(self) -> str
def get_version(self, version_id: str) -> Manifest
def list_versions(self) -> list[Manifest]
def rollback_to(self, version_id: str) -> None
# 回退 = 重新激活指定版本(不删除任何版本)
# 会话版本锁定
class SessionVersion:
def lock_version(self, session_id: str, version_id: str | None = None) -> None
# version_id 为空时锁定当前最新版本
def get_locked_version(self, session_id: str) -> str
# 检索
class RagService:
def search(
self, *,
session_id: str,
query: str,
category: Literal["write", "design"],
top_k: int = 5,
agent: str,
) -> SearchResult
# SearchResult = {chunks: list[RuleChunk], conflicts: list[ConflictGroup]}
# 冲突决策
class ConflictHandler:
def get_pending_conflicts(self, session_id: str) -> list[ConflictGroup]
def resolve_conflict(self, conflict_id: str, decision: str) -> None
# 决策: 采用哪条规则 / 标注给人工
8.3 会话生命周期中的版本管理
① 创建会话 → 锁定当前最新版本 v3
② 用户上传要件定义+模板(不含规则文档)
→ Writer/Impact 检索时自动使用锁定的 v3
③ 规则手册更新为 v4
→ 已存在的会话仍用 v3(会话锁版本)
→ 新会话自动锁定 v4
④ 设计书完成
→ 元数据记录: 使用规则版本 v3 + 冲突决策列表
9. 存储适配层(Storage Adapter)
9.1 定位
向量存储访问的统一抽象层。RAG 业务逻辑(分割/检索/融合)不直接依赖具体向量数据库,而是通过 Storage Adapter 访问。v1 仅实现 ChromaAdapter(本地持久化,零外部依赖)。(T5 架构审查整改:Scope 缩减裁定移除 Qdrant 可切换抽象,QdrantAdapter 为 v2 预留。)
9.2 接口定义
class VectorStoreAdapter(ABC):
"""向量存储统一接口"""
@abstractmethod
def create_collection(self, name: str) -> None: ...
@abstractmethod
def delete_collection(self, name: str) -> None: ...
@abstractmethod
def upsert(
self,
collection: str,
ids: list[str],
embeddings: list[list[float]],
documents: list[str],
metadatas: list[dict],
) -> None: ...
@abstractmethod
def query(
self,
collection: str,
query_embedding: list[float],
top_k: int,
where: dict | None = None, # metadata 过滤条件
) -> list[VectorHit]: ...
@abstractmethod
def count(self, collection: str) -> int: ...
@dataclass
class VectorHit:
id: str
document: str
metadata: dict
score: float
9.3 实现类
| 实现 | 说明 | 使用场景 |
|---|---|---|
| ChromaAdapter | v1 唯一实现,chromadb 本地持久化(/data/shared/rules-handbook/chroma/) |
默认 |
| QdrantAdapter | v2 预留(不实现) | Scope 缩减裁定移除;接口保留供 v2 扩展 |
9.4 配置
# config/inference.yaml 或 config/rag.yaml
vector_store:
adapter: chroma # v1 仅 "chroma"(Scope 缩减裁定)
chroma:
persist_dir: /data/shared/rules-handbook/chroma
9.5 工厂与依赖注入
class StorageAdapterFactory:
@staticmethod
def create(config: dict) -> VectorStoreAdapter:
# v1 仅 ChromaAdapter(Qdrant 分支 v2 预留)
return ChromaAdapter(config["vector_store"]["chroma"])
# 使用: RagService / RuleHandbookManager 通过构造注入 adapter
rag_service = RagService(
adapter=StorageAdapterFactory.create(config),
...
)
9.6 一致性保证
- v1 仅 ChromaAdapter(无跨实现一致性要求;v2 新增实现时需保证对同一数据的操作结果一致)
- 单元测试中可注入 MockAdapter(内存实现),使检索逻辑测试不依赖真实向量库
10. 降级与容错
10.1 无规则手册时的降级
场景1: 用户完全没上传规则文档
→ 系统内置「默认最小规则集」(硬编码基础规范,如「内容可追溯」)
→ search() 返回空时,Writer 正常生成但标记「无规则约束」
场景2: 规则手册构建失败(解析失败/LLM不可用)
→ 返回错误给用户,提示重试
→ 不影响已锁定的旧版本使用
10.2 Embedding 服务故障
Embedding 编码失败:
→ 降级为仅 BM25 检索(单通道)
→ 提示用户「语义检索暂不可用,已降级为关键词检索」
11. v2 迭代预留
以下内容v1 不实现,记入设计文档避免遗漏,v2 迭代:
11.1 检索质量反馈回路
v2: QA → 检索调优的反馈回路
├── QA 发现「规则未被遵守」
├── 判断根因: 「生成了但违规」 vs 「规则未被检索到」
├── 后者 → 记录 (query, 期望规则, 实际召回)
└── 定期分析 → 优化 query 重写策略 / top_k / 关键词扩展
11.2 检索延迟优化
v2: 缓存与批量
├── 同类 query 的 embedding 结果缓存
└── 按章批量检索(一次检索多主题)
12. 与 design.md 的关系
- 本文档是
docs/design.md第5章(RAG 基础设施层)的详细展开 docs/design.md第5章保留概要,并指向本文档