264 lines
6.7 KiB
Markdown
264 lines
6.7 KiB
Markdown
# 文档解析与向量检索说明
|
||
|
||
## 相关文件
|
||
|
||
- `aliyun_doc_parser.py`:调用阿里云文档智能解析 PDF,生成原始 `layouts.json`
|
||
- `layouts_to_vector_chunks.py`:把 `layouts.json` 转成适合向量数据库入库的三层结构
|
||
- `layouts.json`:阿里云返回的原始布局结果
|
||
- `vector_chunks.json`:转换后的结构化输出
|
||
|
||
## 一、`layouts.json` 的结构
|
||
|
||
`layouts.json` 顶层是一个数组,每个元素代表一个布局块(layout)。常见字段如下:
|
||
|
||
- `type`:主类型,例如 `title`、`text`、`table`、`figure`
|
||
- `subType`:更细的语义类型,例如 `doc_title`、`para_title`、`para`、`picture`、`pic_title`、`pic_caption`
|
||
- `text`:当前布局块的纯文本
|
||
- `markdownContent`:带 markdown 标记的文本
|
||
- `pageNum`:页码
|
||
- `index`:页内顺序
|
||
- `level`:标题层级
|
||
- `uniqueId`:布局块唯一标识
|
||
- `blocks`:更细粒度的文本与样式信息
|
||
- `cells`:表格单元格,仅 `table` 类型存在
|
||
|
||
这个结构不是简单 OCR 文本流,而是已经带有版面理解和语义分类的结构化数据。
|
||
|
||
## 二、推荐的三层转换结构
|
||
|
||
### 1. 结构层 `structure_nodes`
|
||
|
||
结构层用于恢复文档标题树,不直接作为最终向量检索单元。
|
||
|
||
示例:
|
||
|
||
- `1 范围`
|
||
- `2 规范性引用文件`
|
||
- `3 术语和定义`
|
||
- `3.1 儿童三轮车`
|
||
- `3.2 轮距`
|
||
|
||
结构层主要用于给下游 chunk 绑定 `section_path`。
|
||
|
||
### 2. 语义层 `semantic_blocks`
|
||
|
||
语义层是按文档意义聚合后的内容块,主要分为三类:
|
||
|
||
- `section_text`:同一章节下连续正文聚合而成
|
||
- `table`:表格内容单独成块
|
||
- `figure`:图、图名、图注等单独成块
|
||
|
||
这一层比单 layout 更适合做语义理解,也适合后续做上下文扩展。
|
||
|
||
### 3. 检索层 `vector_chunks`
|
||
|
||
检索层是最终写进向量数据库的 chunk。
|
||
|
||
处理方式:
|
||
|
||
- 对 `semantic_blocks` 中较短的块直接入库
|
||
- 对较长的块按 `max_chars` 再切分
|
||
- 相邻切片保留 `overlap_chars` 重叠
|
||
- 每个 chunk 都带完整 metadata,便于后续过滤、重排和邻域扩展
|
||
|
||
## 三、当前转换脚本做了什么
|
||
|
||
`layouts_to_vector_chunks.py` 当前已经实现:
|
||
|
||
1. 过滤目录页噪声(如 `目次`)
|
||
2. 根据标题层级维护章节路径
|
||
3. 将正文聚合成 `section_text`
|
||
4. 将表格单独转成 `table`
|
||
5. 将图相关内容单独转成 `figure`
|
||
6. 对长文本继续切分为最终 `vector_chunks`
|
||
7. 为每个检索 chunk 生成 `embedding_text`
|
||
|
||
## 四、为什么不要直接按 layout 入库
|
||
|
||
如果把 `layouts.json` 的每条 layout 直接做向量:
|
||
|
||
- 颗粒度太碎
|
||
- 标题和正文容易分离
|
||
- 表格会丢失结构上下文
|
||
- 图示信息无法完整表达
|
||
- 检索命中结果噪声较大
|
||
|
||
对于标准文档,最合适的单位通常不是“句子”,而是“条款语义块”。
|
||
|
||
## 五、建议的入库字段
|
||
|
||
建议向量数据库每条记录至少保存:
|
||
|
||
- `embedding_text`:用于生成向量
|
||
- `text`:原始 chunk 文本
|
||
- `chunk_id`
|
||
- `semantic_id`
|
||
- `chunk_type`:`section_text` / `table` / `figure`
|
||
- `section_path`
|
||
- `section_title`
|
||
- `section_level`
|
||
- `page_start`
|
||
- `page_end`
|
||
- `doc_id`
|
||
- `doc_title`
|
||
- `source_ids`
|
||
|
||
其中:
|
||
|
||
- 向量化字段:`embedding_text`
|
||
- 展示字段:`text`
|
||
- 检索增强字段:其余 metadata
|
||
|
||
## 六、推荐的检索方式
|
||
|
||
不要只做最简单的 top-k 向量搜索,建议采用:
|
||
|
||
**向量召回 + metadata 重排 + 邻域扩展**
|
||
|
||
### 1. 向量召回
|
||
|
||
使用 `vector_chunks[*].embedding_text` 做 embedding,并在向量数据库中检索 top 10 ~ 15 条。
|
||
|
||
查询时可以对用户问题做轻微改写,例如:
|
||
|
||
原问题:
|
||
|
||
`儿童三轮车的定义是什么?`
|
||
|
||
可改写为:
|
||
|
||
`请检索 GB 14747—2006 儿童三轮车安全要求 中关于“儿童三轮车定义”的条款、术语、表格或图示说明。`
|
||
|
||
这样更适合标准文档检索。
|
||
|
||
### 2. metadata 重排
|
||
|
||
向量召回后,根据 metadata 做轻量规则重排。
|
||
|
||
常见规则:
|
||
|
||
- `chunk_type == section_text`:对定义类、要求类问题优先级更高
|
||
- `section_path` 命中查询关键词:例如查询“定义”时,`术语和定义` 章节优先
|
||
- `chunk_type == table`:对“尺寸 / 参数 / 数值 / 对照 / 要求”类问题加权
|
||
- `chunk_type == figure`:对“图 / 结构 / 状态 / 示意”类问题加权
|
||
|
||
### 3. 邻域扩展
|
||
|
||
检索命中的是最终切片,但回答往往需要更完整上下文。
|
||
|
||
建议命中某个 `vector_chunk` 后:
|
||
|
||
1. 优先回捞同一个 `semantic_id` 下的所有 chunk
|
||
2. 如果还不够,再补充同 `section_path`、相邻页码或相邻 `chunk_index` 的内容
|
||
|
||
这样可以恢复完整条款,而不是只给模型一小段碎片。
|
||
|
||
## 七、不同问题的检索重点
|
||
|
||
### 1. 定义类问题
|
||
|
||
例如:
|
||
|
||
- `儿童三轮车的定义是什么?`
|
||
- `轮距是什么意思?`
|
||
|
||
优先检索:
|
||
|
||
- `section_text`
|
||
- `section_path` 中包含 `术语和定义` 的内容
|
||
|
||
### 2. 要求类问题
|
||
|
||
例如:
|
||
|
||
- `外露突出物有什么要求?`
|
||
- `辅助推杆有哪些安全要求?`
|
||
|
||
优先检索:
|
||
|
||
- `section_text`
|
||
- `table`
|
||
|
||
### 3. 数值 / 尺寸 / 对照类问题
|
||
|
||
例如:
|
||
|
||
- `鞍座到脚蹬距离要求是什么?`
|
||
- `哪些项目需要满足规定尺寸?`
|
||
|
||
优先检索:
|
||
|
||
- `table`
|
||
- `section_text`
|
||
|
||
### 4. 图示说明类问题
|
||
|
||
例如:
|
||
|
||
- `正常乘骑状态是什么意思?`
|
||
- `图1表示什么?`
|
||
|
||
优先检索:
|
||
|
||
- `figure`
|
||
- 同章节相邻 `section_text`
|
||
|
||
## 八、推荐的最终检索流程
|
||
|
||
建议采用以下固定流程:
|
||
|
||
1. 用 `vector_chunks.embedding_text` 做 embedding 检索
|
||
2. 取 top 10 ~ 15 条候选
|
||
3. 按 `chunk_type + section_path` 做规则重排
|
||
4. 以 `semantic_id` 为中心回捞完整语义块
|
||
5. 选 3 ~ 5 组上下文提供给大模型回答
|
||
|
||
## 九、给大模型的上下文组织方式
|
||
|
||
最终不要直接把原始 JSON 扔给模型,建议整理成如下格式:
|
||
|
||
```text
|
||
[命中片段 1]
|
||
章节:3 术语和定义 > 3.1 儿童三轮车
|
||
页码:1-2
|
||
类型:section_text
|
||
内容:
|
||
......
|
||
|
||
[命中片段 2]
|
||
章节:4 要求 > 4.3 外露突出物
|
||
页码:5
|
||
类型:section_text
|
||
内容:
|
||
......
|
||
|
||
[命中片段 3]
|
||
章节:5 试验方法
|
||
页码:8
|
||
类型:table
|
||
内容:
|
||
......
|
||
```
|
||
|
||
这种格式更利于模型稳定回答并引用出处。
|
||
|
||
## 十、转换命令
|
||
|
||
生成三层结构:
|
||
|
||
```bash
|
||
python3 /home/huaci/dev/ai/SuperMew/tests/layouts_to_vector_chunks.py \
|
||
--layouts /home/huaci/dev/ai/SuperMew/tests/layouts.json \
|
||
--out /home/huaci/dev/ai/SuperMew/tests/vector_chunks.json
|
||
```
|
||
|
||
自定义切片大小:
|
||
|
||
```bash
|
||
python3 /home/huaci/dev/ai/SuperMew/tests/layouts_to_vector_chunks.py \
|
||
--layouts /home/huaci/dev/ai/SuperMew/tests/layouts.json \
|
||
--out /home/huaci/dev/ai/SuperMew/tests/vector_chunks.json \
|
||
--max-chars 500 \
|
||
--overlap-chars 80
|
||
```
|