Перейти к основному содержимому

ADR 0100 — rag_search для агентов и модели Ollama, гарантированные при загрузке

  • Статус: Принят
  • Дата: 2026-08-20
  • Контекст: фундамент графа знаний (RN-413/414/415), компаньон ADR 0099

Контекст​

RAG (pgvector, гибридный поиск вектор+лексика, ADR 0079/0080/0082) существует полностью в api с Волны 4/направления G2 Программы 28 — но grep -rn "rag" apps/engine/lib давал НОЛЬ вхождений до этой поставки. Ни один агент ни разу не обращался к RAG собственного проекта. Это самый большой пробел текущего дизайна: продукт строит индекс, а читает его лишь web-вкладка "Chat RAG".

Вторая находка, тоже реальная и не из этой функции: nomic-embed-text (RAG_EMBEDDING_MODEL в rag-search-limits.ts) никогда не скачивается автоматически. Entrypoint сервиса ollama в docker-compose.yml скачивает лишь llama3.2:1b. В любом чистом окружении RAG молча деградирует до чисто лексического поиска, пока кто-нибудь вручную не выполнит ollama pull nomic-embed-text, — никто этого не замечал, потому что у продукта до сих пор никогда не было программного потребителя поиска, который сделал бы деградацию видимой рано.

Решение​

Новый инструмент rag_search (apps/engine/lib/engine/harness/tools/rag_search.ex), категория :direct (чтение, не проходит через ActionPipeline/ proposed_action — чтение не является внешним эффектом, уже установленное правило). Вызывает POST /internal/rag/search (новый маршрут api, переиспользующий HybridSearchUseCase без дублирования логики поиска) и форматирует попадания с явным ЦИТИРОВАНИЕМ (path + фрагмент), чтобы модель могла ссылаться на источник прочитанного. Когда ответ приходит с degraded: true (embedding недоступен, поиск упал до чисто лексического), это появляется в начале текста, возвращаемого модели, — никогда не спрятано в конце, где обрезка по пределу байтов могла бы стереть предупреждение.

Собственные пределы, в духе RN-150 (у каждого инструмента чтения, который может переполниться, СВОЯ переменная, никогда не переиспользуемая от другого): top_k ограничивается максимумом (10) в самом инструменте, и предел БАЙТОВ форматированного текста (16 KiB, меньше 32 KiB у search_workspace/read_file — каждое попадание RAG уже является целым chunk+excerpt, накапливает байты быстрее на элемент).

Зарегистрирован в реестре по умолчанию (Engine.Harness.Tools, обслуживает PO/Arquiteto/разговорных) и в реестре dev agent (Engine.Dev.Tools). Расширен также на читающие gates, которые уже цитируют проиндексированные ADR/соглашения (QaTools, QaEstrategiaAgent, AppSecAgent, QaPerformanceSegurancaAgent), — но не на Infra.WorkflowsAgent (намеренно узкий, сегодня без ReadFile/SearchWorkspace) и не на Psicólogo/Anamnese (рассуждают об event log, а не о документации/коде проекта).

ollama-model-loader: новый одноразовый сервис в docker-compose.yml (dev и prod), который скачивает gemma:1b, yi-coder:1.5b и nomic-embed-text через OLLAMA_REQUIRED_MODELS — аддитивно к существующему сервису ollama (чей entrypoint продолжает скачивать llama3.2:1b для собственного использования engine, нетронутым). Закрывает реальный баг, когда nomic-embed-text никогда не появлялся сам, а не только потребность этой функции.

Последствия​

  • Capability RAG для агентов объявляется только при задействовании — реальный обмен с POST /internal/rag/search зависит от того, поднят ли маршрут api; инструмент деградирует с понятной модели ошибкой (никогда не обрушивает ToolLoop, RN-163), когда api недоступна.
  • deploy/k8s/ получает минимальные манифесты для Neo4j и model-loader, ОБЪЯВЛЕННЫЕ как не проверенные на реальном кластере (та же дисциплина, что у остального deploy/k8s/: capability объявляется лишь при доказательстве).
  • Стоимость embedding rag_search пока не проходит через metering — тот же пробел, уже объявленный в ADR 0075 для RAG в целом (token_usage.session_id — NOT NULL, индексация не происходит внутри сессии).