105 lines
5.5 KiB
Python
105 lines
5.5 KiB
Python
#!/usr/bin/env python3
|
||
"""D39.8 blind-read package builder for the pere-run (rerun2).
|
||
|
||
3 editor arms (glm-5 / mistral / deepseek-pro) of 蛊真人 ch1-5, zh->ru. Per CHAPTER, the 3 versions are
|
||
shuffled to neutral labels A/B/C with a PER-CHAPTER salt (so the reader cannot accumulate "C = one model"
|
||
across chapters). Source at the top of each chapter for fidelity comparison. Auto-QA (CJK-in-ru residual)
|
||
runs before the human. Correspondence key -> a SEPARATE file, withheld from the owner until verdict (D39.8).
|
||
"""
|
||
import json, random, re
|
||
from pathlib import Path
|
||
|
||
RUN = Path("/home/ubuntu/books/gu-zhenren/rerun2")
|
||
OUT = RUN / "blind"
|
||
OUT.mkdir(exist_ok=True)
|
||
ARMS = ["glm", "mistral", "dspro"]
|
||
|
||
def load():
|
||
arms = {}
|
||
for a in ARMS:
|
||
d = json.load(open(RUN / f"export-{a}.json"))
|
||
arms[a] = d["chunks"]
|
||
return arms
|
||
|
||
def by_chapter(chunks):
|
||
ch = {}
|
||
for c in sorted(chunks, key=lambda c: (int(c["chapter"]), int(c["chunk_idx"]))):
|
||
ch.setdefault(c["chapter"], {"final": [], "source": [], "disp": []})
|
||
ch[c["chapter"]]["final"].append(c["final_text"])
|
||
ch[c["chapter"]]["source"].append(c["source"])
|
||
ch[c["chapter"]]["disp"].append(c["disposition"])
|
||
return {k: {"final": "\n\n".join(v["final"]), "source": "\n\n".join(v["source"]),
|
||
"disp": v["disp"]} for k, v in ch.items()}
|
||
|
||
CJK = re.compile(r"[㐀-鿿豈-]")
|
||
def auto_qa(text):
|
||
flags = []
|
||
cjk = CJK.findall(text)
|
||
if cjk:
|
||
flags.append(f"CJK-in-ru residual: {len(cjk)} char(s) e.g. {''.join(cjk[:6])}")
|
||
# crude latin-word leak (>=3 latin letters run, excluding common ok tokens)
|
||
lat = re.findall(r"[A-Za-z]{3,}", text)
|
||
if lat:
|
||
flags.append(f"latin run(s): {lat[:5]}")
|
||
return flags
|
||
|
||
def main():
|
||
arms = load()
|
||
chap = {a: by_chapter(arms[a]) for a in ARMS}
|
||
chapters = sorted(chap["glm"].keys(), key=int)
|
||
|
||
key = {}
|
||
packet = []
|
||
packet.append("# Слепой пакет чтения — пере-прогон rerun2 (蛊真人, главы 1–5, zh→ru)\n")
|
||
packet.append(
|
||
"Три версии перевода каждой главы (**A / B / C**) — это три редакторских «руки» одного и того же "
|
||
"чернового перевода. Метки **перетасованы заново в каждой главе** (в гл.1 «A» и в гл.2 «A» — это, "
|
||
"скорее всего, РАЗНЫЕ руки). Соответствие меток и моделей — в отдельном файле "
|
||
"`blind_read_KEY.json`, **его не смотреть до вердикта**.\n")
|
||
packet.append(
|
||
"**Как читать (планка: ≤2 претензии на версию):** по каждой главе отметьте для A/B/C претензии "
|
||
"по ярусам — **критические** (искажение смысла / пропуск / неверный род / непереведённый "
|
||
"китайский / сломанное слово), **смысловые** (неточность, единицы времени, числа), "
|
||
"**редакторские** (стиль, ритм, канцелярит) — и назовите предпочтительную версию.\n")
|
||
packet.append("---\n")
|
||
|
||
for ch in chapters:
|
||
rng = random.Random(f"rerun2-blind-ch{ch}")
|
||
order = ARMS[:]
|
||
rng.shuffle(order)
|
||
labels = ["A", "B", "C"]
|
||
key[ch] = {labels[i]: order[i] for i in range(3)}
|
||
src = chap[order[0]][ch]["source"] # same source across arms
|
||
packet.append(f"## Глава {ch}\n")
|
||
packet.append(f"<details><summary>Китайский исходник (для сверки верности)</summary>\n\n```\n{src}\n```\n</details>\n")
|
||
for i, lab in enumerate(labels):
|
||
arm = order[i]
|
||
txt = chap[arm][ch]["final"]
|
||
qa = auto_qa(txt)
|
||
qa_note = f"\n> _(авто-QA: {'; '.join(qa)})_\n" if qa else ""
|
||
packet.append(f"### Глава {ch} — версия {lab}\n{qa_note}\n{txt}\n")
|
||
packet.append(f"\n**Ваши претензии по главе {ch}:**\n"
|
||
"- Версия A — критич.: … · смысл.: … · редакт.: …\n"
|
||
"- Версия B — критич.: … · смысл.: … · редакт.: …\n"
|
||
"- Версия C — критич.: … · смысл.: … · редакт.: …\n"
|
||
"- Предпочтение: …\n\n---\n")
|
||
|
||
(OUT / "blind_read_packet.md").write_text("\n".join(packet))
|
||
(OUT / "blind_read_KEY.json").write_text(json.dumps(
|
||
{"note": "WITHHELD from owner until verdict (D39.8). Per-chapter label->arm mapping.",
|
||
"arm_label": {"glm": "glm-5 (base)", "mistral": "mistral-large-2512", "dspro": "deepseek-v4-pro"},
|
||
"mapping": key}, ensure_ascii=False, indent=2))
|
||
# QA summary (mine, not owner-facing)
|
||
qa_summary = {}
|
||
for ch in chapters:
|
||
for arm in ARMS:
|
||
f = auto_qa(chap[arm][ch]["final"])
|
||
if f: qa_summary.setdefault(arm, {})[ch] = f
|
||
(OUT / "auto_qa.json").write_text(json.dumps(qa_summary, ensure_ascii=False, indent=2))
|
||
print("packet:", OUT / "blind_read_packet.md")
|
||
print("key (withheld):", OUT / "blind_read_KEY.json")
|
||
print("chapters:", chapters)
|
||
print("auto-QA hits by arm:", {a: list(v.keys()) for a, v in qa_summary.items()})
|
||
|
||
if __name__ == "__main__":
|
||
main()
|