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

Знания и RAG

Ingestion pipeline

Документы, загруженные в AgentCore, проходят через многоэтапный пайплайн обработки: извлечение текста, очистка PII, разбиение на чанки, генерация embeddings и синтетических Q&A-пар.

Этапы пайплайна

Upload → Parse → PII Scrub → Chunk → Embed → Synthetic Q&A → Store

1. Загрузка

Документы загружаются через POST /api/v1/knowledge/upload (multipart) или создаются по URL через POST /api/v1/knowledge/documents. Файл сохраняется в KB_UPLOAD_DIR, создаётся запись Document со статусом pending.

2. Очередь

BullMQ-задача попадает в очередь knowledge-ingest для асинхронной обработки. У очереди:

  • параллелизм 1 (последовательная обработка);
  • повторные попытки с exponential backoff;
  • graceful shutdown по SIGTERM.

3. Извлечение текста

Обрабатывается в src/knowledge/parsers.ts:

ТипПарсерПримечания
PDFpdf-parseИзвлечение полного текста
DOCXmammothКонвертирует в plain text
TXTПрямое чтение файлаUTF-8
URLHTTP fetch + HTML-стрипУбирает теги
ИзображенияOpenAI Vision APIOCR через gpt-4o

4. Очистка PII (односторонняя)

До чанкинга вся PII необратимо маскируется. Это гарантирует, что сырые персональные данные никогда не попадают в базу знаний.

Распознаваемые паттерны:

  • email-адреса;
  • номера телефонов;
  • номера кредитных карт;
  • IP-адреса;
  • IBAN;
  • денежные суммы.

Заменяются на [REDACTED_EMAIL], [REDACTED_PHONE] и т.д.

Подробности — в разделе PII Scrubber.

5. Чанкинг

Текст режется на чанки специальным юридическим чанкёром:

  • максимальный размер чанка по умолчанию — 2000 символов;
  • overlap между чанками для сохранения контекста;
  • распознавание украинской юридической структуры (Розділ, Стаття, п.);
  • отслеживание section path для иерархического контекста.

Подробности — в разделе Семантический чанкёр.

6. Embeddings

Каждый чанк векторизуется через OpenAI text-embedding-3-small:

  • размерность вектора 1536;
  • batch size — 32 чанка на вызов API;
  • хранится в PostgreSQL через pgvector.

7. Синтетические Q&A

Для каждого чанка gpt-4o-mini генерирует 3 синтетических вопроса, на которые этот чанк может ответить. Эти вопросы:

  • векторизуются отдельно (1536-dim);
  • хранятся в таблице ChunkQuestion;
  • используются как дополнительный путь retrieval при RAG-запросах.

8. Завершение

При успехе:

  • статус документа становится ready;
  • метаданные обновляются (chunkCount, textLength);
  • временный файл загрузки удаляется.

При ошибке:

  • статус документа становится failed;
  • ошибка пишется в metadata.lastError;
  • BullMQ делает retry, если попытки остались.

Мониторинг

Отслеживайте статус ingestion через:

  • GET /api/v1/knowledge/documents?status=processing — задачи в работе;
  • GET /api/v1/knowledge/documents?status=failed — упавшие документы;
  • Langfuse-трейсы по спанам ingestion.synthetic-qa.

RAG pipeline

Retrieval-augmented generation pipeline сочетает гибридный поиск (vector + keyword) с LLM-генерацией, чтобы давать точные, заземлённые на источнике ответы. Вызывается общим agent-task executor при обычной обработке сообщений и эндпоинтом RAG draft для ручных тестов.

Архитектура

Query → Injection Guard → Embed → Hybrid Retrieval → Rank → Context Assembly → LLM → PII Restore

Реализация: src/knowledge/rag.ts (OpenAiRagPipeline).

Стратегия retrieval

Гибридный поиск

Объединяются три пути retrieval:

ПутьВес по умолчаниюКандидатыОписание
Chunk vectors85% от vector-бюджетаTop 18Косинусная близость embeddings чанков
Question vectors15% от vector-бюджетаTop 18Косинусная близость embeddings синтетических Q&A
Keyword search35% (дефолт keyword)Top 18PostgreSQL full-text search по содержимому чанка

По умолчанию распределение: vector 65% + keyword 35% (настраивается per-KB через config.ragWeights, поля vector и keyword). Внутри vector-бюджета 85% на chunk embeddings и 15% на synthetic Q&A embeddings.

Скоринг

Каждый кандидат получает гибридный скор:

score = (vector × 0.85) × vecScore + (vector × 0.15) × qScore + keyword × keyScore

Скоры нормализуются в каждом пути перед объединением.

Top-K selection

После ранжирования берутся 6 лучших чанков. Применяется лимит максимальной длины контекста, чтобы уложиться в токен-бюджет LLM.

Сборка контекста

Выбранные чанки форматируются с:

  • заголовком документа;
  • section path (из метаданных чанкёра);
  • содержимым чанка.

LLM-генерация

ПараметрЗначение
МодельOPENAI_MODEL (дефолт gpt-4o)
Temperature0.2
Max tokens700
StreamingПоддерживается

В prompt входит:

  1. System prompt — из конфигурации namespace.
  2. Профиль сотрудника — краткое описание, текущие проекты, предпочтения (если есть).
  3. История разговора — последние 6 сообщений (нормализованные).
  4. RAG-контекст — собранные чанки с метаданными источника.
  5. Запрос пользователя.

Обработка PII

  1. PII в сообщении пользователя заменяется плейсхолдерами до отправки в LLM.
  2. Плейсхолдеры хранятся зашифрованными в PiiRedactionMap.
  3. После генерации плейсхолдеры в ответе заменяются обратно на реальные значения.

См. PII Scrubber.

Защита от инъекций

Перед сборкой контекста и сообщение пользователя, и фрагменты RAG-контекста сканируются на паттерны prompt injection. При обнаружении:

  • запрос помечается (injectionDetected: true);
  • записывается причина (injectionReason);
  • сообщение уходит на HITL независимо от trust-статуса.

См. Injection Guard.

Тестирование

Через POST /api/v1/rag/draft можно проверять RAG-запросы без отправки в канал:

curl -X POST http://localhost:3000/api/v1/rag/draft \
-H 'Authorization: Bearer <token>' \
-H 'Content-Type: application/json' \
-d '{"departmentId": "<department-id>", "text": "What is the leave policy?"}'

Настройка per-KB

RAG-веса настраиваются на уровне knowledge base через config.ragWeights:

{
"ragWeights": {
"vector": 0.65,
"keyword": 0.35
}
}

См. RAG-веса.


Семантический чанкёр

Чанкёр разбивает документы на чанки с сохранением контекста, опираясь на юридическую структуру документа. Оптимизирован под украинские юридические тексты.

Реализация

src/knowledge/chunker.tssplitLegalTextIntoChunks().

Распознавание юридической структуры

Чанкёр узнаёт заголовки украинских юридических документов:

ПаттернПримерУровень
Розділ N / Розділ IРозділ 3. ОплатаРаздел
Стаття N / Стаття N.NСтаття 15. ВідповідальністьСтаття
Пункт N.N / п. Nп. 5Пункт
N.N. (самостоятельная нумерация)1.1. Загальні положенняНумерованный
N)1) перший варіантЭлемент списка
а) / б) (кириллица)а) перша умоваПодпункт

Стратегия резки

  1. Резка по заголовкам — валидные заголовки создают естественные границы чанков.
  2. Section path tracking — каждый чанк хранит цепочку навигации (например, ["Розділ 3", "Стаття 15", "п. 2"]).
  3. Контроль размера — чанки длиннее maxChunkSize (дефолт 2000 символов) режутся по границам абзацев или предложений.
  4. Overlap — настраиваемое перекрытие между соседними чанками сохраняет контекст через границы.
  5. Метаданные — в каждом чанке лежат chunkIndex, startChar, endChar, sectionPath.

Fallback

Если юридическая структура не найдена (не-юридические документы), чанкёр откатывается к:

  1. Резке по абзацам (двойной перенос).
  2. Резке по предложениям (точка + пробел).
  3. Жёсткой резке по максимальному размеру.

Чанкёр вызывается во время ingestion pipeline после извлечения текста и PII-очистки. Напрямую через API не доступен.