feat(rvw): Implement RVW V2.0 Data Forensics Module - Day 6 StatValidator
Summary: - Implement L2 Statistical Validator (CI-P consistency, T-test reverse) - Implement L2.5 Consistency Forensics (SE Triangle, SD>Mean check) - Add error/warning severity classification with tolerance thresholds - Support 5+ CI formats parsing (parentheses, brackets, 95% CI prefix) - Complete Python forensics service (types, config, validator, extractor) V2.0 Development Progress (Week 2 Day 6): - Day 1-5: Python service setup, Word table extraction, L1 arithmetic validator - Day 6: L2 StatValidator + L2.5 consistency forensics (promoted from V2.1) Test Results: - Unit tests: 4/4 passed (CI-P, SE Triangle, SD>Mean, T-test) - Real document tests: 5/5 successful, 2 reasonable WARNINGs Status: Day 6 completed, ready for Day 7 (Skills Framework) Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
219
docs/03-业务模块/RVW-稿件审查系统/00-系统设计/RVW V2.0 数据侦探:Word 优先架构技术设计文档.md
Normal file
219
docs/03-业务模块/RVW-稿件审查系统/00-系统设计/RVW V2.0 数据侦探:Word 优先架构技术设计文档.md
Normal file
@@ -0,0 +1,219 @@
|
||||
# **RVW V2.0 数据侦探:Word 优先架构技术设计文档**
|
||||
|
||||
**文档性质:** 最终技术规格说明书 (Final Technical Specification)
|
||||
|
||||
**核心策略:** Word-First (优先处理 .docx/.doc), PDF 作为兜底
|
||||
|
||||
**目标模块:** Python-Service, DataForensicsSkill
|
||||
|
||||
**最后更新:** 2026-02-16
|
||||
|
||||
## **1\. 战略转变:为何选择 Word 优先?**
|
||||
|
||||
针对中文核心期刊的投稿场景,利用 Word 原生结构具有压倒性优势:
|
||||
|
||||
| **特性** | **PDF 处理 (旧方案)** | **Word 处理 (新方案)** | **优势分析** |
|
||||
|
||||
| **表格识别** | 视觉/坐标猜测 (易错) | **对象模型 (Object Model)** | 100% 准确识别表格边界 |
|
||||
|
||||
| **单元格** | 需算法计算合并关系 | **XML 属性读取** | 直接读取 gridSpan/vMerge |
|
||||
|
||||
| **表头匹配** | 寻找附近的文本 | **DOM 节点遍历** | 精确获取 Previous Sibling |
|
||||
|
||||
| **数据清洗** | 需处理乱码/错位 | **纯净文本** | 无需 OCR,编码正确 |
|
||||
|
||||
**结论**:技术路径从“视觉还原”转向\*\*“DOM 解析”\*\*。
|
||||
|
||||
## **2\. 总体处理流水线 (The Pipeline)**
|
||||
|
||||
graph TD
|
||||
Input\[稿件上传\] \--\> FormatCheck{格式检查}
|
||||
|
||||
FormatCheck \--".doc (Binary)"--\> Converter\[LibreOffice 转换服务\]
|
||||
FormatCheck \--".docx (XML)"--\> Parser\[Python-docx 解析器\]
|
||||
Converter \--\> Parser
|
||||
|
||||
subgraph "结构化提取 (Structuring)"
|
||||
Parser \--\> DocTree\[文档对象树\]
|
||||
DocTree \--\> MethodExt\[方法学章节提取\]
|
||||
DocTree \--\> TableExt\[表格对象提取\]
|
||||
end
|
||||
|
||||
subgraph "语义清洗 (Cleaning)"
|
||||
TableExt \--\> CellNorm\[合并单元格填充\]
|
||||
TableExt \--\> HeaderMap\[表头语义映射\]
|
||||
HeaderMap \--\> CleanDF\[Pandas DataFrame\]
|
||||
end
|
||||
|
||||
subgraph "多维验证矩阵 (Verification)"
|
||||
CleanDF & MethodExt \--\> L1\[L1: 算术自洽\]
|
||||
CleanDF & MethodExt \--\> L2\[L2: 统计复核\]
|
||||
CleanDF & MethodExt \--\> L3\[L3: 逻辑一致性\]
|
||||
end
|
||||
|
||||
L1 & L2 & L3 \--\> JSON\[验证报告 JSON\]
|
||||
|
||||
## **3\. 详细技术实现方案**
|
||||
|
||||
### **3.1 预处理层:遗留格式兼容 (.doc to .docx)**
|
||||
|
||||
虽然现在大多是 .docx,但仍需兼容老旧的 .doc。
|
||||
|
||||
* **工具**:LibreOffice (Headless mode) 或 Pandoc。
|
||||
* **Python 实现**:
|
||||
import subprocess
|
||||
|
||||
def convert\_to\_docx(input\_path, output\_path):
|
||||
\# 使用 LibreOffice 无头模式转换
|
||||
cmd \= \['soffice', '--headless', '--convert-to', 'docx', input\_path, '--outdir', output\_path\]
|
||||
subprocess.run(cmd, check=True)
|
||||
|
||||
### **3.2 解析层:基于 python-docx 的精准提取**
|
||||
|
||||
这是核心引擎。相比 pdfplumber,代码逻辑更清晰。
|
||||
|
||||
* **核心库**:python-docx
|
||||
* **关键逻辑一:提取表格与 Caption**
|
||||
在 Word XML 中,Table 节点通常紧跟在描述它的 Paragraph 节点之后。
|
||||
from docx import Document
|
||||
|
||||
def extract\_tables\_with\_captions(doc\_path):
|
||||
doc \= Document(doc\_path)
|
||||
tables\_data \= \[\]
|
||||
|
||||
\# 遍历文档元素(保持顺序)
|
||||
for i, element in enumerate(doc.element.body):
|
||||
if element.tag.endswith('tbl'): \# 发现表格
|
||||
\# 向前回溯找 Caption (通常是表格前的最后一个段落)
|
||||
caption \= find\_prev\_paragraph\_text(doc, i)
|
||||
table\_index \= count\_preceding\_tables(doc, i)
|
||||
table\_obj \= doc.tables\[table\_index\]
|
||||
|
||||
df \= parse\_table\_to\_dataframe(table\_obj)
|
||||
tables\_data.append({"caption": caption, "data": df})
|
||||
return tables\_data
|
||||
|
||||
* **关键逻辑二:处理合并单元格 (The Merge Logic)**
|
||||
Word 中合并单元格在 python-docx 中表现为多个单元格共享相同的文本,或者后续单元格为空。
|
||||
* **策略**:**Forward Fill (向前填充)**。
|
||||
* 如果是横向合并(Header常见):将 "Group A" 填充到其覆盖的所有列。
|
||||
* 如果是纵向合并(分类常见):将 "Adverse Events" 填充到其覆盖的所有行。
|
||||
|
||||
### **3.3 验证层:适应复杂统计的规则引擎**
|
||||
|
||||
基于 Word 提取的高质量 DataFrame,我们可以执行更复杂的验证。
|
||||
|
||||
#### **L1: 算术自洽性 (Arithmetic Consistency)**
|
||||
|
||||
* **输入**:清洗后的 DataFrame。
|
||||
* **逻辑**:
|
||||
* **Regex 识别**:识别格式为 n/N (%) 或 n (%) 的单元格。
|
||||
* **计算**:提取 n 和 N,计算 n/N 是否等于括号内的 % (容错范围 ±0.1%)。
|
||||
* **行/列汇总**:对于 Header 包含 "Total" 的列,检查其是否等于其他分组列之和。
|
||||
|
||||
#### **L2: 统计方法与结果匹配 (Method-Result Check)**
|
||||
|
||||
这是针对“复杂统计”的应对策略。我们不盲目计算,而是先看作者“说了什么”。
|
||||
|
||||
1. **方法学定位**:
|
||||
* 利用 python-docx 查找标题包含 "Statistical Analysis" 或 "统计分析" 的段落。
|
||||
* 提取该段落全文。
|
||||
2. **LLM 意图识别**:
|
||||
* 发送给 LLM:“作者在本段中提到了哪些统计方法?返回 JSON List。”
|
||||
* *Result*: \["Chi-square", "T-test", "Logistic Regression"\]
|
||||
3. **表格结果验证**:
|
||||
* 如果表格包含 "OR (95% CI)",则验证是否匹配 "Logistic Regression"。
|
||||
* 如果表格包含 "HR (95% CI)",则验证是否匹配 "Cox Regression"。
|
||||
* **报警**:如果表格用了 HR 但方法学里只字未提 Cox 回归,标记为 **“方法学描述缺失”**。
|
||||
|
||||
#### **L3: 高级逻辑推断 (Logical Inference) \- *无需原始数据***
|
||||
|
||||
针对无法重算的回归分析(Logistic/Cox),采用**区间逻辑验证**。
|
||||
|
||||
* **黄金法则 (Golden Rule)**:
|
||||
* 对于 Ratio 数据 (OR/HR/RR):
|
||||
* 若 95% CI 跨越 1.0 (例如 0.8 \- 1.2),则 P 值 **必须** ![][image1]。
|
||||
* 若 95% CI 不跨越 1.0 (例如 1.1 \- 1.5),则 P 值 **必须** ![][image2]。
|
||||
* **实现**:
|
||||
* Python 解析 "1.23 (0.91-1.56)" \-\> est=1.23, lower=0.91, upper=1.56。
|
||||
* Python 解析 P 值列。
|
||||
* 执行比对。这能有效发现**编造数据者**常犯的逻辑错误。
|
||||
|
||||
## **4\. API 接口设计 (Python Service)**
|
||||
|
||||
python-extraction 服务新增接口,专门处理 Word。
|
||||
|
||||
**Endpoint**: POST /api/v1/forensics/analyze\_docx
|
||||
|
||||
**Request**:
|
||||
|
||||
{
|
||||
"file\_url": "oss://.../manuscript.docx",
|
||||
"config": {
|
||||
"extract\_images": true, // 是否提取图片(为未来OCR做准备)
|
||||
"check\_level": "STRICT"
|
||||
}
|
||||
}
|
||||
|
||||
**Response**:
|
||||
|
||||
{
|
||||
"methods\_found": \["Chi-square", "Cox Regression"\],
|
||||
"tables": \[
|
||||
{
|
||||
"id": "tbl\_1",
|
||||
"caption": "Table 1\. Baseline Characteristics...",
|
||||
"type": "BASELINE",
|
||||
"issues": \[
|
||||
{
|
||||
"type": "ARITHMETIC\_ERROR",
|
||||
"cell": "R3C2",
|
||||
"message": "Calculated percentage (48.0%) does not match reported (50.0%)"
|
||||
}
|
||||
\]
|
||||
},
|
||||
{
|
||||
"id": "tbl\_2",
|
||||
"caption": "Table 2\. Logistic Regression Analysis...",
|
||||
"type": "REGRESSION",
|
||||
"issues": \[
|
||||
{
|
||||
"type": "LOGIC\_ERROR",
|
||||
"message": "95% CI (0.8-1.2) crosses 1.0, but P-value is 0.03. Contradiction detected."
|
||||
}
|
||||
\]
|
||||
}
|
||||
\]
|
||||
}
|
||||
|
||||
## **5\. MVP 实施计划 (基于 Word 优先)**
|
||||
|
||||
### **阶段一:转换与提取 (Week 1\)**
|
||||
|
||||
1. **Docker 环境**:在 python-extraction 镜像中安装 libreoffice 和 default-jre (用于转换)。
|
||||
2. **Parser 开发**:基于 python-docx 开发 DocxTableExtractor 类,重点解决合并单元格的 DataFrame 还原问题。
|
||||
|
||||
### **阶段二:基础验证 (Week 2\)**
|
||||
|
||||
1. **算术引擎**:实现 n/N % 校验和 Sum 校验。
|
||||
2. **统计复核**:实现基于 Summary Data 的 T 检验/卡方检验逆向计算器。
|
||||
|
||||
### **阶段三:复杂逻辑与集成 (Week 3\)**
|
||||
|
||||
1. **回归逻辑**:实现 CI 与 P 值的逻辑互斥检查。
|
||||
2. **方法学匹配**:实现“方法学章节提取” \+ “LLM 意图识别”流程。
|
||||
3. **前端展示**:在 RVW 报告页渲染结构化的“数据疑点”。
|
||||
|
||||
## **6\. 总结**
|
||||
|
||||
切换到 **Word 优先** 是一个极佳的技术决策:
|
||||
|
||||
1. **避开了 PDF 表格识别的深坑**(不再需要纠结表格线框、跨页断裂)。
|
||||
2. **数据提取准确率预计从 70% 提升至 98%**。
|
||||
3. 使得**复杂逻辑验证(如 CI vs P)** 成为可能,因为我们能精准提取出这两个数值。
|
||||
|
||||
这套方案将使 RVW 在中文核心期刊市场具备极强的技术壁垒。
|
||||
|
||||
[image1]: <data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADcAAAAXCAYAAACvd9dwAAACiElEQVR4Xu1WO0xUQRR9xJiIX5QsG/b39pdsbGjWhkILowWFUGplYWNjKKAgIVhSEHoLAhF6QmKjhorORAsajRUFiZWNoVgKjFnP2b2j9919j7ewYMzmneRm3j33zOfOzJsZz0uQIEGCBF0ik8lcttxZwPf9LVhTbM3Go5BOp69Af+jqlkqle1YDvlEsFpdQjqIcKhQKD/D9I5VKXQ0IUbkmDS0HAj2AHcFWlL9CTmvCUC6Xb0hCvuPg/4LNah2TkzFru681AWSz2WEIjmDv4A7YeLfALD5kZ4YekAGMGz4AxD/Dvmgun89P2vb89so9QfkK8Ts6dixkWxzAduFesPE4cHB2MMIzuR3La1CDQW9oDpOeI49JqytdQ2tOjHq9fhGN7MO+53K5QRuPgiQRldyh5R2wFdOiWdU8/FHh5xXXW3IK3FIfYAdcVRu0kIF0dC58R9IOXBnGUc5pXiW3qTj+c7tY5TcoFyUe/c8dh2q1et1vn2BvbcxCOjqP5HYU1+Dho/xxapBs0XGxQIWSdPjaxqLwL5ILgTuwvtpAB7D/x0S8YGNxkHo/I/jI5FQSi2G8PmgwATe1hohr30MDUxSg8lMb6xao/y2sE+l8z/IKrdmPOi1hE/S5i8Sf0TrhOvpl4JkET/dTKqCNF2GdkMPAnztf/uOXRsPLP3DPwZ+QupfEX6WvL/parXaNHBJ//7em15qJdb5SAmRvaK0AL3NHYGCPbcK+vDKge+Q4XMh3RffnEQF/D/W3nc8nFrh954uGL6Amry/NnwtwL95iZ7BPYk17jfjtFTnyzEMByU5Tz+3JOMqPOk6Avy3tt34B2knu4v6ALDNPpFirVCojtv5/jb5Orl/xG+Zg7CIpur7pAAAAAElFTkSuQmCC>
|
||||
|
||||
[image2]: <data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADcAAAAXCAYAAACvd9dwAAACW0lEQVR4Xu1Wv0tbURROEMGi1rYQQsiPm4RA6BwXhzqIHVpoV/0PupQO7SAIHTP0H3AQRdq5CG7i5CbUwUVx0UHoXhySIUXi9yXntuedvJcmr4rL++Bw3/nOufecc3++VCpBggQJEjwwnHO7kK7ItrVHIZvNTsO/7ftWKpVF6wO+VS6Xv6DNoX1SKpWW8f0rk8nMWN87BwNBNpW+SU77hKFarc5JQc5z0G8gn7Qfi/PFK1nSPmMDA7y2nAVm8SWDGTotCSwYPgDYTyFnmisWi2/teK6/cqtoN2Cf17axgYR3XH8Gc9ZmweRsMsKzuEPLa9AHSX/VXD6fL5BHDg3l19I+cTCBQD8w0DXPgTVGQYqIKq5teQ9sxaz4bGkeek74dcXFK67RaEyi8xXkgt/W/i9IIgPBhR8o2oMrQzvaNc2r4r4rjmfuBJO/h7Yp9ugzV6vVHrv+LXUENW3to0IC3Udxh4pr8fJR+gJ9UGzZc97AzjxP3wKGmJBE7rW4EPgL6zzASmcWtxswxIQE+R3BRxanimiG8fqiwQQ81T7E0PFlW167/9+WP8OCSPBLyyv0Zj/qtoS8oi43N/WP2k+4gbgB3MGF8j4siCT+zusymZ+NDx//wDvHoqTvlOhb1PVDX6/XZ8mh8P2/PYeDM3nkxnwKUrICfMw9gcRWbMFO/jLg98ZzeJBfiN+fnQP9Ev0PvM5fLHBXXhcf/gF14yyG3wodN8IjThQKhWcMBjkW6doJcv0V6eBzQvOI9YH+3J60863VdgL8cxm/dwQoiPnI+o0FN8LvV4IECSJxC00v4jBxXopbAAAAAElFTkSuQmCC>
|
||||
Reference in New Issue
Block a user