[bot] LLM-шлюз: GigaChat + mock, extract / risk_classify / phrase / summarize #7

Closed
opened 2026-10-03 14:38:53 +03:00 by Eleboba · 0 comments
Owner

Описание

SPEC §7. Провайдер за протоколом, офлайн mock по умолчанию (ADR-004). Всё входное — только после scrub().

Желаемый результат

  • app/llm/gateway.py: LLMProvider Protocol + фабрика по LLM_PROVIDER. Единственная точка вызова LLM; assert что текст прошёл scrub (маркер/тип ScrubbedText).
  • app/llm/giga.py: пакет gigachat, temperature 0.1, JSON-ответ, валидация pydantic, 2 ретрая, таймаут, при неудаче None.
  • app/llm/mock.py: правило-ориентированные ответы (ключевые слова → ItemScore / флаги), покрывает все реплики демо-сценария.
  • Задачи: extract_item(item, text) -> ItemScore|None; extract_checkin(text) -> CheckinFacts (побочки, намерение бросить, алкоголь, сон, кошмары) с confidence; risk_classify(text) -> none|possible|acute; phrase(intent, context) -> str (короткая эмпатичная реплика без советов/диагнозов); summarize_for_doctor(structured) -> str.
  • app/llm/prompts.py — системные промпты с запретами SPEC §7.

Как проверить

Тесты на mock + валидацию; ручной прогон python -m app.llm.cli extract "сплю плохо, кошмары почти каждую ночь" с LLM_PROVIDER=gigachat при наличии ключа.


Лог выполнения

2026-10-03

2026-10-03

Что сделано

  • app/llm/gateway.py — единственная точка входа: extract_item, extract_checkin, risk_classify, phrase, summarize_for_doctor; фабрика get_gateway() / build_gateway(settings) по LLM_PROVIDER.
  • Двухуровневая архитектура: LLMProvider (транспорт: complete_json / complete_text, реализован GigaChat) и LLMTasks (задачи: PromptedTasks поверх провайдера или rule-based MockTasks). Mock работает на уровне задач, а не разбирает промпты, поэтому политика шлюза одинакова для обоих провайдеров.
  • app/llm/giga.py — GigaChat: temperature 0.1, таймаут 8 с, до 2 ретраев, снятие ```json, валидация pydantic, при неудаче None; в логах только попытка, латентность, токены, класс ошибки.
  • app/llm/mock.py — регулярные выражения по русскому тексту; покрыты все реплики демо-персоны.
  • app/llm/prompts.py — системные промпты с запретами SPEC §7 + защита от prompt injection.
  • app/llm/guard.py — assert_scrubbed: эшелонированная защита (телефон, e-mail, СНИЛС) перед отправкой в провайдер.
  • config/texts/phrases.yaml — шаблоны реплик (база для перефразирования, ответ mock, fallback) и подписи для саммари врачу.
  • CLI python -m app.llm.cli extract|item|risk — без scrub() отказывается работать (exit 2).

Результат

  • tests/test_llm.py: 52 теста (+1 live, пропускается без GIGACHAT_CREDENTIALS). make lint test зелёный.

Заметки/решения

  • Порог ITEM_CONFIDENCE_THRESHOLD = 0.5; балл выше максимума пункта отбрасывается.
  • risk_classify при ошибке → RiskLevel.NONE (вспомогательный сигнал, основной — лексикон).
  • Фильтр запретов в ответах (диагноз, «у вас депрессия», изменение дозы, «отмените», «прекратите приём», обещания конфиденциальности) → fallback на шаблон.
  • LLM_PROVIDER=gigachat при пустом ключе → mock с предупреждением (офлайн-первый, ADR-004).
  • В mock «сплю плохо» не считается побочкой insomnia (только «не могу уснуть», «бессонница»), иначе у ветеранов каждый день был бы флаг побочки. «Не могу уснуть без пива» → alcohol 1-2 (минимальная корзина, кнопка уточняет).
  • Тексты phrases.yaml нужно вычитать психиатру (зона clinical).
## Описание SPEC §7. Провайдер за протоколом, офлайн `mock` по умолчанию (ADR-004). Всё входное — только после `scrub()`. ## Желаемый результат - `app/llm/gateway.py`: `LLMProvider` Protocol + фабрика по `LLM_PROVIDER`. Единственная точка вызова LLM; assert что текст прошёл scrub (маркер/тип `ScrubbedText`). - `app/llm/giga.py`: пакет `gigachat`, temperature 0.1, JSON-ответ, валидация pydantic, 2 ретрая, таймаут, при неудаче `None`. - `app/llm/mock.py`: правило-ориентированные ответы (ключевые слова → ItemScore / флаги), покрывает все реплики демо-сценария. - Задачи: `extract_item(item, text) -> ItemScore|None`; `extract_checkin(text) -> CheckinFacts` (побочки, намерение бросить, алкоголь, сон, кошмары) с confidence; `risk_classify(text) -> none|possible|acute`; `phrase(intent, context) -> str` (короткая эмпатичная реплика без советов/диагнозов); `summarize_for_doctor(structured) -> str`. - `app/llm/prompts.py` — системные промпты с запретами SPEC §7. ## Как проверить Тесты на mock + валидацию; ручной прогон `python -m app.llm.cli extract "сплю плохо, кошмары почти каждую ночь"` с `LLM_PROVIDER=gigachat` при наличии ключа. --- ## Лог выполнения ### 2026-10-03 ### 2026-10-03 **Что сделано** - `app/llm/gateway.py` — единственная точка входа: `extract_item`, `extract_checkin`, `risk_classify`, `phrase`, `summarize_for_doctor`; фабрика `get_gateway()` / `build_gateway(settings)` по `LLM_PROVIDER`. - Двухуровневая архитектура: `LLMProvider` (транспорт: `complete_json` / `complete_text`, реализован GigaChat) и `LLMTasks` (задачи: `PromptedTasks` поверх провайдера или rule-based `MockTasks`). Mock работает на уровне задач, а не разбирает промпты, поэтому политика шлюза одинакова для обоих провайдеров. - `app/llm/giga.py` — GigaChat: temperature 0.1, таймаут 8 с, до 2 ретраев, снятие ```json, валидация pydantic, при неудаче `None`; в логах только попытка, латентность, токены, класс ошибки. - `app/llm/mock.py` — регулярные выражения по русскому тексту; покрыты все реплики демо-персоны. - `app/llm/prompts.py` — системные промпты с запретами SPEC §7 + защита от prompt injection. - `app/llm/guard.py` — `assert_scrubbed`: эшелонированная защита (телефон, e-mail, СНИЛС) перед отправкой в провайдер. - `config/texts/phrases.yaml` — шаблоны реплик (база для перефразирования, ответ mock, fallback) и подписи для саммари врачу. - CLI `python -m app.llm.cli extract|item|risk` — без `scrub()` отказывается работать (exit 2). **Результат** - `tests/test_llm.py`: 52 теста (+1 live, пропускается без `GIGACHAT_CREDENTIALS`). `make lint test` зелёный. **Заметки/решения** - Порог `ITEM_CONFIDENCE_THRESHOLD = 0.5`; балл выше максимума пункта отбрасывается. - `risk_classify` при ошибке → `RiskLevel.NONE` (вспомогательный сигнал, основной — лексикон). - Фильтр запретов в ответах (диагноз, «у вас депрессия», изменение дозы, «отмените», «прекратите приём», обещания конфиденциальности) → fallback на шаблон. - `LLM_PROVIDER=gigachat` при пустом ключе → mock с предупреждением (офлайн-первый, ADR-004). - В mock «сплю плохо» не считается побочкой insomnia (только «не могу уснуть», «бессонница»), иначе у ветеранов каждый день был бы флаг побочки. «Не могу уснуть без пива» → alcohol `1-2` (минимальная корзина, кнопка уточняет). - Тексты `phrases.yaml` нужно вычитать психиатру (зона clinical).
Eleboba added this to the M1 Каркас milestone 2026-10-03 14:38:53 +03:00
Eleboba added the size/1P0botfeature labels 2026-10-03 14:38:53 +03:00
Eleboba added a new dependency 2026-10-03 14:38:59 +03:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Reference: MedITron/PTSRaiAgent#7