textmachine/eval/promptlang/analyze_arms.py

117 lines
5.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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())