Перейти до основного вмісту

Знання та RAG

Ingestion pipeline

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

Етапи pipeline

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

1. Upload

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

2. Черга

У чергу knowledge-ingest додається BullMQ-джоб для асинхронної обробки. Характеристики черги:

  • Concurrency: 1 (послідовна обробка)
  • Retry з exponential backoff
  • Graceful shutdown по SIGTERM

3. Витяг тексту

Реалізація у src/knowledge/parsers.ts:

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

4. PII Scrub (незворотний)

Перед чанкінгом усі PII-дані незворотно маскуються. Це гарантує, що база знань ніколи не зберігає сирі персональні дані.

Виявлювані патерни:

  • Email-адреси
  • Номери телефонів
  • Номери кредитних карт
  • IP-адреси
  • IBAN-номери
  • Грошові суми

Замінюються на [REDACTED_EMAIL], [REDACTED_PHONE] тощо.

Деталі — у PII Scrubber.

5. Chunking

Текст розбивається на чанки через chunker, що розуміє структуру юридичних документів.

  • Default max chunk size: 2000 символів
  • Overlap між чанками зберігає контекст
  • Виявлення української юридичної структури (Розділ, Стаття, п.)
  • Трекінг section-path для ієрархічного контексту

Деталі — у Semantic chunker.

6. Embed

Кожен чанк ембедиться через OpenAI text-embedding-3-small:

  • Розмірність вектора: 1536
  • Batch size: 32 чанки на API-виклик
  • Зберігається у PostgreSQL через pgvector

7. Генерація синтетичних Q&A

Для кожного чанка gpt-4o-mini генерує 3 синтетичних питання, на які цей чанк може відповісти. Ці питання:

  • Ембедяться окремо (1536-dim вектори)
  • Зберігаються у таблиці ChunkQuestion
  • Використовуються як додатковий шлях пошуку під час RAG-запитів

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

При успіху:

  • Статус документа оновлюється на ready
  • Метадані оновлюються полями chunkCount, textLength
  • Тимчасовий upload-файл видаляється

При невдачі:

  • Статус документа оновлюється на failed
  • Помилка пишеться у metadata.lastError
  • BullMQ ретраїть, якщо лишились спроби

Моніторинг

Треккайте статус ingestion через:

  • GET /api/v1/knowledge/documents?status=processing — джоби в процесі
  • GET /api/v1/knowledge/documents?status=failed — невдалі документи
  • Langfuse-трейси для спанів ingestion.synthetic-qa

RAG pipeline

RAG-pipeline (Retrieval-Augmented Generation) поєднує гібридний пошук (вектор + keyword) з LLM-генерацією для точних grounded-відповідей. Викликається agent-task воркером у звичайному flow обробки повідомлень та RAG draft-ендпоінтом для ручних тестів.

Архітектура

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

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

Стратегія пошуку

Гібридний пошук

Комбінуються три шляхи пошуку:

ШляхДефолтна вагаКандидатиОпис
Chunk vectors85% від вектор-бюджетуTop 18Cosine similarity по ембедингах чанків
Question vectors15% від вектор-бюджетуTop 18Cosine similarity по ембедингах синтетичних Q&A
Keyword search35% (дефолтна вага keyword)Top 18PostgreSQL full-text search по контенту чанків

Дефолтний розподіл: векторний пошук 65% vs keyword-пошук 35% (налаштовується для бази знань через config.ragWeights, поля vector і keyword). У вектор-бюджеті 85% іде на content chunk embeddings, 15% — на synthetic Q&A embeddings.

Scoring

Кожен кандидат отримує гібридний score:

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

Scores нормалізуються по кожному шляху перед обʼєднанням.

Top-K selection

Після ранжування вибирається top 6 чанків. Застосовується обмеження максимальної довжини контексту, щоб влізти у token-limit LLM.

Context assembly

Вибрані чанки форматуються з:

  • Назвою документа
  • Section path (з метаданих chunker)
  • Контентом чанка

LLM generation

ПараметрЗначення
ModelOPENAI_MODEL (дефолт: gpt-4o)
Temperature0.2
Max tokens700
StreamingПідтримується

Промпт включає:

  1. System prompt — з персона-конфігу простору імен
  2. Employee profile — summary, поточні проекти, преференції (якщо є)
  3. Історія розмови — останні 6 повідомлень (нормалізовані)
  4. RAG context — зібрані чанки з метаданими джерел
  5. User query

PII handling

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

Див. PII Scrubber.

Injection guard

І повідомлення користувача, і 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?"}'

Конфігурація на рівні бази знань

RAG-ваги налаштовуються для кожної бази знань через config.ragWeights:

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

Див. RAG weights.


Semantic chunker

Chunker розбиває документи на чанки зі збереженням контексту, враховуючи структуру юридичних документів. Оптимізований під українські правові тексти.

Реалізація

src/knowledge/chunker.tssplitLegalTextIntoChunks()

Виявлення юридичної структури

Chunker розпізнає заголовки українських правових документів:

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

Стратегія чанкінгу

  1. Split по заголовках: юридичні заголовки формують природні межі чанків
  2. Трекінг section-path: кожен чанк несе навігаційний шлях (наприклад, ["Розділ 3", "Стаття 15", "п. 2"])
  3. Обмеження розміру: чанки, що перевищують maxChunkSize (дефолт 2000 символів), додатково розбиваються по межах абзацу/речення
  4. Overlap: налаштовуваний overlap між послідовними чанками зберігає контекст на межах
  5. Метадані: кожен чанк зберігає chunkIndex, startChar, endChar, sectionPath

Fallback

Якщо юридична структура не виявлена (неправові документи), chunker переходить на:

  1. Split по абзацах (подвійний newline)
  2. Split по реченнях (крапка + пробіл)
  3. Hard split на максимальному розмірі

Chunker викликається під час ingestion-pipeline після витягу тексту та PII-скраба. Напряму через API не експонується.