← Лендинг Hermes Agent

Опубликовано

Что не пишут в статьях типа "RAG за 5 минут"

Автор: Гусев Николай [портфолио]

Дата обновления не указана в исходнике.

Темы: rag, prompting

Гуглите "RAG за 5 минут" - везде одно и то же: ChromaDB, эмбеддинги, пяток строк кода. Работает. На тестовом корпусе из трёх PDF.

Через месяц реальной эксплуатации картина другая. Дубликаты чанков при каждой переиндексации. Эмбеддинг-модель одинаково уверенно ранжирует "архитектуру RAG" и "рецепт пиццы" - оба получают 0.85. Индекс разрастается мёртвыми записями. LLM вместо ответа получает мусор и уверенно врёт.

Это не баги. Это то, что остаётся за кадром туториалов.

Индекс должен уметь удалять

Базовый RAG умеет добавлять документы. Удалять - не умеет. Переиндексировать тот же файл - создаёт дубликаты, потому что ID чанков зависят от порядка обработки, а не от содержимого. Через несколько итераций в коллекции сотни мёртвых записей.

Исправление: MD5 от пути файла и заголовка секции. Файл не изменился - ID те же, upsert обновляет содержимое. Файл изменился - delete по source, потом upsert новых чанков. Файл удалили из вики - его чанки тоже уходят из индекса. Простая идея, которую ни один туториал не упоминает.

Один порог на все запросы - не работает

Стандартный подход - один cosine threshold. Обычно 0.7. Проблема: на разнородном корпусе эмбеддинг-модель сжимает все скоры в узкий диапазон, и различить релевантное от шума по одному порогу невозможно.

Решение: адаптивные пороги под тип запроса. Фактический вопрос (кто, сколько, когда) - высокий порог, 0.75, нужна точность. Аналитический (сравни, объясни разницу) - 0.65, важнее покрытие. Синтез (как спроектировать, как решить) - 0.60, нужен максимум контекста для размышления.

Плюс детектор шума. Если все найденные чанки кластеризуются в узком диапазоне - разница между лучшим и худшим меньше 0.04 - и ни один не дотягивает до 0.85, значит это не релевантность, а шум эмбеддинг-модели. Запрос идёт в веб-поиск.

Уметь признать, что индекс не знает

Когда поиск ничего не нашёл, стандартный RAG молча отдаёт пустой список. LLM генерирует ответ из общих знаний. Пользователь получает уверенно звучащую ерунду.

Несколько месяцев назад увидел как LLM на вопрос "какая погода в Берлине" выдавала развёрнутый про умеренно-континентальный климат с точными цифрами среднегодовой температуры. Индекс не нашёл ничего, система не сказала "я не знаю", а придумала.

CRAG (Corrective RAG) добавляет оценку после ретрива. Сначала классифицируем запрос (facturn/analytical/synthesis). Потом подбираем порог. Потом оцениваем что нашли: релевантно - используй. Частично - разбей запрос на подзапросы через LLM и попробуй снова. Ничего - признай поражение и пойди в DuckDuckGo. Без API-ключа, результаты в том же формате, что и локальные чанки.

Web fallback - часть основного потока, не запасной вариант. Когда локальный индекс не может ответить, система ищет в интернете, а не придумывает. Это меняет качество ответов на порядок.

Дубликаты - не мусор, а искажение

Дубликаты в индексе - это не просто лишний объём. Один и тот же текст встречается многократно и получает завышенные скоры. Поиск "думает" что нашёл много релевантных документов, а нашёл один и тот же три раза.

Решение: content-hash каждого чанка при индексации. Одинаковый текст попадает в индекс один раз. Не при поиске, а на входе. До фикса было 53 дублирующихся пары - 239 лишних записей. После - ноль.

Сессии - отдельная коллекция

Ещё один источник знаний - история переписки с агентом. Тысячи сообщений в SQLite, и они тоже должны быть доступны для поиска. Отдельная коллекция ChromaDB, отдельный индексер, инкрементальное обновление каждые 2 часа. Поиск идёт параллельно по wiki и сессиям.

127 тестов покрывают весь цикл: evaluate, CRAG, web fallback, CRUD индексацию, сессии. Работает на корпусе из 2000 документов и 750 сессий.

Это не библиотека и не демо. Это рабочий код, который каждый день отвечает на реальные вопросы.


Autolycus - AI-агент для инженерной работы.

autolycus-agent.ru