AI и документы-первоисточники: как заставить LLM не врать
AI и документы-первоисточники: как заставить LLM не врать
TL;DR. «Галлюцинация» LLM — это симптом, а не диагноз. Диагноз — плохой ретривал и/или плохое цитирование. Эта статья — про то, как построить AI-ответы на документах клиента так, чтобы за каждой фразой стояла ссылка на конкретный фрагмент конкретного документа, и чтобы любой инженер мог проверить ответ за 10 секунд. Подход: citation-mandatory generation + hybrid retrieval + structural ingest + хороший eval-сет.
1. Конфликт: «AI всё придумал»
Сцена: внедрили AI-ассистент на документах клиента. Через неделю руководитель пишет: «Он сказал, что в нашем регламенте написано Х. Я открыл регламент — там нет Х. Что делать?»
Если в этот момент команда отвечает «ну, это галлюцинация LLM, такое бывает, мы поработаем над промптом» — проект мёртв. Клиент потерял доверие, и оно обычно не возвращается.
Это решаемая проблема. Решение архитектурное, не «попросить LLM быть аккуратнее». Ключевая идея: каждая фраза в ответе должна иметь ссылку на конкретный chunk конкретного документа, а ссылка должна быть проверяемой автоматически.
2. Кому это касается
- Регулируемым нишам: охрана труда, медицина, юриспруденция, бухгалтерия — там, где «AI ошибся» = ущерб.
- Внутренним базам знаний: где сотрудники должны доверять системе и быстро проверять.
- Customer support: где неправильный ответ — это возврат / претензия / отток.
- Любому проекту, где «AI ответил» рано или поздно перепроверяет человек — и время этой перепроверки умножается на тысячу запросов.
3. Распространённый неправильный подход
- Берут стандартный RAG-шаблон: chunking → embeddings → top-K → LLM.
- Считают, что «LLM умный, он сам разберётся в источниках».
- Получают ответы без цитат. Или цитаты в стиле «согласно документу X» — без указания страницы.
- Когда юрист просит «покажи, где именно» — ответа нет.
Дополнительные грабли:
- Chunking по символам. Структура документа теряется. Таблицы рвутся пополам.
- Только dense retrieval. Точные коды и идентификаторы не находятся (см. предыдущая статья про Taris § 4.2).
- Промпт «отвечай только из контекста» — без post-validation. LLM игнорирует, когда хочет.
- Нет eval-сета на «отказы». Никто не проверял, отказывается ли система отвечать, когда в источниках ответа нет.
4. Инженерный подход: структурный ингест + citation-mandatory generation
Пайплайн «как должен быть»:
flowchart LR RAW[PDF / DOCX / HTML] --> PARSE[Structured parser<br/>page, section, table] PARSE --> CHUNK[Smart chunking<br/>respect structure] CHUNK --> META[Metadata enrichment<br/>page, section, lang] META --> EMB[Embeddings + FTS] EMB --> PG[(Postgres + pgvector)] QUERY[User question] --> HYBRID[Hybrid retrieval<br/>BM25 + dense + RRF] HYBRID --> RERANK[Cross-encoder rerank] RERANK --> PROMPT[Citation-mandatory prompt] PROMPT --> LLM[LLM] LLM --> VALIDATE[Citation validator] VALIDATE -- ok --> ANSWER[Ответ с маркерами<br/>doc:42#p:18#§:4.2] VALIDATE -- fail --> RETRY[Retry с top-3 force-fed] RETRY --> VALIDATE
Ключевые элементы:
4.1. Структурный парсер
Не «PDF → текст». А «PDF → последовательность (page, section, char_range, text, table?, image?)». Для этого:
- PDF —
pdfplumberилиpymupdfс сохранением координат. - DOCX —
python-docxс проходом по style runs. - Сложные документы со сканами — OCR (Tesseract или PaddleOCR) с language detection.
Стоимость: ~2–4 секунды на страницу. Окупается на цитировании.
4.2. Умный chunking
Правила, которые мы применяем:
- Не рвать середину предложения.
- Не рвать таблицу.
- Если секция — короткая (< 500 токенов), вся секция = один chunk.
- Если длинная — sliding window 400 токенов с overlap 80, сбрасывается на границе секции.
- Каждый chunk несёт метаданные:
{page, section, parent_section, lang, source_uri}.
Эти метаданные потом становятся видимой частью цитаты.
4.3. Citation-mandatory prompt
Шаблон (упрощённо):
Ты отвечаешь только на основе фрагментов ниже.
Каждое утверждение в ответе ДОЛЖНО заканчиваться маркером
вида [doc:ID#chunk:N].
Если в фрагментах нет ответа — ответь
"В предоставленных источниках ответа нет."
Не добавляй фактов, которых нет в фрагментах.
Фрагменты:
[doc:42#chunk:7] (страница 18, §4.2) ... текст ...
[doc:42#chunk:8] (страница 19, §4.3) ... текст ...
...
Вопрос: {question}
4.4. Post-validation
После генерации — простой код:
def validate_citations(answer: str, allowed_chunk_ids: set[str]) -> bool:
sentences = split_sentences(answer)
for s in sentences:
markers = re.findall(r"\[doc:(\d+)#chunk:(\d+)\]", s)
if not markers:
return False # фраза без цитаты
for doc_id, chunk_id in markers:
if f"{doc_id}#{chunk_id}" not in allowed_chunk_ids:
return False # цитата на чужой источник
return True
Если валидация не прошла — повторный вызов LLM с top-3 chunks force-fed и более строгим промптом. Если и это не прошло — система отвечает «В предоставленных источниках ответа нет». Это feature, не bug.
5. Диаграмма + таблица: критерии «настоящего» цитирования
| Критерий | Слабо | Хорошо | Отлично |
|---|---|---|---|
| Указан документ | «согласно регламенту» | «согласно регламенту X» | doc-id + название |
| Указана страница | нет | в конце абзаца | у каждой фразы |
| Указан раздел | нет | глава | глава + параграф |
| Цитата проверяема автоматически | нет | regex | machine-readable + hyperlink |
| Reject при отсутствии источника | нет | иногда | всегда |
| Eval-сет на reject | нет | 5–10 вопросов | 30+ probe-questions |
6. Sintaris mini-case
Worksafety Superassistant — наш самый «тяжёлый» по требованиям к цитированию деплой. Корпус: ~3000 страниц регламентов по охране труда (RU + EAEU + EU), плюс внутренние SOP клиента.
Что сделали:
- Структурный парсер с сохранением (page, section, paragraph).
- Hybrid retrieval с приоритетом BM25 (регламенты — keyword-rich).
- Citation-mandatory prompt + validator.
- Probe-set из 30 «вопросов-ловушек» (например: «Какие нормы PPE для подводной сварки на высоте?» — такой нормы не существует, ожидаемый ответ — «не найдено»).
- Eval-сет на 120 реальных вопросов с эталонными цитатами.
Метрики через 6 месяцев работы:
- Citation accuracy (цитата ведёт на chunk, который реально подтверждает фразу): 94%.
- Refusal correctness (на probe-set — отказы): 96%.
- Median latency: 2.1 сек (включая rerank).
- Жалоб от safety officers за квартал: 0.
Подробности — Worksafety § 6 RAG pipeline и Architecture patterns § 2 Citation-mandatory generation.
7. Чек-лист (15 пунктов) для citation-mandatory RAG
- Парсер сохраняет структуру (page, section, paragraph) — не «весь PDF одним блобом».
- Chunking учитывает структуру — не рвёт предложения и таблицы.
- Метаданные (page, section) сохраняются в каждом chunk-е.
- Hybrid retrieval (BM25 + dense + RRF) — не только embeddings.
- Cross-encoder rerank — top-12 → top-5.
- Promp template требует маркеров цитат.
- Post-validation проверяет наличие маркеров.
- Refusal pattern — «в источниках нет» как первый класс поведения.
- Eval-сет — минимум 30 вопросов с эталонными цитатами.
- Probe-сет на отказы — минимум 20 вопросов, на которые нельзя ответить.
- Citation accuracy измеряется — выводится в Grafana, не «иногда вручную».
- Refusal rate измеряется — отдельно от accuracy.
- Drift monitor — раз в неделю проверяет, не падают ли метрики.
- Threshold для retrieval — если top-1 score < X, отказ.
- Hyperlinks в ответах — пользователь кликает и видит исходный документ.
8. Риски
- Latency. Cross-encoder rerank + validation добавляют 200–400 ms. Это нормально для бизнес-сценариев, плохо для чата real-time. Решение — кешировать частые запросы.
- «Слишком осторожный». Если порог слишком высокий — система часто отвечает «не знаю». Лечится калибровкой на eval-сете.
- Структурный парсинг сложных PDF. Сканированные таблицы — это до сих пор боль. Бюджетируйте время на OCR-проверку.
- Multi-language citations. Если регламенты в RU, а ответ в DE — citation labels должны быть language-stable. Лучше использовать numeric IDs, а не «§4.2» (потому что «§» в DE — это «Abs.»).
9. Что делать дальше
Если у вас уже есть RAG-пилот, который иногда «врёт» — начните с двух действий:
- Включите citation-mandatory prompt + validator.
- Сделайте probe-сет на 20 вопросов, на которые ответа в корпусе нет, и измерьте refusal correctness.
Часто этих двух действий хватает, чтобы поднять качество с «иногда правильно» до «всегда либо правильно, либо честно отказ».
Если хочется пройти полный цикл — AI-Pilot за 4–8 недель включает построение citation-mandatory RAG как стандартный паттерн. Для словенских компаний с 1 по 30 июня 2026 — скидка −25%.
10. Источники
- Lewis, P. et al. (2020). Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks. arXiv:2005.11401.
- Karpukhin, V. et al. (2020). Dense Passage Retrieval for Open-Domain Question Answering. arXiv:2004.04906.
- Cormack, G., Clarke, C., Buettcher, S. (2009). Reciprocal Rank Fusion outperforms Condorcet and individual rank learning methods. SIGIR '09.
- Liu, N. F. et al. (2023). Lost in the Middle: How Language Models Use Long Contexts. arXiv:2307.03172.
- Es, S. et al. (2024). RAGAS: Automated Evaluation of Retrieval-Augmented Generation. arXiv:2309.15217.
- pdfplumber — https://github.com/jsvine/pdfplumber
- PaddleOCR — https://github.com/PaddlePaddle/PaddleOCR
- Внутренняя KB SINTARIS: Worksafety Superassistant, Architecture patterns § 2 Citation-mandatory generation, n8n + RAG patterns § 6 RAG node pattern.
Sintaris строит RAG-системы с проверяемыми цитированиями для бизнеса в EU и СНГ. Дискавери-звонок — бесплатно, 30 минут.