xiaowei-system/scripts/kocr-summary-review-4b.py

122 lines
4.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
kocr-summary-review-4b.py — KOCR 输出摘要质量复核2026-09-06 P2
=================================================================
⚠️ 独立复核工具:不接入 KOCR 自动管线(管线 v9.4 冻结,禁止覆盖 OCR 结果)。
用法:读已生成的合并凭证.xls → 本地 4B 逐条判断摘要质量 → 输出可疑清单(纯文本供人工复核)。
用法:
python3 kocr-summary-review-4b.py <合并凭证.xls> # 复核全部摘要
python3 kocr-summary-review-4b.py <file.xls> --limit 30 # 只看前 30 条
判断维度4B 输出 reasons:
- placeholder: 占位符("会计凭证"/公司名/元数据)
- truncated: 疑似截断(过短/不完整)
- suspicious: 与科目明显矛盾 / 含乱码 / 异常字符
- ok: 真实业务摘要
原则:只标记不修改。牧尘铁律——"优先OCR识别为准不添加后处理逻辑覆盖OCR结果"
"""
import json, os, sys, urllib.request
try:
import xlrd
except ImportError:
print("需要 xlrd: pip install xlrd")
sys.exit(1)
LLAMA_URL = "https://apihub.agnes-ai.com/v1/chat/completions"
REVIEW_PROMPT = """你是会计凭证摘要质检员。判断这条凭证摘要是否合格。
不合格情况(输出对应标记):
- placeholder: "会计凭证"/纯公司名/制单审核等元数据占位
- truncated: 明显截断(如"收费-假日"这种半截、结尾异常)
- suspicious: 含乱码/特殊字符/与科目矛盾/明显 OCR 错误
合格: ok
凭证摘要: "{summary}"
科目: {subject}
只输出严格 JSON: {"verdict": "ok|placeholder|truncated|suspicious", "reason": "一句话"}"""
def review_one(summary, subject, timeout=45):
prompt = REVIEW_PROMPT.replace("{summary}", summary[:80]).replace("{subject}", subject[:40])
payload = json.dumps({
"model": "agnes-2.0-flash",
"messages": [
{"role": "system", "content": prompt},
{"role": "user", "content": "请判断"},
],
"max_tokens": 60,
"temperature": 0.1,
}).encode()
req = urllib.request.Request(
LLAMA_URL, data=payload,
headers={"Authorization": "Bearer sk-7k9e9KGcoZdDuYt2LSA4YXdBTioczleGJm2zzLWCku072ikW", "Content-Type": "application/json"},
method="POST",
)
try:
with urllib.request.urlopen(req, timeout=timeout) as resp:
body = json.loads(resp.read())
content = (body.get("choices") or [{}])[0].get("message", {}).get("content", "")
content = content.strip()
if content.startswith("```"):
content = content.split("```")[1] if "```" in content[3:] else content
if content.startswith("json"):
content = content[4:]
start, end = content.find("{"), content.rfind("}")
if start >= 0 and end > start:
return json.loads(content[start:end + 1])
except Exception:
pass
return {"verdict": "error", "reason": "4B 调用失败"}
def main():
if len(sys.argv) < 2:
print("用法: python3 kocr-summary-review-4b.py <合并凭证.xls> [--limit N]")
return
path = sys.argv[1]
limit = 200
if "--limit" in sys.argv:
try:
limit = int(sys.argv[sys.argv.index("--limit") + 1])
except Exception:
pass
wb = xlrd.open_workbook(path)
ws = wb.sheet_by_name("Page1")
# 找列: 凭证摘要 / 科目名称 / 科目代码 / 凭证号
headers = [str(ws.cell_value(0, c)).strip() for c in range(ws.ncols)]
def col(keyword):
for c, h in enumerate(headers):
if keyword in h:
return c
return None
ci_sum, ci_subj, ci_code, ci_vno = col("摘要"), col("科目名称"), col("科目代码"), col("凭证号")
issues = []
checked = 0
for r in range(1, ws.nrows):
summary = str(ws.cell_value(r, ci_sum or 19)).strip()
if not summary:
continue
checked += 1
if checked > limit:
break
subject = str(ws.cell_value(r, ci_subj or 6)).strip()
vno = str(ws.cell_value(r, ci_vno or 4)).strip()
res = review_one(summary, subject)
if res.get("verdict") != "ok":
issues.append(f"记-{vno} | {res.get('verdict')} | {summary[:60]} | 科目:{subject[:20]} | {res.get('reason','')[:40]}")
print(f"复核 {checked} 条摘要, 可疑 {len(issues)} 条:")
for line in issues:
print(line)
if not issues:
print("✅ 全部摘要合格4B 复核)")
if __name__ == "__main__":
main()