textmachine/eval/exp12_candidate.py

72 lines
4.9 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
"""exp12 — «кандидат-фикс» (пост-фидбек владельца, ВНЕ слепого бейк-оффа).
Демонстрация направления фикса на 3 тех же главах: glm-5 БИЛИНГВ (судейский #1, не течёт
преамбулой как gemini) + разрешение переверстать абзацы + жанр-конвенции (культивация,
секты/школы, земной поклон). Спорные термины (人祖, 花酒行者) НЕ хардкожу — оставляю модели
с исходником + пометкой «на утверждение глоссария». НЕ арм пре-регистрации.
Выход: diag/arms/CAND/<ch>_<ci>.txt
"""
from __future__ import annotations
import json, os, sys, urllib.request
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
from refusal_bench import load_env_file
load_env_file()
DIAG = Path("/home/ubuntu/books/gu-zhenren/diag")
OUT = DIAG / "arms" / "CAND"
chunks = json.loads((DIAG / "chunks.json").read_text(encoding="utf-8"))
blocks = json.loads((DIAG / "blocks.json").read_text(encoding="utf-8"))
SYS = (
"Ты — литературный редактор художественного перевода китайской вебновеллы-культиваторки "
"(сянься/уся) на русский. Тебе дан ИСХОДНИК (китайский) и его ЧЕРНОВОЙ перевод. Сделай из "
"черновика ЖИВУЮ, читаемую русскую прозу издательского уровня:\n"
"1. АБЗАЦЫ: собирай предложения в логические ЕВРОПЕЙСКИЕ абзацы. Китайская манера "
"«предложение = отдельная строка» для русской прозы деструктивна — группируй повествование "
"по смыслу. Реплики прямой речи — отдельными строками с тире.\n"
"2. СМЫСЛ И СЮЖЕТ: сверяйся с ИСХОДНИКОМ, следи за тем, ЧТО происходит по сюжету, и правь так, "
"чтобы читатель понимал сцену. Исправляй искажения и машинные кальки черновика; сохраняй полноту.\n"
"3. ЖАНР-КОНВЕНЦИИ русского фан-перевода культиваторок: 修炼 = «культивация» (не «совершенствование»); "
"门派/派 = «секты»/«школы» (не «фракции»); 磕头 = «земной поклон»/«коснулся лбом пола» (не «ударил головой»); "
"меры времени (时辰≈2 часа) переводи или глоссируй. Прочую терминологию имён/титулов держи "
"последовательно (в приложенном глоссарии, если есть).\n"
"4. Убери канцелярит и кальки, оживи синтаксис и ритм (школа Норы Галь). Не пересочиняй сцены, "
"не добавляй отсебятины.\n"
"Выведи ТОЛЬКО отредактированный русский текст, без преамбул, комментариев и пояснений."
)
SPEC = dict(model="glm-5", base="https://api.z.ai/api/paas/v4", keyenv="ZAI_API_KEY",
extra={"thinking": {"type": "disabled"}}, max_tokens=8000)
def call(msgs):
key = os.environ.get(SPEC["keyenv"], "")
body = {"model": SPEC["model"], "temperature": 0.5, "max_tokens": SPEC["max_tokens"],
"messages": msgs, **SPEC["extra"]}
req = urllib.request.Request(SPEC["base"].rstrip("/") + "/chat/completions",
data=json.dumps(body).encode(),
headers={"Content-Type": "application/json", "Authorization": f"Bearer {key}"})
with urllib.request.urlopen(req, timeout=300) as r:
return json.loads(r.read())["choices"][0]["message"]["content"].strip()
def main():
OUT.mkdir(parents=True, exist_ok=True)
for ch, ci in [(5, 0), (5, 1), (7, 0), (7, 1), (14, 0), (14, 1)]:
key = f"{ch}/{ci}"
src, draft, gl = chunks[key]["source"], chunks[key]["draft"], blocks[key]["bilingual_glossary"]
msgs = [{"role": "system", "content": SYS}]
if gl.strip():
msgs.append({"role": "system", "content": gl})
msgs.append({"role": "user",
"content": f"ИСХОДНИК (zh):\n\n{src}\n\n---\n\nЧерновик для редактуры:\n\n{draft}"})
out = call(msgs)
(OUT / f"{ch}_{ci}.txt").write_text(out, encoding="utf-8")
print(f" CAND {key}: {len(out)} ch")
if __name__ == "__main__":
main()