本方法论文档是对为了亲自实践多语言 RAG 流水线的构建与应用而构建的玩具项目进行逆向分析的结果。
选择四柱命理学(Saju)作为主题的原因是,四柱命理学基于统计分析,且韩语、中文、英语原文自然混杂,可以在一个项目中体验到多语言分块/嵌入/检索的所有问题。
所有的实验和运营均在本地环境(M1 Mac)中进行,是一个不依赖外部服务的完整流水线。
本文档由概述和 4 个详细文档组成。
| 文档 | 内容 | 核心问题 |
|---|---|---|
| 本文档 (概述) | 术语解释、背景、架构全景图、设计原则 | 什么是 RAG,为什么要这样构建? |
| Part 1: 数据收集·清洗·分块 | 数据源层级、清洗规则、分块策略、嵌入模型 Bug 发现 | 如何将原始数据转化为可检索的单位? |
| Part 2: 嵌入·索引·检索 | Dense/BM25 双重索引、混合检索、自适应权重、多语言查询扩展 | 如何创建索引,检索是如何工作的? |
| Part 3: 语料库管理·自动强化·智能体 | 语料库退化类型、更新策略、自动强化循环、LLM 智能体应用 | 如何保持语料库最新状态,LLM 如何利用它? |
| Part 4: 扩展·Production·IT 应用 | 10K→1M+ 扩展、分阶段构建选项、IT 公司知识库应用 | 如何将其应用于实际服务? |
首先整理本文档中反复出现的核心术语。如果你熟悉 RAG 或嵌入的概念,可以跳过此部分。
在 LLM(大语言模型)生成回答之前,先检索相关文档并参考其内容的技术。LLM 单独存在时,对于训练数据中没有或不准确的知识,会产生“幻觉 (hallucination)”问题。RAG 通过强制“根据检索到的原文回答”来缓解这一问题。
普通 LLM: 用户提问 → LLM → 回答 (依赖训练数据,有幻觉风险)
RAG: 用户提问 → [检索] → 发现相关文档 → LLM + 文档 → 回答 (有依据)
指 RAG 作为检索对象的文档集合全体。在这个项目中,由 593 个四柱命理学相关的 PDF、Markdown、文本文件构成了语料库。语料库的质量和范围决定了 RAG 性能的上限。无论检索算法多么出色,如果语料库中没有相关信息,就无法找到。
将长文档分割成适合检索的**小单位(块/Chunk)**的过程。如果检索整本书(数万字),无法知道哪部分相关,因此将其切成 300~500 字左右的语义单位,使每个单位成为独立的检索对象。切得太大检索精度会下降,切得太小则会丢失上下文。
将文本转换为数字向量(数百个浮点数数组)。例如,“甲木的性格正直且坚强”这句话会变成类似 [0.12, -0.34, 0.56, ...] 的 384 个数字数组。语义相似的句子会有相似的向量,因此通过计算向量间的距离,可以数值化地比较语义相似度。这就是“语义搜索”的原理。
传统的基于关键词的检索算法。通过结合查询词在文档中出现的次数 (TF) 和该词的稀有程度 (IDF) 来评分。搜索“甲木”时,会精确查找包含“甲木”字样的文档。如果说嵌入是按“语义”查找,那么 BM25 就是按“文字”查找。将两者结合(混合检索)可以互补短板。
将 BM25(关键词匹配)和 Dense Embedding(语义搜索)的结果合并并进行最终排名的策略。例如,搜索“甲木性格”时:
在通过初步检索筛选出候选后,使用更精密的模型重新排名的第二阶段检索。第一阶段 (BM25+Dense) 速度快但较粗略,第二阶段 (Cross-Encoder) 速度慢但更精密。由于它同时接收查询和文档并直接判断相关性,因此能产生比初步检索准确得多的排名。但由于对整个语料库应用太慢,通常只对第一阶段筛选出的 20~40 个候选应用。
通过计算嵌入向量间的距离(相似度)来查找最接近的向量。在这个项目中,计算 9,586 个 Chunk 向量与查询向量的内积 (dot product)。规模扩大时,使用 FAISS、Pinecone 等专用向量数据库,通过近似最近邻 (ANN) 搜索来确保速度。
设定检索结果中仅返回前 K 个。如果是 top-k 5,则仅获取相关性最高的 5 个 Chunk。K 太小会错过相关信息,太大则会混入无关噪音导致 LLM 困惑。本项目中,CLI 单次检索默认使用 top-k 5,RAG 批量检索默认使用 top-k 3。
将文本拆分为可检索的标记(词/词素)单位的工具。韩语需要将“사주명리학을”拆分为“사주/명리/학”的词素分析;中文因为没有空格,需要将“四柱八字”切分为“四柱/八字”;英语通过空格划分基本就能解决。必须使用适合各语言的分词器才能提高 BM25 的检索质量。
LLM 在像四柱命理学这样系统化但非主流的专业知识中幻觉严重。如果问“甲木日干生于寅月,用神为何?”,它会自信地给出听起来有道理但错误的答案。在需要基于经典原文(滴天髓、穷通宝鉴)进行准确解释的领域,单靠 LLM 是不可信的。
这不仅是四柱命理学的问题。在服务策划书、特定框架的最新 API、法律判例、医疗指南等LLM 训练数据中未充分包含的所有领域,都会发生同样的问题。
四柱命理学本质上是一个多语言领域:
| 语言 | 角色 | 示例 |
|---|---|---|
| 中文 | 原典·经典文本 | 滴天髓, 穷通宝鉴, 子平真诠 |
| 韩语 | 现代解释·教材·实战案例 | 格局论, 用神论, 实战 100 句 |
| 英语 | 学术论文·ML 研究 | BaZi career prediction, AI character simulation |
一个查询“甲木 寅月 用神”需要同时检索韩语教材、中文原典《穷通宝鉴》和英语论文,才能构成完整的答案。这在结构上与 IT 公司需要整合检索英语技术文档 + 韩语策划书 + 日语合作伙伴 API 文档的情况完全相同。
┌─────────────────────────────────────────────────────────┐
│ 第 0 阶段:数据收集·清洗 │
│ 原始 PDF/MD/TXT → 结构化标记 (--- 分隔线, 标注出处) │
│ 重复检查 → 出处验证 → 放置在 books/ 目录 │
│ → 详细: Part 1 │
└───────────────┬─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 第 1 阶段:分块 (chunk.py) │
│ PDF→PyMuPDF / TXT→直接读取 │
│ 语言检测 → 尊重段落边界 → 512 字分块 (64 字重叠) │
│ 输出: chunks.jsonl (9,586 个 Chunk, 4.16MB) │
│ → 详细: Part 1 │
└───────────────┬─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 第 2 阶段:索引 (embed.py) │
│ │
│ ┌──────────────────┐ ┌──────────────────────┐ │
│ │ Dense Embedding │ │ BM25 Sparse Index │ │
│ │ MiniLM-L12-v2 │ │ 韩语: Kiwi POS │ │
│ │ 384 维, L2 正规化 │ │ 中文: jieba 分词 │ │
│ │ → embeddings.npy │ │ 英语: regex+stopwords │ │
│ │ (14MB) │ │ → bm25_index.pkl │ │
│ └──────────────────┘ └──────────────────────┘ │
│ → 详细: Part 2 │
└───────────────┬─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 第 3 阶段:混合检索 (search.py) │
│ │
│ 查询 → 语言检测 → 多语言标记扩展 (韩↔汉 143 个键) │
│ → BM25 top-K ∪ Dense top-K 候选 (K=max(20,top_k×4)) │
│ → (可选) bge-reranker-v2-m3 重排序 │
│ → 源多样性惩罚 → 返回 top-K │
│ → 详细: Part 2 │
└───────────────┬─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 第 4 阶段:LLM 智能体利用 + 语料库管理 │
│ │
│ Claude Code Skill 编排器 │
│ → 5 个子智能体并行执行 │
│ → 每个智能体通过 search.py 进行 RAG 检索 │
│ → 生成基于原文引用的分析报告 │
│ → 自动 Gap 分析 → 收集 → 重建循环 │
│ → 详细: Part 3 │
└─────────────────────────────────────────────────────────┘
| 项目 | 数值 |
|---|---|
| 源文档 | 593 个 (PDF + MD + TXT) |
| 总 Chunk 数 | 9,586 个 |
| 总文本容量 | 约 416 万字 (chunks.jsonl 文件大小 10.4MB) |
| 平均 Chunk 大小 | 434 字 |
| Dense 嵌入 | 9,586 × 384 float32 = 14MB |
| BM25 索引 | 14.7MB (bm25_index.pkl 7.1MB + tokenized_corpus.pkl 7.6MB) |
| 语言分布 | 韩语 80.5% / 中文 14.2% / 英语 5.3% |
整理本项目中验证过的、无论领域和规模如何都可以适用的原则。
在专业领域,必须并行使用 BM25(关键词)+ Dense(语义)。在包含专有名词、缩写、代码名、API 端点等需要精确文字匹配的术语的领域,仅靠 Dense 是危险的。Dense 可能会带来“语义相似”但错误的结果,而 BM25 在没有准确关键词时什么也找不到。必须将两者结合。
构建同义词、缩写、多语言对应表可以极大地提高检索召回率 (recall)。本项目中 143 个键(209 对)的韩↔汉映射就是一个例子。如果是法律领域,构建“不法行为 ↔ 违法行为 ↔ tort”;IT 领域构建“K8s ↔ Kubernetes ↔ 库伯内特斯”;医疗领域构建“糖尿病 ↔ diabetes mellitus ↔ DM”等表格。
固定大小的分块虽然简单,但如果语义单位被切断,检索质量就会下降。利用 --- 分隔线或 Markdown 标题 (##) 等文档结构作为原子边界 (atomic boundary) 是有效的。而要实现这一点,在数据收集阶段就必须已经完成结构化。 分块质量是数据清洗质量的反映。
第一阶段通过 BM25+Dense 广泛抓取候选,第二阶段通过 Cross-Encoder 进行精密排名的两阶段流水线能显著提高检索质量。重排序 40 个候选的成本微乎其微,但精度的提升是显而易见的。
没有静态的语料库。必须自动化“Gap 检测 → 资料收集/更新 → 重建 → 验证”的过程,才能随着时间的推移保持或提高质量。手动管理在规模扩大后必然失败。
向 LLM 传递检索结果时,不要摘要,要直接传递原文。在摘要过程中信息会丢失或扭曲。必须让 LLM 直接查看原文并做出判断,才能保证引用的准确性。这就是本项目中子智能体强制要求“全文引用原文”的原因。
| 组件 | 本项目的实现 | 生产环境扩展选项 | 详细文档 |
|---|---|---|---|
| 数据收集 | 手动 + saju-research-collector 智能体 | Confluence API, Git Webhook, S3 事件, Unstructured.io | Part 1 |
| 数据清洗 | --- 分隔线, 必须标注出处, 禁止 AI 生成 |
HTML→MD 转换, PII 脱敏, 版本管理 | Part 1 |
| 分块 | 512 字, 64 字重叠, 尊重 --- 边界 |
LangChain TextSplitter, Semantic Chunking | Part 1 |
| 嵌入 | MiniLM-L12-v2 (384d, 本地) | bge-m3, OpenAI embedding API, Cohere embed | Part 2 |
| BM25 | rank-bm25 (内存中 pickle) | Elasticsearch, OpenSearch, Weaviate 内置 | Part 2 |
| 向量检索 | numpy 内积 (14MB npy) | FAISS, Pinecone, Qdrant, pgvector | Part 2 |
| 重排序器 | bge-reranker-v2-m3 (本地) | Cohere Rerank, Jina Reranker, LLM 重排序 | Part 2 |
| 查询扩展 | 143 个键(209 对) 韩↔汉词典 | WordNet, 领域本体, 基于 LLM 的扩展 | Part 2 |
| 语料库管理 | 自动 Gap→收集→重建循环 | 增量同步, TTL 过期, 用户反馈回路 | Part 3 |
| 智能体 | Claude Code Skills (5+5 并行) | LangChain Agent, LlamaIndex Agent | Part 3 |
| 扩展 | numpy + pickle (9.6K Chunk) | FAISS → Qdrant → Pinecone | Part 4 |
| IT 应用 | — | 策划书 RAG, 知识库机器人, 故障应对 | Part 4 |
该系统的核心价值在于它是一个**“小而全”的 RAG 流水线**。由 593 个文档、9,586 个 Chunk、4 个 Python 脚本 (chunk.py, embed.py, search.py, dedup.py) 构成的系统,包含了多语言混合检索、重排序、自适应权重、自动语料库强化等所有功能。
扩大规模时,只需将上表中的各组件替换为生产环境选项即可。设计原则和流水线结构与规模无关,保持一致。 改变的是基础设施,而不是架构。