Что получится в результате
Соберем RAG-контур для ИИ-агента: документы из базы знаний попадают в реестр, очищаются, режутся на chunks, превращаются в embeddings, сохраняются в vector store, ищутся по вопросу пользователя, передаются в prompt с источниками, а агент отвечает только по найденному контексту.
В первой версии RAG не должен быть "загрузили все файлы и надеемся". Нужны статусы документов, контроль версий, права доступа, фильтры, проверка качества поиска, ссылки на источники и понятное поведение, если ответа нет.
В результате будет рабочий MVP:
- источники описаны в `knowledge_sources`;
- документы лежат в `knowledge_documents`;
- версии документов лежат в `knowledge_document_versions`;
- очищенный текст лежит в `knowledge_text_blocks`;
- chunks лежат в `knowledge_chunks`;
- embeddings лежат в `knowledge_embeddings`;
- vector collection описана в `vector_collections`;
- metadata фильтры лежат в `chunk_metadata`;
- права доступа лежат в `knowledge_access_rules`;
- индексация пишется в `indexing_jobs`;
- поиск пишется в `retrieval_runs`;
- найденные chunks пишутся в `retrieval_results`;
- ответы агента с источниками лежат в `rag_answer_log`;
- вопросы без ответа лежат в `unanswered_questions`;
- тесты качества лежат в `rag_eval_cases`;
- метрики пишутся в `rag_metrics`.
Финальная проверка: вы задаете вопрос по документу, агент находит правильные chunks, отвечает с источником, не выдумывает при отсутствии ответа и не видит документы, к которым у пользователя нет доступа.
Что понадобится
Подготовьте:
- набор документов базы знаний;
- формат источников: Notion, Confluence, Google Docs, Markdown, PDF, HTML или CMS;
- backend для индексации;
- базу данных для metadata;
- vector store: Qdrant, Chroma, Weaviate, pgvector или другой;
- embedding-модель;
- LLM для ответа;
- список прав доступа;
- список документов, которые нельзя индексировать;
- 20-50 тестовых вопросов;
- владельца базы знаний;
- процесс обновления документов;
- лимит стоимости embeddings и LLM.
Для MVP возьмите 20-100 документов одной темы. Не начинайте с тысячи файлов, пока не проверили качество chunks и retrieval.
Шаг 1. Выберите первый RAG-сценарий
Опишите, на какие вопросы агент должен отвечать.
Хороший первый сценарий:
- ответы по FAQ продукта;
- поддержка по инструкциям;
- поиск по базе знаний компании;
- ответы по регламентам;
- подбор статьи из документации;
- помощь оператору с ответом клиенту.
Плохой первый сценарий:
- искать по всем файлам компании без прав;
- отвечать по устаревшим документам;
- смешивать клиентские данные и публичные статьи;
- делать юридические выводы без проверки;
- обещать точные цены без актуального источника.
Проверка: можно перечислить 10 вопросов, на которые RAG обязан отвечать.
Шаг 2. Создайте реестр источников
Создайте `knowledge_sources`.
Колонки:
id
source_key
source_type
source_name
base_url
owner
sync_mode
status
created_at
updated_at
`source_type`:
- `notion`;
- `confluence`;
- `google_docs`;
- `markdown_repo`;
- `pdf_folder`;
- `html_site`;
- `cms`;
- `manual_upload`.
Проверка: у каждого источника есть владелец и понятный способ обновления.
Шаг 3. Создайте таблицу документов
Создайте `knowledge_documents`.
Колонки:
id
source_key
external_id
title
source_url
document_type
status
language
owner
last_seen_at
created_at
updated_at
Статусы:
- `draft`;
- `published`;
- `outdated`;
- `archived`;
- `blocked`;
- `deleted`.
Для retrieval используйте только `published`.
Проверка: документ со статусом `draft` не попадает в ответы агента.
Шаг 4. Введите версии документов
Создайте `knowledge_document_versions`.
Колонки:
id
document_id
version_hash
source_updated_at
raw_content_hash
parsed_content_hash
status
created_at
Зачем нужны версии:
- понимать, что документ изменился;
- не переиндексировать без причины;
- откатывать плохую индексацию;
- связывать answer с конкретной версией;
- удалять chunks старой версии.
Проверка: если документ не изменился, новая индексация не создает дубли chunks.
Шаг 5. Подготовьте pipeline индексации
Создайте `indexing_jobs`.
Колонки:
id
job_id
source_key
document_id
job_type
status
attempts
last_error
started_at
finished_at
created_at
Типы jobs:
- `sync_source`;
- `parse_document`;
- `split_chunks`;
- `embed_chunks`;
- `delete_old_vectors`;
- `reindex_document`;
- `full_reindex`;
Проверка: один документ можно переиндексировать отдельно, без полной пересборки базы знаний.
Шаг 6. Извлеките чистый текст
Создайте `knowledge_text_blocks`.
Колонки:
id
document_version_id
block_index
heading
text
source_anchor
page_number
created_at
Правила:
- убрать навигацию;
- убрать футеры;
- убрать повторяющиеся меню;
- сохранить заголовки;
- сохранить номера страниц для PDF;
- сохранить anchors для HTML;
- не терять таблицы, если они важны;
- не склеивать все в одну строку.
Проверка: 10 случайных blocks читаются как нормальный текст, а не мусор из верстки.
Шаг 7. Разбейте текст на chunks
Создайте `knowledge_chunks`.
Колонки:
id
document_id
document_version_id
chunk_index
heading_path
chunk_text
source_url
source_anchor
token_count
status
created_at
Правила chunking:
- chunk должен отвечать на одну смысловую тему;
- размер для старта: 500-1200 tokens;
- overlap: 50-150 tokens;
- не разрывать таблицу посередине;
- сохранять heading path;
- source URL сохранять в каждом chunk;
- старые chunks выключать при новой версии;
- пустые chunks не сохранять.
Проверка: один chunk можно показать человеку, и он поймет источник, тему и границы текста.
Шаг 8. Добавьте metadata
Создайте `chunk_metadata`.
Колонки:
id
chunk_id
product
topic
audience
language
access_level
department
document_status
valid_from
valid_to
Metadata нужна для фильтров:
- продукт;
- язык;
- отдел;
- тип клиента;
- уровень доступа;
- актуальность;
- регион;
- версия.
Проверка: вопрос по продукту A не получает chunks продукта B, если включен фильтр `product`.
Шаг 9. Выберите embedding-модель
Создайте `embedding_models`.
Колонки:
id
model_key
provider
model_name
vector_size
cost_per_1k_tokens
is_active
created_at
Критерии выбора:
- качество на русском языке;
- размер vector;
- цена индексации;
- скорость;
- совместимость с vector store;
- стабильность версии;
- возможность reindex при смене модели.
Проверка: модель фиксируется в metadata, чтобы потом понять, чем был создан vector.
Шаг 10. Создайте embeddings
Создайте `knowledge_embeddings`.
Колонки:
id
chunk_id
embedding_model
vector_id
content_hash
status
created_at
Правила:
- embedding создается из `chunk_text`;
- не отправлять секретные документы;
- content hash нужен для dedupe;
- vector_id хранит ссылку на vector store;
- при изменении chunk embedding пересоздается;
- при удалении chunk vector удаляется.
Проверка: количество active chunks совпадает с количеством active embeddings.
Шаг 11. Настройте vector collection
Создайте `vector_collections`.
Колонки:
id
collection_name
vector_store
embedding_model
vector_size
distance
status
created_at
Пример:
{
"collection_name": "kb_support_ru",
"vector_store": "qdrant",
"embedding_model": "text-embedding-3-small",
"vector_size": 1536,
"distance": "cosine",
"status": "active"
}
Проверка: vector size в collection совпадает с embedding model.
Шаг 12. Добавьте права доступа
Создайте `knowledge_access_rules`.
Колонки:
id
subject_type
subject_id
document_id
access_level
can_read
created_at
`subject_type`:
- `public`;
- `user`;
- `team`;
- `department`;
- `organization`;
- `tenant`;
Правила:
- public docs доступны всем;
- internal docs доступны только сотрудникам;
- tenant docs доступны только своему tenant;
- retrieval применяет фильтры до ответа модели;
- prompt не должен получать запрещенные chunks.
Проверка: пользователь без доступа не получает закрытый документ даже через похожий вопрос.
Шаг 13. Настройте hybrid retrieval
Один vector search часто недостаточен. Используйте гибрид:
- keyword search;
- vector search;
- metadata filters;
- rerank;
- threshold;
- dedupe chunks;
- citations.
Создайте `retrieval_runs`.
Колонки:
id
run_id
user_id
query_text
filters_json
retrieval_strategy
top_k
created_at
Проверка: retrieval strategy записана, чтобы сравнивать качество разных подходов.
Шаг 14. Сохраняйте результаты поиска
Создайте `retrieval_results`.
Колонки:
id
retrieval_run_id
chunk_id
rank
vector_score
keyword_score
rerank_score
used_in_prompt
created_at
Правила:
- сохранять top 10-20 candidates;
- в prompt отправлять top 3-5;
- не отправлять chunks ниже threshold;
- не отправлять дубликаты соседних chunks без причины;
- логировать, какие chunks реально использованы.
Проверка: можно открыть retrieval run и увидеть, почему агент ответил именно так.
Шаг 15. Добавьте rerank
Rerank помогает выбрать лучшие chunks из кандидатов.
Создайте `rerank_config`.
Колонки:
id
config_key
provider
model_name
input_top_k
output_top_k
min_score
is_active
Старт:
- vector top 20;
- keyword top 20;
- merge и dedupe;
- rerank top 20;
- prompt top 5;
- min_score 0.55-0.75 после тестов.
Проверка: похожие, но неверные chunks не попадают выше точного ответа.
Шаг 16. Сформируйте context для prompt
Context должен быть коротким и с источниками.
Формат:
Контекст базы знаний:
[1] Заголовок: Возврат оплаты
Источник: https://example.com/refund#rules
Текст: ...
[2] Заголовок: Сроки обработки
Источник: https://example.com/refund#time
Текст: ...
Правила:
- каждый chunk имеет номер;
- каждый chunk имеет source URL;
- context не превышает лимит tokens;
- устаревшие chunks не попадают;
- приватные chunks фильтруются;
- context не смешивается с памятью пользователя без маркировки.
Проверка: модель может сослаться на `[1]` и `[2]`, а пользователь видит источник.
Шаг 17. Напишите system prompt RAG-агента
Каркас:
Ты отвечаешь только на основе контекста базы знаний.
Если в контексте нет ответа, скажи, что не нашел подтвержденный ответ.
Не выдумывай факты, цены, сроки, правила и ссылки.
В конце ответа укажи источники по номерам.
Если источники противоречат друг другу, скажи, что нужна проверка человека.
Если вопрос связан с деньгами, юридическими условиями или персональными данными, отвечай осторожно и предлагай handoff.
Верни JSON.
Формат:
{
"answer": "Возврат можно оформить через личный кабинет. Обычно заявка рассматривается до 10 рабочих дней.",
"used_sources": [1, 2],
"confidence_score": 0.86,
"needs_human": false,
"no_answer": false
}
Проверка: если context пустой, `no_answer = true`.
Шаг 18. Сохраняйте ответы с источниками
Создайте `rag_answer_log`.
Колонки:
id
answer_id
run_id
retrieval_run_id
question_text
answer_text
used_chunk_ids_json
confidence_score
no_answer
needs_human
created_at
Правила:
- каждый ответ связан с retrieval;
- used chunks сохраняются;
- no_answer сохраняется отдельно;
- confidence не равен retrieval score автоматически;
- human review нужен для рискованных тем.
Проверка: можно проверить любой ответ и увидеть использованные chunks.
Шаг 19. Обрабатывайте отсутствие ответа
Создайте `unanswered_questions`.
Колонки:
id
question_text
user_id
filters_json
reason
suggested_owner
status
created_at
Причины:
- `no_chunks_found`;
- `low_score`;
- `conflicting_sources`;
- `access_denied`;
- `outdated_document`;
- `needs_human_policy`;
Поведение агента:
- не выдумывать;
- сказать, что ответа нет в базе;
- предложить передать вопрос человеку;
- записать вопрос в backlog базы знаний.
Проверка: вопрос без ответа попадает в `unanswered_questions`, а не превращается в уверенную фантазию.
Шаг 20. Настройте обновление документов
Создайте `source_sync_state`.
Колонки:
id
source_key
last_sync_at
last_cursor
documents_seen
documents_changed
documents_deleted
status
Режимы:
- manual reindex;
- scheduled sync;
- webhook on change;
- full reindex;
- document-level reindex.
Правила:
- changed document получает новую version;
- old chunks выключаются;
- old vectors удаляются;
- new chunks индексируются;
- eval запускается после важных изменений.
Проверка: изменение одного документа не ломает всю коллекцию.
Шаг 21. Добавьте удаление документов
Создайте `knowledge_delete_jobs`.
Колонки:
id
document_id
delete_reason
status
chunks_deleted
vectors_deleted
created_at
finished_at
При удалении:
- document status = `deleted` или `archived`;
- chunks status = `deleted`;
- vectors удаляются из vector store;
- retrieval больше не возвращает документ;
- audit log получает событие.
Проверка: удаленный документ нельзя найти вопросом по старому содержанию.
Шаг 22. Настройте контроль качества chunks
Создайте `chunk_quality_checks`.
Колонки:
id
chunk_id
check_key
result
reason
created_at
Проверяйте:
- chunk не пустой;
- chunk не слишком короткий;
- chunk не слишком длинный;
- есть source URL;
- есть heading;
- нет навигационного мусора;
- язык определен;
- metadata заполнена;
- нет секретов.
Проверка: chunks с мусором не индексируются.
Шаг 23. Создайте тесты RAG
Создайте `rag_eval_cases`.
Колонки:
id
case_key
question_text
expected_chunk_ids_json
expected_answer_contains_json
expected_no_answer
filters_json
is_critical
Минимальные тесты:
- точный вопрос по FAQ;
- вопрос с синонимами;
- вопрос с ошибкой в слове;
- вопрос на другой язык;
- вопрос без ответа;
- вопрос по устаревшему документу;
- вопрос с ограниченным доступом;
- вопрос с конфликтом источников;
- вопрос, где нужны два chunks;
- вопрос с похожим, но неверным документом.
Проверка: critical eval cases проходят перед включением RAG в production.
Шаг 24. Запустите eval
Создайте `rag_eval_runs`.
Колонки:
id
eval_run_id
retrieval_strategy
embedding_model
rerank_model
started_at
finished_at
status
Создайте `rag_eval_results`.
id
eval_run_id
case_key
retrieved_chunk_ids_json
answer_text
passed
fail_reason
latency_ms
created_at
Критерии:
- expected chunk в top 5;
- no_answer срабатывает, когда ответа нет;
- forbidden source не используется;
- ответ содержит нужные факты;
- ответ не содержит запрещенных обещаний;
- sources указаны.
Проверка: eval можно повторить после изменения chunking или embedding model.
Шаг 25. Настройте monitoring
Создайте `rag_metrics`.
Колонки:
id
date
total_questions
answered_questions
no_answer_count
avg_top_score
avg_latency_ms
retrieval_errors
human_handoff_count
created_at
Смотрите:
- долю no_answer;
- top score;
- latency retrieval;
- latency answer;
- частые unanswered;
- документы без использования;
- chunks с низким качеством;
- стоимость embeddings;
- стоимость LLM.
Проверка: через неделю понятно, какие документы нужно улучшать.
Шаг 26. Добавьте feedback
Создайте `rag_feedback`.
Колонки:
id
answer_id
user_id
rating
feedback_text
wrong_source
missing_answer
created_at
Используйте feedback для:
- обновления документов;
- добавления eval cases;
- настройки chunking;
- настройки threshold;
- улучшения prompt;
- handoff сложных тем.
Проверка: плохой ответ превращается в конкретную задачу владельцу базы знаний.
Шаг 27. Проверьте безопасность
RAG может раскрыть лишние документы, если фильтры работают плохо.
Проверьте:
- access filters до prompt;
- tenant isolation;
- public/internal разделение;
- отсутствие секретов в chunks;
- prompt injection в документах;
- HTML/script в документах;
- устаревшие документы;
- deleted vectors;
- audit retrieval;
- no-answer policy.
Создайте `rag_security_events`.
id
run_id
event_type
severity
details_json
created_at
Проверка: закрытый документ не появляется в retrieval даже при точной цитате в вопросе.
Шаг 28. Сделайте smoke test
Порядок:
- добавьте один документ;
- создайте document version;
- извлеките text blocks;
- создайте chunks;
- создайте embeddings;
- проверьте vector collection;
- задайте точный вопрос;
- проверьте retrieval results;
- получите ответ с источником;
- задайте вопрос без ответа;
- проверьте `unanswered_questions`;
- удалите документ;
- проверьте, что он больше не находится.
Проверка: весь путь document -> answer -> source -> delete работает.
Шаг 29. Включите ограниченный production
Первый запуск:
- одна база знаний;
- один язык;
- только published документы;
- top 5 chunks;
- no-answer включен;
- citations обязательны;
- handoff включен;
- feedback включен;
- eval перед релизом;
- monitoring каждый день.
Проверка: RAG можно выключить feature flag, если качество retrieval просело.
Шаг 30. Минимальный результат для запуска
MVP готов, если выполнены условия:
- источники заведены;
- документы синхронизируются;
- версии документов работают;
- чистый текст извлекается;
- chunks читаемые;
- metadata заполнена;
- embeddings созданы;
- vector collection активна;
- access rules работают;
- hybrid retrieval работает;
- retrieval results логируются;
- prompt получает context с источниками;
- агент отвечает JSON;
- citations выводятся;
- no-answer работает;
- reindex одного документа работает;
- удаление удаляет vectors;
- eval cases проходят;
- security checks проходят;
- monitoring и feedback включены.
Финальная проверка: задайте 20 тестовых вопросов. Если правильный chunk попадает в top 5, ответы имеют источники, закрытые документы не раскрываются, а вопросы без ответа не выдумываются, RAG можно включать ограниченно.
Что нельзя автоматизировать в первой версии
В первой версии не автоматизируйте:
- индексацию всех документов компании без разбора;
- доступ к закрытым документам без access rules;
- ответы без sources;
- ответы по устаревшим документам;
- юридические и финансовые выводы без handoff;
- удаление документов без удаления vectors;
- смешивание tenants;
- использование draft документов;
- сохранение секретов в chunks;
- full reindex без backup;
- смену embedding model без eval;
- показ низкокачественных chunks в prompt;
- ответ при пустом retrieval;
- игнорирование feedback;
- отключение no-answer ради красивой конверсии.
Сначала RAG должен честно находить правильные источники и отказываться, когда источника нет. Уже потом можно добавлять сложные rerank, multi-hop reasoning и автоматическое обновление больших баз знаний.
Частые вопросы
Чем RAG отличается от обучения модели на документах?
RAG не переобучает модель. Он находит релевантные фрагменты документов и передает их в prompt, чтобы модель ответила на основе актуального контекста.
Какой размер chunk выбрать?
Для старта берите 500-1200 tokens с overlap 50-150 tokens. Потом подбирайте размер по eval results: слишком маленькие chunks теряют контекст, слишком большие мешают retrieval.
Нужно ли использовать только vector search?
Лучше использовать hybrid retrieval: keyword search, vector search, metadata filters и rerank. Так меньше риск найти похожий, но неверный документ.
Что делать, если RAG не нашел ответ?
Агент должен честно сказать, что подтвержденного ответа нет, записать вопрос в `unanswered_questions` и предложить handoff человеку или владельцу базы знаний.
Как не раскрыть закрытые документы?
Фильтруйте доступ до передачи chunks в prompt: tenant, user, team, access level, status и document visibility. Prompt сам по себе не является защитой доступа.