feat: метрики качества — арифметика (sum==price*qty) + частота JSON-фиксов

This commit is contained in:
2026-06-27 13:18:10 +04:00
parent 909468b5b7
commit 31e5ef7ac5
3 changed files with 97 additions and 8 deletions
+20 -8
View File
@@ -56,7 +56,7 @@ def _is_garbage_by_header(text: str) -> bool:
def _call_llm_classify(header_text):
"""
Прямой вызов LLM для классификации ОДНОГО документа.
Возвращает (parsed_dict, raw_text).
Возвращает (parsed_dict, raw_text, needed_fix).
"""
prompt, _ = build_classify_prompt(header_text)
payload = {
@@ -74,7 +74,8 @@ def _call_llm_classify(header_text):
data = resp.json()
raw = data.get("choices", [{}])[0].get("message", {}).get("content", "")
return _safe_json_parse(raw), raw
parsed, needed_fix = _safe_json_parse(raw)
return parsed, raw, needed_fix
def classify_batch(batch_id):
@@ -93,23 +94,32 @@ def classify_batch(batch_id):
total = len(pending)
done = 0
failed = 0
garbage = 0
json_fixes = 0
json_total = 0
def _classify_one(doc):
"""Классифицировать один документ: фильтр → выжимка → LLM → сохранить."""
nonlocal garbage, json_fixes, json_total
try:
# Stage 1: garbage by filename (0 tokens)
if _is_garbage_by_filename(doc["filename"]):
db_docs.set_classify_garbage(doc["id"], "filename_regex")
garbage += 1
return True
# Stage 2: garbage by header keywords (0 tokens)
text = _smart_extract(doc["elements_json"])
if _is_garbage_by_header(text):
db_docs.set_classify_garbage(doc["id"], "header_keywords")
garbage += 1
return True
# Stage 3: LLM classification (only for remaining)
result, raw = _call_llm_classify(text)
result, raw, needed_fix = _call_llm_classify(text)
json_total += 1
if needed_fix:
json_fixes += 1
db_docs.set_classification(
doc["id"],
result.get("doc_type", "other"),
@@ -133,11 +143,13 @@ def classify_batch(batch_id):
else:
failed += 1
return {"ok": True, "total": total, "done": done, "failed": failed}
return {"ok": True, "total": total, "done": done, "failed": failed,
"garbage": garbage, "json_fix_rate": round(json_fixes / max(json_total, 1), 3)}
def _safe_json_parse(raw):
"""Parse LLM response, fixing common JSON errors."""
"""Parse LLM response, fixing common JSON errors.
Returns (parsed_dict, needed_fix: bool)."""
if not raw:
raise ValueError("empty LLM response")
@@ -156,7 +168,7 @@ def _safe_json_parse(raw):
# Try strict parse
try:
return json.loads(text)
return json.loads(text), False
except json.JSONDecodeError:
pass
@@ -169,7 +181,7 @@ def _safe_json_parse(raw):
# Try again
try:
return json.loads(text)
return json.loads(text), True
except json.JSONDecodeError:
pass
@@ -185,7 +197,7 @@ def _safe_json_parse(raw):
text += '"'
text += "}"
return json.loads(text)
return json.loads(text), True
def _smart_extract(elements_json):