Skip to main content
Glama

agentic-rag

CI

Цитируемый поиск с учётом ревизий по реальному корпусу, взятому из экосистемы IETF RFC — полные тексты RFC, реальный граф замещений Obsoletes/Obsoleted-by/Updates из индекса RFC, реальные исправления RFC, присланные сообществом, и реальные реестры параметров протоколов IANA — загружается по HTTPS (corpus_fetch/) и обрабатывается через ragpack в поисковое векторное хранилище (321 124 реальных чанка), затем маршрутизируется и проходит проверку цитирования через ядро consilium.

Задайте реальный вопрос о спецификации протокола IETF, об авторстве/статусе RFC, о реальном исправлении errata или о записи в реестре IANA — и получите ответ, полностью построенный из реальных цитируемых чанков — или честное воздержание от ответа. Единственное исключение — вопрос об устаревании («что заменило RFC 2616?»): у самого текста RFC 2616 нет «текущей ревизии» (он полностью заменён шестью более поздними документами, RFC 7230–7235 — знаменитый случай многостороннего устаревания в IETF), поэтому на него отвечает SupersessionModule — детерминированный поиск по графу реальных связей Obsoletes/Obsoleted-by, а не поиск по корпусу. Никакой LLM нигде в пути ответа.

Быстрый старт

git clone https://github.com/trentmilam/agentic-rag
cd agentic-rag
py -3.12 -m venv .venv
.venv\Scripts\python -m pip install -r requirements.txt
.venv\Scripts\python -m pip install -e .
.venv\Scripts\python scripts\verify.py

Это весь процесс начальной настройки. Именно редактируемая установка обеспечивает разрешение import consilium, import ragpack, import linkgraph, import activerag и import chainrag — package-dir в pyproject.toml сопоставляет каждое имя с packages/.

Для ответа на реальный вопрос дополнительно нужен обработанный корпус; см. Корпус и Проверка.

Related MCP server: 3GPP Evidence Graph

Один репозиторий, пять объединённых инструментов

agentic-rag раньше импортировал consilium и linkgraph из соседних репозиториев, вставляя их корни в sys.path, и требовал, чтобы RAGpack был установлен в редактируемом режиме из третьего репозитория. Клонирование означало клонирование четырёх репозиториев с тремя коммитами, зафиксированными в трёх отдельных файлах — которые уже разошлись друг с другом. linkgraph также читал файл данных этого репозитория, так что эти два инструмента импортировали друг друга через границу репозиториев.

Теперь это один репозиторий. Каждый объединённый инструмент сохранил всё своё дерево в packages/<name>/, так что всё, что он разрешает относительно своего корня, по-прежнему разрешается, и каждый сохранил свою собственную историю коммитов, а не был сжат в один импортирующий коммит:

пакет

что это

тесты

packages/consilium

ядро маршрутизации / проверки цитирования: Registry, Router, compose, контроль целостности, ComputeModule

5 наборов eval

packages/ragpack

загрузка, разбиение на чанки, эмбеддинги, обёртка хранилища Qdrant

20

packages/linkgraph

граф связей между документами, лежащий в основе инструментов MCP для связей

50

packages/activerag

обнаружение недостаточности доказательств и ограниченный поиск с повторами

54

packages/chainrag

второй вертикальный срез по документации протоколов блокчейна, доказывающий, что ядро не привязано к конкретному корпусу

--

Они вложены в packages/, а не лежат в корне репозитория, по конкретной причине: каталог верхнего уровня с именем consilium затеняет пакет consilium как неявный пакет пространства имён, потому что рабочая директория предшествует редактируемой установке в sys.path. Тогда импорт разрешается в пустое пространство имён, и consilium.__file__ становится None. CI проверяет, что каждый пакет разрешается в файл внутри этого репозитория, так что такой класс молчаливой неправильной привязки приводит к сбою сборки, а не к прохождению всех тестов против неправильной копии.

rag-reliability — единственная зависимость, которая всё ещё находится вне этого репозитория. Она поставляет graphrx, headroom, vecstamp, chunkledger, plumbline и legigate трём из перечисленных выше инструментов и сегодня разрешается по пути; упаковка её так, чтобы она могла быть объявленной зависимостью, — это следующая задача. CI проверяет её с зафиксированной версией и падает, если эти интеграционные тесты пропускаются, так что единственная оставшаяся зависимость по пути не может незаметно сгнить.

Корпус

тип источника

что это

реальных чанков

текущих (не устаревших)

rfc_text

полный текст RFC без изменений

306 939

225 809

rfc_index

карточка индекса для каждого RFC (название/авторы/дата/статус/obsoletes/updates)

5 854

4 663

errata

реальные исправления RFC, присланные сообществом

7 295

7 295 (н/д — нет понятия ревизии)

iana_registry

7 реальных реестров параметров протоколов IANA, представленных в виде таблиц Markdown

1 036

1 036 (н/д — нет понятия ревизии)

Всего 321 124 реальных чанка, встроенных с помощью реальной модели BAAI/bge-base-en-v1.5. Только rfc_text/rfc_index имеют понятие ревизии — is_current там является реальным структурным фактом (см. проверка ревизий), а не догадкой.

Пять модулей, один маршрутизатор

agenticrag/bootstrap.py::build_registry собирает реальный Registry из 5 модулей consilium: 4 модуля поиска, перечисленных выше (каждый загружается прямо из Qdrant, по умолчанию current_only=True — см. agenticrag/registry_loader.py), плюс SupersessionModule (agenticrag/supersession.py) — ComputeModule, который один раз при конструировании разбирает data/entities/revisions.json — реальный граф Obsoletes/Obsoleted-by для всех 9 794 RFC в живом индексе — и отвечает на вопросы об устаревании с помощью детерминированного обхода графа, безопасного для циклов (ограничен 50 посещёнными узлами — защитный предел, а не ожидаемый реальный лимит; реальные компоненты устаревания в IETF малы).

build_registry также громко завершается с ошибкой, если настроенный эмбеддер процесса не совпадает с тем, который обработал хранилище (verify_embedder_marker, вызывается первым) — иначе косинусные оценки были бы молча бессмысленными — и закрывает любой клиент Qdrant, который он сам открыл, после загрузки модулей (поиск после этого полностью в памяти), так что он не удерживает блокировку локального хранилища на всё время жизни процесса вызывающего.

trust_tier у errata (0.55, значительно ниже 0.9–0.95 у rfc_text/rfc_index/iana_registry) измерен, а не угадан: из 5 061 реальной записи errata, загруженных в систему, только 2 400 (47.4%) имеют статус Verified от редактора RFC — 1 781 (35.2%) — Held for Document Update, 679 (13.4%) — outright Rejected, и 201 (4.0%) — всё ещё Reported. Менее половины реально присланных исправлений подтверждены редактором RFC, поэтому этот модуль корректирует основной текст, не будучи сам по себе полностью авторитетным.

MCP-сервер

agenticrag/mcp/server.py предоставляет путь ответа как сервер Model Context Protocol через stdio (mcp>=1.28.1, FastMCP). Четыре инструмента:

  • search(query) — полный путь ответа с цитированием/воздержанием (consilium.compute.answer_v3), передаётся без изменений (честное воздержание остаётся воздержанием);

  • get_obsoletion_chain(rfc_id) / get_corrections(rfc_id) / get_related(entity_id) — граф связей, через соседний linkgraph (agenticrag/relationships.py); каждый возвращает документированную обёртку {"ok": false, "fallback": ...}, если этот соседний пакет отсутствует.

Запуск: .venv\Scripts\python -m agenticrag.mcp.server (транспорт stdio — MCP-клиент запускает его и общается по протоколу через stdin/stdout). Логика инструментов слабо зависит от импортов и не требует mcp/Qdrant, поэтому она тестируется без них (agenticrag/mcp/test_server.py).

Проверка

Быстрая проверка (быстро, без корпуса — не нужен обработанный корпус):

verify.bat        :: or:  .venv\Scripts\python -m pytest -q

Запускает тесты обёрток MCP-инструментов и набор проверок безопасности циклов при обходе графа замещений. Именно это CI запускает при каждом push (см. значок выше).

Полная проверка (требует обработанный корпус из 321k чанков из быстрого старта):

.venv\Scripts\python eval\eval_agenticrag.py

Детерминирована при уже обработанном корпусе; повторная обработка не требуется. Использует реальный эмбеддер — тот самый, который обработал корпус, — так что это доказывает настоящий семантический поиск от начала до конца, а не только связность. Занимает ~3.3 минуты на этом оборудовании (измерено, полный прогон: 196 с): разовое ~60-секундное построение реестра (один проход по корпусу для небольшого набора карантина ядовитых данных — не загрузка всех 321k векторов), затем проходы маршрутизатора/ответа по ~12 с каждый. Каждый проход выполняет маршрутизацию и поиск как нативные векторные поиски Qdrant; встроенный локальный режим Qdrant — это точный перебор (без ANN-индекса), поэтому поиск всё равно сканирует отфильтрованное подмножество — но в нативном коде, материализуя только top-k, что ~16 раз быстрее старого чисто Python-сканирования по чанкам. (Поиск за доли секунды потребовал бы HNSW-индекса серверного режима Qdrant; локальный режим сохраняет репозиторий самодостаточным — не нужно запускать сервер.) Проверяет: один запрос в области действия для каждого типа источника (разумный модуль + >=1 реальная цитата); запрос об устаревании RFC 2616 (множество преемников — точно {7230, 7231, 7232, 7233, 7234, 7235}); один запрос вне области действия (честное воздержание); структурное исключение проверки ревизий (ниже); действительно текущий RFC (791), разрешающийся в current; и отсутствующий номер RFC (99999), разрешающийся в not_found, а не в сбой.

eval/prove_revision_guard.py — то же структурное доказательство в виде отдельного повествовательного скрипта. eval/smoke_ingest_real.py доказывает связность fetch->ingest->Qdrant с помощью нулевого по стоимости HashEmbedder (требует, чтобы corpus_fetch.fetch_all был запущен, но не требует GPU/модели эмбеддинга).

Проверка ревизий, доказанная

RFC 2616 (HTTP/1.1) — это реальный, единый, цельный документ; у него нет «текущей ревизии»; он полностью заменён шестью разными документами. Свойство защиты здесь — это два реальных, проверяемых факта, а не одно «исправленное значение»:

  1. Структурное исключение. Собственные чанки rfc_text RFC 2616 существуют в Qdrant (повторная загрузка модуля rfc_text с current_only=False доказывает, что они там), но отсутствуют в модуле по умолчанию current_only=True, который фактически использует каждый запрос — потому что data/entities/revisions.json["RFC2616"]["obsoleted_by"] реален и не пуст. Данные существуют; они структурно отфильтрованы, а не случайно отсутствуют.

  2. Правильный путь к ответу. SupersessionModule — это явный способ узнать, что случилось с RFC 2616 — запрос, называющий его, возвращает реальный список из 6 преемников (RFC 7230–7235).

Калибровка маршрутизатора — измерено, а не угадано

Заявленные библиотечные значения по умолчанию consilium.router.Router (floor=0.11, anchor_centroid=0.25, anchor_best_chunk=0.25) предполагают почти нулевой базовый косинус между несвязанными текстами — это верно для мешка слов HashEmbedder, но не обязательно для реального плотного эмбеддера по корпусу из 321k чанков (тот же разрыв проявляется даже на гораздо меньшем масштабе ~1 100 чанков). agenticrag/calibrate.py измеряет это напрямую на реальном корпусе + реальном эмбеддере, а не предполагает; см. agenticrag/bootstrap.py::ROUTER_KWARGS для итоговых kwargs для каждого экземпляра и реальных чисел, обосновавших решение.

.venv\Scripts\python agenticrag\calibrate.py

Примечание о GPU (только при загрузке)

GPU важен ровно для одного шага: загрузки корпуса (ingest/run_ingest.py), которая встраивает ~321k чанков с помощью BAAI/bge-base-en-v1.5 через onnxruntime. Во время запроса вы встраиваете только (короткую) строку запроса, поэтому лаунчеры отлично работают на CPU без настройки GPU — именно поэтому они больше не касаются никакой конфигурации GPU.

Ожидаемое и безвредное предупреждение: если onnxruntime-gpu установлен, но соответствующие DLL-библиотеки CUDA runtime отсутствуют в пути поиска, вы увидите пугающий красный блок CUDAExecutionProvider / Error loading ... cublasLt64_*.dll ... missing — при любом запуске, ingest или query, не только при ingest. Это не сбой: onnxruntime переключается на CPU и продолжает работу (для query эмбеддинг — одна короткая строка, так что откат на CPU мгновенный). Игнорируйте это или установите DLL-библиотеки, указанные ниже, чтобы убрать предупреждение.

Для быстрого ingest исполнитель CUDA в onnxruntime-gpu требует, чтобы эти DLL-библиотеки CUDA runtime были в пути поиска DLL. Один только pip install onnxruntime-gpu не включает их, и без них onnxruntime переключается на CPU (измерено: ~6 чанков/сек — расчётно: ≈14,9 часов для полного корпуса из 321 124 чанков), а не вызывает ошибку. Чтобы получить реальное выполнение на GPU (измерено: ~650 чанков/сек на RTX 5090, т.е. ≈8 минут для полного корпуса), установите соответствующие wheels CUDA runtime в собственное виртуальное окружение этого репозитория, например:

.venv\Scripts\python -m pip install nvidia-cublas-cu13 nvidia-cudnn-cu13

(сопоставьте суффикс -cuNN с основной версией CUDA вашей сборки onnxruntime-gpu), или поместите каталог torch/lib любой установки PyTorch с поддержкой CUDA в PATH перед запуском ingest. В любом случае это необязательное ускорение одноразового ingest, никогда не требуется для запуска демо или обработки запросов.

Повторные запуски ingest (известное ограничение)

ingest/run_ingest.py поддерживает полную пересборку с нуля через --recreate и быстрый инкрементальный путь (повторно эмбеддит только те исходные файлы, чей хэш содержимого изменился). У инкрементального пути есть два известных ограничения, которые намеренно не замаскированы: (1) он считает файл "изменённым" исключительно по байтовому хэшу каждого исходного файла, поэтому если RFC становится устаревшим в более позднем обновлении rfc-index.txt без изменения самого текстового файла, его флаг is_current может устареть; и (2) он не удаляет осиротевшие точки Qdrant для документа, который повторно индексируется в меньшее количество чанков. Для гарантированно согласованного хранилища запустите ingest/run_ingest.py --recreate (полная пересборка). Закрытие обоих ограничений в инкрементальном пути — задача на будущее.

Структура

agenticrag/
  embed_config.py     shared Settings (model/qdrant path) + embedder-consistency guard
  registry_loader.py  loads a consilium Module's chunks straight from Qdrant (current_only guard)
  bootstrap.py        build_registry(embedder, client=None) -> Registry; the 5 Descriptors; ROUTER_KWARGS
  supersession.py     SupersessionModule -- real Obsoletes/Obsoleted-by graph walk, cycle-safe
  relationships.py    thin bridge into linkgraph (get_related / _obsoletion_chain / _corrections)
  calibrate.py        real router-score measurement script
  mcp/
    server.py         FastMCP server: search + the 3 relationship tools (stdio)
    test_server.py    fixture-only tests for the tool logic (no mcp package, no Qdrant)
corpus_fetch/         real HTTPS fetch of RFC full text / rfc-index.txt / errata / IANA registries
ingest/
  connectors/         per-source-type extract() -> ExtractedDoc (+ the revisions-index builder)
  run_ingest.py       raw files -> chunk -> embed -> Qdrant, real is_current currency check
eval/
  smoke_ingest_real.py               corpus_fetch -> ingest -> Qdrant wiring smoke (HashEmbedder)
  eval_agenticrag.py                 production eval (full verify; needs the ingested corpus)
  prove_revision_guard.py            standalone, narrated revision-guard proof
  test_supersession_cycle_safety.py  cycle-safety unit tests (synthetic graph; corpus-free)
tests/                unit tests for connectors / registry_loader / bootstrap (corpus-free)
packages/             the five merged tools, each keeping its own tree and history
  consilium/          routing / citation-gating spine (+ its 5 eval suites)
  ragpack/            ingest / chunk / embed / Qdrant store (src-layout)
  linkgraph/          cross-document relationship graph
  activerag/          evidence-thinness detection and bounded hunt-and-retry
  chainrag/           the blockchain-docs vertical
app.py                gr.ChatInterface chat UI
run_demo.py           scripted 3-question narrated transcript

Лицензия

Код: MIT (c) 2026 Trent Milam.

Корпус не включён в этот репозиторий (data/ в gitignore); он загружается во время сборки с rfc-editor.org и iana.org. Тексты IETF RFC/errata подпадают под Правовые положения IETF Trust (при загрузке сохраняется уведомление об авторских правах/Trust каждого документа); данные реестра IANA публикуются IANA. Этот проект ничего из этого не распространяет — он загружает это локально, на вашей машине.

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    B
    maintenance
    Enables retrieval-augmented question answering with source-grounded citations, document retrieval, and idempotent ingestion through MCP tools.
    -
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables any MCP-capable LLM client to search self-hosted long-term memory over markdown and PDF documents, combining dense semantic vectors with BM25 keyword retrieval and optional cross-encoder reranking. Exposes a read-only tool surface for querying incidents, runbooks, and other knowledge-base content, while writes happen out-of-band through ingestion jobs or a token-gated internal API.
    2
    MIT