101 lines
6.4 KiB
Python
101 lines
6.4 KiB
Python
#!/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])
|