Перейти к содержанию

Обновление RAG-базы в n8n: как переиндексировать документы без старых ответов

Обновлено: 2026-05-29

Открыть мой план

Короткий ответ

RAG refresh — это процесс обновления документов в vector store так, чтобы AI-ответы ссылались на актуальные источники, а старые chunks не оставались в индексе. В n8n это лучше делать отдельным workflow: загрузить источник, очистить текст, посчитать checksum, сравнить с registry, удалить старые chunks по source_id, записать новые chunks и embeddings, обновить metadata и запустить retrieval-тесты. Главный риск — не ошибка индексации, а тихое накопление старых версий: пользователь спрашивает по новой политике, а RAG уверенно отвечает по прошлому документу.

Почему RAG-база устаревает

RAG не знает, что документ обновился. Vector store хранит то, что вы туда положили. Если страница изменилась на сайте, Google Doc обновился, PDF заменили, а индекс не пересобрали, RAG продолжит возвращать старые chunks. Если вы просто добавите новые chunks поверх старых, проблема станет хуже: в retrieval попадут обе версии, а модель выберет ту, которая случайно ближе к запросу.

Особенно опасны темы, где актуальность критична: тарифы, политика возвратов, OAuth настройки, production checklist, юридические формулировки, инструкции по безопасности, runbook-и для инцидентов. Устаревший RAG-ответ может быть хуже отсутствия ответа, потому что звучит уверенно.

Что нужно хранить в source registry

Source registry — это таблица или файл, где хранится состояние индекса. Без registry вы не знаете, что уже проиндексировано и какой версии.

Минимальная запись:

{
  "source_id": "ai_rag_refresh",
  "source_url": "https://nodbot.ru/ai/rag-refresh/",
  "title": "Обновление RAG-базы в n8n",
  "content_checksum": "sha256:4d2...",
  "metadata_checksum": "sha256:91a...",
  "embedding_model": "text-embedding-model-name",
  "chunking_strategy": "section_1200_chars_overlap_150_v2",
  "chunk_count": 12,
  "status": "indexed",
  "indexed_at": "2026-05-29T09:00:00Z",
  "last_seen_at": "2026-05-29T09:00:00Z"
}

Registry можно хранить в Postgres, Google Sheets, Airtable, Supabase или любом управляемом источнике. Для production лучше Postgres: проще делать locks, транзакции, history и audit.

Полный refresh workflow в n8n

  1. Trigger: Schedule Trigger раз в ночь, Webhook из CMS или Manual Trigger для редактора.
  2. List sources: получить список страниц/документов.
  3. Fetch source: загрузить HTML/Markdown/документ.
  4. Normalize: очистить от навигации, CTA, повторов, HTML и PII.
  5. Checksum: посчитать hash нормализованного текста.
  6. Compare registry: если hash не изменился, пропустить.
  7. Lock source_id: не дать двум refresh одновременно обновлять одну страницу.
  8. Delete old chunks: удалить chunks с тем же source_id.
  9. Chunk + metadata: создать новую версию chunks.
  10. Embeddings + insert: записать новые embeddings.
  11. Update registry: записать версию, количество chunks, дату.
  12. Run retrieval tests: проверить, что expected questions находят правильный источник.
  13. Alert on failure: если тесты упали, не считать refresh успешным.

Такой workflow можно делать без сложного кода, но важно, чтобы delete old и insert new были контролируемыми.

Delete/insert vs upsert

Есть два подхода.

Delete then insert проще: удалить всё по source_id, затем записать новую версию. Минус — если insert упал посередине, источник временно пропал.

Versioned upsert безопаснее: новые chunks записываются с version_id, проходят тесты, затем registry переключается на новую версию, а старая удаляется позже. Это сложнее, но подходит для критичной базы.

Подход Когда использовать Риск
Delete then insert маленькая база, не критичный downtime источник может исчезнуть при ошибке
Upsert by chunk_id стабильный chunking, точечные изменения сложно при изменении структуры
Versioned index production RAG, важная база знаний больше инфраструктуры
Blue/green collection крупные обновления, миграция embeddings нужно переключение коллекций

Для первого production-варианта достаточно delete/insert + lock + alert. Для важного публичного помощника лучше versioned refresh.

Как считать checksum

Checksum нужно считать не по сырому HTML, а по нормализованному тексту и важной metadata. Иначе любое изменение меню будет запускать переиндексацию, а изменение access_level может остаться незамеченным.

const crypto = require('crypto');

const normalized = $json.normalized_text;
const relevantMetadata = {
  title: $json.title,
  language: $json.language,
  access_level: $json.access_level,
  status: $json.status,
  updated_at: $json.updated_at
};

const contentChecksum = crypto
  .createHash('sha256')
  .update(normalized)
  .digest('hex');

const metadataChecksum = crypto
  .createHash('sha256')
  .update(JSON.stringify(relevantMetadata))
  .digest('hex');

return [{ json: { ...$json, contentChecksum, metadataChecksum } }];

Если изменился только updated_at, возможно, переиндексация не нужна. Если изменился access_level, обязательно обновите metadata или удалите документ из публичной коллекции.

Incremental refresh

Не нужно переиндексировать всё каждый час. Лучше делить источники на типы:

  • частые изменения: тарифы, политики, API-инструкции — refresh по webhook или каждые 1–6 часов;
  • обычные статьи: раз в день;
  • архив/история: раз в неделю или вручную;
  • критичные runbook-и: после merge/publish + smoke test.

Incremental refresh должен уметь удалять документы, которые пропали из источника. Если страница снята с публикации, chunks должны стать status=archived или удалиться. Иначе RAG будет отвечать по несуществующей странице.

Как обрабатывать удалённые и переименованные страницы

Переименование URL — частая причина дублей. Старый source_url изменился, но source_id может остаться тем же. Поэтому source_id должен быть стабильным бизнес-ключом, а не просто URL. Например, ai_rag_refresh, а не https://nodbot.ru/ai/rag-refresh/.

Если страница переехала:

  1. сохранить тот же source_id;
  2. обновить source_url;
  3. удалить старые chunks;
  4. вставить новые;
  5. проверить, что RAG цитирует новый URL;
  6. оставить 301 на сайте для пользователей и поисковиков.

Если документ удалён, registry должен зафиксировать status=deleted, а chunks должны исчезнуть из public retrieval.

Как тестировать актуальность

Добавьте freshness tests. Это не просто “нашёл ли документ”, а “нашёл ли правильную версию”.

[
  {
    "question": "Как сейчас обновлять RAG базу без старых chunks?",
    "expected_source_id": "ai_rag_refresh",
    "expected_min_updated_at": "2026-05-29",
    "forbidden_terms": ["старый pipeline v1", "deprecated"]
  }
]

После refresh workflow должен запускать эти кейсы. Если expected source не найден, значит индекс сломан. Если найден deprecated chunk, значит удаление старых версий не работает.

Как логировать refresh

{
  "refresh_run_id": "rag_refresh_20260529_0100",
  "source_id": "ai_rag_refresh",
  "previous_checksum": "sha256:old",
  "new_checksum": "sha256:new",
  "old_chunk_count": 9,
  "new_chunk_count": 13,
  "deleted_chunks": 9,
  "inserted_chunks": 13,
  "embedding_model": "...",
  "retrieval_tests_passed": true,
  "duration_ms": 28400
}

Эти логи важны, когда пользователь жалуется: “бот отвечает старым текстом”. Вы сразу видите, обновлялся ли источник и какой результат дали тесты.

Rollback

Rollback нужен не только для кода, но и для RAG. Новая версия документа может быть плохой: сломанный HTML, пустой текст после парсинга, неправильная metadata, слишком мелкие chunks. Если вы удалили старые chunks и вставили плохие, база стала хуже.

Минимальная защита:

  • перед удалением сохранить old registry;
  • не считать refresh успешным без retrieval tests;
  • если новых chunks слишком мало, остановить публикацию;
  • если новая версия пустая, не удалять старую;
  • хранить последние N версий для критичных источников.

Частые ошибки

  • добавлять новые chunks поверх старых;
  • использовать URL как единственный ID;
  • не проверять metadata changes;
  • не удалять документы после снятия с публикации;
  • запускать два refresh одновременно для одного источника;
  • не тестировать retrieval после обновления;
  • не логировать embedding model и chunking strategy;
  • обновлять production index без rollback.

FAQ

Как часто обновлять RAG-базу?
Зависит от источника. Критичные документы обновляйте по событию публикации, обычные статьи — раз в день, архив — реже.

Можно ли просто переиндексировать всё каждую ночь?
Можно для маленькой базы, но это дороже и рискованнее. Для роста лучше incremental refresh по checksum.

Почему бот отвечает по старой версии?
Вероятно, старые chunks не удаляются, source_id нестабилен или новая версия не прошла refresh.

Нужно ли пересчитывать embeddings при изменении metadata?
Если изменился только фильтр доступа или статус, embedding можно не пересчитывать, но metadata в store/registry нужно обновить. Если изменился текст, embeddings нужны заново.

Что делать, если refresh упал посередине?
Использовать lock, registry status и rollback. Для критичной базы лучше versioned index, где старая версия остаётся активной до успешного теста новой.