-
Notifications
You must be signed in to change notification settings - Fork 2
Home
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
核心组件:
- Document Loader: 支持PDF、Word、HTML等格式
- Text Splitter: 500字分片,50字重叠
- M3E-Base: 768维向量编码模型
- Elasticsearch: 混合检索引擎
- IK Analyzer: 中文分词器
RAG就是检索增强生成,简单说就是让AI在回答问题前先查资料。
就像考试时可以翻书一样:
- 传统AI:问题 → AI大脑 → 回答
- RAG AI:问题 → 搜索知识库 → 找到资料 → AI大脑+资料 → 更准确的回答
计算机不认识文字,需要把每个词转换成768个数字来表示。相似的词会有相似的数字组合:
- "苹果"和"香蕉"的数字很接近(都是水果)
- "苹果"和"汽车"的数字差很远(完全不同)
这768个数字就像一个人的详细档案,能描述这个词的各种特征。
长文档需要切成小段处理,就像把厚书分成章节:
- 每段500字,便于处理和检索
- 段与段之间重叠50字,保证信息完整性
- 避免重要信息被切断
关键词搜索:
- 只能找到包含确切词汇的内容
- 搜"苹果"找不到"红富士"
语义搜索:
- 理解词汇含义,找到相关内容
- 搜"苹果"能找到"水果营养"相关信息
我们的系统主要用语义搜索,关键词搜索做补充。
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字
- 使用统一术语
- 定期更新内容
- 建立文档关联
用户体验:
- 从用户角度组织内容
- 提供多种表达方式
- 包含实际操作示例
- 收集用户反馈
- 监控搜索日志,找出无结果查询
- 分析用户反馈,发现内容盲点
- 定期评估检索准确率
- 根据使用数据调整优先级
好的知识库需要技术和内容的结合:
- 技术提供检索能力
- 内容决定用户体验
- 持续优化保证效果
核心原则:内容去重、结构清晰、语言统一、上下文完整、术语友好。