feat: метрики качества — арифметика (sum==price*qty) + частота JSON-фиксов
This commit is contained in:
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user