Помощник поддержки уверенно сообщил клиенту, что возврат занимает 30 дней. В параметрах модели ошибки не было. Поиск поднял регламент трёхлетней давности, генератор аккуратно пересказал найденное. RAG сработал именно так, как его построили.
RAG состоит из двух разных систем. Retrieval выбирает свидетельства, generation формулирует ответ по ним. Между ними нужен контракт: фрагмент несёт идентификатор документа, версию, дату, права доступа и оценку релевантности. Без этого в prompt приезжают безымянные абзацы, а расследование заканчивается словом «галлюцинация».
Рабочий поток
question -> normalize -> authorize -> retrieve -> rerank
-> context policy -> generate -> validate citations -> answerСначала область поиска ограничивают tenant, продуктом, языком и действующей версией. Затем получают кандидатов, переранжируют их и только после этого тратят токены генератора.
async def answer(question: str, tenant_id: str) -> dict:
candidates = await search(
query=question,
filters={"tenant_id": tenant_id, "status": "active"},
limit=30,
)
evidence = await rerank(question, candidates, limit=6)
if not evidence or evidence[0].score < 0.62:
return {"status": "insufficient_evidence", "answer": None}
result = await generate(question=question, sources=evidence)
allowed = {item.source_id for item in evidence}
if not set(result.citations) <= allowed:
raise ValueError("model cited a source absent from context")
return result.model_dump()Порог 0.62 здесь не универсальная константа. Его выбирают на своём наборе вопросов, сравнивая полноту и шум. Для запроса «как вернуть деньги» отсутствие свидетельства лучше бодрого вымысла.
Что проверять отдельно
Retrieval оценивают по тому, попал ли нужный источник в top-k. Generation оценивают при заранее выданном правильном контексте. Если смешать проверки, смена prompt замаскирует плохой индекс, а хороший поиск получит вину за неверный пересказ.
Основные отказы просты: индекс отстал от публикации; фильтр выбрал чужого tenant; запрос требует нескольких документов; reranker не знает внутреннего артикула; модель ссылается на источник, которого не видела; все кандидаты слабы, но система всё равно отвечает. Для каждого отказа нужен наблюдаемый статус, а не общий 500.
Правило главы
RAG не добавляет модели знания. Он предъявляет ей несколько свидетельств. Качество системы определяется тем, нашли ли нужные свидетельства, разрешено ли их показывать и умеет ли сервис отказаться при их отсутствии.
Практикум
Соберите 50 вопросов с известными документами-ответами, включая десять вопросов без ответа в корпусе. Измерьте recall@5 retrieval отдельно от точности генерации. Добавьте обязательные ссылки и статус insufficient_evidence. Затем замените один действующий документ устаревшей версией и убедитесь, что фильтр публикации не отдаёт её модели.