Files
2026Technology-Competition/docs/rag-layer-design.md
T

23 KiB
Raw Blame History

RAG 基础设施层详细设计

版本: v1.0 | 日期: 2026-07-30 | 状态: 初版

本文档是 docs/design.md 第5章(RAG 基础设施层)的详细展开。


目录

  1. 定位与职责
  2. 技术选型
  3. 文档分割策略
  4. 存储架构
  5. 版本管理
  6. 检索引擎
  7. 规则冲突处理
  8. 集成接口
  9. 存储适配层(Storage Adapter
  10. 降级与容错
  11. v2 迭代预留
  12. 与 design.md 的关系

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 默认 + 可配置切换 Qdrant 初期用 Chroma(轻量本地,嵌入进程,Docker 部署最简);通过 Storage Adapter 抽象,未来可切换 Qdrant
Embedding 模型 bge-small-zh-v1.5(本地运行) 中文小模型(1024维,~100MB),CPU 可跑;统一配置 config/rag.yamlembedding.model(见 docs/config-design.md §5)可切换为 BAAI/bge-m3(多语言更好)
实现方式 手写实现(不引入 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-small-zh-v1.5
rank_bm25             # BM25 关键词检索
pydantic              # 数据模型(RuleChunk 等)

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-small-zh-v1.5"},
    "rules-design": {"chunk_count": 45, "embedding_model": "bge-small-zh-v1.5"}
  },
  "active_previous": "v2"
}

说明:collections[*].embedding_model 为版本清单的记录字段,记录该版本实际使用的模型名;其值来源于统一配置 config/rag.yamlembedding.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-small-zh 编码查询            │
│  └── BM25Retriever(关键词检索 top-10     │
│                                          │
│  RRF Fusion(结果融合)                     │
│  └── 合并两通道结果,按 RRF 公式排序         │
│  └── 输出 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 上下文增强(Contextual Enrichment

检索时不仅用 query 文本,还拼入当前上下文:

调用方 查询上下文增强
Writer 「第3章 機能一覧」的生成 → query + 章名 + 相关要素ID
Impact 「要素 F001 与 TB001 的关系」推理 → query + 要素类型 + 要素描述
QA 「校验某段内容的规则遵守」→ query + 待校验段落

6.4 各 Agent 查询构造规范

调用方 query 构造方式
Writer query = 章目标题 + 相关要素 ID + 生成意图描述
Impact query = 要素类型 + 要素描述 + 待推论的关联方向
QA query = 待校验段落内容 + 所属章

6.5 top_k 配置

top_k 由调用方传参,允许按章差异配置:

章节类型 建议 top_k
概要章 3
DB 设计章 8
其余章 5

6.6 检索结果上下文组装

检索出的 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 访问。初期使用 Chroma,未来可切换 Qdrant业务层无需改动

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 默认实现,chromadb 本地持久化(/data/shared/rules-handbook/chroma/ 默认
QdrantAdapter 可选实现,通过 Qdrant HTTP/gRPC API 切换时启用

9.4 配置切换

# config/inference.yaml 或 config/rag.yaml
vector_store:
  adapter: chroma          # "chroma" | "qdrant"
  chroma:
    persist_dir: /data/shared/rules-handbook/chroma
  qdrant:
    url: http://qdrant:6333
    api_key: ${QDRANT_API_KEY}
切换流程:
1. 修改配置 adapter: qdrant
2. 系统启动时通过 StorageAdapterFactory 创建对应实现
3. 已有规则手册数据需迁移(重新构建索引)或通过脚本复制
4. 业务层(检索引擎/版本管理)无感知

9.5 工厂与依赖注入

class StorageAdapterFactory:
    @staticmethod
    def create(config: dict) -> VectorStoreAdapter:
        adapter = config["vector_store"]["adapter"]
        if adapter == "qdrant":
            return QdrantAdapter(config["vector_store"]["qdrant"])
        return ChromaAdapter(config["vector_store"]["chroma"])

# 使用: RagService / RuleHandbookManager 通过构造注入 adapter
rag_service = RagService(
    adapter=StorageAdapterFactory.create(config),
    ...
)

9.6 一致性保证

  • ChromaAdapter 与 QdrantAdapter 对同一数据(chunks/embeddings/metadata)的操作结果一致
  • 单元测试中可注入 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章保留概要,并指向本文档