Files
BlackboxBook/book/13_anti_hallucination_loop.md
2026-05-20 20:55:03 +03:00

38 KiB
Raw Blame History

ГЛАВА 13. АНТИГАЛЛЮЦИНАЦИОННЫЙ КОНТУР И ЗАЩИТА ОТ ДЕГРАДАЦИИ


13.1. Почему агенту нужен ревьюер

Принцип разделения ролей

Представьте себе пилота и второго пилота в кабине самолёта. Пилот ведёт машину, второй пилот — контролирует показания приборов, перепроверяет высоту, сверяет курс. Никто не ожидает, что один человек будет одновременно управлять штурвалом и вычитывать чек-лист. То же самое с LLM: Generator — это пилот, Verifier — второй пилот. Два набора глаз всегда лучше одного.

Можно описать это иначе: писатель и редактор. Писатель создаёт текст — живой, богатый, иногда с ошибками. Редактор читает холодным взглядом — точно ли это? есть ли противоречия? Совмещать обе роли в одной голове тяжело человеку и ещё тяжелее модели.

Технически: генератор оптимизирован на fluency (гладкость текста), ревьюер — на accuracy (точность содержания). Совмещать обе функции в одном вызове — значит требовать от модели одновременно быть «креативной» и «критичной», что создаёт конфликт в attention-паттернах.

Эмпирические данные

Разделение генерации и проверки почти всегда снижает hallucination rate по сравнению с одиночным вызовом, но величина выигрыша зависит от задачи, качества verifier'а и eval-протокола. CoVe (Dhuliawala et al., 2023) и последующие production-пайплайны показывают, что двухэтапная схема обычно окупается на задачах с высокой ценой ошибки.

Архитектура: Generator + Verifier

┌──────────────┐     ┌──────────────┐     ┌──────────────┐
│  Generator   │ ──→ │   Output     │ ──→ │  Verifier    │
│  (high temp) │     │  (candidate) │     │  (low temp)  │
└──────────────┘     └──────────────┘     └──────┬───────┘
                                                  │
                                           ┌──────┴───────┐
                                           │              │
                                           ▼              ▼
                                      [PASS]         [FAIL]
                                        │               │
                                        ▼               ▼
                                   [Return]      [Retry / Fix]

Generator: температура 0.30.7, упор на полноту охвата и разнообразие. Verifier: температура 0.00.1, упор на точность и согласованность.

Это могут быть:

  • Два вызова одной модели с разными системными промптами.
  • Две разные модели (generator = GPT-5.4, verifier = Claude Opus 4.6).
  • Одна модель + внешние проверки (тесты, linter, schema validation).

Для начинающих. Самый простой способ внедрить верификацию — добавить второй вызов модели после основного. Первый вызов генерирует ответ, второй получает промпт: «Вот ответ. Проверь каждый факт. Если найдёшь ошибку — исправь и объясни». Даже этот минимальный паттерн уже ловит часть галлюцинаций.

На уровне 2026 года архитектура Generator + Verifier стала обычным production-паттерном. Anthropic встроили эту идею в Constitutional AI (Bai et al., 2022) — подход, при котором модель сначала генерирует, потом сама оценивает ответ через набор принципов (constitution), и переписывает его. RLAIF (Reinforcement Learning from AI Feedback) обучает модель на таких самопроверках ещё на этапе тренировки — до деплоя (подробнее о post-training методах — в Главе 19).


13.2. Chain-of-Verification (CoVe)

Метод (Dhuliawala et al., 2023)

Если Generator + Verifier — это пилот и второй пилот, то CoVe — это перекрёстный допрос в суде. Свидетель (модель) дал показания (черновой ответ). Теперь адвокат (верификатор) разбивает показания на отдельные утверждения и задаёт по каждому точечный вопрос — причём в отдельной комнате, без подсказок от оригинальных показаний. Именно так работает снижение confirmation bias.

CoVe — четырёхэтапный протокол, специально разработанный для снижения галлюцинаций. Разберём его пошагово на конкретном примере:

Этап 1: Draft (Черновик)

Промпт: "Перечисли 5 крупнейших озёр Африки по площади."
Черновик: "1. Виктория, 2. Танганьика, 3. Малави, 4. Чад, 5. Туркана"

Этап 2: Plan Verification Questions (Планирование проверки)

Промпт: "Для каждого факта сформулируй проверочный вопрос."
Вопросы:
- "Виктория — одно из крупнейших озёр Африки?"
- "Танганьика — одно из крупнейших озёр Африки?"
- "Чад — одно из крупнейших озёр Африки по площади?"
- ...

Этап 3: Answer Questions Independently (Независимые ответы)

Критически важно: проверочные вопросы отвечаются в отдельном контексте, без видимости черновика. Это предотвращает confirmation bias.

"Чад — одно из крупнейших озёр Африки по площади?"
→ "Озеро Чад значительно сократилось и не входит в топ-5 по текущей площади.
   Альберт (Albert) — крупнее."

Этап 4: Generate Verified Response (Финальный ответ)

Исправленный ответ:
"1. Виктория, 2. Танганьика, 3. Малави, 4. Туркана, 5. Альберт"

Почему CoVe работает

  1. Декомпозиция проверки: вместо «проверь всё» — отдельные вопросы для каждого факта.
  2. Независимость: ответы на проверочные вопросы не должны быть заражены черновиком.
  3. Итеративность: можно запустить несколько раундов CoVe для повышения точности.

Промпт для генерации кода. «Реализуй async-функцию chain_of_verification(question, model) с четырьмя этапами CoVe: (1) черновик ответа (temp 0.3), (2) генерация проверочных вопросов для каждого факта (temp 0.1, JSON), (3) независимые ответы на вопросы без видимости черновика (temp 0.0) — критично для устранения confirmation bias, (4) финальный ответ с учётом проверок (temp 0.1). Используй актуальный SDK (OpenAI / Anthropic). Верни {draft, verifications, final_answer}.»


13.3. Log-Driven Development (LDD)

Принцип: данные вместо интуиции

Если CoVe — перекрёстный допрос, то LDD — это бортовой самописец (чёрный ящик) самолёта. После инцидента (а в LLM-системах инциденты — это галлюцинации, таймауты, некорректный JSON) вы открываете лог и видите: какой промпт был отправлен, какой ответ получен, сколько токенов стоил вызов, прошла ли валидация. Без логов диагностика невозможна — это как врач, которому описывают симптомы по телефону через неделю после болезни.

Отладка без логов — это диагноз без симптомов. Вы видите, что ответ неправильный, но не знаете: проблема в промпте? в модели? в контексте, который был слишком длинным? LDD даёт вам симптомы.

Традиционный подход к промптингу: написал → попробовал → переписал → попробовал... Это метод тыка, маскирующийся под итерацию.

LDD — структурированный подход:

  1. Логируй всё: промпт, параметры, output, validation, метрики.
  2. Анализируй: где ошибки? На каком шаге? Какой тип?
  3. Формируй гипотезу: «ошибка возникает, когда данные >5K токенов».
  4. Тестируй: измени один параметр, проверь на тех же данных.
  5. Итерируй: на основе данных, а не чутья.

Что логировать

Каждый вызов LLM фиксируется в структуре LLMCallLog со следующими группами полей:

Группа Поля Назначение
Идентификация call_id, timestamp, pipeline_name, step_name Привязка к конкретному шагу пайплайна
Вход system_prompt, user_prompt, model, temperature, max_tokens, tools Полный контекст запроса
Выход response, tool_calls, finish_reason Ответ модели и причина остановки (stop / length / tool_calls)
Метрики input_tokens, output_tokens, latency_ms, cost_usd Стоимость и производительность
Валидация schema_valid, factual_checks, human_rating (15) Качество ответа (автоматическое + ручное)
Контекст error, retry_count Для диагностики проблем

Промпт для генерации кода. «Создай Python dataclass (или Pydantic BaseModel) LLMCallLog с полями из таблицы выше. Добавь to_json() для сериализации и from_dict() для десериализации.»

Метрики для отслеживания

Метрика Формула Цель
Schema compliance rate Valid outputs / Total outputs >99%
Hallucination rate Verified false claims / Total claims <5%
Retry rate Retries / Total calls <10%
Latency P95 95-й перцентиль response time <3s
Cost per task Σ(tokens × price) / tasks Зависит от задачи
Success rate Tasks completed / Tasks attempted >90%

Инструменты для LDD

Инструмент Назначение Тип
LangSmith (LangChain) Tracing, debugging, evaluation SaaS
Weights & Biases (Prompts) Tracking, versioning SaaS
Braintrust Evaluation, logging SaaS
Phoenix (Arize) Observability, traces Open-source
OpenLLMetry OTel-инструментация LLM-вызовов Open-source
OpenTelemetry + custom DIY tracing Open-source
SQLite + JSON logs Минимальный локальный DIY

13.3.1. Выбор платформы

Таблица выше даёт обзор инструментов. Подробное сравнение платформ (LangSmith, Arize Phoenix, W&B Weave, OpenLLMetry), setup и рекомендации по выбору для разных сценариев — в Главе 17, раздел 17.2.

Для минимального старта достаточно SQLite + JSON-логов с полями LLMCallLog (см. выше): записывайте каждый вызов и анализируйте SQL-запросами.


13.4. Anti-Loop Protocol: детекция зацикливания

Паттерны зацикливания

1. Вербальные петли: модель повторяет одну и ту же фразу или абзац.

"Для решения этой задачи необходимо рассмотреть все аспекты. 
Рассмотрев все аспекты, мы можем заключить, что для решения 
необходимо рассмотреть все аспекты..."

2. Tool-call петли: агент вызывает одни и те же инструменты с одинаковыми аргументами.

Action: search_files("config.json")
Observation: Not found
Action: search_files("config.json")  ← повтор!
Observation: Not found
Action: search_files("config.json")  ← повтор!

3. State oscillation: агент переключается между двумя состояниями без прогресса.

State: "needs_fix" → fix_code() → test_fails → "needs_fix" → fix_code() → test_fails → ...

4. Output inflation: модель генерирует всё более длинные ответы, разбавляя содержание.

Детекция

Детектор зацикливания (LoopDetector) хранит историю output'ов и tool-вызовов и проверяет три условия:

  1. Verbal loop: разбивает output на предложения и считает повторы. Если одно предложение встречается ≥ N раз — срабатывание.
  2. Tool-call loop: если последние N tool-вызовов идентичны (одинаковые имя и аргументы) — срабатывание.
  3. State stagnation: если состояние агента не менялось N шагов подряд (сравнение через сериализацию) — срабатывание.

Порог N (обычно 3) и threshold схожести — настраиваемые параметры.

Промпт для генерации кода. «Реализуй класс LoopDetector с тремя методами: check_verbal_loop(output) — детекция повторяющихся предложений, check_tool_loop(tool_name, tool_args) — детекция повторяющихся tool-вызовов, check_progress(state_dict) — детекция застоя через сравнение сериализованных состояний. Параметры: max_repeats=3, similarity_threshold=0.9. Каждый метод возвращает bool.»

Реакция на зацикливание

Тип петли Действие
Вербальная Прервать генерацию, вернуть частичный результат
Tool-call Изменить стратегию (другой tool, другие аргументы)
State oscillation Откат к предыдущему стабильному состоянию + переформулировка
Output inflation Установить жёсткий max_tokens
Все типы (после retry) Human-in-the-loop: запросить помощь оператора

13.5. Guardrails: защитные барьеры

Guardrails — это отбойники на горной дороге. Они не крутят руль за водителя и не выбирают маршрут. Но когда машина (модель) случайно уходит к краю пропасти — токсичный контент, утечка персональных данных, prompt injection — отбойники не дают ей упасть. Без них каждый поворот — русская рулетка.

К 2026 году guardrails-фреймворки — Guardrails AI, NVIDIA NeMo Guardrails, LLM Guard — стали зрелыми инструментами. Guardrails делятся на два класса:

  • Guardrails качества: schema validation, PII-маскирование, content policy, детекция low-confidence ответов. Разбираем ниже.
  • Guardrails безопасности: детекция prompt injection (трёхуровневая защита — regex, ML-классификатор, архитектурная изоляция), jailbreak-фильтры, dual-LLM pattern, secrets scanning, toxicity filtering. Подробно — в Главе 15, §15.9.

Входные guardrails

На входе проверяйте: длину (лимит токенов), PII (маскирование до передачи модели), формат (если ожидается структурированный вход — валидируйте схему).

Промпт для генерации кода. «Напиши функцию validate_input(user_input) → (pass: bool, reason: str, sanitized_input: str): (1) ограничение по токенам, (2) маскирование PII (email, телефон, номер карты), (3) базовая schema-валидация. Логируй обнаруженные типы PII.»

13.5.1. PII-маскирование

В enterprise-деплоях маскирование персональных данных — обязательный компонент. Пользователь может случайно отправить паспортные данные, номер карты, email коллеги. Маскирование применяется как на входе (до передачи модели), так и на выходе (модель может воспроизвести PII из контекста).

Промпт для генерации кода. «Напиши функцию mask_pii(text) → (masked_text, found_types): найди и замаскируй email → [EMAIL], российский телефон (+7...) → [PHONE], номер карты (16 цифр) → [CARD], SSN → [SSN], российский паспорт → [PASSPORT]. Используй regex. Верни маскированный текст и список типов.»

13.5.2. Выходные guardrails

На выходе проверяйте:

  1. Schema validation: JSON Schema проверка для structured outputs — 100% гарантия формата.
  2. PII leak detection: сканируйте выход теми же regex-паттернами — модель может воспроизвести PII из контекста.
  3. Content policy: запрещённые темы, упоминания конкурентов, медицинские/финансовые рекомендации — по конфигурируемому набору правил.
  4. Confidence check: избыточное количество hedging-фраз («я не уверен», «возможно») сигнализирует о низкой уверенности.
  5. Hallucination markers: фразы «as of my knowledge cutoff» указывают на параметрические знания вместо контекста.

Промпт для генерации кода. «Напиши функцию validate_output(output, schema=None, content_policy=None) → (is_valid: bool, issues: list[str]): (1) JSON Schema validation, (2) PII leak scan, (3) content policy (словарь regex-паттернов), (4) hedging phrase count > 3 → warning, (5) hallucination markers. Верни список обнаруженных проблем.»


13.6. Цена верификации: баланс точности, задержки и стоимости

Каждый уровень защиты стоит денег и времени. Верификация — не бесплатная:

Метод Дополнительная задержка Дополнительная стоимость Типичный эффект
Второй LLM-вызов (Verifier) +13 сек около ×2 стоимости Часто заметно снижает factual/runtime errors
Полный CoVe (4 этапа) +515 сек около ×35 стоимости Даёт лучший контроль на high-risk задачах
Regex guardrails <10 мс ~бесплатно Ловят только очевидное
LLM-классификатор injection +0.51 сек +$0.0010.01 за запрос Высокая для injection
Schema validation <10 мс ~бесплатно 100% для формата

Стратегия: дифференцированная верификация. Не нужно прогонять полный CoVe на каждый чат-ответ. Определите уровень риска задачи:

  • Низкий риск (чат-бот отвечает на FAQ) → regex guardrails + schema validation.
  • Средний риск (генерация контента для клиентов) → Verifier + content policy + PII check.
  • Высокий риск (медицина, юриспруденция, финансы) → полный CoVe + human-in-the-loop + аудит-лог.

Функция select_verification_level(task_metadata) проверяет домен задачи (medical, legal, financial → full CoVe), флаг external_facing (→ Verifier + guardrails) или возвращает lightweight (только schema + regex).


13.7. Продакшн-мониторинг: дашборд на каждый день

Логи бесполезны, если их никто не смотрит. Настройте дашборд, который команда видит ежедневно:

Рекомендуемые панели дашборда

Панель Что показывает Алерт при
Hallucination rate (скользящее окно 24ч) % ответов с подтверждёнными ошибками Выше вашего SLO
Latency P50 / P95 Время ответа P95 > 5 сек
Error rate % вызовов с ошибками (timeout, 429, invalid JSON) >2%
Cost per task (скользящее окно) Средняя стоимость одного завершённого задания Рост >20% за неделю
Retry rate Доля запросов, потребовавших повтора >15%
Guardrail triggers Срабатывания по типу (injection, PII, toxicity) Всплеск injection
User satisfaction (если есть thumbs up/down) % положительных оценок <80%

Минимальный стек мониторинга

Подробнее об observability-стеке, SLO, инструментах мониторинга и incident response — в Главе 17.


13.8. Hallucination incident response: что делать, когда продакшн-система нагаллюцинировала

Галлюцинация в чат-боте — неприятно. Галлюцинация в агенте, который отправил письмо клиенту или выполнил SQL-запрос — инцидент. Этот playbook — протокол действий от обнаружения до предотвращения повторения.

Фаза 0: Детекция

Галлюцинация обнаружена. Источники детекции:

  • Автоматическая — Verifier (CoVe, schema validation, NLI) пометил ответ как недостоверный.
  • Пользовательская — жалоба клиента, тикет в поддержку.
  • Мониторинг — hallucination rate в дашборде превысил SLO (§13.7).
  • Ручная проверка — инженер заметил при аудите логов.

Фаза 1: Остановка ущерба (первые 15 минут)

Действия в порядке приоритета:

  1. Оцените масштаб. Сколько пользователей затронуто? Ответ уже доставлен клиенту (email, SMS, публикация) или только в интерфейсе чата?
  2. Остановите распространение. Если галлюцинация воспроизводится систематически — переключите трафик на fallback-модель или rule-based ответ. Если модель продолжает галлюцинировать на однотипных запросах — временно включите блокировку темы (content filter).
  3. Отзовите неверную информацию. Если письмо отправлено — отправьте коррекцию. Если данные записаны в БД — пометьте как «требует верификации».
  4. Зафиксируйте инцидент. Создайте запись: время, затронутые пользователи, тип галлюцинации, предположительная причина.

Фаза 2: Диагностика (первые 2 часа)

  1. Восстановите контекст. Какой промпт был отправлен? Какой ответ получен? Какие документы были в контексте (для RAG)? Какие tool calls предшествовали?
  2. Классифицируйте галлюцинацию по таксономии из Главы 3:
    • Factual fabrication — модель выдумала факт (несуществующий API, вымышленное имя).
    • Context contradiction — модель проигнорировала контекст и выдала параметрические знания.
    • Logical inconsistency — внутреннее противоречие в ответе.
    • Overgeneralization — модель применила общее правило к частному случаю, где оно не работает.
  3. Проверьте воспроизводимость. Запустите тот же промпт 5 раз с temp=0. Воспроизводится? Если да — проблема детерминированная (промпт, данные). Если нет — стохастическая (нужен verifier).
  4. Проверьте промпт и данные. Есть ли в промпте неоднозначности? Есть ли в контексте (RAG) противоречивые или устаревшие данные?
  5. Проверьте модель. Не было ли моделью апдейта в день инцидента? Нет ли known issues в changelog провайдера?

Фаза 3: Исправление (первые 24 часа)

В зависимости от классификации:

Тип галлюцинации Краткосрочное исправление Долгосрочное
Factual fabrication Добавить Verifier (CoVe) + citation grounding Улучшить RAG-покрытие домена
Context contradiction Усилить промпт ("Answer ONLY from context") + structured output с source_doc_id Пересмотреть conflict cases в eval-наборе
Logical inconsistency Добавить Self-Consistency (3+ сэмпла, голосование) Улучшить chain-of-thought промпт
Overgeneralization Добавить few-shot примеры с контрпримерами в промпт Fine-tune на доменных данных

Фаза 4: Предотвращение повторения

  1. Добавьте тест-кейс в eval-набор. Инцидент → тест-кейс → regression gate. Это главный механизм обучения системы на ошибках.
  2. Обновите guardrails. Если галлюцинация могла быть поймана автоматически — добавьте правило (regex, content filter, confidence threshold).
  3. Проведите post-mortem. Встреча команды: что произошло, почему не поймали, что изменим в процессе.
  4. Обновите runbook. Добавьте данный тип инцидента в operational runbook команды.

Чек-лист incident response

# Шаг Тайминг Статус
1 Оценён масштаб (пользователи, доставка) 5 мин
2 Остановлено распространение (fallback/блокировка) 15 мин
3 Отозвана неверная информация (коррекция) 30 мин
4 Восстановлен контекст инцидента (логи) 1 час
5 Определён тип галлюцинации 1 час
6 Проверена воспроизводимость 2 часа
7 Применено краткосрочное исправление 4 часа
8 Тест-кейс добавлен в eval-набор 24 часа
9 Проведён post-mortem 72 часа
10 Runbook обновлён 1 неделя

Инструменты

Промпт для ИИ: «Напиши Python-скрипт для автоматической классификации галлюцинаций в ответах LLM. Скрипт принимает запрос, ответ модели, retrieved context (опционально), ground truth (опционально). Классифицирует по четырём типам: factual_fabrication, context_contradiction, logical_inconsistency, overgeneralization. Для factual_fabrication — проверяет claims через верификацию (второй LLM-вызов или NLI). Для context_contradiction — сверяет claims с retrieved context. Для logical_inconsistency — ищет внутренние противоречия в ответе. Возвращает JSON с типом галлюцинации, confidence и affected claims. Используй OpenAI API или Anthropic API.»


Практический вывод

Минимальный антигаллюцинационный контур

Input → [Input Guardrails] → [Generator] → [Output Guardrails] →
   → [Verifier (CoVe / tests / schema)] → Output / Retry

Чек-лист

# Компонент Внедрено?
1 Ревьюер отделён от генератора Generator ≠ Verifier (пилот + второй пилот)
2 CoVe для фактуальных задач 4-step protocol (перекрёстный допрос)
3 Логи всех вызовов Промпт, output, метрики, validation (чёрный ящик)
4 Observability-платформа LangSmith / Phoenix / W&B (подробнее — Глава 17)
5 Детекторы петель Verbal, tool-call, state oscillation
6 Input guardrails Длина, PII-маскирование, injection detection (подробнее — Глава 15)
7 Output guardrails Schema, toxicity, content policy, PII leak
8 Дифференцированная верификация Уровень проверки зависит от риска задачи
9 Продакшн-дашборд Hallucination rate, latency, cost, alerts
10 Human-in-the-loop Есть fallback к человеку для высокого риска?

Задания

  1. Внедрите Generator + Verifier для одного пайплайна. Выберите задачу с высокой ценой ошибки (генерация ответов для клиентов, суммаризация документов). Добавьте второй LLM-вызов (Verifier) с промптом «Проверь каждый факт. Если найдёшь ошибку — исправь и объясни». Измерьте hallucination rate до и после на 20+ примерах. Ожидаемый результат: количественная оценка снижения галлюцинаций и понимание trade-off по латенси и стоимости.

  2. Настройте LDD-логирование. Реализуйте LLMCallLog (таблица из §13.3) и начните писать логи всех LLM-вызовов в SQLite. Через неделю проанализируйте: какой retry rate? какие ошибки чаще всего? в каких шагах пайплайна? Ожидаемый результат: первый дашборд с метриками schema compliance rate, retry rate, latency P95.

  3. Проведите CoVe-сессию вручную. Возьмите 5 фактуальных вопросов из вашей области. Для каждого: попросите модель ответить (черновик), сформулируйте проверочные вопросы, задайте их в отдельном контексте, сравните. Ожидаемый результат: понимание, какие типы ошибок ловит CoVe, а какие нет; оценка cost/benefit для вашей задачи.

  4. Проведите учебную тревогу (fire drill). Сымитируйте инцидент: намеренно внесите галлюцинирующий промпт или уберите Verifier из пайплайна. Пройдите все 4 фазы playbook: детекция → остановка → диагностика → предотвращение. Замерьте время от обнаружения до исправления. Ожидаемый результат: заполненный incident response checklist + время реакции команды < 4 часов.


Источники

  • Dhuliawala, S., et al. (2023). "Chain-of-Verification Reduces Hallucination in Large Language Models."
  • Rebedea, T., et al. (2023). "NeMo Guardrails: A Toolkit for Controllable and Safe LLM Applications with Programmable Rails." NVIDIA.
  • Anthropic. "Reducing Hallucination." Claude Best Practices (20242026).
  • Bai, Y., et al. (2022). "Constitutional AI: Harmlessness from AI Feedback." Anthropic.
  • Guardrails AI. Documentation. https://www.guardrailsai.com/docs (20252026).
  • LangSmith. "Tracing and Evaluation." https://docs.smith.langchain.com/
  • Arize AI. "Phoenix: Open-Source LLM Observability." https://docs.arize.com/phoenix (20252026).
  • Weights & Biases. "W&B Weave: LLM Monitoring." https://docs.wandb.ai/guides/weave (20252026).

Навигация: