117 lines
5.6 KiB
Python
117 lines
5.6 KiB
Python
#!/usr/bin/env python3
|
||
"""$0 детерминированная батарея по армам (задачи A и B). Судья не участвует.
|
||
|
||
Приборы:
|
||
· ЭХО — `cjk_share > 0.15`, определение движка (`disposition.go:269-281`, Han|Hiragana|Katakana);
|
||
порог тот же, что у P4, поэтому числа сравнимы с 09.07 напрямую.
|
||
· ЯЗЫК ВЫХОДА — доля кириллицы против латиницы среди БУКВ. Это встречный риск en-промпта,
|
||
названный в промте сессии: английская инструкция может утянуть выход в английский.
|
||
· АНТИ-ОМИССИЯ — len_ratio (симв. без пробелов, коридор пары [2.2, 4.2] из `pairs/zh-ru.yaml`)
|
||
и отношение числа предложений (оракул `refusal_bench.split_sentences`).
|
||
· ФОРМА — преамбула, markdown-заголовок, утёкший разделитель банкноты, латинские прогоны.
|
||
· ПРОВОД — reasoning_tokens (сырьё, движок их для deepseek сознательно обнуляет), finish, цена.
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import argparse
|
||
import json
|
||
import re
|
||
import sys
|
||
import unicodedata
|
||
from pathlib import Path
|
||
|
||
REPO = Path("/home/ubuntu/projects/textmachine")
|
||
sys.path.insert(0, str(REPO / "eval"))
|
||
from refusal_bench import split_sentences # noqa: F401 (оракул уже применён в run_arms)
|
||
|
||
PL = Path.home() / "books" / "gu-zhenren" / "promptlang"
|
||
PREAMBLE_RE = re.compile(r"^\s*(вот|ниже|перевод[:\s]|here is|translation[:\s]|note[:\s])", re.I)
|
||
MD_RE = re.compile(r"^\s{0,3}#{1,6}\s", re.M)
|
||
LAT_RUN_RE = re.compile(r"[A-Za-z]{3,}")
|
||
|
||
|
||
def script_mix(s: str) -> tuple[float, float, int]:
|
||
cyr = lat = 0
|
||
for ch in s:
|
||
if not ch.isalpha():
|
||
continue
|
||
n = unicodedata.name(ch, "")
|
||
if n.startswith("CYRILLIC"):
|
||
cyr += 1
|
||
elif n.startswith("LATIN"):
|
||
lat += 1
|
||
tot = cyr + lat
|
||
return (cyr / tot if tot else 0.0), (lat / tot if tot else 0.0), tot
|
||
|
||
|
||
def main() -> int:
|
||
ap = argparse.ArgumentParser()
|
||
ap.add_argument("--arms", default="a0,a1,a2,e0")
|
||
ap.add_argument("--raw", type=Path, default=PL / "armsraw")
|
||
a = ap.parse_args()
|
||
|
||
per = {}
|
||
for arm in a.arms.split(","):
|
||
rs = []
|
||
for f in sorted(a.raw.glob(f"{arm}-f*.json")):
|
||
j = json.loads(f.read_text(encoding="utf-8"))
|
||
if "error" in j:
|
||
rs.append(j)
|
||
continue
|
||
c = j["content"]
|
||
cyr, lat, letters = script_mix(c)
|
||
j["cyr_share"] = round(cyr, 4)
|
||
j["lat_share"] = round(lat, 4)
|
||
j["off_language"] = bool(letters) and cyr < 0.5 # выход не в целевом письме
|
||
j["preamble"] = bool(PREAMBLE_RE.match(c))
|
||
j["markdown"] = bool(MD_RE.search(c))
|
||
j["bank_marker"] = "⟦TM-BANK" in c
|
||
j["latin_runs"] = len(LAT_RUN_RE.findall(c))
|
||
j["in_corridor"] = (j["len_ratio"] is not None and 2.2 <= j["len_ratio"] <= 4.2)
|
||
rs.append(j)
|
||
per[arm] = rs
|
||
|
||
print(f"{'арм':<5}{'n':>3}{'эхо':>5}{'off_lang':>9}{'вне кор.':>9}{'преамб':>8}{'md':>4}"
|
||
f"{'лат.прог':>9}{'ср.lr':>7}{'ср.sent':>9}{'ср.rt':>7}{'сумма $':>10}")
|
||
for arm, rs in per.items():
|
||
ok = [r for r in rs if "error" not in r]
|
||
if not ok:
|
||
continue
|
||
clean = [r for r in ok if not r["echo"]]
|
||
lr = [r["len_ratio"] for r in clean if r["len_ratio"] is not None]
|
||
sr = [r["sent_out"] / r["sent_src"] for r in clean if r["sent_src"]]
|
||
print(f"{arm:<5}{len(ok):>3}{sum(r['echo'] for r in ok):>5}"
|
||
f"{sum(r['off_language'] for r in ok):>9}"
|
||
f"{sum(1 for r in clean if not r['in_corridor']):>9}"
|
||
f"{sum(r['preamble'] for r in ok):>8}{sum(r['markdown'] for r in ok):>4}"
|
||
f"{sum(r['latin_runs'] for r in ok):>9}"
|
||
f"{(sum(lr)/len(lr) if lr else 0):>7.2f}{(sum(sr)/len(sr) if sr else 0):>9.2f}"
|
||
f"{sum(r['reasoning_tokens'] for r in ok)/len(ok):>7.0f}"
|
||
f"{sum(r['cost_usd'] for r in ok):>10.6f}")
|
||
|
||
print("\nпофрагментно (эхо и коридор — по фрагментам, чтобы видеть, ГДЕ именно):")
|
||
frags = sorted({r["frag"] for rs in per.values() for r in rs})
|
||
print(f"{'фраг':<6}" + "".join(f"{arm:>18}" for arm in per))
|
||
for fr in frags:
|
||
cells = []
|
||
for arm, rs in per.items():
|
||
r = next((x for x in rs if x["frag"] == fr), None)
|
||
if r is None or "error" in r:
|
||
cells.append(f"{'—':>18}")
|
||
continue
|
||
mark = "ЭХО" if r["echo"] else ("OFFLANG" if r["off_language"] else
|
||
("вне кор." if not r["in_corridor"] else "ok"))
|
||
cells.append(f"{mark + ' lr=' + str(r['len_ratio']):>18}")
|
||
print(f"{fr:<6}" + "".join(cells))
|
||
|
||
out = PL / "analyze_arms.json"
|
||
out.write_text(json.dumps({k: [{kk: vv for kk, vv in r.items()
|
||
if kk not in ("system", "user", "reasoning_content")}
|
||
for r in v] for k, v in per.items()},
|
||
ensure_ascii=False, indent=1), encoding="utf-8")
|
||
print(f"\nсырьё сведено: {out}")
|
||
return 0
|
||
|
||
|
||
if __name__ == "__main__":
|
||
raise SystemExit(main())
|