{player}
diff --git a/docs/ИНТЕГРАЦИЯ-TALKSCORE.md b/docs/ИНТЕГРАЦИЯ-TALKSCORE.md
index 4a5094a..568d74e 100644
--- a/docs/ИНТЕГРАЦИЯ-TALKSCORE.md
+++ b/docs/ИНТЕГРАЦИЯ-TALKSCORE.md
@@ -371,3 +371,47 @@ Sonnet 5 и Gemini 3.1 Pro.
сбои не заходя на машину.
- **Описание методов**: `http://АДРЕС:8756/docs` в браузере, открывается
с разрешённых адресов.
+
+
+## Черновик с вариантами нескольких моделей
+
+Обычная расшифровка - выбор одной модели. Черновик показывает, где модели
+разошлись, и позволяет выбрать вариант заново - человеком или языковой
+моделью.
+
+```
+GET /v1/jobs/{id}/draft?build=1 сборка (минуты, идёт фоном)
+GET /v1/jobs/{id}/draft?format=json голоса как есть
+GET /v1/jobs/{id}/draft?format=text строкой для языковой модели
+```
+
+Первый запрос запускает сборку и отвечает 202. Готовый черновик отдаётся
+кодом 200. Порядок для сервиса:
+
+```python
+import time
+
+import requests
+
+head = {"Authorization": f"Bearer {token}"}
+while True:
+ r = requests.get(f"{base}/v1/jobs/{job_id}/draft?format=text", headers=head)
+ if r.status_code == 200:
+ text = r.text
+ break
+ if r.status_code >= 400:
+ raise RuntimeError(r.text)
+ time.sleep(10) # 202: ещё собирается
+```
+
+В формате `text` спорное слово записано как `{основной|вариант}`. Промпт для
+выбора варианта - в `docs/ПРОМПТЫ.md`, раздел «Сборка чистого текста».
+
+В формате `json` каждое слово несёт словарь голосов
+`{"вариант": сколько моделей его предложили}`. Пустой словарь - все модели
+согласны. Порог сервис выбирает сам: голос одной модели из двух означает,
+что большинство за основным вариантом.
+
+Сборка занимает от минуты и требует, чтобы запись хранилась на диске
+(`keep_records = true`). Модели подтягиваются при первом обращении,
+около 500 МБ.
diff --git a/docs/ПРОМПТЫ.md b/docs/ПРОМПТЫ.md
index fd0bc62..7433c45 100644
--- a/docs/ПРОМПТЫ.md
+++ b/docs/ПРОМПТЫ.md
@@ -196,3 +196,60 @@
Отдельно: этим моделям нельзя задавать большой `max_tokens` - они выбирают
его целиком на размышления и до ответа не доходят. У Sonnet 5 такого
поведения не наблюдалось.
+
+## Сборка чистого текста из вариантов нескольких моделей
+
+Черновик отдаётся в машинном виде по адресу
+`GET /v1/jobs/{id}/draft?format=text&token=...`
+
+Спорное слово записано как `{основной|вариант|вариант}` - ровно тот выбор,
+который стоял перед человеком. Знак `—` значит, что другая модель промолчала,
+`+слово` - что услышала лишнее.
+
+```
+[00:23] Угу По вторникам и четвергам {А|+А-а|—} в мае или {В|—} июне
+[00:41] Стоимости по {пакетным|пакет обсуждения} {обсуждения|пакет обсуждения}
+[03:23-03:32] (речь не распознана, 9 с)
+```
+
+Языковая модель выбирает вариант там, где акустика неоднозначна, а смысл
+фразы - нет. Это её сильная сторона: распознавание слышит слово отдельно,
+модель видит предложение целиком.
+
+### Промпт
+
+```
+Ты редактор расшифровок телефонных разговоров на русском языке.
+
+Текст распознан тремя моделями. Там, где они разошлись, вариант записан
+в фигурных скобках: {основной|другой|третий}. Первый вариант - основной,
+остальные предложили другие модели. Знак — означает, что модель промолчала,
++слово - что она услышала лишнее слово.
+
+Твоя задача: выбрать по смыслу наиболее вероятный вариант в каждых скобках
+и вернуть связный текст без разметки.
+
+Правила:
+- Выбирай только из предложенных вариантов. Не придумывай слова, которых
+ нет ни в одном варианте.
+- Если ни один вариант не подходит по смыслу, оставь основной.
+- Числа приводи к цифрам: «двадцать четыре тысячи» -> «24 000».
+- Не исправляй разговорные обороты и повторы: это живая речь, а не текст.
+- Строки вида [03:23-03:32] (речь не распознана, 9 с) сохраняй как есть -
+ там пропущен кусок разговора.
+- Тайм-код в начале строки сохраняй.
+- Ничего не сокращай и не пересказывай. Число строк на выходе должно
+ совпадать с числом строк на входе.
+
+Верни только текст, без пояснений.
+```
+
+Разговор длиннее 20 минут стоит резать по строкам на части по 150-200 строк:
+так ответ не упрётся в предел длины, а по тайм-кодам части легко сшить.
+
+### Чего этот приём не делает
+
+Он не восстанавливает пропущенное. Если все три модели промолчали, в скобках
+не появится ничего - строка о пропуске означает, что кусок разговора потерян
+на записи, а не в разметке. Языковая модель на таком месте начнёт выдумывать,
+поэтому в промпте прямо запрещено дописывать слова.
diff --git a/tests/test_draft.py b/tests/test_draft.py
index 42d98a3..ce41cc8 100644
--- a/tests/test_draft.py
+++ b/tests/test_draft.py
@@ -57,3 +57,57 @@ class TestGaps:
def test_overlapping_turns_do_not_make_negative_gap(self):
assert _gaps([self.turn(0, 30), self.turn(10, 20)], 30) == []
+
+
+class TestDraftText:
+ """Машинный вид: сервис Talkscore отдаёт его языковой модели."""
+
+ @staticmethod
+ def draft():
+ return {"turns": [
+ {"start": 3.0, "end": 5.0, "words": [
+ {"text": "Стоимость", "votes": {}},
+ {"text": "пакетным", "votes": {"пакет обсуждения": 2}}]},
+ {"start": 30.0, "end": 33.0, "words": [
+ {"text": "Да", "votes": {"—": 1}}]}],
+ "duration_sec": 40.0, "compared": 2}
+
+ def test_disputed_word_carries_its_variants(self):
+ from app.draft import draft_text
+
+ assert "{пакетным|пакет обсуждения}" in draft_text(self.draft())
+
+ def test_agreed_word_stays_plain(self):
+ """Согласованное слово без скобок: модели нечего выбирать."""
+ from app.draft import draft_text
+
+ assert "{Стоимость" not in draft_text(self.draft())
+
+ def test_single_vote_hidden_by_default_shown_in_strict(self):
+ from app.draft import draft_text
+
+ assert "{Да|—}" not in draft_text(self.draft())
+ assert "{Да|—}" in draft_text(self.draft(), need=1)
+
+ def test_gap_between_turns_is_stated(self):
+ from app.draft import draft_text
+
+ assert "[00:05-00:30] (речь не распознана, 25 с)" in draft_text(self.draft())
+
+ def test_tail_gap_is_stated(self):
+ from app.draft import draft_text
+
+ assert "[00:33-00:40]" in draft_text(self.draft())
+
+ def test_timecode_starts_every_turn(self):
+ from app.draft import draft_text
+
+ lines = [ln for ln in draft_text(self.draft()).splitlines()
+ if "не распознана" not in ln]
+ assert [ln[:8] for ln in lines] == ["[00:03] ", "[00:30] "]
+
+ def test_leading_silence_is_reported(self):
+ """Запись может начаться с молчания - это тоже надо знать."""
+ from app.draft import draft_text
+
+ assert draft_text(self.draft()).startswith("[00:00-00:03] ")