# 文档解析与向量检索说明

## 相关文件

- `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
```
