diff --git a/agent/nodes.py b/agent/nodes.py index 4e9874b..91b26a9 100644 --- a/agent/nodes.py +++ b/agent/nodes.py @@ -1207,8 +1207,8 @@ def _check_ocr_fidelity(jrxml: str, state: dict) -> dict: else: element_coverage = 1.0 - # 2. 字段名覆盖 - jrxml_fields = set(re.findall(r' dict: if ocr_field_names and jrxml_fields: matched = jrxml_fields & ocr_field_names - field_coverage = len(matched) / max(len(ocr_field_names), 1) - unmatched = ocr_field_names - jrxml_fields - if unmatched: - sample = list(unmatched)[:8] + # 尝试通过 _sanitize_field_name 转义后匹配(中文→_uXXXX_) + sanitized_ocr = {_sanitize_field_name(n) for n in ocr_field_names} + matched_via_sanitize = jrxml_fields & sanitized_ocr + all_matched = matched | matched_via_sanitize + field_coverage = len(all_matched) / max(len(ocr_field_names), 1) + still_unmatched = {n for n in ocr_field_names + if n not in jrxml_fields and _sanitize_field_name(n) not in jrxml_fields} + if still_unmatched: + sample = list(still_unmatched)[:8] issues.append(f"OCR 字段未在 JRXML 中声明: {', '.join(sample)}") elif ocr_field_names and not jrxml_fields: field_coverage = 0.0 @@ -1247,8 +1252,9 @@ def _check_ocr_fidelity(jrxml: str, state: dict) -> dict: f"OCR 布局分析有 {ocr_columns} 列" ) - # 综合评分 - score = round(field_coverage * 0.5 + element_coverage * 0.5, 3) + # 综合评分(元素覆盖为主权重 0.8,字段覆盖为辅助 0.2) + # 英文字段名 vs 中文 OCR 字段名天然不匹配是预期行为,字段覆盖仅作参考 + score = round(element_coverage * 0.8 + field_coverage * 0.2, 3) return { "score": score, "field_coverage": round(field_coverage, 3), @@ -1291,7 +1297,8 @@ def validate(state: AgentState) -> Dict: if fidelity["issues"]: if state["status"] == "pass": # XSD 通过但内容保真度不足 → 降级为 fail - if fidelity["score"] < 0.5: + # 需要 score < 0.5 且 element_coverage < 0.4(字段名语言不匹配不应单独导致 fail) + if fidelity["score"] < 0.5 and fidelity.get("element_coverage", 0) < 0.4: state["status"] = "fail" state["error_msg"] = ( f"[内容保真度不足] 得分 {fidelity['score']:.2f}/1.0。" diff --git a/prompts/correction.md b/prompts/correction.md index 262dca7..babd0df 100644 --- a/prompts/correction.md +++ b/prompts/correction.md @@ -8,7 +8,7 @@ - 如果当前 JRXML 内容为空或过短(<200 字符),请根据下方提供的 OCR 识别数据和布局 schema 重新生成完整的 JRXML,而非输出一个占位桩。 - 如果错误是"字段 'field_N' 未在 部分声明",**必须**为每个缺失的 field_N 添加 `` 声明。这些是占位字段,不可删除。同时确保所有 $F{{field_N}} 引用都有对应的 声明。 - 如果错误是"字段 'field_N' 未在 部分声明"且有 OCR 字段数据,尝试将 $F{{field_N}} 替换为 OCR 中对应的真实字段名(如 $F{{invoice_code}}),同时更新 声明和所有引用。 -- **如果错误包含 "No matching global declaration available for the validation root" 或 "namespace" 或 "命名空间"**,说明命名空间 URL 错误。正确的根元素格式必须为:``。删除所有 ns0: 前缀,删除所有 `xmlns:ns0` 声明,删除所有元素标签上的 `ns0:` 前缀。 +- **始终检查并修复命名空间**:正确的根元素格式必须为:``。删除所有 ns0: 前缀,删除所有 `xmlns:ns0` 声明,删除所有元素标签上的 `ns0:` 前缀。 当前 JRXML(带错误): {current_jrxml}