context = "\n---\n".join([doc.content for doc in vs.similarity_search(query, k=50)])
system_prompt = f"You are an AI assistant. Use context:\n{context}"
response = llm(system_prompt + query)
Это прямое признание в архитектурной несостоятельности. Заливать 128k токенов в локальную модель по принципу «пусть сама разберётся» — преступление против железа и здравого смысла. Написать хитрый промпт и надеяться на чудо больше не работает. Prompt engineering здесь — лишь поверхностная косметика.
Если вы кормите корпоративную базу знаний через наивный RAG, никакие инструкции не спасут on-prem ассистента от деградации в бою. В игру вступает контекст-инжиниринг для LLM. Это архитектурный слой, который решает, что именно попадет в контекстное окно на каждом шаге траектории агента, а что будет безжалостно отброшено.
Анатомия context rot и lost-in-the-middle
Точность извлечения фактов из модели падает пропорционально размеру окна. Это суровая физика механизма внимания (attention mechanism). Феномен lost in the middle замерялся сотни раз: если релевантный документ лежит в начале или в самом конце промпта, модель его видит. Но если он зарыт в середине 32k-окна, вероятность его использования стремится к 40%.
Инженеры растягивают контекст с помощью RoPE (Rotary Position Embedding) scaling, думая, что победили ограничение длины. Модель действительно не падает с ошибкой, но начинается context rot (гниение контекста). Внимание нейросети размазывается по тысячам мусорных токенов. Вы буквально сжигаете вычислительные мощности за то, чтобы заставить модель галлюцинировать.
Наивный чанкинг добивает систему окончательно. Разрезание сложного технического регламента слепым окном по 512 токенов оставляет субъект в первой части, а во второй — только предикат. Модель вытягивает этот обрубок через векторный поиск и послушно додумывает недостающее. Галлюцинации on-prem решений — это крайне редко проблема весов. Почти всегда это проблема информационного мусора на входе.
Четыре такта контекстного окна
Контекстное окно LLM нужно воспринимать как жесткий L1-кэш процессора. В него нельзя сгружать сырые текстовые дампы. У инженера в арсенале есть ровно четыре базовые операции работы с этим кэшем.
Write определяет, что уходит в долгосрочную память. Если вы не извлекаете плотные метаданные и не строите графовые связи между чанками на этапе индексации, дальше оптимизировать нечего.
Select — это динамическая выборка. Не примитивный top_k=50 через cosine similarity, а строгий pipeline: сначала грубый поиск, затем reranking через легковесный cross-encoder с жестким отсечением по скору релевантности.
Compress требует агрессивного сжатия сырых чанков. Мы вытаскиваем саммари, экстрагируем факты или схлопываем историю диалога строго до того, как они займут токены.
Isolate гарантирует разделение скоупов. Контекст подается так, чтобы агент не скрещивал противоречащие инструкции из разных документов. Каждая сущность изолируется control-токенами.
Бюджет токенов: токены, VRAM и latency
На дефицитном железе каждый лишний токен в окне бьёт по latency и throughput инференс-сервера. Большой контекст требует сохранения матриц Key и Value. Этот KV-cache мгновенно выедает VRAM видеокарт, провоцируя OOM-киллеры (Out of Memory) под нагрузкой. Бюджет токенов должен рассчитываться в деньгах и миллисекундах.
Ниже замеры для 8-миллиардной Llama на одной NVIDIA A100 (80GB). Чистая физика context window optimization.
| Входные токены (Окно) | Time To First Token (TTFT) | Расход VRAM на KV-cache | Удельная стоимость GPU на 1k запросов |
|---|---|---|---|
| 4 000 | ~250 мс | ~0.5 GB | База |
| 16 000 | ~900 мс | ~2.0 GB | 4x от базы |
| 64 000 | ~3.8 сек | ~8.0 GB | 16x (рост отказов балансировщика) |
| 128 000 | ~8.2 сек | ~16.0 GB | 32x (критический риск OOM) |
Здесь виден экспоненциальный распад полезности. Динамическая пересборка контекста вместо забрасывания всего объема — единственный выживающий паттерн.
Чек-лист сборки перед продом
Прежде чем катить RAG-контур в бой, прогоните пайплайн по этим метрикам. Это 9 проверок, которые отличают proof-of-concept от стабильной enterprise-системы.
- Настроен ли Cross-Encoder для reranking'а извлеченных чанков?
- Измеряется ли hit rate на эталонном наборе (golden set) до и после внедрения реранкера?
- Установлены ли жесткие лимиты на размер контекстного окна под каждый интент?
- Изолирован ли системный промпт от пользовательского ввода?
- Пересчитывается ли объем токенов в миллисекунды задержки для SLA?
- Нивелирован ли эффект lost-in-the-middle смещением релевантных кусков к краям промпта?
- Используется ли семантический чанкинг по абзацам вместо деления по символам?
- Реализована ли компрессия исторического контекста чата?
- Мониторится ли аллокация KV-cache в метриках инференса в реальном времени?
В Morana Labs мы решаем деградацию качества переходом от монолитного RAG к агентам, которые собирают данные динамически. Контур сам оценивает информационную плотность запроса: если точный ответ можно сформировать из 1.5k токенов, алгоритм не пошлёт 32k. На выходе обеспечивается жесткий контроль над latency, а уровень галлюцинаций падает до околонулевых значений на тех же GPU. Ассистент просто перестаёт задыхаться от мусора.