textmachine/eval/exp14/exp14_prompts.py

101 lines
6.4 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
"""exp14 — рендер промптов армов (ЗАМОРОЖЕНЫ; sha256 в §2 дока).
- baseline editor = backend/prompts/editor.md (v2-bilingual-reflow), brief-filled.
- discourse editor = baseline + дискурс-reflow ядро (§1.2: 5 техник Ван Цуй + Розенталь) + few-shot.
- translator (для re-draft глав/нарезки) = backend/prompts/translator.md (v1-reflow).
Few-shot — минимальные ортогональные (narrative N:1, dialogue 1:N, focal-shift). НЕ ручной CoT.
"""
from __future__ import annotations
import hashlib
from pathlib import Path
PROMPTS = Path("/home/ubuntu/projects/textmachine/backend/prompts")
BRIEF = dict(source_lang="zh", target_lang="ru", genre="вебновелла",
audience="взрослые читатели вебновелл", title="蛊真人",
honorifics="keep", transcription="palladius", venuti="0.6", footnotes="minimal")
def _fill(t: str) -> str:
for k, v in BRIEF.items():
t = t.replace("{{" + k + "}}", v)
return t
def _split(md_text: str) -> tuple[str, str]:
sysmd, usermd = md_text.split("---USER---", 1)
return _fill(sysmd).strip(), _fill(usermd).strip()
# ── Дискурс-reflow ядро (§1.2; арм для ЗАМЕРА, не «оптимальный промпт») ──────────────
DISCOURSE_CORE = """
ДИСКУРС-ПЕРЕВЁРСТКА (обязательная операция передачи китайского паратаксиса в русскую гипотактическую прозу — это КОНВЕНЦИЯ русского языка, а не вольность):
1. Молча определи смысловые ходы фрагмента: единый фокал/наблюдатель; действие и его непосредственная реакция; одна тема рассуждения; границы — смена говорящего, времени, места или фокала.
2. Один повествовательный ход оформляй ОДНИМ русским абзацем, ДАЖЕ если китайский исходник разбит на однофразовые строки. Инструмент слияния — причастные и деепричастные обороты, подчинительные союзы и связки (построй иерархию, сегментируй по смыслу, промаркируй вид/время предикатов, варьируй лексику, добавляй связки). Не сливай ТОЛЬКО ради длины и не теряй смысловые атомы.
3. Каждую новую реплику начинай с нового абзаца через тире «—»; слова автора при той же реплике оставляй в её абзаце.
4. Разрешено объединять и делить русские предложения; число предложений и абзацев совпадать с исходником НЕ обязано. Запрещено терять смысловые атомы (участников, действия, числа, полярность, заголовки).
"""
FEWSHOT = """
Примеры требуемой ПЕРЕВЁРСТКИ (показана структура вывода, не стиль):
[narrative N:1] Исходник построчно — «Он вышел за дверь.» / «Небо было серым.» / «Он вздохнул.» / «Дорога уходила вдаль.» — становится ОДНИМ абзацем:
Он вышел за дверь. Небо было серым; вздохнув, он смотрел, как дорога уходит вдаль.
[dialogue 1:N] Исходник — «Дядя нахмурился и сказал: „Время летит. Садись.“ Племянник ответил: „Спасибо.“» — становится:
Дядя нахмурился.
— Время летит. Садись, — сказал он.
— Спасибо, — ответил племянник.
[focal-shift] При смене наблюдателя/места/времени — новый абзац, даже если в исходнике это продолжение той же строки.
"""
REFERENCE_NOTE = ("\n\nСПРАВОЧНЫЙ КОНТЕКСТ (соседние фрагменты — ТОЛЬКО для разрешения связей, "
"местоимений и опущенных подлежащих; НЕ переводи и НЕ пересказывай его в выходе):")
def editor_system(variant: str, few_shot: bool = True) -> str:
sysmd, _ = _split((PROMPTS / "editor.md").read_text(encoding="utf-8"))
if variant == "baseline":
return sysmd
if variant == "discourse":
out = sysmd + "\n" + DISCOURSE_CORE
if few_shot:
out += FEWSHOT
return out
raise ValueError(variant)
def editor_user(source: str, draft: str, reference: str = "") -> str:
_, usermd = _split((PROMPTS / "editor.md").read_text(encoding="utf-8"))
u = usermd.replace("{{text}}", source).replace("{{draft}}", draft)
if reference.strip():
u += REFERENCE_NOTE + "\n" + reference.strip()
return u
def translator_system() -> str:
sysmd, _ = _split((PROMPTS / "translator.md").read_text(encoding="utf-8"))
return sysmd
def translator_user(source: str) -> str:
_, usermd = _split((PROMPTS / "translator.md").read_text(encoding="utf-8"))
return usermd.replace("{{text}}", source)
def sha_frozen() -> dict:
return {
"editor_baseline_sys": hashlib.sha256(editor_system("baseline").encode()).hexdigest()[:16],
"editor_discourse_sys": hashlib.sha256(editor_system("discourse").encode()).hexdigest()[:16],
"discourse_core": hashlib.sha256(DISCOURSE_CORE.encode()).hexdigest()[:16],
"fewshot": hashlib.sha256(FEWSHOT.encode()).hexdigest()[:16],
"translator_sys": hashlib.sha256(translator_system().encode()).hexdigest()[:16],
}
if __name__ == "__main__":
import json
print("=== frozen prompt sha256[:16] ===")
print(json.dumps(sha_frozen(), indent=1, ensure_ascii=False))
print("\n=== editor discourse system (first 600) ===")
print(editor_system("discourse")[:600])