docs+ui: страница «Документация», единый стиль заголовков, перевод на оператора

Добавлена /docs.html — обзор мультиагентной системы для оператора. Все
термины в формате «русский (english)», жирным: ветка (intent), маршрутизатор
(router), пошаговый сценарий (state machine), шаг (step), допустимые
переходы (allowed_next), слоты (slots), условия выхода (exit conditions),
переключение ветки (hard handoff), удержание в ветке (sticky state machine),
структурированный ответ (structured output), отложенный сценарий
(suspended/resume), защита от петли (routing loop guard), состояние диалога
(thread state). Плюс пошаговая схема обработки реплики и резюме защитных
механизмов. Ссылка «Документация» добавлена в шапку всех страниц.

Унификация заголовков под стиль «Версии» в правом сайдбаре Настроек: убран
uppercase, переход на 13px / var(--fg) / font-weight 600 / зажатый
letter-spacing. Применилось к .col-head во всех колонках, .field label в
редакторе, .section-header в списке веток, заголовкам столбцов на странице
Отладки и заголовкам секций RAG-результата. Бейджи (АКТИВНАЯ, система)
оставлены прежними — это статусные метки, не заголовки.

Переименование ветки escalate_human для согласованности с русским UI:
«Эскалация на оператора» → «Перевод на оператора», описание тоже. Точечная
миграция при старте (intent_service.migrate_intent_copy) обновляет
существующие записи в БД, только если поле в точности совпадает со старым
значением — операторские правки не затираются.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
AR 15 M4
2026-04-25 16:41:58 +05:00
parent 932b488bcb
commit 3c71372ec8
6 changed files with 510 additions and 68 deletions
+36 -1
View File
@@ -22,7 +22,7 @@ SEED_INTENTS: list[dict] = [
{"code": "price_question", "name": "Цены и ДМС", "description": "Вопросы про стоимость услуг, оплату, ДМС."},
{"code": "medical_question", "name": "Медицинский вопрос", "description": "Симптомы, лекарства, диагноз — требует врача."},
{"code": "general_info", "name": "Общая справка", "description": "Адрес, часы работы, как доехать, общие вопросы."},
{"code": "escalate_human", "name": "Эскалация на оператора", "description": "Передача диалога живому оператору."},
{"code": "escalate_human", "name": "Перевод на оператора", "description": "Перевод диалога на живого оператора."},
{"code": ROUTER_INTENT_CODE, "name": "Маршрутизатор", "description": "Системная ветка: промпт классификатора намерений. Пациенту напрямую не отвечает."},
]
@@ -81,3 +81,38 @@ async def ensure_seed_intents(session: AsyncSession) -> None:
if added:
await session.commit()
logger.info("Seeded %d missing intents", added)
# Точечные переименования: пользователь правит UI-копию, но в БД уже залит
# старый сид. Применяем мягко — только если поле в точности совпадает со старым
# значением (значит оператор не правил его сам).
_INTENT_NAME_MIGRATIONS: list[dict] = [
{
"code": "escalate_human",
"old_name": "Эскалация на оператора",
"new_name": "Перевод на оператора",
"old_description": "Передача диалога живому оператору.",
"new_description": "Перевод диалога на живого оператора.",
},
]
async def migrate_intent_copy(session: AsyncSession) -> None:
"""Обновляет name/description у системных веток, если в БД лежит старый текст."""
updated = 0
for spec in _INTENT_NAME_MIGRATIONS:
intent = await get_intent_by_code(session, spec["code"])
if intent is None:
continue
changed = False
if intent.name == spec["old_name"]:
intent.name = spec["new_name"]
changed = True
if intent.description == spec["old_description"]:
intent.description = spec["new_description"]
changed = True
if changed:
updated += 1
if updated:
await session.commit()
logger.info("Migrated copy for %d intent(s)", updated)