Реалізація Contextual Compression для RAG
Уявіть: ваша RAG-система городить контекст із 10 чанків по 800 токенів, а корисної інформації — на 100 токенів. LLM платить за шум, а відповіді — каша. Типовий кейс — підтримка технічної документації: 10 чанків, релевантні 2–3. Кожен чанк важить близько 800 токенів, а корисної інформації — на 100. LLM витрачає контекстне вікно на нерелевантний текст, що призводить до галюцинацій і неповних відповідей. Тратиться контекст, зростає вартість. Contextual Compression — техніка, яка викушує з кожного чанка лише релевантний запиту фрагмент. Знижуємо шум, скорочуємо токени, підвищуємо faithfulness. За час роботи в AI/ML ми впровадили це в 15+ проєктах — ділимося досвідом. Отримайте консультацію з оптимізації вашої RAG-системи.
Проблема без Contextual Compression
Стандартний RAG передає LLM повні чанки (512–1024 токена). Типова картина: чанк містить 600 токенів, з яких 80 дійсно відповідають на питання, решта — нерелевантний контекст. Це:
- Збільшує вартість (більше input tokens)
- Знижує точність (LLM «губиться» у нерелевантному тексті)
- Зменшує effective context window (менше місця для дійсно важливих чанків)
Як працює LLM-based Contextual Compression?
from langchain.retrievers import ContextualCompressionRetriever
from langchain.retrievers.document_compressors import LLMChainExtractor
from langchain_openai import ChatOpenAI
# Компресор на основі LLM
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
compressor = LLMChainExtractor.from_llm(llm)
compression_retriever = ContextualCompressionRetriever(
base_compressor=compressor,
base_retriever=vectorstore.as_retriever(search_kwargs={"k": 8}),
)
compressed_docs = compression_retriever.invoke(
"Який порядок погодження договорів?"
)
# Кожен документ містить лише релевантний фрагмент
for doc in compressed_docs:
print(len(doc.page_content), "chars (vs оригінальних ~2000)")
Згідно з документацією LangChain, LLMChainExtractor використовує ту саму LLM для вилучення релевантного контенту. Це дає високу точність, але збільшує latency.
Коли використовувати Embedding-based Compressor?
Швидший і дешевший варіант — фільтрація за косинусною схожістю. Ми часто використовуємо його як перший етап пайплайну:
from langchain.retrievers.document_compressors import EmbeddingsFilter
from langchain_openai import OpenAIEmbeddings
embeddings = OpenAIEmbeddings()
embeddings_filter = EmbeddingsFilter(
embeddings=embeddings,
similarity_threshold=0.76,
)
filtering_retriever = ContextualCompressionRetriever(
base_compressor=embeddings_filter,
base_retriever=vectorstore.as_retriever(search_kwargs={"k": 8}),
)
Поріг 0.76 – емпіричне значення, яке дає хороший баланс між повнотою та точністю. Для вашого датасету його потрібно калібрувати.
Чому варто комбінувати Compression і Reranking?
EmbeddingsFilter відсікає явно нерелевантні чанки, але не ранжує решту. Cross-encoder reranker (наприклад, BAAI/bge-reranker-large) дає більш точне сортування за релевантністю, але дорожчий. Комбінація дає золоту середину: фільтр прибирає 40–60% чанків, reranker уточнює порядок top-N. Pipeline з Filter і Reranker дає faithfulness на 15% вищий, ніж без компресії, при цьому в 2,5 рази дешевший за LLM Extractor.
Порівняємо методи:
| Метод |
Cost per query |
Latency p99 |
Faithfulness gain |
| Без compression |
1× |
1.8 с |
— |
| EmbeddingsFilter |
0.2× |
0.3 с |
+8% |
| LLM Extractor |
0.5× |
2.4 с |
+19% |
| Pipeline (Filter + Reranker) |
0.4× |
0.9 с |
+15% |
Як побудувати Pipeline з Compression і Reranking?
from langchain.retrievers.document_compressors import DocumentCompressorPipeline
from langchain_community.document_transformers import EmbeddingsRedundantFilter
from langchain.retrievers.document_compressors import CrossEncoderReranker
from langchain_community.cross_encoders import HuggingFaceCrossEncoder
cross_encoder = HuggingFaceCrossEncoder(model_name="BAAI/bge-reranker-large")
reranker = CrossEncoderReranker(model=cross_encoder, top_n=3)
compressor_pipeline = DocumentCompressorPipeline(
transformers=[
EmbeddingsFilter(embeddings=embeddings, similarity_threshold=0.75),
EmbeddingsRedundantFilter(embeddings=embeddings),
reranker,
]
)
pipeline_retriever = ContextualCompressionRetriever(
base_compressor=compressor_pipeline,
base_retriever=vectorstore.as_retriever(search_kwargs={"k": 10}),
)
Який компресор обрати для вашого сценарію?
У production ми віддаємо перевагу гібридному підходу: EmbeddingsFilter для фільтрації шуму, потім LLM-компресор для ключових запитів, де важлива висока точність. Якщо latency критична — використовуємо лише EmbeddingsFilter з низьким порогом (0.7–0.75).
Кроки впровадження Contextual Compression
- Аудит поточної RAG-системи: метрики, вузькі місця, сценарії.
- Вибір та калібрування компресора (LLM / Embedding / Pipeline).
- Інтеграція через LangChain або кастомний код.
- Тестування: faithfulness, relevancy, latency, cost.
- Документація та навчання команди.
- Моніторинг та оптимізація threshold під нові дані.
Практичний кейс: з практики нашого клієнта
Завдання: асистент для технічних мануалів (чанки ~800 токенів). Після compression середній контекст зменшився з 4800 до 1200 токенів на запит.
| Метрика |
Без Compression |
З Compression (LLM) |
| Input tokens/запит |
5200 |
1450 |
| Faithfulness (RAGAS) |
0.79 |
0.94 |
| Answer Relevancy |
0.81 |
0.89 |
| Вартість (GPT-4o-mini) |
1× |
0.3× |
| Latency |
1.8с |
2.4с (+compression LLM) |
Стиснення знизило вартість у 3.3× при зростанні faithfulness на 19%. Наші інженери підібрали threshold та модель компресора за 2 дні, ще 2 дні пішло на інтеграцію.
Що входить у реалізацію
- Аудит поточної RAG-системи: метрики, вузькі місця, сценарії
- Вибір та калібрування компресора (LLM / Embedding / Pipeline)
- Інтеграція через LangChain або кастомний код
- Тестування: faithfulness, relevancy, latency, cost
- Документація та навчання команди
- Гарантія на результати оптимізації за KPI
Ми супроводжуємо проєкт після впровадження — фіксимо threshold під нові дані, додаємо моніторинг. Замовте оптимізацію вашої RAG-системи та отримайте зниження токенів до 4 разів. Зв'яжіться з нами для обговорення вашого кейсу.
Терміни та вартість
- Базова інтеграція: від 2 днів
- Калібрування та тестування: 2–3 дні
- Повний цикл (включаючи pipeline та reranker): 1 тиждень
Вартість розраховується індивідуально під обсяг даних та вимоги. Середня економія на токенах — 60–70%. Замовте оптимізацію вашої RAG-системи — наші інженери допоможуть підібрати правильний компресор.
Практичний розбір LLM: fine-tuning, RAG, агенти, деплой
Модель GPT‑4 або Claude 3.5 Sonnet через публічне API — не рішення, а просто інструмент. Коли приходить вимога «зробити як ChatGPT, але на наших даних», за нею стоїть реальна інженерна задача: від налаштування промптів до навчання 70B‑моделі на власній інфраструктурі. LLM розробка під ключ — це складний стек, і ми займаємося цим понад 5 років. За цей час реалізовано понад 20 проєктів у галузі генеративного AI: від RAG‑систем для юридичних департаментів до кастомних агентів для техпідтримки. Де саме знаходиться ваша задача — залежить від даних, latency‑вимог, бюджету та того, наскільки критична конфіденційність.
Типова ситуація: клієнт уже спробував ChatGPT, але результати нестабільні — то відповідає точно, то галюцинує. Або потрібна інтеграція в корпоративний портал з дотриманням політик безпеки. Розберемо кожен шар стеку в деталях — від RAG до production‑деплою.
Чому RAG‑системи ламаються і як це виправити?
RAG (Retrieval‑Augmented Generation) виглядає просто: знайшли релевантні документи, поклали в контекст, модель відповіла. На практиці збоїть у кількох місцях.
Chunking без перекриття. Класична помилка: chunk_size=512, overlap=0. Якщо відповідь лежить на межі двох чанків, retrieval не знайде жодного з достатньою впевненістю. Рішення: overlap 15–25% від chunk_size, а краще sentence‑aware splitting через spaCy або NLTK, а не наївне розбиття за символами.
Поганий embedder. Текст‑embedding‑ada‑002 — хороший для загального випадку, але на юридичних або медичних текстах програє спеціалізованим моделям: E5‑large‑v2, BGE‑M3 або fine‑tuned sentence‑transformers на доменних даних. Різниця в Recall@5 може становити 15–25%.
Відсутність re‑ranking. Векторний пошук оптимізований за швидкістю, не за релевантністю. Cross‑encoder re‑ranker (ms‑marco‑MiniLM‑L‑6‑v2, bge‑reranker‑large) після первинного retrieval піднімає точність топ‑3 при прийнятній затримці (+50–150 ms). Це часто важливіше за покращення embedding‑моделі.
Гібридний пошук. Тільки dense вектори погано працюють на точних запитах: імена, артикули, коди. BM25 (sparse) добре знаходить точні збіги, але не розуміє семантику. Гібрид через RRF (Reciprocal Rank Fusion) — оптимальний компроміс. Qdrant, Weaviate та pgvector 0.7+ підтримують гібридний пошук нативно.
Типова production‑архітектура корпоративного knowledge base
- Документи → preprocessing (PyMuPDF, Unstructured)
- Chunking → embedding (BGE‑M3)
- Qdrant (гібридний dense+sparse)
- Cross‑encoder re‑ranking
- Контекст → LLM (vLLM або OpenAI API)
- Відповідь з джерелами (RAGAS для оцінки якості)
Коли варто fine‑tune, а не промпт‑інжиніринг?
Промпт‑інжиніринг вирішує ~70% завдань адаптації LLM під домен. Решта 30% вимагають донавчання. Три ознаки: модель ігнорує специфічний формат виведення навіть при детальному описі в промпті; задача вимагає глибокого знання спеціалізованої лексики (медицина, право); потрібно значно знизити витрати на токени, замінивши велику модель меншою спеціалізованою.
LoRA та QLoRA — стандарт для SFT. LoRA додає trainable low‑rank матриці до attention‑шарів. Типова конфігурація для Llama‑3 8B: r=64, lora_alpha=128, target_modules=["q_proj","v_proj","k_proj","o_proj"] — параметрів, що навчаються, ~0.8%, навчання на одній A100 40GB. QLoRA додає 4‑бітну квантизацію (NF4) і дозволяє fine‑tune 70B модель на двох A100 40GB, хоча швидкість падає вдвічі порівняно з bf16.
DPO замість RLHF. Direct Preference Optimization вимагає лише пари (chosen, rejected), а не скалярні reward‑сигнали. DPOTrainer з бібліотеки trl (Hugging Face) реалізує це кількома десятками рядків.
Типова помилка. Датасет з 500 прикладів, 5 епох, validation loss 0.8 — здається норм. Але на тесті модель деградувала на загальних інструкціях. Причина: catastrophic forgetting. Рішення — додати 10–20% загальних instruction‑following прикладів (Alpaca, FLAN) у навчальну вибірку, щоб не зруйнувати вихідні здібності.
Як обрати базову модель: 8B чи 70B?
| Модель |
Параметри |
Сильні сторони |
Контекст |
| Llama‑3.1 8B |
8B |
Баланс якість/швидкість |
128k |
| Llama‑3.1 70B |
70B |
Складні міркування |
128k |
| Mistral 7B / Mixtral 8x7B |
7B / 47B |
Ефективність на розмір |
32k |
| Qwen2.5 72B |
72B |
Код, мультимовність |
128k |
| Gemma 2 27B |
27B |
Відкрита ліцензія |
8k |
Для більшості задач fine‑tuning 8B моделі достатньо. 70B потрібен, коли потрібне глибоке міркування або baseline 8B не досягає потрібної якості навіть після донавчання. Вартість інференсу Llama‑3 8B через vLLM на A100 значно нижча, ніж у GPT‑4, що робить його економічно вигідним.
Що дає PagedAttention в production?
vLLM — перший вибір для serving open‑source моделей. PagedAttention — ключове технічне рішення: KV‑cache керується як virtual memory в ОС, без фрагментації. Це дає throughput у 2–4 рази вище порівняно з наївним HuggingFace Transformers inference. Документація vLLM підтверджує: continuous batching та PagedAttention — стандарт для високонавантажених LLM‑сервісів.
Типові числа на A100 80GB для Llama‑3 8B (bf16): 400–600 req/s, P50 latency 200–400ms, P99 latency 600–900ms при concurrency 64. Для 70B на двох A100 з tensor parallelism: 80–120 req/s, P99 latency 1.5–2.5s. Квантизація AWQ або GPTQ знижує споживання пам'яті в 2 рази при втраті якості в межах 1–3%.
Мультиагентні системи
Агенти — LLM з доступом до інструментів: пошук, виконання коду, запити до API, робота з БД. Основні патерни:
-
ReAct (Reason + Act): модель розмірковує → обирає інструмент → спостерігає результат → знову розмірковує. LangChain та LlamaIndex реалізують з коробки.
- Multi‑agent orchestration: кілька спеціалізованих агентів з координатором зверху. Приклад: coordinator → researcher (пошук + summarization) → coder (генерація та виконання коду) → critic (перевірка). Інструменти: AutoGen (Microsoft), CrewAI, кастомна реалізація на LangGraph.
В продакшені агентні системи недетерміновані. Обов'язкові guardrails, ліміти кроків, логування кожного кроку, human‑in‑the‑loop для критичних дій.
Як ми гарантуємо якість LLM рішення?
Ми використовуємо RAGAS для автоматичної оцінки відповідей: faithfulness, answer relevancy, context precision. Система трекінгу експериментів на базі MLflow фіксує всі метрики, датасети та конфіги. Це дозволяє порівнювати різні гіпотези та доводити покращення з цифрами. Гарантію стабільної роботи забезпечує continuous integration з тестами на специфічних сценаріях (prompt injection, edge‑cases).
Як почати LLM розробку: наступні кроки
Ми передаємо:
- Технічну документацію (model card, конфіги, інструкції з розгортання)
- Доступ до інфраструктури (репозиторій з кодом, навчені ваги)
- 1 місяць підтримки після деплою (консультації, виправлення багів)
- Навчання команди замовника (2–3 заняття з експлуатації системи)
Терміни: базовий RAG‑прототип — 1–2 тижні. Fine‑tuning з даними замовника — 3–6 тижнів (з урахуванням підготовки даних). Production‑система з моніторингом та перенавчанням — 2–4 місяці.
| Етап |
Тривалість |
Що отримуєте |
| Аудит та збір даних |
1–2 тиж. |
Eval‑датасет з 100+ прикладів, формалізація задачі |
| Baseline (промпт + RAG) |
1–2 тиж. |
Робочий прототип, метрики якості |
| Fine‑tuning (якщо потрібно) |
2–4 тиж. |
Навчена модель, LoRA‑ваги, model card |
| Деплой та моніторинг |
1–2 тиж. |
vLLM сервер, Grafana + Prometheus |
| Документація та навчання |
1 тиж. |
API‑документація, навчання команди |
Вартість розраховується індивідуально і залежить від обсягу даних, складності моделі та вимог до інфраструктури. Хочете оцінити свій проєкт? Зв'яжіться з нами — ми підготуємо попереднє резюме за 1–2 робочі дні. Або замовте консультацію фахівця з вибору підходу: RAG, fine‑tuning або гібрид — розповімо, що підійде саме вам.