B 방식(JSON 양식)의 결과를 파이썬 규칙으로 한 번 더 검사하는 C 방식을 만든다.
import json
import re
import sys
import time
REQUIRED_KEYS = ["tasks", "prohibitions", "message_log", "changes", "questions"]
FEED_WORDS = ["급여", "사료", "밥", "식사", "먹이", "간식 주"]
WHEN_ANCHORS = ["방문", "식사", "내내", "때"]
AMOUNT_RE = re.compile(r"\d|반|한|하나|두|세")
TIME_RE = re.compile(r"\d")
def is_feeding(t):
text = f"{t.get('task') or ''} {t.get('food') or ''}"
return any(w in text for w in FEED_WORDS)
def empty(v):
return v is None or str(v).strip() == ""
def check(raw):
start = time.perf_counter()
warns = []
try:
d = json.loads(raw)
except Exception:
return [("R0", True, "JSON 해석 실패 — 자동 점검 미완료, 원문 확인 필요")], 0.0
missing = [k for k in REQUIRED_KEYS if k not in d]
if missing:
warns.append(("R0", True, f"필수 키 없음 {missing} — 자동 점검 미완료"))
tasks = d.get("tasks") or []
questions = d.get("questions") or []
log = d.get("message_log") or []
for t in tasks:
label = f"{t.get('animal')} {t.get('date')} {t.get('task')}"
for k in ("animal", "date", "when", "task"):
if empty(t.get(k)):
warns.append(("R1", True, f"[{label}] {k} 없음"))
when = str(t.get("when") or "")
if when and not TIME_RE.search(when) and not any(a in when for a in WHEN_ANCHORS):
warns.append(("R1", True, f"[{label}] 실행 시점이 구체적이지 않음: '{when}'"))
if is_feeding(t):
amt = t.get("amount")
if empty(amt) or not AMOUNT_RE.search(str(amt)):
warns.append(("R1", True, f"[{label}] 급여량 없음/불명확: '{amt}'"))
groups = {}
for t in tasks:
key = (t.get("animal"), t.get("date"), str(t.get("when")), t.get("task"))
groups.setdefault(key, set()).add((str(t.get("amount")), str(t.get("food"))))
for key, vals in groups.items():
if len(vals) > 1:
warns.append(("R2", True, f"같은 업무에 값이 여러 개 {key}: {sorted(vals)}"))
task_ids = set()
for t in tasks:
task_ids.update(t.get("source_ids") or [])
for q in questions:
qids = set(q.get("source_ids") or [])
if qids and not (qids & task_ids):
warns.append(("R3", True, f"확인 질문의 대상 업무가 할 일 목록에 없음 (근거 {sorted(qids)})"))
order = [m.get("id") for m in log]
sender = {m.get("id"): m.get("sender") for m in log}
for m in log:
tgt = m.get("corrects")
if not tgt or tgt not in sender:
continue
if sender[tgt] and m.get("sender") and sender[tgt] != m.get("sender"):
later = order[order.index(m.get("id")) + 1:] if m.get("id") in order else []
if not any(sender.get(x) == sender[tgt] for x in later):
warns.append(("R4", True, f"{m.get('id')}({m.get('sender')})가 {tgt}({sender[tgt]})를 정정 — 원래 발신자 승인 확인 필요"))
for c in d.get("changes") or []:
b, a = str(c.get("before") or ""), str(c.get("after") or "")
if b and (b == a or "변동 없" in a):
warns.append(("N1", False, f"값이 같은데 변경으로 기록됨: {c.get('what')}"))
return warns, round((time.perf_counter() - start) * 1000, 3)
def main(path):
rows = [json.loads(l) for l in open(path, encoding="utf-8")]
out_path = "c_" + path.split("/")[-1]
print(f"{'사례':<8}{'B질문':>6}{'C경고':>6}{'최종확인':>8} 경고 내용")
with open(out_path, "w", encoding="utf-8") as f:
for r in rows:
if r["method"] != "B":
continue
warns, ms = check(r["output"]) if r["ok"] else ([("R0", True, "호출 실패 — 자동 점검 미완료")], 0.0)
try:
b_q = len(json.loads(r["output"]).get("questions") or [])
except Exception:
b_q = 0
c_confirm = [w for w in warns if w[1]]
final = b_q > 0 or len(c_confirm) > 0
rec = {"case_id": r["case_id"], "rep": r.get("rep", 1), "b_questions": b_q,
"c_warnings": [{"rule": w[0], "confirm": w[1], "msg": w[2]} for w in warns],
"b_needs_confirm": b_q > 0, "c_needs_confirm": final, "check_ms": ms}
f.write(json.dumps(rec, ensure_ascii=False) + "\n")
tag = r["case_id"] + (f"#{r['rep']}" if "rep" in r else "")
print(f"{tag:<8}{b_q:>6}{len(c_confirm):>6}{('예' if final else '-'):>8} " +
" | ".join(f"{w[0]}:{w[2]}" for w in warns))
print(f"\n저장: {out_path}")
if __name__ == "__main__":
main(sys.argv[1])