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