baodan/docs/PPT与海报生成全链路深度审计报告_20260802.md
wsb1224 7a3e2870dc 主要成果:
海报确认、文案生成、海报生成增加服务端失败关闭门禁,绑定文件哈希、解析快照哈希和确认数据哈希,并返回 422 业务错误。[validators.py (line 65)](D:/work/code/python/coding/baodanagent/api/insurance/plan_data/validators.py:65)
缺失金额不再转换为 0;删除错误字段兜底和“年缴×年期=合同总保费”事实推导;里程碑冲突会阻断确认。[normalizer.py (line 14)](D:/work/code/python/coding/baodanagent/api/insurance/ppt/normalizer.py:14)
PPT 渲染器支持可空金额和实际币种,缺失值显示“待确认”,避免 float(None)、空值除法等异常。
模板必须覆盖全部输入保司和产品;自动选择排序确定化,同优先级歧义时阻断。[template_selection.py (line 4)](D:/work/code/python/coding/baodanagent/api/insurance/ppt/template_selection.py:4)
场景判定写入 scenarioOverrideTrace,记录请求、模板、服务端及 Worker 最终判定。[routes.py (line 555)](D:/work/code/python/coding/baodanagent/api/insurance/ppt/routes.py:555)
前端增加哈希提交、人工调整原因、模板歧义提示及真实能力说明。
冻结三份核心 Schema,并建立 Goldens manifest、说明和评估脚本。
验证结果:
后端目标回归:111 passed, 1 skipped
PPT 运行时回归:81 passed
前端生产构建和 vue-tsc:通过
Python compileall:通过
三份 Schema JSON:解析通过
git diff --check:通过,仅有换行符提示
2026-08-02 12:58:41 +08:00

43 KiB
Raw Blame History

PPT 与海报生成全链路深度审计报告

审计日期2026-08-02
审计范围PDF 解析、结构化数据、人工复核、海报生成、PPT 生成、PPT 模板与场景管理、异步任务、质量校验、测试与数据留存
报告性质:代码级静态审计 + 现有自动化测试验证,不等同于基于生产 PDF 样本的准确率测评

1. 执行摘要

当前问题不是某一个提示词、某一个正则或某一个 PPT 模板配置失效,而是整条链路缺少一个统一、可追溯、可版本化的“计划书事实层”。

目前系统实际上存在三套相互分离的解析路径:

  1. PPT 计划书使用完整解析流程;
  2. 海报计划书优先使用面向里程碑年份的紧凑解析流程;
  3. 产品手册使用另一套页面筛选和大模型解析流程。

三条路径的数据结构、字段兜底、准确性判定和证据保存方式均不一致。因此,同一份 PDF 在海报和 PPT 中可能得到不同的数据;修复其中一条路径,也不能保证另外两条同步正确。

本次审计确认了四个根因级问题:

  • PDF 解析当前主要判断“有没有可读文字”和“字段齐不齐”,没有判断“值是否来自正确的页、表、列和场景”;
  • 海报复核界面没有真正展示原 PDF 和完整利益表,后端又允许空数据或不完整数据被确认;
  • 后台保存的 PPT 页面配置与场景逻辑,在生成阶段被硬编码的 scenarioSlides 和场景检测结果覆盖;
  • 生成任务没有冻结完整的数据、模板和规则版本,排队后再读取可变数据库状态,存在“提交时看到的数据”和“最终生成的数据”不一致的问题。

因此,不建议继续在现有紧凑解析、字段兜底和模板覆盖链路上逐点加补丁。正确方向是:

  • 先建立通用 PDF 文档中间层和字段级证据链;
  • 再形成唯一的、经过校验或人工确认的 PlanData 快照;
  • 海报和 PPT 都只消费该快照;
  • 场景定义负责业务内容,模板负责视觉承载,两者使用显式、可验证的合并规则;
  • 最终导出前进行数值对账,关键字段无法追溯或存在冲突时禁止导出。

1.1 总体风险判断

领域 当前判断 主要风险
PDF 文字读取 可用但不可靠 能读出字符,不代表表格列和场景正确
利益表解析 高风险 多页表、同年份多场景、列错位可能被静默合并
海报数据确认 高风险 无原文对照、无完整表格、后端可确认空数据
PPT 数据标准化 高风险 缺失值变 0、不同保险含义字段互相替代
PPT 场景应用 已确认失效 后台场景与模板页配置被运行时硬编码覆盖
模板资产应用 部分生效 主要作为背景或画框,不是可执行业务模板
异步生成一致性 高风险 未冻结完整输入,队列执行期间数据可漂移
导出质量门禁 不足 质量检查偏存在性检查,且失败通常不阻断导出
自动化测试 代码回归较好、业务准确率证据不足 100 个相关测试通过,但没有真实 PDF 金标准集
隐私与留存 高风险 客户源 PDF 和确认数据缺少完整的到期清理闭环

2. 审计方法、判定边界与成功标准

本报告遵循“先还原事实,再提出方案”的原则。

2.1 审计方法

  • 追踪前端上传、解析、确认、模板选择、生成和下载的调用链;
  • 检查后端解析器、标准化器、比较器、渲染器和异步任务之间的数据契约;
  • 对比后台管理界面承诺的配置能力与运行时真正读取的字段;
  • 检查错误处理、降级、缓存、版本、并发、日志和数据清理;
  • 运行与 PPT、海报、模板、任务生命周期相关的现有自动化测试。

2.2 结论标签

  • 已确认:能够从当前代码路径直接证明,或已有测试/探针验证;
  • 高风险设计:代码结构表明很容易发生,但发生比例需真实 PDF 样本测量;
  • 运行待核验:依赖生产模型、生产数据、部署参数或用户行为,当前仓库无法独立证明。

2.3 本次测试结果

执行以下相关测试集合:

tests/ppt_poster_optimization_test.py
tests/ppt_template_asset_test.py
tests/ppt_task_lifecycle_test.py
tests/ppt_parse_route_test.py
tests/poster_render_document_test.py
tests/poster_render_validation_test.py
tests/user_product_material_test.py

结果为100 passed1 skipped。

这说明当前实现的既有行为具有一定代码回归保护,但不能证明解析准确。仓库内没有可用于端到端验证的真实计划书 PDF 金标准集;现有测试主要使用合成文本、矩阵、临时文件和 mock。被跳过的测试与当前本地 Python 环境缺少 python-pptx 有关,而部署镜像中已有对应依赖配置。

3. 当前真实业务链路

3.1 PPT 链路

PDF 上传
  → ExtractionOrchestrator.extract_plan
  → PDF 文本/OCR候选
  → 正则提取 + 大模型分段提取
  → 字段合并、标准化、完整性评估
  → session.extractions_json
  → 前端 PptDataReview 人工编辑
  → Normalizer 转 DeckContract
  → detect_generation_scenario 硬编码场景识别
  → build_scenario_slides 硬编码页面内容
  → Renderer + fast_pptx_renderer
  → 生成 PPTX
  → 非阻断质量检查

3.2 海报链路

PDF 上传并绑定后台产品
  → extract_for_poster 紧凑解析
  → 少量目标年份与标量字段
  → 若少数字段满足要求则不执行完整解析
  → 映射为海报扁平字段
  → PosterSourcePanel 人工编辑部分字段
  → confirm_case_upload
  → ContentBuilder 再次推导利益数据和文案
  → 海报渲染

3.3 产品手册链路

产品手册 PDF
  → manual_parser 页面关键词筛选
  → 大模型提取产品规则
  → 人工确认
  → 产品资料解析结果
  → 海报文案/规则来源

3.4 核心断点

PPT 和海报没有消费同一份确认后的事实数据。海报紧凑解析不是完整计划书数据的一个只读视图,而是一条独立解析路径;产品手册也没有共享统一的页、文本块、表格和证据表示。这是数据不一致的首要根因。

4. 已确认问题清单

4.1 P0可能直接造成错误金额、错误比较或配置完全不生效

编号 问题 证据位置 影响
P0-01 海报与 PPT 使用不同计划书解析路径 api/insurance/ppt/extraction.py、api/insurance/poster/tasks.py 同一 PDF 可产生两套不一致数据
P0-02 PDF 解析只验证字符可读性,不验证表格列、行和场景归属 extraction.py 的 _extract_pdf_text、_looks_corrupted、_score_page_quality 列错位仍可能被判为高质量
P0-03 利益表按 policy_year 单键去重 extraction.py 的 _merge_rows_by_policy_year 同年份不同场景或不同表格可能互相覆盖
P0-04 多页利益表筛页依赖关键词命中,续页可能被漏掉 extraction.py 的 _llm_extract_split 首页面表头保留,后续数据页丢失
P0-05 海报紧凑解析只要少量标量字段不失败,就不回退完整解析 poster/tasks.py 的 _execute_case_parse 利益表错误或缺失仍进入确认
P0-06 海报确认接口不校验必填字段、解析状态和业务一致性 poster/service.py 的 confirm_case_upload 空对象也可被确认并进入生成
P0-07 海报复核没有挂载原 PDF 预览,也没有完整利益表编辑 PosterSourcePanel.vuePdfPagePreview.vue 未被引用 用户无法发现列错、页错、场景错
P0-08 后台 slidesConfig 被 scenarioSlides 优先覆盖 fast_pptx_renderer.py 的 _resolve_page_types、render_deck 后台设置页序、页面类型和内容提示不生效
P0-09 后台场景被文件类型硬编码识别结果覆盖 generation/celery_tasks.py、comparison.py 管理员配置的同类/异类比较逻辑无法控制生成
P0-10 场景数据模型没有真正的选择规则、比较指标和计算配置 models/ppt_config.py 后台只能保存标签,无法表达所需业务逻辑
P0-11 缺失或解析失败的数值大量被标准化为 0 normalizer.py、comparison.py “未知”被当成真实 0 参与图表和比较
P0-12 重疾计划把缺失的 totalSurrenderValue 直接设为 deathBenefit normalizer.py 退保价值与身故保障被错误等同
P0-13 多处储蓄险页面硬编码 US$ fast_pptx_renderer.py HKD、CNY、SGD 等保单展示错误币种
P0-14 生成任务不冻结结构化数据和完整模板配置 generation/celery_tasks.py 排队期间编辑可能改变最终输出
P0-15 任务完成时用当前 draft_revision 标记 generated_revision generation/celery_tasks.py 旧数据生成物可能被错误标记为最新版本
P0-16 导出后质量检查不做完整数值对账,失败通常不阻断成功状态 renderer.py、质量检查相关代码 明知存在警告或缺失仍可交付
P0-17 没有真实 PDF 金标准回归集和端到端数值一致性门禁 tests、api/insurance/ppt/golden_test.py 无法度量“精准”或阻止准确率回退

4.2 P1高概率造成错误、漂移、不可维护或合规风险

编号 问题 证据位置 影响
P1-01 文本最大长度 120000 字符,可能从中间截断后续页面 extraction.py 长计划书后段条款或表格丢失
P1-02 OCR 最多处理 40 页 extraction.py 的 _extract_pdf_text_ocr 长扫描件后续页面无法解析
P1-03 OCR 触发主要依赖乱码/低分,版面错序但字符可读时不触发 extraction.py 多栏和复杂表格错误不能被补救
P1-04 大模型利益表 schema 约束较弱,行字段和类型不严格 extraction.py 的 _llm_extract_split 输出结构和列含义不稳定
P1-05 source_page 由模型给出,未与真实坐标证据绑定 extraction.py 页面号本身可能不可信
P1-06 正则结果因“有标签”就可覆盖大模型结果 extraction.py 的 _merge_extraction_data 错误正则值可能静默取得优先权
P1-07 文件名提示可覆盖产品、年龄、币种、缴费期等字段 extraction.py 的 _apply_filename_hints 重命名文件或命名不规范会污染事实数据
P1-08 后台所选产品名称优先于 PDF 提取名称 extraction.py、poster/tasks.py 上传错 PDF 时错误被掩盖
P1-09 准确性评估侧重字段完整度,不校验字段正确性 extraction.py 的 assess_extraction_payload 内容错误也可能返回 success
P1-10 provenance 是按解析方法估算置信度,不是字段级原文证据 extraction.py 的 _build_provenance 无法审计每个金额从哪里来
P1-11 缓存键没有完整纳入模型、OCR、规则和产品提示版本 extraction.py 配置变更后可能复用旧结果
P1-12 海报映射使用 Python or 选择数值,合法的 0 会被当成缺失 poster/tasks.py、content_builder.py 零值或特殊产品数据被替换
P1-13 年缴保费乘缴费年期推导总保费 poster/tasks.py、normalizer.py 折扣、附加险、缴费频率等情况下错误
P1-14 海报非保证价值字段与完整解析字段命名不一致 poster/content_builder.py 红利和终期红利可能显示为 0
P1-15 显式里程碑字段可覆盖利益表值,未做一致性检查 poster/content_builder.py 同一海报内部事实来源冲突
P1-16 海报产品匹配只比较用户选择的产品 ID不比较 PDF 内容 poster/service.py 等 错误计划书可能套用另一产品模板和名称
P1-17 海报 case 对外返回服务器绝对源文件路径 models/poster_case_upload.py 暴露内部目录结构
P1-18 PPT 模板适用产品使用“有任一交集”判断 PPT 后端模板路由、PptGenerate.vue 多产品比较时不兼容模板仍可通过
P1-19 前端重复实现硬编码场景检测 PptGenerate.vue、comparison.py 前后端规则漂移
P1-20 模板自动选择未显式排序 generation/celery_tasks.py 多模板匹配时结果可能不稳定
P1-21 历史记录可能只保存请求 template_id而非实际解析出的模板 ID generation/celery_tasks.py 无法准确追溯实际使用模板
P1-22 源 PPT 中的文本、图表、表格和多数形状被清除 fast_pptx_renderer.py 的 _sanitize_template_shape 所谓模板主要退化为背景画框
P1-23 页面匹配失败时静默使用第一张未用页面 fast_pptx_renderer.py 的 _auto_frame_map 版式与业务页类型可能错配
P1-24 模板只要能解析就被标记 clone_ready ppt_admin_service.py 没有槽位、页类型和场景契约校验
P1-25 比较年份固定为 5、10、20、30 年 comparison.py 管理后台不能配置重点年份
P1-26 跨计划兼容性主要只阻断币种,其他条件多为警告 comparison.py 不同年龄、性别、缴费条件可能被直接横向比较
P1-27 PptDataReview 只能编辑 source_page 数字,不能并排看 PDF PptDataReview.vue 人工复核缺乏可信证据
P1-28 extraction 更新按 pdfName 匹配 PPT session 更新逻辑 同名 PDF 可能碰撞或更新错对象
P1-29 前端 modificationLog 没有被后端持久化 PptDataReview.vue、更新接口 看似有审计记录,实际刷新后丢失
P1-30 客户源 PDF 和确认数据缺少完整留存到期清理 api/insurance/utils/cleanup.py 敏感保险资料可能长期保留

4.3 P2影响易用性、可观察性和长期演进

编号 问题 影响
P2-01 海报 parsed 状态资料可能被列为可用,但实际解析结果解析器要求 confirmed 用户会看到不可用材料
P2-02 场景管理前端可新增/删除/启停,但缺少完整编辑能力 配置维护困难
P2-03 前端文案暗示可手动选择场景,生成页实际没有手动选择入口 管理员预期与用户行为不一致
P2-04 多个模板风格映射到同一 renderer theme 后台风格选项视觉差异有限
P2-05 旧 parse_worker.py 与当前 Celery 路径并存 容易误维护非生效代码
P2-06 长任务恢复主要依赖创建时间,不是心跳 合法长任务可能被误判为超时
P2-07 缺少按保险公司、产品、版式、扫描类型的解析质量看板 无法定位准确率下降来源
P2-08 缺少生成后视觉回归和溢出检测的稳定门禁 模板变化可能造成版式回退

5. PDF 解析为何“不精准”

5.1 当前判断标准错把“可读”当成“正确”

系统会尝试 PyMuPDF、PyPDF2、pypdf、pdfplumber 等读取方式,但一般采用首个看起来字符正常的结果。现有质量评分关注:

  • 可读字符比例;
  • 保险关键词命中;
  • 数字密度;
  • 是否存在 CID 等乱码。

这些指标只能发现“读不出来”,发现不了以下更危险的问题:

  • 左右两列被串成一行;
  • 表头与数值错位;
  • 页脚和正文混入表格;
  • 基本情景、红利情景、退保情景的同年份行被混在一起;
  • 跨页表格第二页没有表头,因而被当作普通数字文本;
  • 一个金额被正确识别,但归到了错误字段。

因此,当前 success 更接近“数据结构填出来了”,不是“数据已与 PDF 对账正确”。

5.2 表格被降维为文本后,几何信息永久丢失

保险计划书的核心不是连续段落,而是复杂表格。当前流程早期就把 PDF 页面降成线性文本,大部分字段后续只能依靠关键词、正则和大模型猜测列关系。

一旦丢失以下信息,后续再强的大模型也难以稳定还原:

  • 文本块坐标;
  • 行列边界;
  • 合并单元格;
  • 表头层级;
  • 表格跨页关系;
  • 同一页多个表格的归属;
  • 颜色、线条和版面分区。

通用 PDF 解析层必须先保存这些信息,再生成便于模型理解的语义视图,而不是只保存纯文本。

5.3 按年份去重会破坏保险场景

当前利益行合并以 policy_year 为核心键。同一个年份可能同时存在:

  • 保证现金价值;
  • 非保证红利;
  • 基本/悲观/乐观情景;
  • 提取前和提取后;
  • 身故赔偿;
  • 退保价值;
  • 不同币种或不同单位。

仅按年份合并会导致正确的多场景数据被当成重复行。当前实现倾向选择“非空字段更多”的一行,而不是保留每张表、每个场景的身份,也不会可靠合并互补字段。这是数据与原 PDF 对不上最关键的代码级原因之一。

5.4 紧凑解析优化了速度,却提前丢弃了事实

海报紧凑解析最多选少量页面和少量目标年份。对只展示 10、20、30 年价值的海报来说,这一思路看似合理,但它把“展示需求”错误地提前放到了“事实提取阶段”。

正确顺序应为:

  1. 完整、准确地解析计划书;
  2. 形成唯一事实快照;
  3. 海报视图再从快照中选择 10、20、30 年。

当前顺序是先按海报需要删减页面,再尝试从残缺上下文恢复事实。这样无法保证页表连续性,也无法在后续 PPT 中复用。

5.5 文件名和后台选择不应覆盖 PDF 事实

文件名、后台所选产品和用户输入可以作为提示或冲突检测条件,但不能静默覆盖 PDF 中提取出的事实。

建议将来源拆为:

  • document_valuePDF 中有证据的值;
  • context_hint文件名、所选产品或上游业务上下文
  • confirmed_value用户确认后的值
  • conflictdocument_value 与 context_hint 不一致。

只有 confirmed_value 才能成为最终生成快照。任何覆盖都必须留痕。

6. 海报生成专项问题

6.1 复核流程实际上无法完成“核对”

PosterSourcePanel 当前主要展示文件名、若干基础字段和里程碑值。虽然项目已有 PdfPagePreview 组件,但它没有在实际复核页中挂载。用户看不到:

  • 当前字段对应的原 PDF 页面;
  • 原文片段;
  • 表格所在区域;
  • 完整逐年利益表;
  • 保证与非保证列;
  • 多个情景之间的差异;
  • 哪些值来自自动提取、推导、后台产品或人工输入。

所以“确认”按钮表达的是业务确认,但界面只提供了有限编辑,无法支撑真正的事实确认。

6.2 后端没有承担最终数据门禁

确认接口对 confirmedData 缺少严格 schema 和业务校验。即使前端后续补了校验,也不能替代后端,因为请求可以绕过前端。

确认时至少必须验证:

  • 解析任务状态成功;
  • 文件哈希与解析快照一致;
  • 关键身份字段非空;
  • 年龄、缴费期、保费和保额在合理范围;
  • 币种明确;
  • 海报展示的每个金额有证据或明确人工录入原因;
  • 完整利益表和里程碑字段互相一致;
  • 所选产品与 PDF 产品一致,或已有带原因的人工覆盖;
  • 未解决的冲突为零。

6.3 海报字段二次推导制造了新的差异

海报在解析后又通过映射和 ContentBuilder 重组数据。期间存在:

  • 使用 or 导致 0 值被当成缺失;
  • 年缴保费乘年期推导总保费;
  • 非保证字段命名与完整解析不一致;
  • 显式里程碑值覆盖利益表值;
  • 后台所选产品名优先于 PDF 产品名。

因此,即使上游解析正确,海报最终展示也可能再次偏离。

7. PPT 模板与场景配置为何没有应用

7.1 已确认的覆盖链

后台模板编辑器会保存 slidesConfig场景管理会保存 scenarioTag、baseScenario 和 generationMode 等字段。但实际生成时:

  1. comparison.detect_generation_scenario 根据上传文件类型重新推断场景;
  2. generation_mode_for_scenario 根据硬编码映射重新计算 generationMode
  3. build_scenario_slides 根据硬编码场景生成页面列表和内容;
  4. fast_pptx_renderer 优先使用 scenarioSlides
  5. 只有 scenarioSlides 不存在时,才会使用模板 slidesConfig 或 requiredPageTypes。

而正常生成总会构造 scenarioSlides所以后台 slidesConfig 在页面业务逻辑上的优先级实际上永远排在后面。

这不是偶发现象,而是确定的代码优先级。

7.2 当前后台配置“看起来能配”,数据模型却无法执行

用户所说的“同类型计划书对比”和“不同计划书比较”至少需要以下配置:

  • 选择条件:计划类型、产品、公司、数量、币种、客户条件;
  • 兼容规则:哪些字段必须一致,哪些允许差异;
  • 分组方式:按险种、产品、公司或计划分组;
  • 指标定义保费、现金价值、身故利益、IRR 等;
  • 比较年份;
  • 缺失值策略;
  • 排名和结论规则;
  • 页面构成;
  • 图表类型;
  • 不满足条件时的降级或阻断方式。

当前 PptScenario 模型只有名称、编码、基础场景、生成模式、描述等元数据,无法保存以上可执行规则。因此,即使运行时不覆盖,它也不足以表达完整业务逻辑。

7.3 模板当前更像“视觉皮肤”

上传源 PPT 后,渲染器会清除其中的文本、图表、表格和大量形状,再使用固定坐标的页面构造器绘制内容。模板主要提供:

  • 背景;
  • 部分保留形状;
  • 页面尺寸与视觉框架;
  • 自动匹配的源页面。

它没有稳定的语义槽位契约,所以不能可靠表达“把某个比较表放在这里、把某个结论放在那里”。页面匹配失败时还会静默选取第一张未用页面,进一步增加错配风险。

7.4 建议重新定义三个概念

概念 应负责什么 不应负责什么
ScenarioDefinition 业务选择规则、页面语义、比较指标、年份、数据要求 具体品牌颜色和坐标
TemplateDefinition 视觉主题、页面母版、语义槽位、字体与版式 判断用哪种保险比较逻辑
GenerationPolicy 兼容性、缺失值、排名、阻断和降级规则 任意修改源数据

运行时应先得到确定的 ScenarioDefinition再把其页面语义映射到 TemplateDefinition 的槽位。合并规则必须唯一、显式、可测试,不应存在隐藏覆盖。

8. PPT 数据标准化和比较逻辑问题

8.1 缺失值不能等于零

当前多个 _safe_number 或 _number 辅助方法把空值、非法值转换成 0。保险计划书中以下三种状态必须分开

  • 明确为 0
  • PDF 没有该字段;
  • 解析失败或存在冲突。

如果合并,图表会把未知当成零,排名和 IRR 也会被污染。目标数据模型应使用 value、status、reason 和 evidence 表示,而不是只传一个 number。

8.2 不同保险概念不能互相兜底

本次发现的高危示例包括:

  • deathBenefit 代替 totalSurrenderValue
  • accountValue、cashValue、nonGuaranteedCashValue 互相兜底;
  • sumAssured 代替缺失的 deathBenefit
  • annualPremium 乘年期代替合同总保费。

这些字段在业务上可能相关,但不是同义词。可以将其作为“待人工确认的推断候选”,不能作为无提示的最终值。

8.3 比较条件不完整

当前主要强制阻断混合币种,年龄、性别等差异多以警告处理。至少还需考虑:

  • 年龄与性别;
  • 吸烟状态;
  • 保费与缴费期;
  • 保额;
  • 币种与汇率时点;
  • 保单年度与受保人年龄;
  • 保证/非保证情景;
  • 提取计划;
  • 红利实现率或演示利率;
  • 是否包含附加险。

不满足同口径时,不应生成“谁更优”的结论;最多只能并列展示,并清楚标记不可直接比较。

9. 异步任务、版本和可复现性

生成任务虽然保存了部分 input_snapshot但工作进程仍会读取 session.extractions_json 和当前模板数据库记录。由此产生以下竞态:

  1. 用户提交生成任务;
  2. 任务在队列中等待;
  3. 用户修改解析数据,或管理员修改模板/场景;
  4. Worker 启动并读取新状态;
  5. 输出与提交时预览不一致;
  6. Worker 又用当前 draft_revision 标记 generated_revision。

目标做法是生成前冻结完整快照:

  • PlanData 快照 ID、版本和内容哈希
  • 每个源 PDF 哈希;
  • ScenarioDefinition 版本和哈希;
  • GenerationPolicy 版本和哈希;
  • TemplateDefinition 版本、配置哈希和源资产哈希;
  • renderer 版本;
  • 用户选择和覆盖记录;
  • 提交时 draft_revision。

Worker 只能读取该不可变快照。完成时若任务 revision 与当前 session revision 不一致,应标记“已生成旧版本”,不能冒充最新输出。

10. 目标架构:统一 PDF 事实层

10.1 分层原则

PDF 原文件
  → Document IR页、坐标、文本块、表格、图片、OCR候选
  → Extraction Candidates多个解析器的字段候选
  → Reconciliation规则校验、冲突检测、场景归属
  → Review原 PDF 与字段/表格并排确认
  → Confirmed PlanData Snapshot唯一事实快照
  → Poster Projection / PPT DeckContract
  → 模板和场景组合
  → 渲染
  → 输出数值对账与视觉检查

10.2 建议的核心数据结构

Document

  • document_id、sha256、page_count
  • parser_version、ocr_version
  • pages
  • document_type
  • extraction_profile
  • 处理日志与耗时。

Page

  • page_number、width、height
  • text_blocks每个包含 bbox、文本、字体信息和读取顺序
  • tables每个包含 bbox、行列、合并单元格和跨页关系
  • images
  • native_text_quality、ocr_quality
  • 页面分类。

EvidenceRef

  • document_id、page_number、bbox
  • raw_text、text_hash
  • extractor、extractor_version
  • confidence
  • table_id、row_id、column_id
  • evidence_status。

FieldValue

  • raw_value、normalized_value
  • unit、currency
  • statusauto、confirmed、conflict、missing、derived
  • evidence 列表;
  • derived_formula
  • confirmed_by、confirmed_at、override_reason。

ScenarioTable

  • table_id、scenario_type
  • currency、unit
  • headers
  • rows
  • source_pages
  • continuation_of
  • table_conflicts。

PlanData

  • identity
  • policy
  • insured
  • benefits_by_scenario
  • withdrawals_by_scenario
  • riders
  • assumptions
  • validation_results
  • snapshot_version、snapshot_hash。

10.3 解析策略

建议采用“确定性表格提取优先,大模型做语义识别”的组合:

  1. 判断原生文本、扫描、混合型 PDF
  2. 同时保存原生文本块与页面坐标;
  3. 使用线条、坐标聚类和表头关系恢复表格;
  4. 对扫描页或低可信区域执行 OCR并保留 OCR 坐标;
  5. 通过保险公司/产品/版式 profile 识别列语义;
  6. 通用 profile 作为未知版式回退;
  7. 大模型用于页面分类、表头语义、同义词映射和段落摘要;
  8. 大模型不作为密集金额表逐格抄录的唯一来源;
  9. 多解析候选产生冲突时保留冲突,不静默选一个;
  10. 通过业务约束和人工复核形成确认快照。

10.4 关键校验规则

  • 币种必须在文档内一致,或每张表明确币种;
  • 保单年度与受保人年龄关系必须一致;
  • 保证、非保证、总额之间满足明确公式时必须对账;
  • 同年份不同场景不得合并;
  • 身故利益与退保价值不得互相替代;
  • 提取前后、不同红利实现率必须分场景保存;
  • 每个导出金额必须有 EvidenceRef 或带理由的人工录入记录;
  • 任何冲突不得被 completeness 分数掩盖;
  • 不把缺失值变成 0
  • 文件名和后台选择只作为提示或冲突源,不覆盖文档事实。

11. 目标架构:模板和场景真正可配置

11.1 场景定义

ScenarioDefinition 建议至少包含:

  • code、name、version、status
  • selector文件数量、险种组合、产品组合和客户条件
  • compatibilityRules
  • pageSpecs
  • metricSpecs
  • comparisonYears
  • calculationPolicy
  • missingValuePolicy
  • conclusionPolicy
  • fallbackScenario
  • sampleFixtureIds
  • publishedAt。

每个 pageSpec 使用稳定 page_type 和 content_contract例如

  • 输入字段;
  • 表格列;
  • 图表序列;
  • 标题模板;
  • 数据为空时的行为;
  • 不允许比较时的说明。

11.2 模板定义

TemplateDefinition 建议包含:

  • 支持的 page_type
  • 每种 page_type 对应的母版页;
  • semantic slots
  • 字体、颜色、间距和图表主题;
  • 每个槽位的容量与溢出规则;
  • 支持的 ScenarioDefinition 版本范围;
  • 预览样本;
  • draft、validated、published 状态。

上传 PPT 后不能只验证“文件可打开”。必须验证每个必需 page_type 都有可用槽位,且样本渲染通过。

11.3 明确解析优先级

建议运行时优先级:

  1. 用户明确选择的已发布场景,且输入满足其 selector
  2. 规则精确匹配到唯一已发布场景;
  3. 使用明确配置的 fallback
  4. 多个同优先级匹配或无匹配时阻断,并展示 matchTrace。

禁止运行时静默把管理员或用户选择替换为硬编码值。

matchTrace 应记录:

  • 输入事实;
  • 命中的规则;
  • 未命中的规则与原因;
  • 最终场景和版本;
  • 模板和版本;
  • 页面合并结果;
  • 所有降级。

12. 分阶段整改计划

阶段 0先止损和建立基线

目标:在大重构前阻止明显错误继续流入输出。

  • 海报确认接口增加后端 schema、必填、冲突和状态校验
  • 海报复核页接入 PDF 预览和完整利益表;
  • 禁止未解析、解析失败和空 confirmedData 进入生成;
  • 禁止缺失值自动变 0
  • 删除 deathBenefit 代替退保价值等错误兜底;
  • 统一所有页面币种显示;
  • 修复多产品模板适用条件,要求全部产品满足;
  • 记录当前场景覆盖链并临时在后台明确标注“仅视觉配置”,避免误导;
  • 建立第一批真实 PDF 金标准集和人工双人复核基线。

验收:

  • 空数据和未解决冲突无法确认;
  • 海报所有展示金额可从界面跳转到 PDF 证据;
  • 同一快照的海报与 PPT 基础字段完全一致;
  • 不再出现未知值显示为 0
  • 不再出现非美元保单显示 US$。

阶段 1建立通用 Document IR 与字段证据链

目标:解决“读到字符但对应不上原 PDF”。

  • 新建统一 PDF 文档层;
  • 保存页面坐标、原生文本块、OCR 文本块和表格结构;
  • 引入 PDF 类型识别和 parser profile
  • 设计 FieldValue、EvidenceRef、ScenarioTable
  • 取消按年份单键去重;
  • 引入跨页表格识别;
  • 文件名和产品选择改为冲突提示;
  • 复核界面支持字段、表格、原 PDF 双向定位。

验收:

  • 每个关键金额具有页码和 bbox
  • 所有表格行保留 table_id、scenario_type 和 source_page
  • 同年份多场景数据零污染;
  • 真实样本集达到第 15 节的准确率门槛。

阶段 2统一 PlanData 快照,海报/PPT 共用

目标:消除两套计划书事实。

  • 完整解析只执行一次;
  • 海报紧凑数据改为 PlanData 的 projection不再独立解析
  • 海报和 PPT 均引用 confirmed snapshot ID
  • 所有派生字段记录公式和输入证据;
  • 重构字段命名,统一保证、非保证、红利、退保和身故利益语义;
  • 为旧数据提供显式迁移或重新解析策略。

验收:

  • 同一 PDF 在海报和 PPT 中字段、币种和里程碑值 100% 一致;
  • 任何修改都会产生新 snapshot version
  • 已生成物可追溯到唯一 snapshot hash。

阶段 3重构 PPT 场景与模板系统

目标:后台配置真正控制业务生成。

  • 扩展 ScenarioDefinition 和 GenerationPolicy
  • 取消硬编码场景对后台选择的静默覆盖;
  • 统一前后端场景解析,由服务端返回结果和 matchTrace
  • 明确 scenario pageSpecs 与 template slots 的合并契约;
  • 模板建立 draft、validated、published 生命周期;
  • 管理后台增加样本预览和规则测试;
  • 比较年份、指标和兼容条件可配置;
  • 历史记录保存实际场景、模板和规则版本。

验收:

  • 修改已发布场景的比较年份后,样本 PPT 页面和数据随版本变化;
  • 修改模板页面映射后,输出页面顺序按配置变化;
  • 不兼容输入被阻断并给出命中轨迹;
  • 同一输入和同一版本重复生成得到相同结构结果。

阶段 4冻结任务输入并建立导出门禁

目标:保证生成可复现,交付物与确认数据完全一致。

  • 任务保存完整不可变输入快照;
  • Worker 禁止读取可变 session 当前值;
  • 使用提交时 revision 标记生成物;
  • 生成后解析 PPTX XML对文本、表格和图表序列与 DeckContract 对账;
  • P0 数值不一致直接失败;
  • 增加页面溢出、遮挡、空图、空表和字体检查;
  • 建立任务心跳和幂等策略。

验收:

  • 队列等待期间修改数据不会改变已提交任务;
  • 所有导出金额与 PlanData 快照对账 100%
  • 不一致时不产生可下载“成功”文件;
  • 任务、输出和审计日志可完整重放。

阶段 5质量运营、隐私与持续回归

目标:让准确率可持续,而不是一次性修复。

  • 按保险公司、产品、版式和 PDF 类型统计字段准确率;
  • 记录人工修改率、冲突率、OCR 率和失败原因;
  • 建立解析 profile 灰度发布;
  • 建立真实样本回归、视觉回归和性能基线;
  • 完善客户 PDF、确认快照、生成物和日志的留存策略
  • 对源文件下载使用受控接口,不暴露服务器路径;
  • 敏感字段脱敏并限制日志内容。

13. 建议的代码边界

遵循现有项目原则,全部自研改造继续放在 api/insurance 下,不侵入 BaoDan 其他目录。建议结构:

api/insurance/document/
  ingest.py
  pdf_classifier.py
  native_text.py
  ocr.py
  layout.py
  tables.py
  evidence.py
  schemas.py

api/insurance/plan_data/
  profiles/
  extractors/
  reconcile.py
  validators.py
  snapshots.py
  projections.py

api/insurance/generation/
  scenarios.py
  policies.py
  resolver.py
  snapshots.py
  reconcile_output.py

现有 api/insurance/ppt 和 api/insurance/poster 保留各自的展示与渲染责任,但不再各自定义 PDF 事实。

14. 测试矩阵

14.1 PDF 样本维度

  • 原生文本、全扫描、混合 PDF
  • 单栏、多栏、横向页面;
  • 有线框表、无线框表、合并表头;
  • 单页表、跨页表、续页无表头;
  • 中文、英文、中英混合;
  • 不同保险公司和产品系列;
  • 同一年存在多个利益情景;
  • 文件名正确、错误、无业务含义;
  • 加密、损坏、超长和大文件;
  • OCR 模糊、倾斜、低对比度。

14.2 数据正确性测试

  • 字段精确值;
  • 表格单元格精确值;
  • 行召回率;
  • 场景分类;
  • 页码和 bbox
  • 币种和单位;
  • 年龄/年度关系;
  • 保证、非保证和总额公式;
  • 冲突触发;
  • 未知值保持未知;
  • 人工覆盖和审计记录。

14.3 海报测试

  • PDF 与复核字段双向定位;
  • 完整利益表编辑;
  • 不允许空确认;
  • 产品冲突阻断;
  • 同一 PlanData projection 的值一致;
  • 文案与数值来源分离;
  • 海报渲染文本与快照对账;
  • 中文溢出、金额长度和多币种。

14.4 PPT 测试

  • 后台场景 selector 命中;
  • 同类比较、跨类型比较和不兼容输入;
  • 管理员修改比较年份后输出变化;
  • 管理员修改 pageSpecs 后输出变化;
  • 模板槽位缺失时发布失败;
  • 场景与模板版本追溯;
  • 表格、图表和结论文案与 DeckContract 对账;
  • 队列中修改 session 不影响已提交任务;
  • 同输入重复生成的确定性;
  • 多产品模板必须全部适用。

14.5 视觉与稳定性测试

  • 每页渲染为图片做基线对比;
  • 文本越界、重叠、裁切;
  • 图表空序列和异常比例;
  • 不同 Office/WPS 打开兼容性;
  • 超长名称、长数字和英文断行;
  • 并发生成和重复提交;
  • Worker 重试、超时和幂等;
  • 源 PDF、快照和输出的到期清理。

15. 可量化验收标准

“感觉更准”不能作为上线标准。建议至少建立以下门槛:

指标 建议门槛
关键标量精确率:年龄、性别、币种、保费、缴费期、保额 ≥ 99.5%
金额表格单元格精确率 ≥ 99.0%
利益表目标行召回率 ≥ 99.0%
导出金额具有正确页码与区域证据 100%
同年份不同场景污染 0
未解决冲突被自动确认 0
未知值被静默变为 0 0
海报与 PPT 同快照字段一致率 100%
PPT 文本、表格、图表与 DeckContract 数值一致率 100%
后台已发布场景和模板配置应用测试 100%
生成物可追溯到数据、场景、模板和渲染器版本 100%

建议首批金标准集不少于 30 份,覆盖至少 3 家保险公司、多个产品版式和原生/扫描/混合 PDF在进入稳定运营前扩展到 50 至 100 份。关键样本必须由两名业务人员独立标注并处理分歧。

16. 监控与审计指标

每次解析建议记录:

  • 文档哈希、类型、页数和 profile
  • 原生文本页数、OCR 页数和耗时;
  • 每个字段的 extractor 和 confidence
  • 字段冲突数;
  • 表格数、跨页表数、未识别列数;
  • 自动通过、人工修改和阻断数量;
  • 按字段统计的人工修改率;
  • 最终快照版本。

每次生成建议记录:

  • 输入 snapshot hash
  • scenario、policy、template 和 renderer 版本;
  • matchTrace
  • 页面类型列表;
  • 降级和警告;
  • 输出数值对账结果;
  • 视觉检查结果;
  • 生成耗时和重试次数。

这些指标应能按保险公司、产品、版式和解析版本聚合。人工修改率突然上升通常比任务失败率更早暴露解析回退。

17. 隐私与数据留存

计划书可能包含姓名、年龄、保费、保额和资产安排,属于高度敏感资料。当前清理逻辑没有覆盖所有源 PDF、海报 case 数据和确认快照,且海报 case 返回值包含服务器源文件绝对路径。

建议:

  • 所有源 PDF 通过带鉴权、短时效的下载/预览接口访问;
  • API 不返回服务器绝对路径;
  • 为源 PDF、解析中间产物、确认快照、海报、PPT 和日志分别定义留存期;
  • 到期清理同时删除数据库记录和文件对象,并留下不含敏感内容的审计记录;
  • 日志不得记录原始全文、完整身份证明或大段客户数据;
  • 非生产环境样本必须脱敏;
  • 金标准集使用获批且脱敏的样本。

18. 推荐实施顺序与文件影响

第一批应优先修改的模块:

  1. api/insurance/poster/service.py确认与生成门禁
  2. frontend/src 下海报复核页:接入原 PDF 和完整利益表;
  3. api/insurance/ppt/normalizer.py取消错误兜底和缺失转 0
  4. api/insurance/ppt/scripts/fast_pptx_renderer.py修复币种和配置覆盖
  5. api/insurance/generation/celery_tasks.py冻结任务输入和 revision
  6. api/insurance/ppt/comparison.py把硬编码场景逐步迁移为版本化定义
  7. 新建 api/insurance/document 与 api/insurance/plan_data统一文档和事实层
  8. tests 下建立脱敏 PDF 金标准、字段标注和输出对账测试。

不建议首先大改视觉模板或继续扩充 LLM 提示词。没有统一事实层和证据链时,视觉层越丰富,错误数据传播的范围越大。

19. 仍需运行环境核验的事项

以下问题无法仅凭仓库静态代码得出发生比例,应在实施阶段采集真实证据:

  • 各保险公司 PDF 的实际解析准确率和最常见错列模式;
  • 生产环境所用模型和提示词版本;
  • OCR 在中英混合、低清扫描件上的质量;
  • 当前后台已保存的模板、场景和 slidesConfig 数据;
  • 是否已有用户因错误数据手工修改或放弃生成;
  • 生产队列等待时间是否足以触发输入漂移;
  • 客户 PDF 实际留存规模和访问权限;
  • Office、WPS 与不同字体环境下的渲染差异。

这些待核验项不会改变本报告确认的架构问题,只影响优先级细分和工作量估算。

20. 最终结论

要实现“通用 PDF 精准解析,并基于精准数据生成海报和 PPT”系统必须从“每个功能自己解析、自己兜底、自己生成”转为“一个事实层、多个受控输出”。

最重要的六条决策是:

  1. PDF 先形成包含坐标和表格的 Document IR不能只保留纯文本
  2. 所有关键字段必须有字段级证据,不能用完整度代替正确性;
  3. 海报和 PPT 只能消费同一个确认后的 PlanData 快照;
  4. 场景负责业务逻辑,模板负责视觉槽位,运行时不得静默覆盖后台配置;
  5. 异步任务必须冻结数据、场景、模板和渲染器版本;
  6. 最终导出必须进行数值对账,任何关键金额无法追溯或存在冲突时都应失败关闭。

本次发现的问题中PPT 后台配置不生效、海报无法有效核对、缺失值转 0、错误字段兜底、任务快照漂移和缺少真实金标准均应在继续扩展新模板或新海报功能之前处理。否则新增功能会继续放大同一类数据可信度问题。

附录 A关键证据文件

  • api/insurance/ppt/extraction.py
  • api/insurance/ppt/regex_extractor.py
  • api/insurance/ppt/normalizer.py
  • api/insurance/ppt/validator.py
  • api/insurance/ppt/comparison.py
  • api/insurance/ppt/renderer.py
  • api/insurance/ppt/scripts/fast_pptx_renderer.py
  • api/insurance/generation/celery_tasks.py
  • api/insurance/admin/ppt_admin_service.py
  • api/insurance/models/ppt_config.py
  • api/insurance/poster/tasks.py
  • api/insurance/poster/service.py
  • api/insurance/poster/content_builder.py
  • api/insurance/poster/manual_parser.py
  • api/insurance/models/poster_case_upload.py
  • frontend/src/pages/components/ppt/PptDataReview.vue
  • frontend/src/pages/components/ppt/PptGenerate.vue
  • frontend/src/pages/admin/PptTemplatesAdmin.vue
  • frontend/src/pages/components/ppt/PdfPagePreview.vue
  • frontend/src/components/poster/workspace/PosterSourcePanel.vue

附录 B与现有文档的关系

仓库中已有 PDF 解析、PPT 优化、模板重构和后续演进相关文档。本报告不替代具体实施计划,而是提供一次横跨 PDF、海报、PPT、模板、异步任务、质量门禁和隐私留存的统一根因审计。

后续拆解开发任务时,应以本报告的 P0/P1 优先级、统一 PlanData 快照和可量化验收标准为约束,避免把工作重新拆成互不相通的海报解析优化与 PPT 解析优化。