Техническое руководство
Как проверять источники и цитаты в RAG-ответах
Проверять источники и цитаты в RAG-ответах нужно до того, как ссылка попадёт к пользователю. Модель умеет связно пересказать контекст, но не может сама доказать, что документ существует, актуален, разрешён для этого пользователя и действительно подтверждает конкретное утверждение. Надёжный процесс связывает каждое показанное утверждение с document_id, версией и фрагментом, а затем измеряет это на тестовых вопросах. Если подтверждения нет, интерфейс обязан выбрать честную формулировку, а не сгенерировать убедительную ссылку.
Короткий ответ: ссылка модели — это не доказательство
В RAG цитата должна появляться из вашего контролируемого retrieval-результата, а не извлекаться из свободного текста ответа. До генерации сервер передаёт модели ограниченный набор разрешённых фрагментов с устойчивыми идентификаторами. После генерации приложение проверяет, какие из этих идентификаторов разрешено показать, и строит ссылку самостоятельно. Так система не превращает похожее название файла, устаревший URL или придуманную сноску в видимость надёжного источника.
Разделите три вопроса: существует ли источник, подтверждает ли он утверждение и может ли текущий пользователь его видеть. Документ может быть реальным, но относиться к другой версии политики; фрагмент может содержать похожее слово, но не отвечать на вопрос; разрешённый для поиска текст может не иметь публичной ссылки. Для каждого случая нужен отдельный исход: показать проверенный источник, показать ответ с оговоркой, запросить уточнение или ответить, что подтверждённой информации нет. Это полезнее, чем обещать «точные цитаты» без измеримого контракта.
Опишите контракт фрагмента и источника
Минимальная запись контекста содержит document_id, chunk_id, версию, заголовок, дату действия, tenant, ACL и безопасный URL или внутренний маршрут. Не подменяйте документ ID названием файла: имена меняются, а одинаковые версии могут существовать одновременно. Для каждого chunk храните границы текста и позицию в документе, чтобы reviewer мог понять, относится ли фраза к нужному условию. Полные prompt, Authorization-заголовки и необработанные пользовательские запросы в эту запись не попадают.
Нужно также определить допустимую силу ответа. Прямая цитата требует, чтобы утверждение читалось в конкретном фрагменте; пересказ допускает несколько фрагментов, но должен быть отмечен как краткое изложение; вывод или рекомендация не должны маскироваться под положение документа. При конфликте версий алгоритм не выбирает удобную формулировку автоматически: он предпочитает действующую версию по вашей политике либо сообщает о расхождении. Это правило особенно важно для регламентов, прайс-листов и инструкций, которые обновляются без сохранения старых ссылок.
Добавьте автоматические тесты для трёх типов ошибок
Первый набор тестов ловит несуществующие источники: ответ не должен ссылаться на document_id, которого нет среди предоставленных кандидатов. Второй набор проверяет entailment вручную или классификатором с обязательной выборочной проверкой: поддерживает ли фрагмент утверждение, а не просто повторяет термин. Третий набор проверяет доступ: пользователь одного tenant не получает название, URL или текст из другого. Фикстуры должны быть синтетическими или обезличенными, чтобы сами тесты не стали хранилищем чувствительных данных.
Храните для каждого теста вопрос, разрешённые источники, запрещённые источники, ожидаемый режим ответа и версию теста. Примеры «в документе нет ответа» особенно ценны: система должна отказаться от ссылки, а не улучшать правдоподобие. После изменения индекса, reranker, prompt template или модели запускайте одинаковый набор и сравнивайте результат. Одна успешная демонстрация не равна стабильной корректности; заметный рост неподтверждённых ссылок — причина остановить rollout и изучить retrieval до того, как менять генерацию.
Постройте интерфейс, который не скрывает неопределённость
Показывайте рядом с ответом название источника, дату или версию и короткий фрагмент только если пользователь вправе его видеть. Ссылка должна вести на маршрут, который повторно проверяет доступ, а не на постоянный приватный файл. Если несколько фрагментов поддерживают разные части ответа, не склеивайте их в одну безымянную сноску: лучше перечислить подтверждения или сократить вывод. Это делает спорный ответ проверяемым человеком и снижает риск, что модельный пересказ примут за официальный текст.
В интерфейсе полезны понятные статусы: «источник подтверждён», «нужна проверка», «в доступном корпусе нет подтверждения». Не называйте последний статус ошибкой пользователя и не предлагайте снять ограничения доступа. Для внутренних reviewer добавьте кнопку обратной связи с question_id и chunk_id, а не с полным текстом чата. Так исправление попадает в тестовый набор, индекс или правило маршрутизации, а не исчезает в отдельном сообщении без воспроизводимого контекста.
Наблюдайте метаданные и подготовьте процесс исправления
В production собирайте агрегаты: долю ответов с проверенным источником, долю отказов, число конфликтов версий, среднее количество ссылок и количество исправлений reviewer. Эти показатели не доказывают истинность каждого ответа, но помогают увидеть регрессию после изменения pipeline. Логи должны содержать минимальные технические события — request_id, document_id, версии, outcome — и храниться по вашей политике. Текст prompt, внешний ключ и приватный документ не нужны для базовой диагностики.
Когда пользователь указывает на неверную цитату, зафиксируйте инцидент как тест-кейс: какой вопрос задан, какой источник был показан, почему он не поддерживал утверждение и какое безопасное поведение ожидается. Затем исправьте индекс, правило доступа, шаблон или UI и повторите набор. RussiaAPI предоставляет независимый gateway, а не подтверждение содержания сторонних документов или официальную поддержку производителя модели. Используйте свой ключ только на сервере и проверяйте доступные возможности по текущей документации перед тем, как связывать конкретный формат ответа с production-процессом.
Server-side пример
Пример показывает локальную проверку на сервере. Ключи берутся только из окружения; до запуска подтвердите маршрут, model ID и параметры в текущем каталоге RussiaAPI.
export function safeCitations(answer, retrieved, canRead) {
const byId = new Map(retrieved.map((chunk) => [chunk.chunk_id, chunk]));
return answer.citation_ids
.map((id) => byId.get(id))
.filter((chunk) => chunk && canRead(chunk.tenant, chunk.document_id))
.map((chunk) => ({ document_id: chunk.document_id, version: chunk.version, title: chunk.title }));
}
// The UI builds links from verified metadata. Model text is never treated as an URL authority.
Проверьте синтаксис через node --check, добавьте аутентификацию своего маршрута и негативные тесты. Не логируйте тело запроса или заголовки только ради отладки.
Проверьте сценарий в RussiaAPI
Создайте собственный тестовый ключ в консоли, сверьте текущий каталог моделей и выполните обезличенный server-side smoke test. Расширяйте нагрузку и доступ только после измеримой проверки.
FAQ
Достаточно ли попросить модель всегда добавлять ссылки?
Нет. Такая инструкция улучшает формат, но не подтверждает существование, актуальность или доступность источника. Ссылки нужно строить на сервере из разрешённого retrieval-контекста и проверять через тесты, включая случаи, где правильного источника в корпусе нет.
Можно ли показывать пользователю внутренний document_id?
Обычно нет необходимости. Для интерфейса лучше использовать контролируемое название и маршрут, который повторно проверяет права. В журнале document_id полезен для воспроизводимости, но он не должен раскрывать структуру другого tenant или заменять авторизацию на доступ к файлу.
Что делать при конфликте двух версий документа?
Не выбирайте удобный вариант молча. Примените заранее определённое правило действующей версии, покажите дату либо верните ответ на проверку. Конфликт должен попадать в тестовый набор и журнал, чтобы команда исправила индекс или процесс обновления, а не повторяла ошибку.