# **RVW V2.0 表格提取疑难杂症专项解决方案 (v1.1 务实版)** **问题焦点:** Word 表格“隐性多行”(单元格内多段落)导致的提取与验证错位 **核心策略:** **提取层保持原貌,验证层“懒分裂” (Lazy Split)** **技术栈:** Python (python-docx, pandas) ## **1\. 核心判断:技术选型定调** | 维度 | 方案 A: 视觉模型 (VLM) | 方案 B: 结构重组 (预分裂) | 方案 C: 懒分裂 (推荐) | | :---- | :---- | :---- | :---- | | **原理** | 用 GPT-4V 截图识别 | 提取时把 Table 拆成 N 倍行 | **提取保持 \\n,验证时 split** | | **准确性** | 低 (幻觉/小数点风险) | 中 (容易破坏合并单元格结构) | **高 (数据无损,逻辑灵活)** | | **复杂度** | 高 (GPU/Prompt) | 高 (重构 DataFrame 结构) | **低 (仅在 Validator 中处理)** | | **前端适配** | 难 (无法定位) | 难 (需定制虚拟行渲染) | **易 (原生 HTML \)** | **最终决策:** 1. **坚决不用视觉模型**:数值准确性是底线。 2. **放弃“预分裂”**:不在提取阶段破坏表格的物理结构(Row/Span),避免引入元数据丢失风险。 3. **采用“懒分裂”**:在验证逻辑中,针对特定单元格内容进行 split('\\n'),实现细粒度验证。 ## **2\. 提取层规范 (Extractor Layer)** **目标**:忠实还原 Word 文档的物理结构,不自作聪明地拆行。 ### **2.1 Python 实现逻辑** 在 DocxTableExtractor 中,对于单元格内的多段落,直接使用换行符 \\n 连接。 def extract\_cell\_text(cell): """ 提取单元格文本,保留段落结构 """ \# 过滤掉完全空白的段落,保留有内容的段落 paragraphs \= \[p.text.strip() for p in cell.paragraphs if p.text.strip()\] return "\\n".join(paragraphs) **输出数据结构示例 (JSON)**: { "row\_index": 3, "cells": \[ { "text": "并发症\\n颅内出血\\n牙龈出血" }, // Col 0 { "text": "277 (14.65)\\n85 (4.49)\\n94 (4.97)" }, // Col 1 { "text": "χ²=5.687\\nχ²=0.003\\nχ²=13.745" }, // Col 3 (统计值) { "text": "0.017\\n0.01\\n\<0.001" } // Col 4 (P值) \] } ## **3\. 验证层规范 (Validator Layer)** **核心逻辑:** 验证器在读取数据时,动态检测是否存在多行内容。如果存在,则在内存中“临时分裂”并逐一验证。 ### **3.1 懒分裂验证算法 (Lazy Verification Logic)** def verify\_row\_statistics(row\_data, col\_map): """ 验证单行数据的统计逻辑(支持隐性多行) """ issues \= \[\] \# 1\. 获取目标单元格的原始文本 \# 假设我们要验证 Col 1 (Group A) vs Col 2 (Group B) \-\> P Value cell\_a\_text \= row\_data\[col\_map\['group\_a'\]\] cell\_b\_text \= row\_data\[col\_map\['group\_b'\]\] cell\_p\_text \= row\_data\[col\_map\['p\_value'\]\] \# 2\. 懒分裂 (Lazy Split) lines\_a \= cell\_a\_text.split('\\n') lines\_b \= cell\_b\_text.split('\\n') lines\_p \= cell\_p\_text.split('\\n') \# 3\. 确定对齐基准(取最大行数) max\_lines \= max(len(lines\_a), len(lines\_b), len(lines\_p)) \# 4\. 逐行验证 (Line-by-Line Validation) for i in range(max\_lines): \# 安全获取当前行的数据(处理长度不一致情况) val\_a \= lines\_a\[i\] if i \< len(lines\_a) else "" val\_b \= lines\_b\[i\] if i \< len(lines\_b) else "" \# P 值匹配策略: \# 如果 P 值列只有 1 行,但数据有 N 行 \-\> 广播机制 (Broadcast) \# 如果 P 值列有 N 行 \-\> 一一对应 (One-to-One) if len(lines\_p) \== 1 and max\_lines \> 1: val\_p \= lines\_p\[0\] \# 策略 A: 共享 P 值 else: val\_p \= lines\_p\[i\] if i \< len(lines\_p) else "" \# 策略 B: 独立 P 值 \# 跳过空行 if not val\_a or not val\_b or not val\_p: continue \# 执行具体的统计验证 \# 传入 line\_index=i 以便报错时定位 error \= validate\_single\_line(val\_a, val\_b, val\_p, line\_index=i) if error: issues.append(error) return issues ### **3.2 优势分析** 1. **兼容性强**:完美支持您截图中的 颅内出血 | 85 | 90 | P=0.01 这种每行独立 P 值的场景。 2. **鲁棒性**:如果只有第一行有 P 值(合并单元格视觉效果),代码中的 Broadcast 逻辑也能兜底。 3. **定位精准**:报错信息可以包含 line\_index,告诉前端是单元格里的第几行出错了。 ## **4\. 前端渲染规范 (Frontend Layer)** **目标**:使用最简单的 Web 技术还原 Word 样式,避免过度设计。 ### **4.1 HTML 渲染策略** 后端返回的 html 字段中,直接将 \\n 替换为 \。 **Python 端处理:** def generate\_html\_cell(text): \# 转义 HTML 特殊字符,并将换行转为 \ safe\_text \= html.escape(text) return safe\_text.replace("\\n", "\") **前端展示效果:** \ 277 (14.65)\ 85 (4.49)\ 94 (4.97) \ ### **4.2 错误高亮策略** 由于我们不再拆分表格行(DOM 结构),高亮的最小单位是 **Cell(单元格)**。 * **交互设计**: * 当发现第 2 行子数据错误时,**高亮整个单元格**。 * **Tooltip 提示**:鼠标悬停时,显示具体错误信息:“第 2 行数据 P 值校验不通过”。 * **进阶优化(V2.1 可选)**: * 如果确实需要高亮某一行,Python 生成 HTML 时可以用 \ 包裹每一行: \277 (14.65)\\\85 (4.49)\ * 但 MVP 阶段建议**只高亮单元格**,性价比最高。 ## **5\. 总结** | 模块 | 核心动作 | 复杂度 | | :---- | :---- | :---- | | **Python 提取** | 保持 \\n,不拆行,输出标准 JSON | ⭐ (低) | | **Python 验证** | split('\\n'),循环对齐,独立计算 | ⭐⭐ (中) | | **前端渲染** | 使用 \ 换行,CSS 控制对齐 | ⭐ (低) | | **前端高亮** | 高亮整个单元格,Tooltip 说明行号 | ⭐ (低) | **这是目前最务实、风险最低的实施路径。** 请开发团队以此为准。