Перейти к содержанию

Reasoning Layer minimum — DEPLOYED 2 мая 2026

Дизайн (см. [[lyumi/reasoning_layer_design]]) предписывал не начинать до 9 мая. Камал решил: «не хочу промтом лечить, давай как-то технически» — и мы развернули минимум в тот же день. Архитектура есть. Real measurement отложен на observation week.

Триггер

Юзерский кейс утром 2 мая: «можно ли использовать СИЗ с истёкшим сертификатом ТР ТС 019, если по паспорту срок ещё 5 лет?» Lyumi ответила категоричным «нельзя использовать, выкинуть в хоз.нужды». Это reasoning mistake, не factual hallucination — факты правильные (ТР ТС 019 — техрегламент, ст. 156 УК РК — норма), но логика применения неверна.

v4 SQL grounding такие ошибки не ловит. Reflection (Haiku) тоже — она проверяет факты, не reasoning.

Что задеплоено (4 слоя, $0/мес, +20-50мс latency)

Слой 1: Source Authority Labels

Файл: source_authority.py (новый, 13 KB).

Функция classify_authority(metadata, title, doc_text) возвращает один из 4 ярлыков:

Ярлык Что Регулярка-триггер
[НОРМА] НПА РК, ТР ТС, кодексы, законы, приказы Трудовой кодекс, ТР ТС 019, Приказ МТСЗН, ҚР ДСМ, [VKUP]\d{10} (Әділет ID)
[ОПЕРАТИВНО] Норма + конкретный пункт/статья (для прямых цитат) НОРМА + (статья N\|пункт N\|глава N)
[МЕТОДИЧКА] Международные стандарты (рекомендации) OSHA, IOGP, ISO, NIOSH, NEBOSH, NFPA, API N, ANSI, ILO, ACGIH, AIHA, WHO, IEC, OHSAS
[КОММЕНТАРИЙ] Учебники, обзоры, презентации default

Интегрировано в retriever.format_context_for_llm — каждый hit идёт в LLM с меткой:

--- [НОРМА] Документ 1: Трудовой кодекс РК (релевантность: 0.85) ---
--- [МЕТОДИЧКА] Документ 2: OSHA 1910.146 (релевантность: 0.62) ---

Промпт-инструкция в llm.py (новая секция «МЕТКИ АВТОРИТЕТА ИСТОЧНИКОВ»): Lyumi обязана сохранять метки в финальном ответе — «По [НОРМА] Трудовому кодексу РК ст. 182…», «По [МЕТОДИЧКА] OSHA рекомендуется…, в РК это не обязательно».

15/15 unit tests прошли.

Слой 2A: Reasoning Rules (5 правил)

Файл: reasoning_validator.py (новый, 40 KB), функция validate(query, response).

Каждое правило — ReasoningRule dataclass:

@dataclass
class ReasoningRule:
    rule_id: str
    title: str
    trigger_patterns: list[Pattern]   # когда проверять
    violation_patterns: list[Pattern] # что считать reasoning ошибкой
    correction: str                   # текст уточнения в конец
    suppress_if_present: list[Pattern] # если бот сам ОК — не срабатываем
    severity: str = "warning"         # warning|critical
    short_alert: str = ""             # для critical — prepend в начало

5 правил задеплоены:

ID Severity Что ловит
tr_ts_019_certificate_vs_lifetime critical Категоричное «нельзя использовать СИЗ» при истёкшем сертификате — путаница «сертификат производителя» vs «срок службы изделия»
koap_vs_uk_distinction warning Смешение КоАП штрафа и УК статьи (например «штраф по ст. 156 УК»)
medosmotr_office_workers warning «Все обязаны проходить медосмотр» — false для офисников без вредных факторов
act_n1_authority warning «Работодатель сам подписывает Н-1» / «сроки не регламентированы»
ptw_storage_period warning «Наряд-допуск можно выкидывать после закрытия»

Severity = critical означает что short_alert prepend'ится в начало ответа (юзер видит ДО прочтения wrong статтей). Warning только append'ится в конец.

Prepend-стратегия для critical (ТР ТС 019) — fix после live теста: если ответ начинается с «нельзя использовать», append correction в конец слабо помогает (юзер мог прочитать только первую строку).

11/11 unit tests прошли (включая reggression test с реальным Sonnet ответом из live compare-теста).

Слой 2B: Positive Citation Check

Функция positive_citation_check(response, cards). Каждое НПА-цитирование в response (Приказ N, ст. N, ТР ТС, ГОСТ, OSHA, ISO, NFPA, IOGP, СТ РК, СанПиН, ҚР ДСМ) проверяется на физическое присутствие в текущих retrieval cards.

Это сильнее существующей verify_citations: та говорит «приказ существует в общей базе», positive check — «приказ существует И мы его реально достали для этого вопроса». Phantom приказы (типа «Приказ МЗСР №1descanso» — Sonnet вставил испанское слово) ловятся даже если в общей базе нет аналога.

Regex для citations поддерживает: - Падежные формы (Приказ/Приказу/Приказом) - Alphanumeric номера (1descanso, X-15) — типичный признак phantom от LLM

Слой 3: Static Claim Flagging

Функция static_claim_flagging(response, cards). Разбор response на абзацы (\n\n), regex поиск specifics (статьи, приказы, ГОСТ, ТР ТС, OSHA, ISO, NFPA, IOGP, ҚР ДСМ + метрические значения с единицами).

Если в абзаце есть specifics, но ни один не найден в cards → flag. Threshold: ≥3 flagged абзацев ИЛИ ≥30% от paragraphs_with_specifics → warning.

При триггере дописывается общее уведомление:

ℹ️ Часть конкретных ссылок в ответе не подтверждена источниками этого поиска. Это не значит что они ложные — возможно, упомянутые НПА просто не попали в выдачу retrieval'а. Перепроверь по официальной нормативке.

Pipeline после деплоя

Generate response (Sonnet/Opus)
  ↓ context уже содержит [НОРМА]/[ОПЕРАТИВНО]/[МЕТОДИЧКА]/[КОММЕНТАРИЙ] метки (Слой 1)
  ↓
fix_gender / fix_ru_to_kz / fix_url_wrapping
  ↓
verify_citations  ← FACTUAL grounding (existing, март 21)
  ↓
reasoning_validate (5 правил, Слой 2A)
  → если violation → apply_corrections
      • severity=warning  → append correction в конец
      • severity=critical → prepend short_alert в начало + append correction
  → log в logs/reasoning_validations_YYYY-MM-DD.jsonl
  ↓
[если NOT npa_hits] positive_citation_check + static_claim_flagging (Слои 2B + 3)
  → если any issue → append UNSUPPORTED_CITATION_NOTICE
  ↓
_split_for_telegram → send

Skip 2B+3 когда SQL grounding активен (npa_hits != []) — там structured ground truth, доп. проверки только шумят.

Сравнение с Harvey/OpenEvidence

Компонент Harvey OpenEvidence Lyumi (после 2 мая)
Reasoning rules вне промпта
Детерминированная проверка
Логирование для калибровки ✅ JSONL
Source authority labels ✅ Слой 1
Severity stratification ✅ warning/critical
Claim decomposition (LLM) ❌ (вместо — static regex Слой 3)
Cross-reference на claim level ⚠️ partial (Слой 2B negative + 3 positive)
Custom reasoning model ❌ (overkill для нас)
Expert feedback loop ⚠️ один эксперт (Камал)

Coverage ~75% от full Harvey стека. Без LLM-decomposition coverage ниже, но trade-off в обмен на 0 latency / 0 cost / детерминированность.

Eval — honest assessment

3 evaluator'а написаны: - evaluate_reasoning_layer.py — 12 синтетических сценариев. Pass на всех 4 слоях. - compare_sonnet_opus.py — 5 reasoning queries, 2 модели. На 4/5 EQUAL, 1 OPUS лучше (ТР ТС 019). Opus ×6.5 дороже. - compare_vanilla_lyumi_opus.py + evaluate_accuracy.py — 5 reasoning queries × 3 конфигурации (Vanilla / Lyumi Sonnet / Lyumi Opus) с positive/negative markers regex.

Итог 3-way eval (n=5):

Конфигурация Avg score CORRECT PARTIAL INCORRECT $/тест
Vanilla Sonnet 0.65 60% 20% 20% $0.04
Lyumi Sonnet 0.72 40% 40% 20% $0.06
Lyumi Opus 0.73 40% 60% 0% $0.67

Inconclusive. Avg score у Lyumi выше, но CORRECT count такой же. n=5 недостаточно для статистики, regex markers легко сдвигаются на ±0.2 при изменении формулировки.

Что устойчиво: - 0% INCORRECT у Lyumi Opus — единственная stable метрика. Opus + наша архитектура не выдаёт катастрофических ответов на reasoning. - Live prod test ТР ТС 019 (Камал, 2 мая) — Lyumi ответила правильно, использовала [НОРМА] метку, признала прошлую ошибку. Это prod-доказательство что слои работают, micro-test не показывает. - Vanilla дала 1 INCORRECT (наряд-допуск 30 суток вместо 1 года). Lyumi Sonnet тоже INCORRECT (формулировка), но в обоих случаях по существу не катастрофа.

Lesson: не тюнить markers до желаемой картинки. После 2-3 итераций tuning'а stop'нул — иначе cooking the books.

Тех-долг и TODO

Не сделано (отложено на Sprint A после 9 мая): - G_reasoning категория в eval — собрать 30-50 reasoning queries с manual ground truth - Расширить reasoning_rules до 15-20 правил (текущие 5 покрывают только hot-spots) - Claim decomposition через Haiku — Слой 3 в полной форме (сейчас static regex). Только если G_reasoning eval покажет нужду. - HSE expert feedback loop — формализация раз в неделю, после 50+ активных юзеров. - lyumi/reasoning_validator.py → больше тестов с edge cases формулировок

Калибровка через observation week: JSONL логи reasoning_validations_*.jsonl за 7-14 дней дадут реальные данные для расширения rules.

Дельта по файлам

Новые: - source_authority.py (13 KB) - reasoning_validator.py (40 KB) — 5 правил + 3 функции (validate, positive_citation_check, static_claim_flagging) - evaluate_reasoning_layer.py — 12-сценарный eval всех 4 слоёв - compare_sonnet_opus.py — 2-way модель сравнение - compare_vanilla_lyumi_opus.py — 3-way конфигурация сравнение - evaluate_accuracy.py — accuracy с ground truth markers

Изменены: - bot.py — импорт + 3 hook'а (text, photo, multi-photo) после verify_citations - retriever.pyformat_context_for_llm добавляет authority tags - llm.py — секция «МЕТКИ АВТОРИТЕТА ИСТОЧНИКОВ» в системном промпте

Связи

  • [[lyumi/reasoning_layer_design]] — дизайн (этот sprint реализовал минимум, дизайн остаётся актуальным для слоёв 4-5)
  • [[lyumi/v4_structured_retrieval]] — factual grounding (parallel pillar)
  • [[lyumi/sprints/2026-05-01-sql-acgih-ax41]] — предыдущий sprint (SQL grounding)
  • [[lyumi/strategy_2026]] — общая стратегия

Что сегодня было ещё (контекст)

Reasoning layer — это четвёртый sprint дня. До него: 1. Sprint 1 multi-language SQL detection + cross-code disambig (4:00) 2. Reflection regression hot-fix — skip on SQL hits (4:30) 3. Multi-photo split + retry-aware Telegram send (5:00) 4. log_analysis.py — 4-channel pipeline breakdown (10:00) 5. Document Intelligence v2 — idempotency + photo verify + multi-doc stack + OCR Tesseract (12:00) 6. Reasoning Layer minimum (12:30-15:00)

Все шесть в проде. День.