Skip to content
pickmemory edited this page Aug 28, 2025 · 7 revisions

RAG知识库系统原理介绍和文档优化思路

系统架构图

graph TD
    A[文档输入] --> B[文档处理]
    B --> C[文本分割]
    C --> D[向量化]
    D --> E[存储索引]

    F[用户查询] --> G[查询处理]
    G --> H[向量检索]
    G --> I[关键词检索]
    H --> J[结果融合]
    I --> J
    J --> K[返回结果]

    subgraph 文档处理流程
    B --> |Document Loader| B1[文档解析]
    B1 --> |Text Splitter| C
    C --> |M3E-Base| D
    D --> |Elasticsearch| E
    end

    subgraph 查询处理流程
    G --> |Query Parser| G1[查询解析]
    G1 --> |M3E-Base| H
    G1 --> |IK Analyzer| I
    H --> |Cosine Similarity| J
    I --> |BM25| J
    end
Loading

核心组件

  • Document Loader: 支持PDF、Word、HTML等格式
  • Text Splitter: 500字分片,50字重叠
  • M3E-Base: 768维向量编码模型
  • Elasticsearch: 混合检索引擎
  • IK Analyzer: 中文分词器

什么是RAG?

RAG就是检索增强生成,简单说就是让AI在回答问题前先查资料。

就像考试时可以翻书一样:

  • 传统AI:问题 → AI大脑 → 回答
  • RAG AI:问题 → 搜索知识库 → 找到资料 → AI大脑+资料 → 更准确的回答

智能搜索的原理

把文字变成数字

计算机不认识文字,需要把每个词转换成768个数字来表示。相似的词会有相似的数字组合:

  • "苹果"和"香蕉"的数字很接近(都是水果)
  • "苹果"和"汽车"的数字差很远(完全不同)

这768个数字就像一个人的详细档案,能描述这个词的各种特征。

文档分片的必要性

长文档需要切成小段处理,就像把厚书分成章节:

  • 每段500字,便于处理和检索
  • 段与段之间重叠50字,保证信息完整性
  • 避免重要信息被切断

两种搜索方式

关键词搜索

  • 只能找到包含确切词汇的内容
  • 搜"苹果"找不到"红富士"

语义搜索

  • 理解词汇含义,找到相关内容
  • 搜"苹果"能找到"水果营养"相关信息

我们的系统主要用语义搜索,关键词搜索做补充。

ElasticSearch的作用

ElasticSearch就像一个超级图书管理员,用倒排索引快速定位信息:

传统方式:逐个翻找 → 很慢
倒排索引:直接定位 → 很快

"苹果"这个词 → 出现在文档1、5、10
"营养"这个词 → 出现在文档1、3、7

中文分词

中文没有天然的词汇分隔符,需要IK分词器来处理:

"我爱吃苹果" → "我" + "爱吃" + "苹果"

IK分词器的ik_max_word模式会生成多种分词组合,提高搜索匹配率。

相似度计算原理

向量相似度的几何意义

每个文档片段被转换成768维向量后,可以想象成768维空间中的一个点。相似度计算就是测量两个点之间的关系。

余弦相似度计算

向量A = [a1, a2, a3, ..., a768]
向量B = [b1, b2, b3, ..., b768]

余弦相似度 = (A·B) / (|A| × |B|)
其中:
- A·B = a1×b1 + a2×b2 + ... + a768×b768 (点积)
- |A| = √(a1² + a2² + ... + a768²) (向量长度)
- |B| = √(b1² + b2² + ... + b768²) (向量长度)

相似度值的含义

  • 1.0:完全相同的语义
  • 0.8-0.9:高度相关
  • 0.6-0.8:中等相关
  • 0.3-0.6:弱相关
  • 0.0:完全无关
  • 负值:语义相反

为什么用余弦相似度而不是欧氏距离

欧氏距离的问题

文档A:"苹果很好吃" → 向量[1, 1, 0]
文档B:"苹果很好吃很好吃" → 向量[2, 2, 0] (重复表达)

欧氏距离 = √[(2-1)² + (2-1)² + (0-0)²] = √2 ≈ 1.41
看起来差异很大,但实际语义相同

余弦相似度的优势

余弦相似度 = (1×2 + 1×2 + 0×0) / (√2 × √8) = 6 / 4 = 1.0
正确识别为完全相同的语义

余弦相似度只关注方向(语义),不关注长度(表达强度),更适合文本语义比较。

混合检索的权重计算

系统同时使用向量检索和关键词检索,最终得分计算:

最终得分 = 向量相似度 × 1.0 + BM25得分 × 0.00001

例如:
文档1:向量相似度0.85,BM25得分100
最终得分 = 0.85 × 1.0 + 100 × 0.00001 = 0.851

文档2:向量相似度0.82,BM25得分200
最终得分 = 0.82 × 1.0 + 200 × 0.00001 = 0.822

可以看出,向量相似度占主导地位,关键词匹配只是微调。

文档存储机制

文档解析和存储流程

1. 文档上传和解析

原始文档 → Document Loader → 提取纯文本
支持格式:PDF、Word、HTML、TXT等

2. 文本分片处理

长文档 → Text Splitter → 多个500字片段
每个片段包含:
- chunk_id: 唯一标识符
- content: 文本内容
- metadata: 元数据(来源文档、页码等)
- overlap: 与前后片段的重叠部分

3. 向量化处理

文本片段 → M3E-Base模型 → 768维向量
例如:
chunk_1: "苹果营养丰富..." → [0.1, 0.8, 0.3, ..., 0.9]
chunk_2: "苹果含有维生素..." → [0.2, 0.7, 0.4, ..., 0.8]

4. ElasticSearch存储结构

{
  "_index": "knowledge_base",
  "_id": "doc_123_chunk_1",
  "_source": {
    "document_id": "doc_123",
    "chunk_id": "chunk_1",
    "content": "苹果营养丰富,含有多种维生素...",
    "vector": [0.1, 0.8, 0.3, ..., 0.9],
    "metadata": {
      "filename": "水果营养指南.pdf",
      "page": 1,
      "upload_time": "2024-01-15T10:30:00Z"
    },
    "chunk_index": 1,
    "total_chunks": 5
  }
}

5. 索引建立

  • 倒排索引:每个词对应包含它的文档片段
  • 向量索引:HNSW算法建立向量间的邻近关系
  • 元数据索引:按文档ID、时间等字段建立索引

检索过程详解

用户查询的完整流程

1. 查询预处理

用户输入:"苹果有什么营养价值?"
↓
Query Parser处理:
- 去除停用词
- 标准化表达
- 提取关键信息

2. 双路检索

向量检索路径

查询文本 → M3E-Base → 查询向量[0.15, 0.75, 0.35, ...]
↓
在向量空间中搜索相似片段
↓
计算余弦相似度,返回Top-K结果

关键词检索路径

查询文本 → IK分词器 → ["苹果", "营养", "价值"]
↓
在倒排索引中查找包含这些词的片段
↓
使用BM25算法计算相关性得分

3. 结果融合和排序

向量检索结果:
- chunk_1: 相似度0.89
- chunk_3: 相似度0.85
- chunk_7: 相似度0.82

关键词检索结果:
- chunk_1: BM25得分120
- chunk_2: BM25得分95
- chunk_3: BM25得分88

融合计算:
chunk_1: 0.89 × 1.0 + 120 × 0.00001 = 0.8912
chunk_3: 0.85 × 1.0 + 88 × 0.00001 = 0.85088
chunk_2: 0.0 × 1.0 + 95 × 0.00001 = 0.00095

最终排序:chunk_1 > chunk_3 > chunk_7 > chunk_2

4. 结果返回

{
  "results": [
    {
      "chunk_id": "doc_123_chunk_1",
      "content": "苹果营养丰富,含有维生素C、纤维素...",
      "score": 0.8912,
      "metadata": {
        "filename": "水果营养指南.pdf",
        "page": 1
      }
    }
  ],
  "total": 15,
  "took": 45
}

文档删除机制

删除操作的影响范围

1. 标记删除

DELETE /knowledge_base/_doc/doc_123_chunk_1
DELETE /knowledge_base/_doc/doc_123_chunk_2
...
DELETE /knowledge_base/_doc/doc_123_chunk_N

2. 索引更新

  • 倒排索引:移除已删除片段的词汇映射
  • 向量索引:从HNSW图中移除对应节点
  • 元数据索引:清理相关元数据记录

3. 存储回收

ElasticSearch的段合并过程:
旧段:[chunk_1, chunk_2, chunk_3, chunk_4]
删除chunk_2后:
新段:[chunk_1, chunk_3, chunk_4]
物理空间得到释放

4. 缓存清理

  • 清理查询结果缓存
  • 更新统计信息
  • 重建相关的聚合数据

文档更新机制

更新策略:删除+重建

为什么不能直接更新?

  • 文档内容变化可能导致分片数量改变
  • 向量表示完全不同,需要重新计算
  • 保持数据一致性,避免部分更新的复杂性

1. 更新流程

步骤1:标记旧版本文档为删除状态
步骤2:解析新版本文档
步骤3:重新分片和向量化
步骤4:写入新的文档片段
步骤5:更新索引
步骤6:物理删除旧版本数据

2. 版本控制

{
  "document_id": "doc_123",
  "version": 2,
  "previous_version": 1,
  "update_time": "2024-01-16T14:20:00Z",
  "status": "active"
}

3. 更新过程中的查询处理

更新期间的状态:
- 旧版本:标记为"deleting"
- 新版本:标记为"indexing"
- 查询时:优先返回"active"状态的片段
- 避免返回不一致的结果

4. 批量更新优化

单文档更新:逐个处理,实时生效
批量更新:
1. 收集所有待更新文档
2. 批量删除旧版本
3. 批量处理新版本
4. 一次性提交索引更新
5. 减少索引重建次数

性能影响分析

删除操作

  • 轻量级:只是标记删除
  • 索引更新:异步进行
  • 存储回收:段合并时进行

更新操作

  • 重量级:需要重新处理整个文档
  • 临时存储增加:新旧版本同时存在
  • 索引压力:需要重建相关索引

优化建议

  • 批量操作减少频次
  • 非高峰期进行大量更新
  • 监控存储空间使用情况

知识库文档优化指南

问题一:重复内容和冗余信息

问题表现

文档A:"如何申请年假?请登录人事系统,点击假期申请..."
文档B:"年假申请流程:登录人事系统,选择假期申请选项..."
文档C:"申请年假怎么办?进入人事系统,找到假期申请..."

对RAG系统的影响

  • 向量化后这些文档高度相似
  • 用户搜索时返回多个重复答案
  • 造成困惑:到底哪个是正确的?
  • 浪费存储和计算资源

优化方案

统一文档:"年假申请完整指南"
├── 1. 常规申请流程
├── 2. 特殊假期处理
├── 3. 紧急申请处理
└── 4. 常见问题解答

好处:单一权威来源,向量更准确,检索效率提升

问题二:标题和内容不匹配

问题表现

标题:"办公设备问题"
内容:主要讲打印机配置,只有一小段提到办公设备

标题:"员工入职"
内容:包含入职、培训安排、权限申请、设备领取等多个主题

对RAG系统的影响

  • IK分词器处理标题和内容得到不同的关键词
  • 用户搜索"设备故障"匹配到标题,但内容是打印机配置
  • 答案不相关,用户体验差

优化方案

原标题:"办公设备问题"
优化后:"打印机配置指南"

原标题:"员工入职"
优化后:拆分为多个文档
├── "员工入职流程"
├── "新员工培训指南"
├── "权限申请流程"
└── "设备领取手册"

好处:标题即内容预览,向量匹配更精准

问题三:缺乏层次结构

问题表现

"员工福利相关问题大全"
- 健身补贴流程是什么?答:请通过福利平台...
- 补贴发放日期?答:每月20日和月底...
- 如何选择补贴类型?答:分为健身补贴和学习补贴...
- 申请截止时间?答:发放日前三天...
- 申请附件要求?答:请提供消费凭证...
(50个问题混在一起)

对RAG系统的影响

  • 500字分片可能把相关问题分开
  • 用户问"补贴流程"可能匹配到主要讲类型的片段
  • 流程信息不完整

优化方案

"员工福利指南"
├── 1. 基础流程
│   ├── 1.1 申请步骤
│   ├── 1.2 审批流程
│   └── 1.3 发放时间
├── 2. 操作细节
│   ├── 2.1 类型选择
│   ├── 2.2 凭证要求
│   └── 2.3 截止时间
└── 3. 常见问题
    ├── 3.1 发放延迟
    ├── 3.2 审批失败
    └── 3.3 系统故障

好处:逻辑清晰便于分片,相关内容聚集,检索精度提升

问题四:多语言混杂

问题表现

"如何申请系统权限?How to apply system permission?
请通过工单系统提交申请。Please submit via ticket system.
填写申请表单时注意以下事项:
1. 请填写具体系统名称
2. 需要说明申请原因
3. 确保信息准确无误"

对RAG系统的影响

  • IK分词器处理中英混合文本效果差
  • M3E模型向量化不稳定
  • 相似度计算不准确

优化方案

方案1:完全分离
├── "系统权限申请指南(中文版)"
└── "System Permission Application Guide (English)"

方案2:结构化双语
"系统权限申请 / System Permission Application"
├── 中文说明:详细的中文操作步骤
├── English Guide: Detailed English instructions
└── 关键术语对照表:
   - 申请表单 = Application Form
   - 系统名称 = System Name

好处:分词准确率提升,向量化质量改善

问题五:缺乏上下文信息

问题表现

Q: "支付日期是什么时候?"
A: "每月15日和最后第二个工作日"

Q: "如何提交?"
A: "通过飞书工作台的审批系统"

Q: "需要什么附件?"
A: "收据或发票"

对RAG系统的影响

  • 用户搜索"补贴发放时间",系统返回"每月20日和月底"
  • 用户困惑:这是什么的发放时间?工资?补贴?奖金?
  • "发放时间"可能匹配到多种不同场景,无法区分

优化方案

优化前:"发放日期是每月20日和月底"

优化后:"员工福利补贴发放时间安排
公司员工福利补贴的发放日期为每月20日和每月最后一个工作日。
如果发放日恰逢非工作日,将提前至最近的工作日进行发放。
注意:福利补贴与工资发放是分开处理的。"

好处:上下文完整,消除歧义,向量包含更多语义信息

问题六:技术术语缺乏解释

问题表现

"请通过工单系统提交业务申请,在管理平台配置系统权限,
确保身份认证通过后访问数据平台进行信息查询操作。"

对RAG系统的影响

  • 专业术语向量表示不稳定
  • 用户用自然语言搜索时匹配度低
  • 新员工完全看不懂
  • 用户搜索"如何申请系统权限"无法匹配到"业务申请"

优化方案

"系统权限申请指南

1. 什么是工单系统?
   工单系统是公司的服务管理系统,
   用于处理各种业务相关的申请和工单。

2. 申请流程:
   - 登录工单系统(服务管理平台)
   - 选择业务申请(系统权限申请)
   - 在管理平台(统一管理平台)配置权限
   - 等待身份认证(登录认证)通过

3. 常用术语对照:
   - 系统权限申请 = 业务申请
   - 服务平台 = 工单系统
   - 统一平台 = 管理平台"

好处:新手友好,多种表达方式提高匹配率

优化检查清单

内容质量

  • 消除重复内容
  • 标题与内容匹配
  • 建立清晰层次
  • 统一语言表达
  • 丰富上下文信息
  • 解释专业术语

技术要求

  • 文档长度控制在2000-5000字
  • 使用统一术语
  • 定期更新内容
  • 建立文档关联

用户体验

  • 从用户角度组织内容
  • 提供多种表达方式
  • 包含实际操作示例
  • 收集用户反馈

持续优化

  1. 监控搜索日志,找出无结果查询
  2. 分析用户反馈,发现内容盲点
  3. 定期评估检索准确率
  4. 根据使用数据调整优先级

总结

好的知识库需要技术和内容的结合:

  • 技术提供检索能力
  • 内容决定用户体验
  • 持续优化保证效果

核心原则:内容去重、结构清晰、语言统一、上下文完整、术语友好。

Clone this wiki locally