143 lines
7.4 KiB
Python
143 lines
7.4 KiB
Python
#!/usr/bin/env python3
|
||
"""Д7 — МИКРО-ПРОБА САМООЦЕНКИ ЖИЛЬЦОВ (идея владельца). Платный, ≤$0.05.
|
||
|
||
⚠ РЕШЕНИЙ НЕ НЕСЁТ, и это записано ДО покупки. Заказ формулирует прямо: «печатается как
|
||
любопытство с оговоркой „самооценка моделей ненадёжна“; никакие решения на ней не стоят».
|
||
Ценность в СВЕРКЕ: модель говорит, что ей будет трудно, а фаза уже знает, что вышло на самом
|
||
деле — и расхождение между предсказанием модели о себе и фактом само по себе любопытно.
|
||
|
||
Спрашиваются ОБА жильца обеих ролей по ТРЁМ книгам фазы. Вопрос один и тот же, чтобы ответы
|
||
сравнивались между парами: «сможешь ли перевести эту книгу на издательском уровне; что будет
|
||
трудным?» Отрывок даётся настоящий — из тех же приколотых единиц, что покупала фаза.
|
||
|
||
Запуск: d7_self.py --dry | --run | --show | --selftest
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import importlib.util
|
||
import json
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
REPO = Path("/home/ubuntu/projects/textmachine")
|
||
ZONE = Path(__file__).resolve().parent
|
||
for d in ("dovodka", "editor_tier", "tenant_panel", "role_topology", "bank_arbitration"):
|
||
sys.path.insert(0, str(REPO / "eval" / d))
|
||
|
||
|
||
def _load(name: str, path: Path):
|
||
spec = importlib.util.spec_from_file_location(name, path)
|
||
mod = importlib.util.module_from_spec(spec)
|
||
sys.modules[name] = mod
|
||
spec.loader.exec_module(mod)
|
||
return mod
|
||
|
||
|
||
C = _load("contour_d7", ZONE / "contour.py")
|
||
PE = _load("pair_en_d7", REPO / "eval" / "role_topology" / "pair_en.py")
|
||
MJ = _load("mat_ja_d7", ZONE / "material_ja.py")
|
||
MONEY, ROSTER, ED22 = C.MONEY, C.ROSTER, C.ED22
|
||
|
||
RESIDENTS = ("deepseek-v4-flash", "deepseek-v4-pro")
|
||
PHASE, PFX = "ФД-D", "dv-d"
|
||
MAX_OUT = 8000
|
||
|
||
SYS = ("Ты — переводчик художественной литературы. Тебе дают отрывок книги. Ответь ЧЕСТНО и "
|
||
"коротко, двумя абзацами:\n"
|
||
"1) Сможешь ли ты перевести ЭТУ книгу на русский на ИЗДАТЕЛЬСКОМ уровне (так, чтобы "
|
||
"текст читался как написанный по-русски, а не переведённый)? Ответь прямо: да, нет или "
|
||
"с оговорками — и какими.\n"
|
||
"2) Что в ЭТОЙ книге будет для тебя самым трудным? Назови конкретные места и явления, "
|
||
"а не общие слова про «культурные различия».")
|
||
|
||
|
||
def books() -> dict[str, tuple[str, str]]:
|
||
"""{ключ: (язык, отрывок)} — из ПРИКОЛОТЫХ единиц фазы, а не из случайного места книги."""
|
||
zh = C.units_ok()[0]["source"]
|
||
en = PE.units(8)[0]["source"]
|
||
ja = MJ.units()[0]["source"]
|
||
return {"zh": ("китайская вебновелла", zh), "en": ("английское тёмное фэнтези", en),
|
||
"ja": ("японская веб-новелла", ja)}
|
||
|
||
|
||
def tag(res: str, book: str) -> str:
|
||
return f"{PFX}-self-{res.replace('.', '')}-{book}"
|
||
|
||
|
||
def text_of(res: str, book: str) -> str:
|
||
f = MONEY.OUT / f"{tag(res, book)}.json"
|
||
if not f.exists():
|
||
return ""
|
||
return json.loads(f.read_text(encoding="utf-8")).get("content") or ""
|
||
|
||
|
||
def cmd_run() -> None:
|
||
led = MONEY.Guarded(PHASE, default_expect=0.004)
|
||
for res in RESIDENTS:
|
||
for book, (what, src) in books().items():
|
||
tg = tag(res, book)
|
||
if text_of(res, book).strip():
|
||
continue
|
||
msgs = [{"role": "system", "content": SYS},
|
||
{"role": "user", "content": f"Книга: {what}.\n\nОтрывок:\n{src[:2500]}"}]
|
||
rec = MONEY.purchase(led, tg, res, ED22.client(res),
|
||
ROSTER.call_kwargs(res, msgs, max_out=MAX_OUT),
|
||
role="selfassess", book=book, arm="SELF")
|
||
if rec.get("skipped"):
|
||
raise SystemExit(f"⛔ ГАРД ОТКАЗАЛ на {tg} — СТОП")
|
||
MONEY.report()
|
||
|
||
|
||
def cmd_show() -> None:
|
||
print("Д7 — САМООЦЕНКА ЖИЛЬЦОВ\n")
|
||
print("⚠ САМООЦЕНКА МОДЕЛЕЙ НЕНАДЁЖНА. Печатается как любопытство; никакие решения фазы на "
|
||
"этих ответах НЕ стоят — так объявлено ДО покупки.\n")
|
||
fact = {"zh": "факт фазы: связка 4.00 ошибок/ед., черновик хуже",
|
||
"en": "факт фазы: черновик 2.78 → связка 1.22 ошибок/ед.",
|
||
"ja": "факт фазы: черновик 5.44 → связка 2.22 ошибок/ед. (ХУДШИЙ черновик фазы)"}
|
||
for res in RESIDENTS:
|
||
for book in books():
|
||
t = text_of(res, book)
|
||
if not t.strip():
|
||
continue
|
||
print(f"### {res} · {book} — {fact[book]}")
|
||
print(" " + t.strip().replace("\n", "\n ")[:900] + "\n")
|
||
|
||
|
||
def cmd_dry() -> None:
|
||
have = sum(1 for r in RESIDENTS for b in books() if text_of(r, b).strip())
|
||
n = len(RESIDENTS) * len(books())
|
||
sp = MONEY.Guarded(PHASE).spent()
|
||
print(f"Д7 · клеток {have}/{n} · жильцы {' · '.join(RESIDENTS)} · книги zh/en/ja")
|
||
print(f" {PHASE}: потрачено ${sp:.4f} · потолок ${MONEY.CEILINGS[PHASE]:.2f} · "
|
||
f"смета ${(n - have) * 0.004:.4f}")
|
||
|
||
|
||
def cmd_selftest() -> int:
|
||
bad = 0
|
||
|
||
def ck(n: str, ok: bool, d: str = "") -> None:
|
||
nonlocal bad
|
||
bad += not ok
|
||
print(f"[{'OK ' if ok else 'ПРОВАЛ'}] {n}" + (f" {d}" if d else ""))
|
||
|
||
bk = books()
|
||
ck("три книги фазы", len(bk) == 3, " ".join(bk))
|
||
ck("отрывки — из приколотых единиц, непустые",
|
||
all(len(v[1]) > 500 for v in bk.values()))
|
||
ck("тег покрыт глобом кассы ФД-D", tag("m", "zh").startswith("dv-d-"))
|
||
ck("вопрос НЕ подсказывает ответ (нет слов «трудно»/«не сможешь» в постановке)",
|
||
"не сможешь" not in SYS)
|
||
# Оговорка обязана быть в САМОЙ печати, а не в моей памяти: иначе при чтении отчёта
|
||
# «любопытство» незаметно превратится в довод.
|
||
src = Path(__file__).read_text(encoding="utf-8")
|
||
ck("оговорка о ненадёжности зашита в печать", "САМООЦЕНКА МОДЕЛЕЙ НЕНАДЁЖНА" in src)
|
||
print(f"\n{'Д7 ГОТОВ' if not bad else f'ПРОВАЛОВ: {bad}'}")
|
||
return bad
|
||
|
||
|
||
if __name__ == "__main__":
|
||
a = sys.argv[1] if len(sys.argv) > 1 else "--dry"
|
||
fn = {"--dry": cmd_dry, "--run": cmd_run, "--show": cmd_show,
|
||
"--selftest": lambda: sys.exit(1 if cmd_selftest() else 0)}.get(a)
|
||
fn() if fn else print(__doc__)
|