Я веду базу знаний по тайским визам и пишу справочный контент с помощью LLM. Ниша неприятная тем, что ошибка в цифре это не опечатка, а чей-то испорченный переезд: человек прочитает про «минимальный остаток 300 тысяч бат», принесёт консульству на 200 тысяч меньше, чем нужно, и получит отказ с невозвратным сбором. Поэтому за год у меня собрался пайплайн, в котором модель физически не может протащить выдуманное число в публикацию. Расскажу архитектуру: она не привязана ни к визам, ни к конкретной модели, и подойдёт любой нише, где факты дороже стиля медицина, налоги, финансы.

Почему «не выдумывай» в промпте не работает

Начну с наблюдения из практики. Визовые правила волатильны, а поисковая выдача по ним токсична: SEO-сайты штампуют «обновления», не ссылаясь ни на что. В моём ресёрче был показательный случай: по запросу об изменениях одной визы на 2026 год выдача возвращала несколько страниц одного и того же сайта, противоречащих друг другу одна писала про ужесточение финансовых требований, вторая про него не знала, и ни одна не ссылалась на официальный источник.

Теперь представьте, что на этом корпусе учат модель или, хуже, дают ей это в RAG. Модель не врёт в привычном смысле, она честно усредняет мусор. Инструкция «не выдумывай факты» тут бесполезна: модель и не считает, что выдумывает. Значит, запрет должен быть не в промпте, а в архитектуре.

Принцип 1: у чисел есть единственный источник истины, и это не модель

Центральная идея: в генерируемом тексте не бывает чисел «из головы». Все факты живут в базе записей примерно такой структуры:

{

  "key": "visa.finance.min",

  "value": 500000,

  "display": "500,000 THB",

  "updatedAt": "2026-07-17",

  "source": "https://image.mfa.go.th/.../Checklist.pdf",

  "attribution": "official",

  "approvedBy": ["reviewer"],

  "history": [

    {"value": 400000, "updatedAt": "2024-05-01", "source": "..."}

  ]

}

Модель при генерации не «вспоминает» сумму, а подставляет запись по ключу. Гейт на выходе проверяет обратное соответствие: каждое число в тексте обязано матчиться на запись из базы. Число без записи блокировка, независимо от того, насколько правдоподобно оно выглядит. Особенно если правдоподобно: самые опасные галлюцинации те, что похожи на правду.

Приятный побочный эффект поле history. Когда сбор меняется, старое значение не затирается, а уезжает в историю, и из этого бесплатно рождается контент, которого нет у конкурентов: «сбор вырос с X до Y в таком-то месяце» это данные, которые нельзя нагуглить одной страницей.

Принцип 2: у каждого факта есть уровень доверия, и он не повышается молча

Вторая типовая ошибка LLM-контента тоньше галлюцинаций: смешение статусов. «Требуется страховка» это официальное требование, практика конкретного консульства или чей-то пересказ с форума? Для читателя разница критическая, а модель радостно нивелирует её до уверенного «требуется».

Поэтому каждая запись в базе несёт атрибуцию одного из трёх уровней: официальное требование (только со ссылкой на госисточник), практика (наблюдение с датой: «в последних кейсах консульство X запрашивало...») или внутренний стандарт («мы дополнительно просим...»). Гейт проверяет не только само число, но и уровень: если в тексте практика подана языком официального требования блокировка. Повышение уровня факта это отдельное осознанное действие человека с новым источником, а не решение генератора.

Принцип 3: fail-closed, или почему сомнение = отказ

Все проверки пайплайна работают по принципу fail-closed: не удалось однозначно подтвердить считается ошибкой. Это противоположность типовым «мягким» ревью, где сомнительное пропускают с пометкой.

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

Про стоп-лист стоит сказать отдельно. В каждой YMYL-нише есть формулировки, которые нельзя публиковать никогда (у меня это обещания «гарантированного одобрения» во всех вариациях). Наивная реализация регэкспы не работает: «завизируем без отказов» регэксп по слову «гарантия» не поймает. Поэтому стоп-лист матчится семантически, самой же моделью: «найди в тексте утверждения, эквивалентные по смыслу следующим запрещённым». LLM, которой нельзя доверить факты, отлично справляется с поиском перефразировок это ровно та задача, где она сильна.

Принцип 4: факты протухают, и это надо признать схемой

Визовый сбор, который я проверила в феврале, в августе уже нельзя считать проверенным. В базе это закреплено жёстко: запись старше 90 дней считается недействительной и не проходит гейт, пока её не переподтвердят по источнику. Чтобы не жить в вечном пожаре, есть очередь переверификации с буфером: всё, что старше 75 дней, попадает в план проверки заранее.

Из всех принципов этот самый противный в эксплуатации и самый важный. База фактов без TTL деградирует молча: через год она выглядит солидно и врёт в каждой третьей записи.

Принцип 5: мифы проверяются адверсариально

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

Живой пример: в моей нише все «знают», что деньги для визы должны «отлежаться» на счету 90 дней. Три независимых прохода по официальным чек-листам дали ноль подтверждений: правила такого не существует, это фольклор, выросший из практики отдельных консульств. В базу утверждение попало с атрибуцией «миф» и это тоже контент, причём самый цитируемый.

Человек в контуре: последняя миля не автоматизируется

Финальное «опубликовать» в пайплайне нажимает человек, и у записей есть поле approvedBy, которое заполняется только вручную. Это не дань бюрократии, а честность по поводу пределов автоматизации: гейт ловит несоответствия базе, но не ловит несоответствие базы реальности. Если я неправильно прочитала PDF консульства, все автоматические проверки радостно сойдутся на неправильном числе.

По этой же причине ведётся audit trail: у каждого факта видно, откуда он взялся, когда проверялся и кто одобрил. Когда через полгода находишь странную запись, история отвечает на вопрос «что мы вообще думали», не поднимая переписок.

Чего пайплайн не умеет (честный раздел)

Не ловит ошибки в первоисточнике: официальный сайт тоже бывает неправ или неоднозначен. Не решает проблему интерпретации: «выписка за 3 месяца» календарных или банковских? Тут спасает только практика и фиксация трактовок как отдельных записей. И не защищает от лени оператора: fail-closed гейт, который надоел и который начали «пропускать руками», хуже его отсутствия, потому что создаёт ложное чувство безопасности.

Итого

Скелет переносимый: единый источник истины для чисел с обязательными источниками и датами; уровни доверия как часть схемы данных; проверяющий проход, отделённый от генерирующего, с правилом «сомнение = блок»; TTL на каждый факт; адверсариальная проверка фольклора; человек на финальном гейте. Ничего из этого не требует конкретной модели или фреймворка это дисциплина данных, а не ML-магия.

Интересен опыт тех, кто решает похожую задачу в других нишах: как вы храните волатильные факты для генерации и что делаете с их протуханием? И отдельный вопрос к тем, кто пробовал semantic-стоп-листы: на каком проценте ложных срабатываний вы остановились как на приемлемом?

Комментарии (0)