baodan/api/insurance/ppt/comparison_tool.py

184 lines
5.9 KiB
Python
Raw Normal View History

"""新旧解析器对比工具。
对比 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')}")