184 lines
5.9 KiB
Python
184 lines
5.9 KiB
Python
|
|
"""新旧解析器对比工具。
|
|||
|
|
|
|||
|
|
对比 regex-only(旧)和 split-LLM(新)两种提取方式的结果差异。
|
|||
|
|
用于评估新解析器的改进效果。
|
|||
|
|
|
|||
|
|
用法:
|
|||
|
|
# Python
|
|||
|
|
from insurance.ppt.comparison_tool import run_comparison
|
|||
|
|
report = run_comparison("path/to/plan.pdf", "savings")
|
|||
|
|
|
|||
|
|
# CLI
|
|||
|
|
python -m insurance.ppt.comparison_tool path/to/plan.pdf --type savings
|
|||
|
|
"""
|
|||
|
|
import json
|
|||
|
|
import time
|
|||
|
|
import logging
|
|||
|
|
import asyncio
|
|||
|
|
from typing import Optional
|
|||
|
|
|
|||
|
|
logger = logging.getLogger(__name__)
|
|||
|
|
|
|||
|
|
|
|||
|
|
async def _extract_regex_only(pdf_path: str, plan_type: str) -> tuple[dict, float]:
|
|||
|
|
"""旧方式:纯正则提取(零 LLM 调用)。"""
|
|||
|
|
from insurance.ppt.regex_extractor import extract_insurance_regex
|
|||
|
|
|
|||
|
|
start = time.time()
|
|||
|
|
# 读取 PDF 文本
|
|||
|
|
from insurance.ppt.extraction import _extract_pdf_text
|
|||
|
|
pdf_text, _ = _extract_pdf_text(pdf_path)
|
|||
|
|
if not pdf_text:
|
|||
|
|
return {"error": "无法提取 PDF 文本"}, 0
|
|||
|
|
data = extract_insurance_regex(pdf_text)
|
|||
|
|
elapsed = (time.time() - start) * 1000
|
|||
|
|
return data, elapsed
|
|||
|
|
|
|||
|
|
|
|||
|
|
async def _extract_new(pdf_path: str, plan_type: str) -> tuple[dict, float, dict]:
|
|||
|
|
"""新方式:质量门 + 分块 LLM 提取。"""
|
|||
|
|
from insurance.ppt.extraction import ExtractionOrchestrator
|
|||
|
|
|
|||
|
|
start = time.time()
|
|||
|
|
orchestrator = ExtractionOrchestrator(use_cache=False)
|
|||
|
|
result = await orchestrator.extract_plan(pdf_path, plan_type, force_reparse=True)
|
|||
|
|
elapsed = (time.time() - start) * 1000
|
|||
|
|
return result.data or {}, elapsed, result.provenance or {}
|
|||
|
|
|
|||
|
|
|
|||
|
|
def _diff_fields(old: dict, new: dict, path: str = "") -> list[dict]:
|
|||
|
|
"""递归对比两个 dict,返回差异列表。"""
|
|||
|
|
diffs = []
|
|||
|
|
all_keys = set(list(old.keys()) + list(new.keys()))
|
|||
|
|
|
|||
|
|
for key in sorted(all_keys):
|
|||
|
|
full_path = f"{path}.{key}" if path else key
|
|||
|
|
old_val = old.get(key)
|
|||
|
|
new_val = new.get(key)
|
|||
|
|
|
|||
|
|
if old_val == new_val:
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
if isinstance(old_val, dict) and isinstance(new_val, dict):
|
|||
|
|
diffs.extend(_diff_fields(old_val, new_val, full_path))
|
|||
|
|
elif isinstance(old_val, list) and isinstance(new_val, list):
|
|||
|
|
# 对比列表长度
|
|||
|
|
if len(old_val) != len(new_val):
|
|||
|
|
diffs.append({
|
|||
|
|
"path": full_path,
|
|||
|
|
"type": "list_length",
|
|||
|
|
"old": len(old_val),
|
|||
|
|
"new": len(new_val),
|
|||
|
|
})
|
|||
|
|
# 逐元素对比(前 N 个)
|
|||
|
|
for i in range(min(len(old_val), len(new_val))):
|
|||
|
|
if isinstance(old_val[i], dict) and isinstance(new_val[i], dict):
|
|||
|
|
diffs.extend(_diff_fields(old_val[i], new_val[i], f"{full_path}[{i}]"))
|
|||
|
|
elif old_val[i] != new_val[i]:
|
|||
|
|
diffs.append({
|
|||
|
|
"path": f"{full_path}[{i}]",
|
|||
|
|
"type": "value",
|
|||
|
|
"old": old_val[i],
|
|||
|
|
"new": new_val[i],
|
|||
|
|
})
|
|||
|
|
else:
|
|||
|
|
diffs.append({
|
|||
|
|
"path": full_path,
|
|||
|
|
"type": "value",
|
|||
|
|
"old": old_val,
|
|||
|
|
"new": new_val,
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
return diffs
|
|||
|
|
|
|||
|
|
|
|||
|
|
async def run_comparison(pdf_path: str, plan_type: str = "savings") -> dict:
|
|||
|
|
"""运行新旧解析器对比。
|
|||
|
|
|
|||
|
|
返回:
|
|||
|
|
{
|
|||
|
|
"pdf_path": str,
|
|||
|
|
"plan_type": str,
|
|||
|
|
"old_result": dict,
|
|||
|
|
"new_result": dict,
|
|||
|
|
"old_ms": float,
|
|||
|
|
"new_ms": float,
|
|||
|
|
"diffs": list[dict],
|
|||
|
|
"diff_count": int,
|
|||
|
|
"old_benefit_rows": int,
|
|||
|
|
"new_benefit_rows": int,
|
|||
|
|
"provenance": dict,
|
|||
|
|
"summary": str,
|
|||
|
|
}
|
|||
|
|
"""
|
|||
|
|
logger.info(f"[对比工具] 开始对比: {pdf_path} ({plan_type})")
|
|||
|
|
|
|||
|
|
# 旧方式
|
|||
|
|
old_data, old_ms = await _extract_regex_only(pdf_path, plan_type)
|
|||
|
|
old_benefit_rows = len(old_data.get("benefit_illustration") or [])
|
|||
|
|
|
|||
|
|
# 新方式
|
|||
|
|
new_data, new_ms, provenance = await _extract_new(pdf_path, plan_type)
|
|||
|
|
new_benefit_rows = len(new_data.get("benefit_illustration") or [])
|
|||
|
|
|
|||
|
|
# 对比
|
|||
|
|
diffs = _diff_fields(old_data, new_data)
|
|||
|
|
|
|||
|
|
# 生成摘要
|
|||
|
|
summary_parts = [
|
|||
|
|
f"PDF: {pdf_path}",
|
|||
|
|
f"险种: {plan_type}",
|
|||
|
|
f"旧方式: {old_ms:.0f}ms, {old_benefit_rows} 行利益表",
|
|||
|
|
f"新方式: {new_ms:.0f}ms, {new_benefit_rows} 行利益表",
|
|||
|
|
f"差异项: {len(diffs)}",
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
# 关键字段对比
|
|||
|
|
old_name = old_data.get("product_name", "unknown")
|
|||
|
|
new_name = new_data.get("product_name", "unknown")
|
|||
|
|
old_age = (old_data.get("insured") or {}).get("age")
|
|||
|
|
new_age = (new_data.get("insured") or {}).get("age")
|
|||
|
|
summary_parts.append(f"产品名: 旧={old_name}, 新={new_name}")
|
|||
|
|
summary_parts.append(f"年龄: 旧={old_age}, 新={new_age}")
|
|||
|
|
|
|||
|
|
report = {
|
|||
|
|
"pdf_path": pdf_path,
|
|||
|
|
"plan_type": plan_type,
|
|||
|
|
"old_result": old_data,
|
|||
|
|
"new_result": new_data,
|
|||
|
|
"old_ms": round(old_ms, 1),
|
|||
|
|
"new_ms": round(new_ms, 1),
|
|||
|
|
"diffs": diffs,
|
|||
|
|
"diff_count": len(diffs),
|
|||
|
|
"old_benefit_rows": old_benefit_rows,
|
|||
|
|
"new_benefit_rows": new_benefit_rows,
|
|||
|
|
"provenance": provenance,
|
|||
|
|
"summary": "\n".join(summary_parts),
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
logger.info(f"[对比工具] 完成:\n{report['summary']}")
|
|||
|
|
return report
|
|||
|
|
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
import sys
|
|||
|
|
logging.basicConfig(level=logging.INFO)
|
|||
|
|
|
|||
|
|
if len(sys.argv) < 2:
|
|||
|
|
print("用法: python -m insurance.ppt.comparison_tool <pdf_path> [--type savings|ci|iul]")
|
|||
|
|
sys.exit(1)
|
|||
|
|
|
|||
|
|
pdf = sys.argv[1]
|
|||
|
|
ptype = "savings"
|
|||
|
|
if "--type" in sys.argv:
|
|||
|
|
idx = sys.argv.index("--type")
|
|||
|
|
if idx + 1 < len(sys.argv):
|
|||
|
|
ptype = sys.argv[idx + 1]
|
|||
|
|
|
|||
|
|
report = asyncio.run(run_comparison(pdf, ptype))
|
|||
|
|
print("\n" + report["summary"])
|
|||
|
|
if report["diffs"]:
|
|||
|
|
print(f"\n差异详情 ({report['diff_count']} 项):")
|
|||
|
|
for d in report["diffs"][:20]:
|
|||
|
|
print(f" {d['path']}: {d.get('old')} → {d.get('new')}")
|