# 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 passed,1 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.vue;PdfPagePreview.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_value:PDF 中有证据的值; - context_hint:文件名、所选产品或上游业务上下文; - confirmed_value:用户确认后的值; - conflict:document_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; - status:auto、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 解析优化。