|
|
from __future__ import annotations |
|
|
|
|
|
import re |
|
|
from typing import Tuple |
|
|
|
|
|
|
|
|
class OutputValidator: |
|
|
def validate(self, query: str, qtype: str, output: str) -> Tuple[bool, str]: |
|
|
s = (output or "").strip() |
|
|
if not s: |
|
|
return False, "empty" |
|
|
|
|
|
|
|
|
if len(s) < 260: |
|
|
return False, "too_short" |
|
|
|
|
|
|
|
|
bad = [ |
|
|
"もう少し具体化", "精度が上がります", "情報が不足", "受理しました", |
|
|
"章タイトル", "Available routes" |
|
|
] |
|
|
if any(x in s for x in bad): |
|
|
return False, "guard_phrase" |
|
|
|
|
|
|
|
|
|
|
|
lines = [ln.strip() for ln in s.splitlines() if ln.strip().startswith("-")] |
|
|
if lines: |
|
|
title_like = 0 |
|
|
for ln in lines[:10]: |
|
|
|
|
|
if not re.search(r"(=>|because|therefore|implies|bound|<|>|ため|したがって|ゆえに|上限|制約)", ln, re.IGNORECASE): |
|
|
title_like += 1 |
|
|
if title_like >= 6: |
|
|
return False, "headings_only" |
|
|
|
|
|
|
|
|
if qtype == "advanced_physics": |
|
|
need = ["χ", "N", "Page", "QES", "RT", "complexity"] |
|
|
hit = sum(1 for w in need if w.lower() in s.lower()) |
|
|
if hit < 2: |
|
|
return False, "missing_phys_terms" |
|
|
|
|
|
return True, "ok" |