Стратегии соблюдения ограничений контекста
Реализуйте память со скользящим окном, суммирование сообщений и выборочное сокращение контекста, чтобы обрабатывать длинные диалоги, не превышая ограничения на число токенов.
«Стратегии соблюдения ограничений контекста» — бесплатный урок AI Engineering Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения AI Engineering Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс AI Engineering Academy содержит 4 уроков всего.
Растущая проблема бесед
Каждое сообщение в беседе увеличивает количество токенов в следующем вызове API. Во время длительной сессии поддержки клиентов или многочасовой сессии программирования накопленная история беседы легко может превысить 20 000–50 000 токенов — и всё это необходимо отправлять в API при каждом ходе, повторно оплачивая уже обработанные токены.
Без стратегии управления контекстом приложение либо достигнет предела контекста и завершится с ошибкой, либо начнёт незаметно обрезать сообщения, из-за чего модель потеряет важный контекст из начала беседы. Каждому рабочему приложению на базе LLM нужна явная стратегия управления ростом контекста.
Стратегия 1: скользящее окно (последние N сообщений)
Самая простая стратегия — оставлять в контексте только последние N сообщений, отбрасывая более старые. Это называется скользящим окном. Его легко реализовать, затраты предсказуемы, и для многих случаев этого достаточно, когда недавний контекст важнее старых сообщений.
import openai
client = openai.OpenAI()
class SlidingWindowConversation:
def __init__(self, system_prompt, window_size=10):
self.system = system_prompt
self.window_size = window_size
self.history = []
def chat(self, user_message):
self.history.append({'role': 'user', 'content': user_message})
# Keep only the last N messages
windowed = self.history[-self.window_size:]
messages = [{'role': 'system', 'content': self.system}] + windowed
response = client.chat.completions.create(
model='gpt-4o-mini',
messages=messages
)
reply = response.choices[0].message.content
self.history.append({'role': 'assistant', 'content': reply})
print(f'Context: {len(windowed)} messages ({len(self.history)} total)')
return reply
conv = SlidingWindowConversation('You are a helpful assistant.', window_size=6)
print(conv.chat('Hello! My name is Alice.'))
print(conv.chat('What is the capital of France?'))
print(conv.chat('What is my name?')) # Might forget if window is smallСтратегия 2: обрезка с учётом токенов
Вместо обрезки по количеству сообщений обрезка с учётом токенов удаляет сообщения, пока общее количество токенов не станет меньше заданного порога. Это точнее, поскольку длина сообщений сильно различается: ограничение по количеству сообщений может включать 10 коротких сообщений или 3 очень длинных, что приводит к совершенно разным затратам токенов.
import tiktoken
def trim_to_token_budget(
messages, system_prompt, model='gpt-4o-mini', max_input_tokens=8000
):
enc = tiktoken.encoding_for_model(model)
def count(msgs):
return sum(len(enc.encode(m.get('content',''))) + 4 for m in msgs) + 3
# System prompt token count
system_tokens = len(enc.encode(system_prompt)) + 4
budget = max_input_tokens - system_tokens
# Trim from the oldest messages until we fit
trimmed = list(messages)
while trimmed and count(trimmed) > budget:
trimmed.pop(0) # Remove oldest message
removed = len(messages) - len(trimmed)
if removed:
print(f'Trimmed {removed} old messages to stay within {max_input_tokens} tokens')
return trimmedСтратегия 3: суммаризация беседы
Суммаризация сжимает старые ходы беседы в краткое резюме, сохраняя ключевые факты, решения и контекст, упомянутые пользователем. Это более продвинутая стратегия, чем скользящее окно, поскольку она сохраняет суть прошлых сообщений, а не просто отбрасывает их.
Шаблон работает следующим образом: когда беседа превышает заданный порог, отправьте модели последние N самых старых ходов с запросом «Суммируйте текущую беседу», замените эти ходы одним системным сообщением с резюме и продолжите беседу с новой сжатой историей.
import openai
client = openai.OpenAI()
def summarize_conversation(messages_to_summarize, model='gpt-4o-mini'):
summary_prompt = [
{'role': 'system', 'content': 'You summarize conversations. Be concise but preserve key facts, decisions, and any specific information the user shared (names, numbers, preferences).'},
{'role': 'user', 'content': 'Summarize this conversation:\n' + '\n'.join(
f"{m['role'].upper()}: {m['content']}" for m in messages_to_summarize
)}
]
resp = client.chat.completions.create(model=model, messages=summary_prompt, max_tokens=500)
return resp.choices[0].message.content
def compress_history(history, keep_recent=4):
if len(history) <= keep_recent:
return history
to_summarize = history[:-keep_recent]
summary = summarize_conversation(to_summarize)
summary_msg = {'role': 'system', 'content': f'Earlier conversation summary: {summary}'}
return [summary_msg] + history[-keep_recent:]
print('Summarization strategy compresses old turns into a single summary message')Стратегия 4: память сущностей
Память сущностей извлекает и поддерживает структурированное представление ключевых фактов, упомянутых в беседе, — предпочтений пользователя, имён, сведений о проекте и принятых решений, — вместо хранения необработанной истории сообщений. Перед каждым ходом сохранённые факты добавляются в системный запрос.
Это эффективнее с точки зрения количества токенов, чем полная история, поскольку Вы включаете только семантически важную информацию, а не каждое слово каждого хода. Память сущностей особенно хорошо подходит для долго работающих помощников, когда пользователи ссылаются на предпочтения и факты, установленные значительно раньше в беседе.
import openai
import json
client = openai.OpenAI()
def extract_entities(messages, model='gpt-4o-mini'):
prompt = [
{'role': 'system', 'content': 'Extract key facts from this conversation as JSON: {"user_name": null, "preferences": [], "key_facts": []}'},
{'role': 'user', 'content': '\n'.join(f"{m['role']}: {m['content']}" for m in messages)}
]
resp = client.chat.completions.create(
model=model,
messages=prompt,
response_format={'type': 'json_object'}
)
return json.loads(resp.choices[0].message.content)
conversation = [
{'role': 'user', 'content': 'Hi, I am Alice and I prefer Python over JavaScript.'},
{'role': 'assistant', 'content': 'Great to meet you, Alice! Python is an excellent choice.'},
{'role': 'user', 'content': 'I am building a REST API for my e-commerce startup.'}
]
entities = extract_entities(conversation)
print(json.dumps(entities, indent=2))Когда применять каждую стратегию
Выбирайте стратегию с учётом особенностей Вашего приложения:
- Скользящее окно: простые чат-приложения, где важен только недавний контекст и пользователи не ссылаются на старые сообщения. Дешевле всего в реализации.
- Обрезка с учётом токенов: любое рабочее приложение, где длина сообщений существенно различается. Всегда лучше обрезки по количеству сообщений.
- Суммаризация: долго работающие помощники, сессии поддержки клиентов и боты для управления проектами. Пользователи ожидают, что помощник будет помнить ключевые факты из сообщений, отправленных несколько часов назад.
- Память сущностей: персональные помощники, чат-боты, учитывающие предпочтения пользователей, и системы обучения, где профиль пользователя важен на протяжении всей сессии.
Эти стратегии также можно сочетать: использовать память сущностей для основных фактов и скользящее окно для недавней беседы.
RAG как альтернатива наполнению контекста
Для приложений, работающих с большими объёмами знаний, лучшая стратегия управления контекстом — вообще не помещать документы в контекст, а использовать RAG (генерацию с дополнением извлечённой информацией), чтобы извлекать только конкретные фрагменты, релевантные текущему запросу. Это сохраняет сфокусированность контекста, снижает затраты и часто даёт более качественные ответы, чем добавление целого документа.
Ключевая идея заключается в том, что длинные контекстные окна решают проблему «поместится ли это?», но не проблему «сможет ли модель эффективно это использовать?». Точное извлечение решает обе проблемы, предоставляя модели только информацию, действительно необходимую для текущего вопроса.
Корректная обработка ошибок максимального контекста
Несмотря на все усилия, в рабочей среде Вы можете столкнуться с ошибками ограничения контекста, особенно из-за неожиданно длинного содержимого, созданного пользователями. Всегда обрабатывайте ошибку context_length_exceeded явно, а не позволяйте ей передаваться пользователю как необработанное исключение.
import openai
import tiktoken
client = openai.OpenAI()
def chat_with_context_guard(messages, model='gpt-4o-mini', max_tokens=100000):
enc = tiktoken.encoding_for_model(model)
total = sum(len(enc.encode(m.get('content',''))) + 4 for m in messages) + 3
while total > max_tokens and len(messages) > 2:
# Remove the second message (keep system prompt)
removed = messages.pop(1)
total -= len(enc.encode(removed.get('content',''))) + 4
print(f'Trimmed a message. New total: {total} tokens')
try:
return client.chat.completions.create(model=model, messages=messages)
except openai.BadRequestError as e:
if 'context_length' in str(e):
return {'error': 'Message history too long. Please start a new conversation.'}
raiseСохранение контекста между сессиями
В реальном приложении пользователи закрывают и снова открывают приложение. Сервер перезапускается. Управление контекстом должно учитывать сохранение между сессиями, а не только работу в рамках одной сессии. Это означает, что историю беседы нужно хранить в базе данных и загружать в начале каждой сессии.
Разумный подход: хранить полную необработанную историю в PostgreSQL для аудита и дообучения, но при загрузке контекста для нового вызова API применять стратегию суммаризации или обрезки, чтобы уложиться в бюджет токенов. Так Вы никогда не теряете данные, но не платите за полную историю при каждом запросе.
Мониторинг роста контекста в рабочей среде
Записывайте количество токенов каждого вызова API и отслеживайте его изменение во времени по идентификатору беседы. В здоровой беседе рост должен быть постепенным, а затем сменяться плато, когда начинает работать суммаризация. Беседа, которая неограниченно растёт до предела контекста, указывает на то, что логика обрезки работает неправильно.
Также отслеживайте среднюю длину контекста для всех бесед. Если со временем она увеличивается, это может означать, что Ваш системный запрос по умолчанию растёт из-за добавления функций, пользователи вставляют более длинные входные данные или RAG возвращает постепенно увеличивающиеся фрагменты. Для каждой из этих причин требуется своё исправление.
Сочетание стратегий: рабочий шаблон
Надёжная система управления контекстом в рабочей среде сочетает несколько стратегий, применяемых по уровням:
- Перед сборкой контекста: проверьте бюджет токенов и запишите его
- Примените извлечение сущностей: добавьте структурированный блок памяти с ключевыми фактами
- Добавьте недавнюю историю: включите последние 6–10 сообщений без изменений
- Суммируйте старую историю: если старая история существует, добавьте постоянно обновляемое резюме
- Проверка ограничений: если общий объём всё ещё превышает бюджет, обрежьте самые старые сообщения без изменений
Такой многоуровневый подход сохраняет наиболее важную информацию — память сущностей и недавний контекст, — постепенно заменяя старую историю резюме и прибегая к обрезке только в крайнем случае.
Быстрая проверка
Проверьте, насколько хорошо Вы усвоили концепции разработки систем ИИ из этого урока.
Итоги урока
В этом уроке Вы узнали, что скользящие окна и обрезка с учётом токенов — это простые стратегии, которые отбрасывают старый контекст, чтобы оставаться в установленных пределах, суммаризация сжимает старые ходы в компактное представление, сохраняя ключевые факты, а память сущностей извлекает структурированные факты для эффективной по токенам долговременной памяти в рамках беседы. Далее мы рассмотрим, как добиться от LLM надёжного возврата JSON с помощью режима JSON и структурированных выходных данных.
Изучай Python с ИИ-репетитором — бесплатно
Пиши и запускай код прямо в браузере, получай мгновенную помощь от ИИ-репетитора 24/7 и продолжи учиться на сайте или в приложении.
- Курсы
- 30
- Уроки
- 120
Часто задаваемые вопросы
Урок «Стратегии соблюдения ограничений контекста» бесплатный?
Да — полный текст урока «Стратегии соблюдения ограничений контекста» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс AI Engineering Academy, подпишись на CoddyKit PRO. Курс AI Engineering Academy содержит 4 уроков всего.
Чему я научусь в уроке «Стратегии соблюдения ограничений контекста»?
Реализуйте память со скользящим окном, суммирование сообщений и выборочное сокращение контекста, чтобы обрабатывать длинные диалоги, не превышая ограничения на число токенов. Ты практикуешь AI Engineering Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать AI Engineering Academy?
Предыдущий опыт не требуется. AI Engineering Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.
Сколько времени занимает урок «Стратегии соблюдения ограничений контекста»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке AI Engineering Academy?
Да. Каждый урок AI Engineering Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Что такое токен
- Контекстные окна: размер и последствия
- Расчёт и прогнозирование стоимости API
- Стратегии соблюдения ограничений контекста