baodan/docs/PDF实时解析提速与精准抽取方案.md
2026-07-28 16:45:14 +08:00

773 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# PDF 实时解析提速与精准抽取方案
## 1. 背景
当前 PPT 生成和海报生成都依赖用户上传 PDF 计划书,再从 PDF 中抽取结构化数据。实际业务中,用户上传的 PDF 不一定是公共资料,也不一定会重复使用,同一份 PDF 缓存命中率有限。
因此,优化目标不能只依赖“同文件缓存”,而应该围绕实时解析链路做改造:
- 少读:不全量读取无关页面
- 少传:不把整本 PDF 文本都交给模型
- 先准:金额、年龄、保费、利益表等字段优先用规则和表格抽取
- 补漏:模型只处理缺失字段和语义判断
- 可追溯:关键字段保留来源页和置信度
- 可降级:快速解析失败时进入精确解析或人工确认
## 2. 总体目标
将现有链路:
```text
上传 PDF -> 全文抽取 -> 截断前 2 万字 -> LLM 完整解析 -> 等待 -> 生成 PPT/海报
```
改造为:
```text
上传 PDF
-> 按页快速抽文本
-> 页面打分和字段覆盖分析
-> 选取关键页
-> 规则/表格优先抽取
-> LLM 补全缺失字段
-> 完整性校验
-> 缺什么补什么
-> 返回可编辑结构化数据
-> 生成 PPT/海报
```
预期效果:
```text
海报解析5-15 秒返回可编辑数据
PPT 快速解析20-60 秒返回初版结构化数据
PPT 精确解析:仅在字段缺失或用户要求时触发
```
## 3. 核心原则
### 3.1 不依赖重复 PDF 缓存
PDF 每次可能不同,所以缓存只能作为辅助能力。主优化方向是让每次实时解析都更轻。
### 3.2 不一次性相信关键页筛选
关键页筛选只是第一步,后面必须有字段完整性校验。如果缺字段,需要定向补页。
### 3.3 不让 LLM 自由生成精确数值
年龄、保费、保额、利益演示表、现金价值、身故赔偿等字段应优先来自规则、表格解析或原文证据。LLM 主要负责:
```text
识别字段语义
补全规则抽不到的内容
总结产品卖点
处理复杂版式
输出统一 JSON
```
### 3.4 每个关键字段都要有来源
关键字段建议保存:
```json
{
"value": 100000,
"source_page": 3,
"confidence": 0.92,
"source_type": "rule"
}
```
这样可以支持前端展示“来自第几页”,方便用户确认。
## 4. 解析模式设计
系统提供两种解析模式。
### 4.1 快速解析模式
默认模式,适合海报和大部分 PPT 初稿。
特点:
```text
只读取关键页
优先规则抽取
LLM 输入控制在 6000-10000 字
返回可编辑结构化草稿
```
适用场景:
```text
海报生成
销售快速演示
用户希望尽快看到结果
```
### 4.2 精确解析模式
用户主动点击,或快速解析校验不通过时触发。
特点:
```text
扩大关键页范围
补充更多候选页面
加强表格解析
必要时读取全文
模型输入允许更大
```
适用场景:
```text
正式 PPT
关键字段缺失
利益演示表识别失败
用户对金额准确性要求较高
```
## 5. 字段清单
### 5.1 海报必填字段
```text
product_name 产品名称
company_name 保司名称
age 年龄
gender 性别
currency 币种
annual_premium 年缴保费
premium_term 缴费期
coverage_period 保障期
sum_assured 保额/保障额
key_benefits 核心利益/卖点
```
### 5.2 PPT 必填字段
```text
product_name 产品名称
company_name 保司名称
plan_type 计划类型
age 年龄
gender 性别
currency 币种
annual_premium 年缴保费
premium_term 缴费期
coverage_period 保障期
sum_assured 基本保额
benefit_illustration 利益演示表
death_benefit 身故保障
cash_value 现金价值/退保价值
```
## 6. 页面筛选策略
### 6.1 按页抽取文本
不要一开始就拼整本文本。应先得到页面数组:
```json
[
{
"page": 1,
"text": "第一页文本",
"char_count": 1200
},
{
"page": 2,
"text": "第二页文本",
"char_count": 900
}
]
```
### 6.2 页面关键词打分
海报关键词:
```text
被保人、投保人、年龄、性别、保费、缴费期、保障期、保额、基本保额、身故、重疾、现金价值、退保价值、利益演示
```
PPT 关键词:
```text
计划书、产品名称、投保摘要、保障摘要、利益演示、现金价值、保证现金价值、非保证、红利、退保价值、身故赔偿、年度保费、缴费期、保障期、内部回报率
```
英文关键词:
```text
Insured, Policyholder, Age, Gender, Premium, Annual Premium, Sum Assured,
Payment Term, Benefit Term, Cash Value, Surrender Value, Death Benefit,
Policy Year, Guaranteed, Non-guaranteed, Illustration
```
### 6.3 页面字段覆盖分析
每页不仅要打分,还要记录它可能覆盖哪些字段:
```json
{
"page": 5,
"score": 18,
"candidate_fields": [
"annual_premium",
"premium_term",
"sum_assured"
]
}
```
### 6.4 页面选择规则
不是固定选前 5 页,而是按字段覆盖选择:
```text
1. 必选首页、摘要页、利益演示页
2. 按得分从高到低加入候选页
3. 加到字段覆盖率达到目标值
4. 或达到最大字符数限制
```
建议限制:
```text
海报:最多 6000 字
PPT 快速解析:最多 10000 字
PPT 精确解析:最多 20000-30000 字
```
## 7. 规则抽取策略
新增规则抽取层,先从关键页文本中抽取确定性字段。
### 7.1 基础字段规则
可优先抽取:
```text
年龄
性别
币种
年缴保费
缴费期
保障期
基本保额
产品名称
保司名称
```
示例规则方向:
```text
年龄:年龄 35 / Age: 35 / Insured Age 35
性别:男 / 女 / M / F / Male / Female
币种USD / HKD / CNY / 美元 / 港元 / 人民币
年缴保费:年缴保费 / 年度保费 / Annual Premium
缴费期:缴费期 5年 / Payment Term 5 Years
保障期:终身 / 至100岁 / Whole Life / To Age 100
基本保额:基本保额 / 保额 / Sum Assured / Basic Sum Assured
```
### 7.2 表格字段规则
利益演示表是 PPT 里最重要、也最容易出错的部分。建议单独解析,不要完全交给模型。
重点识别列:
```text
保单年度
年龄
年度保费
累计保费
保证现金价值
非保证红利
终期红利
总退保价值
身故赔偿
```
英文列:
```text
Policy Year
Age
Annual Premium
Total Premium Paid
Guaranteed Cash Value
Non-guaranteed Bonus
Terminal Dividend
Total Surrender Value
Death Benefit
```
## 8. LLM 补全策略
LLM 不应该收到整本 PDF而应该收到
```json
{
"mode": "poster",
"rule_data": {
"age": 35,
"currency": "USD",
"annual_premium": 100000
},
"missing_fields": [
"gender",
"sum_assured",
"coverage_period",
"key_benefits"
],
"pdf_text": "关键页文本..."
}
```
Prompt 要求:
```text
请基于 PDF 关键页文本补全缺失字段。
已有 rule_data 中的字段优先保留,不要随意覆盖。
如果无法确认,字段返回 null。
金额字段必须来自原文,不得估算。
输出 JSON不要输出 markdown。
```
## 9. 校验与补页闭环
### 9.1 第一次解析后校验
解析后立即运行 validator检查必填字段
```text
字段是否存在
金额是否为正数
币种是否合理
利益演示表是否为空
年度表是否连续
总退保价值是否小于保证现金价值
```
### 9.2 缺失字段定向补页
如果缺字段,不直接失败,而是按缺失字段补页。
示例:
`benefit_illustration` 时,补页关键词:
```text
利益演示、现金价值、退保价值、保证现金价值、非保证、保单年度、Policy Year
```
`sum_assured` 时,补页关键词:
```text
保额、基本保额、保障金额、Sum Assured、Basic Sum Assured
```
`annual_premium` 时,补页关键词:
```text
保费、年缴、年度保费、Annual Premium、Premium
```
### 9.3 补页后再次解析
第二轮只把新增页面和缺失字段交给模型:
```json
{
"missing_fields": ["sum_assured", "benefit_illustration"],
"additional_pages": [
{
"page": 8,
"text": "..."
}
],
"current_data": {
"product_name": "xxx",
"age": 35
}
}
```
## 10. 结果状态设计
解析结果分为四类:
```text
success 字段齐全,关键字段有来源页
partial 基本可用,但存在低置信度字段
needs_review 缺关键字段,需要人工确认或精确解析
error 解析失败
```
建议每个结果保存解析元信息:
```json
{
"parse_meta": {
"mode": "fast",
"selected_pages": [1, 2, 5, 8],
"page_count": 36,
"text_chars": 5820,
"rule_fields": ["age", "annual_premium", "currency"],
"llm_fields": ["coverage_period", "key_benefits"],
"missing_fields": [],
"duration_ms": 12600
}
}
```
## 11. 后端改造范围
### 11.1 新增 PDF 预处理模块
新增:
```text
api/insurance/ppt/pdf_preprocessor.py
```
职责:
```text
按页抽取文本
页面关键词打分
字段覆盖率分析
构建关键页文本
必要时兜底全文抽取
```
核心函数:
```python
def extract_pages(pdf_path: str) -> list[dict]:
pass
def rank_key_pages(pages: list[dict], mode: str) -> list[dict]:
pass
def build_compact_text(key_pages: list[dict], max_chars: int) -> str:
pass
def extract_compact_pdf_text(pdf_path: str, mode: str) -> dict:
pass
```
### 11.2 新增规则抽取模块
新增:
```text
api/insurance/ppt/rule_extractor.py
```
职责:
```text
正则抽基础字段
解析利益演示表
生成字段来源和置信度
```
核心函数:
```python
def extract_basic_fields(text: str) -> dict:
pass
def extract_benefit_rows(text: str) -> list[dict]:
pass
def merge_rule_and_llm_data(rule_data: dict, llm_data: dict) -> dict:
pass
```
### 11.3 改造 PPT 抽取服务
修改:
```text
api/insurance/ppt/extraction.py
```
调整点:
```text
extract_for_poster 使用关键页文本
extract_plan 使用关键页文本
保留 _extract_pdf_text 作为兜底
LLM prompt 改为“补全缺失字段”
返回 parse_meta
```
### 11.4 改造 PPT 后台任务
修改:
```text
api/insurance/ppt/parse_worker.py
```
调整点:
```text
取消 force_reparse=True
支持 fast / accurate 两种解析模式
多 PDF 有限并发,建议并发数 2
进度信息显示当前解析阶段
```
### 11.5 改造海报后台任务
修改:
```text
api/insurance/poster/tasks.py
```
调整点:
```text
海报解析默认 fast 模式
开启缓存作为辅助
保存解析元信息
解析结果允许 partial但前端要求用户确认
```
## 12. 前端改造范围
### 12.1 PPT 解析页面
修改:
```text
frontend/src/pages/components/ppt/PptParsing.vue
```
新增体验:
```text
显示解析模式:快速解析 / 精确解析
显示解析进度:扫描 PDF、筛选关键页、规则抽取、AI 补全、校验
如果 partial显示“精确解析”按钮
展示字段来源页
```
### 12.2 PPT 数据确认页
修改:
```text
frontend/src/pages/components/ppt/PptDataReview.vue
```
新增:
```text
字段置信度展示
来源页展示
缺失字段高亮
用户可手动修正
```
### 12.3 海报上传页
修改:
```text
frontend/src/components/poster/PosterStepUpload.vue
```
新增:
```text
上传后快速解析
解析完成后进入字段确认
字段不完整时允许用户手动补齐
不强制等待精确解析
```
## 13. 接口设计建议
### 13.1 PPT 触发解析
```http
POST /insurance/ppt/parse/{session_id}
```
请求:
```json
{
"mode": "fast",
"force": false
}
```
精确解析:
```json
{
"mode": "accurate",
"force": true
}
```
### 13.2 解析状态返回
```json
{
"sessionId": "xxx",
"status": "parsing",
"progress": 55,
"message": "正在补全缺失字段",
"extractions": [
{
"pdfName": "plan.pdf",
"planType": "savings",
"status": "partial",
"productName": "xxx",
"yearCount": 20,
"error": null,
"parseMeta": {
"mode": "fast",
"selectedPages": [1, 2, 5, 8],
"pageCount": 36,
"textChars": 5820,
"missingFields": ["sum_assured"]
}
}
]
}
```
## 14. 实施顺序
第一阶段:低风险提速
```text
1. 取消 PPT 强制重解析
2. 海报解析开启缓存
3. 增加耗时日志
4. 增加 parse_meta
```
第二阶段:关键页筛选
```text
1. 新增 pdf_preprocessor.py
2. 按页抽文本
3. 页面关键词打分
4. 海报使用关键页文本
5. PPT 使用关键页文本
```
第三阶段:规则抽取
```text
1. 新增 rule_extractor.py
2. 抽取基础字段
3. 抽取利益演示表
4. LLM 只补缺失字段
```
第四阶段:精准闭环
```text
1. validator 检查字段完整性
2. 缺字段定向补页
3. 二次补全
4. 前端展示来源页和置信度
```
第五阶段:体验优化
```text
1. 增加快速解析 / 精确解析切换
2. 增加字段确认页面
3. 支持用户手动修正后生成
4. 记录人工确认后的金标数据
```
## 15. 验收标准
### 15.1 性能标准
```text
海报快速解析:常规 PDF 15 秒内返回
PPT 快速解析:常规 PDF 60 秒内返回
同一会话重复解析:优先命中缓存
多文件解析:支持最多 2 个并发
```
### 15.2 准确性标准
```text
关键金额字段必须有来源页
利益演示表不能由模型凭空生成
缺少必填字段时不能标记 success
partial 状态必须允许用户确认或精确解析
```
### 15.3 用户体验标准
```text
用户能看到解析进度
用户能看到哪些字段缺失
用户能看到关键字段来源页
用户能手动修正解析结果
用户能选择精确解析
```
## 16. 最终结论
PDF 每次都不同的情况下,不能主要依赖同文件缓存。最佳方案是建立实时解析闭环:
```text
关键页筛选
+ 规则/表格优先抽取
+ LLM 补漏
+ 字段校验
+ 缺失字段定向补页
+ 来源页证据
+ 人工确认
```
这套方案既能减少等待时间,又能保证关键数据不会因为”只抽部分页面”而漏掉。快速解析负责效率,校验和补页负责准确性,精确解析和人工确认负责兜底。
## 17. 实施状态
> 更新于 2026-07-27
### 已完成 ✅
| 模块 | 文件 | 说明 |
|------|------|------|
| 正则提取引擎 | `api/insurance/ppt/regex_extractor.py` | 纯正则提取产品信息 + 利益演示表 + 提领表,零 LLM 调用 |
| 产品类型识别 | `regex_extractor._detect_product_type()` | savings / ci / iul 自动识别 |
| 轻量分析 Prompt | `api/insurance/ppt/prompts.py``ANALYSIS_SYSTEM_PROMPT` | 正则提取后仅做关键发现分析(~300字输出 |
| 关键页筛选 | `prompts.select_key_pages()` | 从全文中选取最相关的 4-5 页(~8000字符 |
| Orchestrator 重构 | `api/insurance/ppt/extraction.py` | 正则优先 → 行数≥3用正则+分析 → 不足则回退 LLM |
| LLM 回退 Prompt 精简 | `prompts.py` | SAVINGS/CI/IUL prompt 移除销售分析职责,仅保留数据提取 |
| 任务重命名 | `parse_worker.py` + `PptParsing.vue` | “解析” → “处理/结构化” |
| 缓存版本升级 | `extraction.py` | `CACHE_VERSION` 3 → 4 |
| 繁体中文列名 | `regex_extractor._COLUMN_ALIASES` | AIA/CTF/FWD 风格表头全覆盖 |
| 单元测试 | `tests/test_regex_extractor.py` | 58 个测试全部通过 |
| 前端字段匹配 | `frontend/src/utils/ppt-parser.ts` | 模糊匹配、行标准化、回本计算、数据校验 |
| 死代码清理 | `extraction.py` | 移除未使用的 `import field` |
### 未完成
| 优先级 | 任务 | 说明 |
|--------|------|------|
| 中 | 真实 PDF 集成测试 | 用真实计划书批量验证提取率 > 80% |
| 低 | 提取率报告脚本 | 批量测试工具 |