生产级 RAG 处理流程与异常补偿清单¶
本文档描述标书知识库从文件解析、正文与 Chunk 落库、Embedding、向量索引,到最终检索和上下文返回的生产级处理流程。
租户、用户权限、知识库与标书的业务关系由 tender-service 负责;本文只描述 rag-service 内部的 RAG 能力。
整体 RAG 处理链路如下:
flowchart TD
A["tender-service 提交标书文件与业务范围"] --> B["文件接收与异步处理任务"]
B --> C["文档解析与标准化"]
C --> D["Markdown + Document Blocks + 表格/图片资产"]
D --> E["通用切片,生成基础 Chunk"]
E --> F["标书业务识别与内容关联"]
F --> G["有检索价值的基础 Chunk"]
F --> H["完整业务基础 Chunk 增加业务标签"]
F --> I["分散业务内容生成业务派生 Chunk"]
G --> J["Embedding 向量化"]
H --> J
I --> J
J --> K["写入候选向量索引"]
K --> L["索引完整性检查"]
L --> M{"候选索引是否完整"}
M -->|"否"| N["候选版本失败,保留当前活动版本"]
M -->|"是"| O["切换活动索引并清理旧版本"]
P["AI 助手问题 + tender-service 检索范围"] --> Q["问题理解、查询改写与检索路由"]
O --> R["活动向量索引"]
Q --> S["通用 / 业务 / 关键词混合召回"]
R --> S
S --> T["范围与阈值过滤、去重"]
T --> U["Rerank 与相邻内容补充"]
U --> V["按 Token 预算组装上下文"]
V --> W["返回 Chunk 正文、分数和引用信息"]
W --> X["tender-service / AI 助手用于问答或正文生成"]
一、核心功能¶
| 核心功能 | 基本说明 |
|---|---|
| 1. 文档解析与标准化 | 将 PDF、Word、Excel 等文件转换为结构化 Markdown,识别正文、标题、表格和图片,并保留页码等来源信息 |
| 2. 文档切块 | 按标题、段落和语义边界生成可检索 Chunk;表格保留表头与数据关系,图片通过 OCR 或内容描述转成可检索信息 |
| 3. Embedding 与向量索引 | 批量生成 Chunk 向量并写入向量数据库(当前为 Qdrant),支持模型和索引版本管理 |
| 4. 索引版本管理 | 使用候选版本构建新索引,完成后切换活动版本,并清理旧版本 |
| 5. 向量检索 | 根据用户问题召回相关 Chunk,支持知识库、文件和 metadata 范围过滤 |
| 6. 检索结果处理 | 对候选结果进行阈值过滤、去重、相邻 Chunk 合并和来源多样性控制 |
| 7. Rerank 与混合检索 | 使用 Rerank 提升语义相关度,并通过关键词或稀疏检索补充编号、参数和条款原文召回 |
| 8. 上下文与引用输出 | 按 Token 预算组装 AI 上下文,返回 Chunk 正文、文件、标题路径、页码和引用信息 |
文档解析与标准化重点问题¶
一份标书在解析阶段主要经过格式识别、格式解析、内容分类和标准化输出,处理正文、表格、图片、原文位置、质量信息五类内容:
flowchart TD
A["标书文件"] --> B["文件校验与真实格式识别"]
B --> C{"格式解析适配器"}
C -->|"PDF"| P["文本提取 / 版面分析 / 扫描页 OCR"]
C -->|"DOCX / DOC"| W["DOC 转换 / 段落、列表、表格、图片解析"]
C -->|"XLSX / XLS"| X["XLS 转换 / Sheet、单元格、公式、图片解析"]
P --> N["统一 Document Block 标准化"]
W --> N
X --> N
N --> T["正文、标题与列表"]
N --> TB["表格 TableBlock"]
N --> I["图片 ImageBlock"]
N --> L["原文位置记录"]
N --> Q["解析质量与警告"]
T --> O["标准化输出"]
TB --> O
I --> O
L --> O
Q --> O
O --> M["Markdown + Document Blocks + 图片资产 + 解析元数据"]
-
文件解析与标准结构
-
统一格式识别
- 不能只根据文件扩展名选择解析器,需要同时校验扩展名、MIME、文件头和 OOXML 包内容。
- 通过 OLE 魔数识别旧版
.doc、.xls,通过 Zip 文件头和[Content_Types].xml区分.docx、.xlsx,解决扩展名与真实格式不一致的问题。
-
PDF
- 文本型 PDF:建议使用 PyMuPDF 按页提取文本 Block、坐标和阅读顺序;现有
pypdf可以保留为基础或降级解析器。 - 扫描型 PDF:根据页面文本量判断是否需要 OCR,将页面渲染为图片后调用 OCR Provider,再合并回对应页的标准结构。
- 图文混合 PDF:保留原生文本,同时对无文本区域或关键图片单独执行 OCR/视觉分析,避免整页重复识别。
- 针对问题:多栏排版可能导致阅读顺序错乱;页眉页脚和水印可能重复;加密、损坏或字体编码异常的 PDF 需要返回明确解析错误或降级处理。
- 文本型 PDF:建议使用 PyMuPDF 按页提取文本 Block、坐标和阅读顺序;现有
-
Word
- 新格式
.docx:使用python-docx解析 OOXML,按文档真实顺序遍历段落、列表、表格和图片关系;结合样式名称、编号和文本特征识别标题层级。 - 旧格式
.doc:先通过 OLE 魔数确认真实格式,再使用 LibreOffice/sofficeheadless 转换为.docx,转换成功后复用同一 DOCX 解析器。 - 扩展名错误:文件名为
.docx但内容是 OLE 时仍走旧格式转换;文件名为.doc但内容实际是 OOXML 时直接按 DOCX 解析。 - 针对问题:LibreOffice 可能未安装、转换超时或转换后排版发生变化;Word 没有稳定的物理页码,默认使用段落和 Block 位置定位,需要精确页码时可额外渲染为 PDF。
- 新格式
-
Excel
- 新格式
.xlsx:使用openpyxl解析工作表、单元格、合并区域、公式、显示值和隐藏状态,并记录 Sheet 名和单元格范围。 - 旧格式
.xls:先通过 OLE 魔数确认格式,再使用 LibreOffice/soffice转换为.xlsx,后续统一交给 XLSX 解析器。 - 公式处理:同时保留公式文本和可获得的缓存计算值,避免只保存公式或只保存结果导致信息缺失。
- 针对问题:多级表头、合并单元格、隐藏行列、跨 Sheet 引用和复杂公式可能无法完整还原;Excel 没有自然页码,来源定位应使用
sheet_name + cell_range。
- 新格式
-
统一标准结构
- 使用 Pydantic 或 dataclass 定义 Document Block,统一表示
heading、paragraph、list、table、image等类型。 - 每个 Block 保存正文、层级、页码或 Sheet、原文位置、坐标、资源引用和解析警告,Markdown 只是标准输出之一,结构化 Block 作为后续处理的事实数据。
- 使用 Pydantic 或 dataclass 定义 Document Block,统一表示
-
正文、标题与列表
-
技术方案:按原文顺序提取正文、标题、段落和列表,保留原始编号与样式信息,并统一转换为 Markdown 和 Document Block;解析阶段不判断内容是否属于业务条款。
-
可能问题:非标准标题、多栏排版、自动编号和自定义样式可能导致正文顺序、标题层级或列表编号还原错误。
-
原文位置记录(辅助能力)
-
基本作用:给解析后的段落、表格和图片记录其在原文件中的大致位置,方便后续展示引用和排查解析问题;它不是核心解析结果。
- 实现方式:PDF 记录页码和内容顺序,Word 记录段落或表格顺序,Excel 记录 Sheet 和单元格范围。第一版保存这些基础信息即可,不要求精确坐标还原。
- 可能问题:Word、Excel 没有稳定的物理页码,文件重新排版后位置也可能变化;精确定位能力可以后续按实际引用需求增强。
表格与图片解析方案¶
-
表格
-
应用方案
- 解析器发现表格时,将其作为独立
TableBlock保留在文档原始顺序中,不与前后正文直接拼成普通文本。 - 优先使用文档原生结构提取行列;原生结构不可用时再使用版面分析或 OCR 表格识别。
- 同时保存 Markdown 表示、结构化行列数据和原文位置,供统一展示、精确还原和后续处理使用。
- 解析器发现表格时,将其作为独立
-
Word 表格
- 使用
python-docx按文档顺序读取表格,保留表格与前后正文的相对位置。 - 提取行列、单元格文本、合并关系和附近标题,将表格转换为 Markdown,同时保留结构化行列数据。
- 使用
-
Excel 表格
- 使用
openpyxl读取 Sheet、单元格、合并区域、隐藏行列、公式和缓存值。 - 使用
sheet_name + cell_range记录来源位置;多个独立数据区域应分别保存,避免把整个 Sheet 误认为一个表格。
- 使用
-
文本型 PDF 表格
- 优先根据文字 Block、坐标和线条信息恢复表格区域、行列和阅读顺序。
- 无边框表格需要结合文字对齐和间距推断,不应只依赖表格边框。
-
扫描型 PDF 表格
- 将表格所在页面或区域渲染为图片,通过 OCR 表格识别提取单元格和文字。
- OCR 结果需要保留置信度;低置信度表格应记录解析警告,避免被当成可靠原文。
-
标准输出
- 输出
TableBlock,保存表名、Markdown、表头、行列数据、合并单元格、页码或 Sheet、原文位置和解析警告。 - Markdown 用于统一展示,结构化行列数据用于后续精确还原和处理,两者不能互相替代。
- 输出
-
可能问题
- 多级表头、合并单元格、跨页表格、嵌套表格和无边框表格可能还原不准确。
- PDF 表格可能出现错列、漏行和阅读顺序错误;Excel 公式可能缺少缓存结果;Word 表格可能包含图片或嵌套表格。
-
图片
-
应用方案
- 解析器先提取原图并创建独立
ImageBlock,保存图片与标题、正文、页面或 Sheet 的位置关系。 - 有明显文字的图片优先执行 OCR;流程图、架构图和示意图再调用视觉模型补充内容描述。
- 原图、OCR 原文和视觉描述分别保存,任何派生结果都不能覆盖或替代原始图片。
- 解析器先提取原图并创建独立
-
图片提取
- 从 PDF、Word 中提取原始图片或页面区域,保存文件格式、尺寸、所在页、文档顺序和附近标题、图题、正文。
- Excel 中的浮动图片需要保存所属 Sheet、锚定单元格和附近数据区域。
-
图片分类与过滤
- 根据尺寸、重复次数、位置和内容特征区分业务图片、扫描页、Logo、水印、页眉图标和装饰图片。
- 装饰图片不进入后续内容处理,但仍可保留基础审计信息,避免误删后无法追溯。
-
OCR 解析
- 对文字截图、扫描页、证书、参数图等调用 OCR Provider,保存原始识别文本、文本区域和置信度。
- OCR 前可进行旋转矫正、裁边、降噪和清晰度增强,提高中文、数字和表格文字识别率。
-
视觉内容解析
- 对流程图、架构图、示意图和无法仅靠 OCR 理解的图片调用视觉模型 Provider。
- 视觉模型输出内容描述、关键实体和结构关系;模型描述属于派生信息,不能替代 OCR 原文和原图。
-
标准输出
- 输出
ImageBlock,保存原图引用、图题、OCR 文本、视觉描述、页码或 Sheet、原文位置、置信度和解析警告。 - 原图、OCR 结果和视觉描述分别保存,便于后续校验、重新分析和引用展示。
- 输出
-
可能问题
- OCR 可能误识别数字、单位和中文字符;视觉模型可能遗漏细节或产生不可靠描述。
- 图片与附近正文的关联可能错误;过滤规则也可能误删较小但有业务价值的印章、证书或参数图片。
通用 Chunk 与业务 Chunk 的关系¶
通用 Chunk 是忠实保留原始文档结构的基础数据;业务 Chunk 是基于一个或多个通用 Chunk 识别、关联后生成的业务派生数据。
flowchart TD
A["Document Blocks"] --> B["通用 Chunk"]
B --> C["基础文档检索"]
B --> D["标书业务识别与内容关联"]
D --> E["业务 Chunk"]
E --> F["资格、评分、参数、废标等业务检索"]
E -. "通过 source_chunk_ids 关联" .-> B
C --> G["统一候选结果"]
F --> G
G --> H["去重、Rerank 与上下文组装"]
H --> I["返回通用 Chunk、原文位置和引用信息"]
- 一个业务 Chunk 可以组合多个通用 Chunk;一个通用 Chunk 也可能参与多个业务 Chunk。
- 业务 Chunk 可以随业务规则重新生成,通用 Chunk 和原文引用关系保持不变。
- 两类 Chunk 可以分别召回,最终统一去重和排序;引用仍回到通用 Chunk 及原始文件。
通用文档切片(Chunk)方案¶
文档切片只处理解析和标准化后的 Document Block,不再直接读取 PDF、Word 或 Excel 原文件。
flowchart TD
A["Markdown + Document Blocks + 图片资产 + 解析元数据"] --> B["恢复文档顺序与标题层级"]
B --> C["识别标题、段落、列表、表格和图片边界"]
C --> D{"按 Block 内容类型处理"}
D -->|"正文 / 列表"| T["按结构组合相邻内容"]
D -->|"表格"| TB["小表整体保留 / 大表按行组拆分"]
D -->|"图片"| I["组合图题、OCR 文本和视觉描述"]
T --> L["Token 长度检查"]
TB --> L
I --> L
L -->|"超长"| S["按段落、句子或表格行二次拆分"]
L -->|"长度合适"| M["生成候选 Chunk"]
S --> M
M --> H["继承标题上下文"]
H --> O["补充必要的相邻内容重叠"]
O --> R["关联来源 Block、页码、Sheet、表格和图片"]
R --> Q["空内容、重复、超长和来源完整性检查"]
Q --> Z["通用 Chunk 输出"]
Z --> E["进入 Embedding 或标书业务切片"]
-
结构边界优先
-
优先按照标题、段落、列表、表格和图片等 Block 边界切片,不从一个完整 Block 的中间截断。
-
一个 Chunk 不跨文件;默认也不跨一级章节,避免不同主题内容混在一起。
-
长度控制
-
使用 Token 数控制 Chunk 长度,不使用固定字符数作为唯一标准。
- 初始可采用目标
500~800 Tokens、最大1000~1200 Tokens,后续根据 Embedding 模型和检索效果调整。 -
单个 Block 超过最大长度时,再按段落、句子或行进行二次拆分。
-
标题上下文继承
-
每个 Chunk 保存完整
heading_path,正文中可附带必要的上级标题,使片段脱离原文后仍能表达所属章节。 -
标题只作为上下文补充,不应在每个 Chunk 中重复过多无关目录内容。
-
相邻内容重叠
-
连续正文 Chunk 保留少量 overlap,避免定义、条件或句子在边界处断开。
-
初始 overlap 可设置为
80~150 Tokens;标题变化、表格和图片边界不采用简单文本重叠。 -
正文与列表规则
-
相邻短段落可以合并,但不跨越明显标题边界。
- 列表标题与列表项尽量保存在同一 Chunk;超长列表拆分时,每个 Chunk 保留列表标题和必要上下文。
-
条款语义识别属于切片或内容增强阶段,可根据编号模式辅助确定边界,但不能修改原文内容。
-
表格规则
-
小型表格作为一个独立
tableChunk,保留表名、表头和完整数据。 - 大型表格按行组拆分,每个 Chunk 重复必要表头,并保存同一个
table_id和行范围。 -
表格默认不与大段正文合并;可以附带附近标题和简短说明作为检索上下文。
-
图片规则
-
有效图片使用 OCR 文本、视觉描述和图题形成独立
imageChunk,并保留原图引用。 - 图片附近的标题和说明文字可以作为上下文,但不能用模型描述覆盖 OCR 原文。
-
无有效文字或描述的装饰图片不生成可检索 Chunk。
-
Chunk 类型与元数据
-
Chunk 类型至少区分
text、list、table、image和mixed。 -
每个 Chunk 保存
chunk_index、content_hash、token_count、heading_path、来源 Block、页码或 Sheet、表格或图片引用和处理版本。 -
质量检查
-
过滤空 Chunk、纯标题 Chunk、明显重复 Chunk 和无业务内容的 Chunk。
- 检查 Chunk 是否超长、是否缺少来源信息、表格是否缺少表头、图片是否缺少可检索内容。
- 切片结果通过检查后再进入 Embedding,避免无效内容污染向量索引。
标书业务切片(待确认)¶
标书业务切片建立在 Document Block 和通用 Chunk 之上,不替代通用切片。它的作用是将分散在标题、正文、列表和表格中的内容重新关联为可独立检索、可直接回答问题的完整业务单元。
flowchart TD
A["Document Blocks + 通用 Chunk"] --> B["识别章节、关键词、编号和表头"]
B --> C{"判断标书业务类型"}
C --> Q["资格要求 / 实质性要求 / 废标条件"]
C --> S["评分标准 / 分值 / 证明材料"]
C --> T["技术参数 / 功能要求 / 偏离要求"]
C --> P["报价 / 付款 / 合同商务条款"]
C --> D["工期 / 交付 / 验收 / 售后服务"]
C --> F["投标文件编制 / 递交 / 开标要求"]
Q --> U["关联相邻正文、列表项和表格行"]
S --> U
T --> U
P --> U
D --> U
F --> U
U --> V["组合为一个完整业务单元"]
V --> W["检查条件、数值、对象和结果是否完整"]
W --> X["生成业务类型和业务标签"]
X --> Y["生成业务派生 Chunk"]
Y --> R["关联通用 Chunk、原文位置和引用信息"]
R --> O["输出通用 Chunk + 业务派生 Chunk"]
-
业务模块识别
-
根据章节标题、条款编号、关键词和表头,初步识别资格审查、评分办法、技术要求、商务要求、合同条款、投标文件编制等模块。
-
业务模块识别发生在通用切片之后,不在文档解析阶段判断业务含义。
-
资格要求与废标条件
-
将资格条件、适用对象、证书或证明材料、有效期要求组合为一项完整资格要求。
-
将触发条件、否决或废标结果以及例外说明保存在同一个业务单元中,避免只检索到“不得”“无效”等孤立文字。
-
评分标准
-
将评分项、分值、评分档次、判断条件和证明材料组合为一条完整评分标准。
-
对表格形式的评分办法,按评分项或评分档次处理,保留上级评分分类和表头,不能只保存单独的分值。
-
技术参数与功能要求
-
将参数名称、要求值、单位、范围、是否必须满足、证明方式和偏离规则组合为完整技术要求。
-
技术参数表可以按单行或相关行组形成业务单元;同一设备、部件或功能组的关联参数不应被完全拆散。
-
报价与商务合同条款
-
报价清单按项目、产品或费用组保留名称、数量、单位、限价、税费和报价说明。
-
付款条款按付款节点保留触发条件、付款比例、时间和所需材料;合同条款保留权利义务、违约条件和处理结果。
-
工期、交付、验收与售后
-
将交付对象、地点、期限、实施条件和交付成果组合为完整交付要求。
- 将验收标准、验收方式、参与方和不通过后的处理方式组合为完整验收要求。
-
将质保期限、服务范围、响应时间、到场时间和维护要求组合为完整售后服务要求。
-
投标文件编制与递交要求
-
将文件组成、签章、装订、份数、格式和密封要求按事项形成业务单元。
-
将递交截止时间、地点、方式、开标时间和注意事项保留为可单独检索的要求,避免关键时间与适用事项分离。
-
业务切片基本原则
-
一个业务 Chunk 应能独立回答一个具体问题,至少保留“要求对象、条件或数值、要求内容、结果或证明材料”等必要信息。
- 业务切片只组合和标注原文,不修改原文事实;模型生成的分类或摘要只能作为辅助字段。
- 同一内容在不同文件或章节中出现时,分别保留原文来源,不直接合并成无法追溯的新结论。
- 每个业务派生 Chunk 关联原始
source_block_ids、source_chunk_ids和原文位置,保证引用能够返回基础内容。
初步建议同时保留两类结果:
- 通用 Chunk:忠实保留文档原始结构,保证基础召回完整性。
- 业务派生 Chunk:面向标书问答,增强对资格、评分、参数和合同条款等内容的检索能力。
以上业务类型和边界是初始方案,仍需使用实际招标文件、投标文件和评分办法验证后确认首期范围。
Embedding 与向量索引方案¶
Embedding 与向量索引承接通用切片和标书业务切片的结果,负责判断哪些内容需要生成向量、组织基础与业务索引数据,并在索引完整后切换为可检索版本。
这里将“通用 Chunk”理解为基础 Chunk(通用切片结果):它按文档结构产生,本身也服务标书业务;业务 Chunk 则是多个基础内容经过业务关联后形成的派生结果。
flowchart TD
A["基础 Chunk(通用切片结果)"] --> B{"是否具有可检索内容"}
B -->|"否:空内容、装饰或无效重复"| X["不生成向量,仅保留必要解析记录"]
B -->|"是"| C{"基础 Chunk 的业务状态"}
C -->|"完整业务单元"| D["基础 Chunk 增加业务类型和标签"]
D --> E["作为基础 Chunk 生成一次向量"]
C -->|"普通基础内容 / 分散业务内容"| V{"是否具有独立检索价值"}
V -->|"是"| F["基础 Chunk 生成向量"]
V -->|"否"| Y["不单独生成向量,保留来源数据"]
C -->|"分散业务内容,同时关联"| G["关联多个基础 Chunk"]
G --> H["生成业务派生 Chunk"]
H --> I{"业务内容是否完整且可追溯"}
I -->|"否"| J["不进入业务索引,记录识别结果或警告"]
I -->|"是"| K["业务派生 Chunk 单独生成向量"]
E --> M["统一 Embedding 模型与处理版本"]
F --> M
K --> M
M --> N["写入候选向量索引"]
N --> O["核对 Chunk、向量和来源关系"]
O --> P{"索引是否完整"}
P -->|"否"| Q["候选版本失败,不影响当前活动索引"]
P -->|"是"| R["切换为活动索引版本"]
R --> S["进入通用检索与标书业务检索"]
R --> T["异步清理旧版本和无效向量"]
-
进入向量索引的内容范围
-
能独立表达正文、条款、列表、表格、图片信息或必要上下文的基础 Chunk,可以进入向量索引。
- 已通过完整性检查的业务派生 Chunk,可以进入向量索引。
- 空内容、纯装饰图片、无意义重复、解析失败内容和无法追溯来源的业务 Chunk,不进入向量索引。
-
只有结构作用、不能独立检索的标题或过渡内容,可以保留在正文或 Block 数据中,用于相邻上下文补充,不必单独生成向量。
-
基础 Chunk 的索引方式
-
基础 Chunk 是默认的原文检索数据,保留正文、标题路径、内容类型和原文位置后生成向量。
- 一个基础 Chunk 如果已经构成完整的资格要求、评分项、参数或合同条款,只需增加
business_type等业务标签,仍以基础 Chunk 身份生成一次向量。 -
基础 Chunk 即使没有业务标签,只要具有独立检索价值,也可以进入索引,用于普通原文问答、相似内容检索和相邻上下文补充。
-
业务派生 Chunk 的索引方式
-
当一项业务内容分散在多个基础 Chunk、列表项或表格行中时,先关联并生成完整的业务派生 Chunk。
- 业务派生 Chunk 通过完整性和来源检查后单独生成向量,并保存业务类型及
source_chunk_ids。 - 业务派生 Chunk 默认只组合同一文件、同一内容版本中的基础 Chunk;跨文件内容在检索阶段汇总,不在索引阶段拼成一个新 Chunk。
-
业务规则变化后可以重新生成业务派生 Chunk,不需要改变基础 Chunk 和原文数据。
-
避免重复向量
-
业务处理只给基础 Chunk 增加标签时,不再复制正文生成第二条业务向量。
- 只有业务派生 Chunk 对多个基础内容进行了有效组合,形成新的完整检索单元时,才单独生成业务向量。
-
业务派生 Chunk 与基础 Chunk 内容完全相同或高度重复时,优先复用基础 Chunk,通过业务标签参与业务检索。
-
表格与图片的向量化
-
表格以保留表头和行列关系的可检索文本生成向量;大型表格按行组产生的 Chunk 分别进入索引。
- 图片使用图题、可靠的 OCR 文本和必要的视觉描述生成向量,原图本身作为引用资产保留。
-
表格行或图片已经形成评分、参数、证书等完整业务单元时,可以直接增加业务标签;需要关联其他正文时再生成业务派生 Chunk。
-
Embedding 一致性
-
基础 Chunk、业务派生 Chunk 和用户查询需要使用兼容的 Embedding 配置,保证向量可以在同一检索空间比较。
-
每次向量化记录模型、向量维度和处理版本;模型或处理规则升级时重新构建候选索引,不直接覆盖当前活动索引。
-
候选索引与活动版本
-
新文件、文件更新、切片规则变化或 Embedding 模型升级时,先构建候选版本。
- 候选版本完成前,当前活动版本继续提供检索,避免 AI 助手读取只处理了一部分的数据。
-
候选版本检查通过后再切换为活动版本,切换失败时保留旧活动版本。
-
索引完整性与清理
-
切换前核对应该索引的基础 Chunk、业务派生 Chunk、成功向量和来源关系是否完整。
- 文件删除或版本切换后,检索立即排除旧数据,再异步清理旧 Chunk、旧向量和失败候选。
- 定期检查正文数据库与向量数据库是否一致,发现缺失向量时补写,发现孤儿向量时清理。
首期建议将基础 Chunk 和业务派生 Chunk 放在同一逻辑索引中,通过 chunk_category=base/business 和业务类型区分,并采用两路召回后统一去重、Rerank。是否拆分为独立索引,可在数据规模和业务检索效果明确后再决定。
通用检索能力¶
检索模块负责根据 AI 助手的问题,从通用 Chunk 和业务 Chunk 中找到可靠依据,完成过滤、排序和上下文组装后返回给 tender-service;答案生成仍由上层 AI 助手负责。
通用检索能力主要包括:
flowchart TD
A["用户问题 + tender-service 检索范围"] --> B["问题规范化、意图识别与查询改写"]
B --> S["按知识库、标书、文件和业务范围过滤"]
S --> C{"选择通用 / 业务召回路径"}
C --> G["通用 Chunk 向量召回"]
C --> U["业务 Chunk 向量召回"]
C --> K["关键词 / 稀疏 / 编号 / 参数召回"]
G --> M["合并多路候选结果"]
U --> M
K --> M
M --> F["候选数量控制与相关度阈值过滤"]
F --> D["去重与来源多样性控制"]
D --> R["Rerank 重新排序"]
R --> J{"是否存在可靠结果"}
J -->|"否"| X["降级检索;仍无结果返回 no_reliable_context"]
J -->|"是"| N["补充必要的相邻 Chunk"]
N --> P["按 Token 预算组装上下文"]
P --> O["返回 Chunk 正文、分数和引用信息"]
O -. "检索日志与标注样本" .-> E["检索效果评估与参数调优"]
X -. "无结果与降级记录" .-> E
-
问题理解与查询改写
-
识别用户是在查询资格、评分、技术参数、废标条件、合同条款还是普通原文内容。
-
对口语化问题补充标书常用表达、同义词和必要关键词,但保留原始问题用于结果判断。
-
检索范围控制
-
使用
tender-service传入的知识库、标书、文件和业务范围限制检索内容。 -
rag-service只执行范围过滤,不自行判断租户、用户权限和标书归属。 -
通用 Chunk 与业务 Chunk 召回策略
-
普通文档问答以通用 Chunk 为基础;资格、评分、参数和废标等明确业务问题提高业务 Chunk 的召回权重。
-
两类 Chunk 可以并行召回,避免业务识别错误时完全漏掉原文内容。
-
向量、关键词与混合检索
-
向量检索负责语义相似内容,关键词或稀疏检索补充项目编号、参数值、证书名称、金额和条款原文。
-
混合检索用于兼顾自然语言问题和标书中的精确表达。
-
候选数量与阈值过滤
-
先召回相对充足的候选内容,再根据相关度阈值筛除明显无关结果。
-
阈值和候选数量需要按问题类型调整,不能只使用一个固定值覆盖所有标书场景。
-
去重、来源与多样性控制
-
合并通用 Chunk、业务 Chunk 和不同检索路径中的重复结果。
-
避免结果全部集中在一个重复章节,同时不能为了多样性丢弃真正相关的关键条款。
-
Rerank 排序
-
根据“用户问题 + Chunk 内容”对候选结果重新评分,优先保留能够直接回答问题且信息完整的内容。
-
Rerank 不可用时可以降级使用原始召回分数,保证基础检索仍然可用。
-
相邻内容补充
-
命中内容缺少标题、前置条件、表头或后续说明时,按来源关系补充必要的相邻 Chunk。
-
相邻扩展只补充上下文,不应无条件返回整章内容。
-
上下文组装与引用输出
-
按总 Token 预算、单个来源占比和最大引用数量组装最终上下文。
-
返回正文、Chunk 类型、文件、标题路径、页码或 Sheet、检索分数及原文引用信息。
-
无可靠结果与降级处理
-
没有结果或结果低于可靠阈值时返回明确的
no_reliable_context,避免 AI 助手把无关内容当作标书依据。 -
查询改写、业务召回或 Rerank 失败时,可逐级降级到通用向量检索。
-
检索效果评估
-
使用真实标书问题建立评测集,分别观察资格、评分、参数、废标、合同和普通问答的召回效果。
- 重点评估是否找到正确原文、前几条结果是否相关、引用是否完整,以及无答案问题是否被正确拒绝。
以上为检索能力的设计角度,具体采用纯向量还是混合检索、两类 Chunk 的权重、Rerank 模型和阈值,需要通过实际标书评测后确定。
标书业务检索(待确认)¶
标书业务检索建立在通用检索能力之上,主要服务标书正文写作。用户输入当前章节、写作要求或已有正文后,系统查找可参考的相似条款、政策标准、技术方案、商务内容和资格材料,并返回原文及可靠引用。
flowchart TD
A["当前章节 + 已有正文 + 写作要求 + 项目信息"] --> B["识别写作任务与所需参考资料"]
B --> C{"选择业务检索场景"}
C --> T["相似条款与历史写法"]
C --> P["政策法规与行业标准"]
C --> S["技术方案与产品能力"]
C --> BZ["商务、合同与服务条款"]
C --> Q["资格要求与证明材料"]
T --> R["调用通用检索进行多路召回"]
P --> R
S --> R
BZ --> R
Q --> R
R --> F["按行业、地区、项目类型、时间和资料类型过滤"]
F --> K["按相似度、适用性、完整性和来源可靠性排序"]
K --> D["识别重复、冲突和可能失效的参考资料"]
D --> O["分类返回可引用原文、参考写法和政策依据"]
O --> I["附带来源文件、原文位置和适用性提示"]
-
写作上下文输入
-
检索输入不只包含用户的一句话,还可以包含当前章节标题、相邻正文、招标要求、项目行业、地区和项目类型。
-
tender-service负责提供当前写作场景和允许检索的资料范围,rag-service负责据此检索。 -
相似条款与历史写法
-
根据当前条款或写作要求,查找历史标书中语义相近、结构完整的正文内容。
-
优先返回包含完整适用条件、要求和结果的条款,避免只返回相似短句或脱离上下文的段落。
-
政策法规与行业标准参考
-
根据行业、地区、采购类型和写作主题检索相关政策、法规、规范和标准原文。
-
结果应展示发布单位、文件名称、文号、发布日期、实施日期和知识库记录的有效状态;知识库未维护有效状态时,不应声称政策仍然有效或最新。
-
技术方案与产品能力参考
-
根据技术要求、设备名称、系统模块和功能目标,查找相似项目中的技术方案、实施方法和产品能力说明。
-
返回内容需要保留适用项目和前置条件,避免把其他行业或其他产品的方案直接作为当前项目结论。
-
商务、合同与服务条款参考
-
检索交付、工期、付款、验收、质保、售后、违约和风险分担等相似条款。
-
重点保留比例、时间、地点、触发条件和责任主体,避免只召回笼统的商务描述。
-
资格要求与证明材料参考
-
根据资格条件查找相似项目要求的证书、业绩、人员、财务和信用证明材料。
-
区分招标方的资格要求与历史投标文件中的响应材料,避免将响应内容误认为当前项目要求。
-
适用范围过滤
-
支持按资料类型、行业、地区、项目类型、采购方式、时间范围和业务章节过滤参考材料。
-
政策和标准优先匹配适用地区及有效时间,历史写法优先匹配相近项目场景。
-
业务价值排序
-
除语义相似度外,同时考虑业务类型匹配度、适用范围、内容完整性、来源可靠性和资料时间。
-
对相似条款优先完整可参考的内容,对政策依据优先权威来源和知识库中状态明确的内容。
-
重复与冲突提示
-
对多个文件中的重复条款合并展示,但保留各自来源。
-
当不同资料的金额、时间、参数或要求存在冲突时,不自动合成单一结论,应同时返回并标记差异。
-
写作参考结果组织
-
结果可以按“可引用原文、历史参考写法、政策或标准依据、相关技术与商务材料”分类返回。
-
每条结果附带来源文件、标题路径、页码或 Sheet、内容类型和适用性提示,方便 AI 助手选择和引用。
-
无结果与使用边界
-
没有可靠参考资料时明确返回无结果,不通过大模型补造条款、政策名称或标准编号。
- 业务检索只提供写作材料和依据,不判断最终条款是否合法、政策是否仍有效,也不直接替代 AI 助手生成正文。
以上是面向标书正文写作的初始检索方案,后续需要结合实际写作入口、知识库资料分类和典型检索问题进一步完善业务类型、排序规则和结果展示方式。
外部能力接入候选(待调研)¶
rag-service 保留文档处理、切片、索引和检索等核心流程,以下能力通过统一 Provider 接口接入。这里仅列举候选方案,具体产品需要结合标书效果、成本、性能、数据安全和部署方式进一步调研。
-
OCR 与文档识别
-
国内云服务:阿里云 OCR/文档智能、百度智能云 OCR、腾讯云 OCR、华为云 OCR、火山引擎 OCR。
- 开源或自部署:PaddleOCR、MinerU、Tesseract、Surya、Docling、Unstructured。
-
主要用途:扫描 PDF、图片文字、表格、印章、证书和复杂版面的识别。
-
视觉理解模型
-
国内模型:通义千问 VL、豆包视觉模型、文心多模态模型、腾讯混元视觉模型、盘古多模态模型、智谱 GLM 视觉模型。
- 海外模型:OpenAI 视觉模型、Google Gemini、Anthropic Claude Vision。
- 开源或自部署:Qwen-VL、InternVL、LLaVA、MiniCPM-V 等视觉模型。
-
主要用途:理解流程图、技术架构图、证书、产品图片和无法只靠 OCR 表达的图像内容。
-
Embedding 模型
-
国内云服务:阿里云百炼 Embedding、火山方舟 Embedding、百度智能云 Embedding、腾讯混元 Embedding、智谱 Embedding。
- 海外服务:OpenAI Embedding、Cohere Embed、Voyage AI Embedding、Jina Embeddings。
- 开源或自部署:BGE/BGE-M3、E5、GTE、M3E、Jina Embeddings 开源模型。
-
主要用途:生成正文、表格、图片描述和用户问题的语义向量。
-
Rerank 模型
-
国内云服务:阿里云百炼 Qwen Rerank、百度智能云 Reranker、火山方舟 Rerank、智谱 Rerank。
- 海外服务:Cohere Rerank、Jina Reranker、Voyage AI Rerank。
- 开源或自部署:BGE Reranker、Qwen Reranker、Jina Reranker 开源模型。
-
主要用途:对初步召回的候选 Chunk 重新排序,提高最终上下文相关度。
-
向量数据库与检索引擎
-
当前及自部署方案:Qdrant、Milvus、PostgreSQL + pgvector、Elasticsearch、OpenSearch、Weaviate。
- 阿里云托管方案:DashVector、OSS Vectors、阿里云 Elasticsearch、OpenSearch 向量检索版。
- 其他托管方案:Zilliz Cloud、Pinecone、Qdrant Cloud、Weaviate Cloud。
-
主要用途:保存 Chunk 向量和检索元数据,提供向量召回、条件过滤及混合检索能力。
-
大语言模型
-
国内模型平台:阿里云百炼/通义千问、火山方舟/豆包、百度千帆/文心、腾讯混元、华为盘古、智谱、DeepSeek、月之暗面、MiniMax、零一万物。
- 海外模型平台:OpenAI、Anthropic Claude、Google Gemini、Mistral AI、Cohere。
- 自部署方案:通过 vLLM、SGLang、Ollama 等运行 Qwen、DeepSeek、Llama、Mistral 等开源模型。
-
主要用途:查询改写、业务内容识别、答案生成和引用组织;不负责替代 RAG 的基础索引与检索流程。
-
AI 应用与工作流编排
-
平台方案:Dify、阿里云百炼应用与工作流、FastGPT、Coze/扣子、Flowise、n8n。
- 开发框架:LangGraph、LangChain、LlamaIndex、Haystack、Semantic Kernel。
- 主要用途:编排模型调用、检索工具、Prompt、对话状态和 AI 助手工作流;
rag-service仍作为独立检索能力被调用。
后续选型时分别验证中文标书效果、表格与图片能力、接口稳定性、调用限额、延迟、费用、数据保存策略、私有化能力和供应商锁定风险。
二、正常流程技术细节¶
| 序号 | 功能点 | 基本说明 |
|---|---|---|
| 1 | 文件接收 | 接收 tender-service 提供的文件地址、文件名、类型、大小、checksum 和业务来源信息 |
| 2 | 文件完整性校验 | 校验文件是否存在、大小是否一致、checksum 是否匹配 |
| 3 | 文件格式识别 | 通过扩展名、MIME 和文件头判断 PDF、Word、Excel 等真实格式 |
| 4 | 文档下载 | 从对象存储获取文件,限制下载超时和最大文件大小 |
| 5 | 结构化解析 | 提取标题、段落、列表、表格、页码等文档结构 |
| 6 | Markdown 标准化 | 将不同格式文档转换为统一 Markdown,作为后续处理的标准正文 |
| 7 | 原文位置映射 | 记录 Markdown 内容与原始页码、段落、表格位置的对应关系 |
| 8 | 解析质量检查 | 检查空内容、乱码、内容过少、重复页和结构异常 |
| 9 | Markdown 正文落库 | 保存标准 Markdown、内容 hash、页数、解析器版本和解析元数据 |
| 10 | 标题感知切块 | 优先按标题、段落和语义边界切块,长内容再按句子拆分 |
| 11 | Chunk overlap | 在相邻 Chunk 之间保留适量重叠,避免语义从边界断开 |
| 12 | Chunk 去重 | 去除重复页眉页脚、模板内容和完全相同的 Chunk |
| 13 | Chunk 元数据生成 | 生成 Chunk index、标题路径、页码、字符位置、Token 数和内容 hash |
| 14 | 候选 Chunk 落库 | 新内容先写入候选版本,不覆盖当前活动版本 |
| 15 | Embedding 配置选择 | 根据 Embedding profile 确定 provider、model、dimension 和 normalization |
| 16 | Embedding 批处理 | 按批次、并发限制生成所有 Chunk 的向量 |
| 17 | 向量合法性校验 | 校验向量数量、维度、空向量、NaN 和模型版本 |
| 18 | 确定向量 ID | 根据 source、内容版本和 Chunk 生成稳定且可重试的 point ID |
| 19 | 候选向量写入 | 将向量和轻量过滤字段写入 Qdrant 候选版本 |
| 20 | 索引完整性检查 | 核对候选 Chunk 数、成功向量数和 Qdrant point 数 |
| 21 | 活动版本切换 | 候选版本完整后,原子切换为当前可检索版本 |
| 22 | 旧版本清理 | 异步清理旧版本 Chunk、向量和无用解析产物 |
| 23 | 查询向量化 | 将 AI 助手的问题使用相同 Embedding profile 转换为查询向量 |
| 24 | 检索范围过滤 | 根据 tender-service 提供的知识库、文件和 metadata scope 限定检索范围 |
| 25 | 向量候选召回 | 使用 recall_k 从 Qdrant 召回较多候选 Chunk |
| 26 | 相似度阈值过滤 | 过滤低于 min_score 的候选,避免无关内容进入 AI 上下文 |
| 27 | 检索结果去重 | 按内容 hash 和相似度删除重复或高度相似结果 |
| 28 | 相邻 Chunk 合并 | 合并同文件、同版本且连续命中的相邻 Chunk |
| 29 | 来源多样性控制 | 防止最终结果全部来自同一文件或同一章节 |
| 30 | Rerank | 根据“用户问题 + Chunk 正文”重新评分并排序 |
| 31 | 最终 TopK 选择 | 从 Rerank 结果中选择最终少量高质量材料 |
| 32 | Context Packing | 按总 Token、单 Chunk 长度和最大引用数量组装 AI 上下文 |
| 33 | 引用元数据组装 | 返回文件名、标题路径、页码、source、版本、Chunk 和检索分数 |
| 34 | 检索结果返回 | 向 tender-service 返回 Chunk 正文、结构化引用、策略版本和检索状态 |
三、异常、重试与补偿技术细节¶
| 序号 | 异常场景 | 处理方式 |
|---|---|---|
| 1 | 重复提交同一文件 | 根据 idempotency key、checksum 和处理版本返回已有任务,避免重复解析和写向量 |
| 2 | 同一文件并发处理 | 同一个 source 只允许一个活动摄入任务,其他请求复用或返回冲突 |
| 3 | 文件不存在或下载失败 | 标记下载失败;瞬时网络错误自动重试,永久不存在直接终止 |
| 4 | 下载超时 | 按指数退避重试,超过最大次数进入失败或死信状态 |
| 5 | 文件过大 | 在下载或解析前拒绝处理,返回明确错误码 |
| 6 | 文件格式不匹配 | 以文件头检测结果为准;无法识别时终止任务,不进入切块和 Embedding |
| 7 | 文件损坏 | 记录解析错误和解析器信息,终止候选版本,保留旧活动版本 |
| 8 | 解析结果为空 | 视为解析失败,不生成 Chunk,不切换活动版本 |
| 9 | 解析质量过低 | 标记质量警告或进入人工复核状态,不直接覆盖当前版本 |
| 10 | Worker 意外退出 | 通过 heartbeat 和任务租约识别超时任务,再由新 Worker 恢复 |
| 11 | 旧 Worker 延迟写回 | 使用 fencing token/CAS version 拒绝旧 Worker 更新任务和活动版本 |
| 12 | 切块失败 | 删除当前候选 Chunk,记录阶段错误,旧活动版本继续使用 |
| 13 | Chunk 数量异常 | 例如正文非空但 Chunk 为零时终止任务,避免生成空索引 |
| 14 | Embedding 请求超时 | 对失败批次自动重试,不重复处理已经成功的批次 |
| 15 | Embedding 限流 | 识别 429,按服务端提示或指数退避降低并发后重试 |
| 16 | Embedding 鉴权或配置错误 | 直接失败,不做无意义重试,输出明确配置错误码 |
| 17 | Embedding 数量不一致 | 不写入活动版本,清理候选向量并终止任务 |
| 18 | Embedding 维度不一致 | 阻止写入原 collection,要求使用匹配的 Embedding profile 或新 collection |
| 19 | Qdrant 部分写入失败 | 使用确定性 point ID 幂等重试,避免产生重复向量 |
| 20 | Qdrant 完全不可用 | 保留候选 Chunk,重试向量写入;超过次数后将候选版本标记失败 |
| 21 | 候选版本处理失败 | 只删除失败候选的 Chunk 和向量,当前活动版本继续提供检索 |
| 22 | 活动版本切换冲突 | 使用数据库锁和 CAS 重试;发现任务已过期则停止旧任务 |
| 23 | 切换成功但旧向量清理失败 | 保持新版本活动,任务进入 cleaning,由后台巡检继续清理 |
| 24 | 删除文件时 Qdrant 不可用 | 先写删除标记,使检索立即排除,再异步重试物理清理 |
| 25 | 检索时 Qdrant 超时 | 短暂重试;仍失败则向 tender-service 返回明确的检索不可用状态 |
| 26 | 查询 Embedding 失败 | 不调用 LLM,返回检索失败,避免助手在无知识上下文时自由回答 |
| 27 | 检索结果低于阈值 | 返回 no_reliable_context,由 AI 助手提示当前知识库没有可靠依据 |
| 28 | Rerank 超时或失败 | 降级使用向量召回排序,不影响基础检索可用性 |
| 29 | Context 超出 Token 预算 | 按最终分数和来源优先级裁剪,在段落边界截断 |
| 30 | 引用对应 Chunk 已失效 | 回表校验活动版本,丢弃失效结果;必要时扩大候选重新检索 |
| 31 | PostgreSQL 与 Qdrant 不一致 | 定时核对活动 Chunk 与 point,清理孤儿向量或重新补写缺失向量 |
| 32 | 多次自动重试仍失败 | 进入 dead letter,保留任务阶段、错误码、最后异常和人工重试入口 |
| 33 | 服务重启后存在未完成任务 | 扫描 running/cleaning 任务,根据 heartbeat 和活动版本恢复或补偿 |
| 34 | 旧版本清理误操作风险 | 所有删除必须附带 source 和 version 条件,禁止仅按文件名或外部 ID 模糊删除 |
| 35 | 模型或切块策略升级 | 创建新候选版本重新索引,验证成功后切换,禁止直接覆盖原活动索引 |