Контекст — это весь текст, который модель видит перед собой, когда генерирует ответ: ваш запрос, приложенные файлы, история диалога, инструкции. У модели нет памяти между обращениями — она знает ровно то, что сейчас в контексте, и ничего больше. Понимать это критично: почти все проблемы «модель не поняла / забыла / перепутала» — это проблемы контекста.

Обязательно

Контекстное окно

У каждой модели есть контекстное окно — предел того, сколько токенов она может держать за раз (вход и выход вместе). Это как размер рабочего стола: что на нём лежит — с тем модель и работает; что не поместилось — для неё не существует.

Окна бывают разные — от тысяч до сотен тысяч и миллионов токенов. Большое окно позволяет отдать модели больше (целые файлы, длинную историю), но «большое» не значит «нужно заполнять целиком».

Чтобы эти числа что-то значили, переведём их в файлы вашего проекта. Ориентир простой: файл кода на 300 строк — это примерно 4–5 тысяч токенов (в коде токенов на символ больше, чем в английском тексте, а в русских комментариях — ещё больше). Отсюда порядки:

ОкноСколько это примерноНа что хватает
8 тысяч2 файла по 300 строкОдин вопрос по одному файлу; для агента мало
32 тысячи7–8 таких файловНебольшая задача в паре файлов
200 тысяч40–50 файлов, или 150 страниц текстаОбычная рабочая сессия агента целиком
1 миллион200+ файлов, небольшой репозиторийРазбор незнакомого проекта, длинные документы

Что стоит забрать из таблицы: окно в 200 тысяч токенов — это не весь проект, а несколько десятков файлов, и типичный репозиторий в него не влезает. Поэтому агент не «загружает проект», а читает файлы по мере надобности, и в окне лежит только то, что он успел прочитать, плюс история разговора.

И вторая мысль, важнее первой: окно расходуется быстрее, чем кажется. Одна команда с длинным выводом (сборка, список файлов, журнал) — это тысячи токенов, которые останутся в истории до конца сессии. Именно поэтому окно кончается не на сороковом файле, а на пятнадцатой минуте разговора, где половина занята выводом команд.

Отдельно от окна у ответа есть свой потолок — сколько токенов модель имеет право сгенерировать за один раз. Он обычно в разы меньше окна: окно на двести тысяч токенов не означает, что можно получить ответ на сто пятьдесят тысяч.

правила проекта читаются каждый раз файлы середина теряется история растёт с ходами ваш запрос ближе к ответу не поместилось обрезка или ошибка

Что лежит в окне по порядку и что в него не влезло: подсвечена середина, которую модель учитывает хуже начала и конца.

Почему модель «забывает»

Два разных механизма, которые новички путают:

  • Между разговорами модель не помнит вообще ничего. Новый чат — чистый лист. То, что вы обсуждали вчера, надо дать снова (это и решает настройка агента: правила, скиллы и память кладут нужное в контекст автоматически).
  • Внутри длинного диалога всё, что помещается в окно, модели доступно. А когда история перестаёт помещаться, сама модель ничего не выбрасывает: на запрос, который не влез в окно, приходит ошибка. Разбирается с этим программа вокруг модели — чат или агент: обрезает старые сообщения, заменяет их кратким пересказом или честно останавливается.

Поэтому «модель забыла, что я просил в начале» — это почти всегда не сбой модели, а то, как ваш инструмент подрезал историю; в разных инструментах это сделано по-разному, и полезно знать, как именно в вашем.

Посмотрим, как эта подрезка выглядит изнутри: её пишут руками все, кто делает свой чат или помощника поверх модели. На входе четыре вещи: постоянная часть (правила помощника, они же системная часть), история прошлых ходов, свежий вопрос и бюджет — сколько токенов мы готовы отдать на вход.

Правило простое. Системная часть и последний вопрос остаются всегда: без правил помощник перестанет быть собой, без вопроса отвечать не на что. История берётся с конца, потому что свежие ходы нужнее старых, и добавляется, пока укладывается в бюджет; что не влезло — выбрасывается молча. Цену хода считают один раз, когда кладут его в историю, и хранят рядом с самим сообщением.

record Turn(ChatCompletionMessageParam message, int tokens) {}

String ask(String system, List<Turn> history, String question, int budget) {
    int used = (system.length() + question.length()) / 3;
    Deque<Turn> kept = new ArrayDeque<>();
    for (Turn turn : history.reversed()) {
        if (used + turn.tokens() > budget) {
            break;
        }
        kept.addFirst(turn);
        used += turn.tokens();
    }
    ChatCompletionCreateParams.Builder params = ChatCompletionCreateParams.builder()
            .model("gpt-4o-mini")
            .addSystemMessage(system);
    kept.forEach(turn -> params.addMessage(turn.message()));
    params.addUserMessage(question);
    return client.chat().completions().create(params.build())
            .choices().get(0).message().content().orElse("");
}
type Turn struct {
	Message openai.ChatCompletionMessageParamUnion
	Tokens  int
}

func ask(ctx context.Context, system string, history []Turn, question string, budget int) (string, error) {
	used := (len(system) + len(question)) / 3
	kept := []openai.ChatCompletionMessageParamUnion{}
	for i := len(history) - 1; i >= 0; i-- {
		if used+history[i].Tokens > budget {
			break
		}
		kept = slices.Insert(kept, 0, history[i].Message)
		used += history[i].Tokens
	}
	messages := append([]openai.ChatCompletionMessageParamUnion{openai.SystemMessage(system)}, kept...)
	messages = append(messages, openai.UserMessage(question))
	resp, err := client.Chat.Completions.New(ctx, openai.ChatCompletionNewParams{
		Model:    "gpt-4o-mini",
		Messages: messages,
	})
	if err != nil {
		return "", err
	}
	return resp.Choices[0].Message.Content, nil
}
async function ask(system, history, question, budget) {
  let used = Math.floor((system.length + question.length) / 3);
  const kept = [];
  for (const turn of [...history].reverse()) {
    if (used + turn.tokens > budget) break;
    kept.unshift(turn.message);
    used += turn.tokens;
  }
  const answer = await client.chat.completions.create({
    model: "gpt-4o-mini",
    messages: [{ role: "system", content: system }, ...kept, { role: "user", content: question }],
  });
  return answer.choices[0].message.content;
}
def ask(system, history, question, budget):
    used = (len(system) + len(question)) // 3
    kept = []
    for turn in reversed(history):
        if used + turn["tokens"] > budget:
            break
        kept.insert(0, turn["message"])
        used += turn["tokens"]
    answer = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "system", "content": system}, *kept, {"role": "user", "content": question}],
    )
    return answer.choices[0].message.content

Где тут спотыкаются. Бюджет это не всё окно: из окна вычитают потолок ответа, иначе вход уйдёт впритык и модели негде будет писать. Ходы выбрасывают парами «вопрос плюс ответ»: одинокий ответ без своего вопроса модель читает как обрывок и отвечает мимо. Деление длины на три это грубая оценка для примера, настоящий счётчик токенов даст другое число, поэтому бюджет берут с запасом, а не вплотную к пределу. И главное: системная часть осталась на месте, а вот договорённость из пятого хода уже выброшена, и помощник про неё не знает — ровно то, что пользователь назовёт «он забыл».

«Память» продуктов — это тот же контекст

Утверждение «между разговорами модель не помнит ничего» верно про модель и противоречит опыту: чат помнит, что вы просите отвечать по-русски и короче, помнит имя вашего проекта, а агент помнит, что в прошлый раз вы отказались от определённого подхода. Кажется, что статья врёт.

Никакого противоречия нет: память имитирует программа вокруг модели, а не модель. Устроено это тремя способами, и полезно различать их, потому что вести себя они ведут по-разному.

Файлы правил. Самое простое и самое предсказуемое: в проекте лежит файл с правилами и договорённостями, и агент читает его в начале каждой сессии, добавляя во вход. Это не память, а автоматическая подстановка одного и того же текста — зато вы видите этот текст и правите его руками.

Сохранённые факты. Продукт по ходу разговора выписывает в отдельное хранилище то, что похоже на устойчивый факт про вас («работает с Java, предпочитает короткие ответы»), и подмешивает подходящие факты во вход следующих разговоров. Отсюда ощущение памяти. Отсюда же два подвоха: во-первых, туда попадает не то, что вы считаете важным, а то, что счёл важным продукт; во-вторых, устаревшее оттуда не исчезает само — записанное однажды предпочтение будет подмешиваться и через полгода, когда оно уже не ваше. Практика простая: раз в какое-то время смотреть список сохранённого и чистить.

Поиск по прошлым разговорам. Некоторые продукты умеют найти релевантный кусок старой переписки и подставить его в текущий вход. Это уже поиск, а не память, и работает он как любой поиск: иногда находит не то.

Что из этого следует для работы. Во-первых, всё, что «помнится», на самом деле занимает контекст — то есть стоит денег и разбавляет важное; длинный список сохранённых фактов работает против вас. Во-вторых, на память нельзя опираться в важном: если ограничение обязано соблюдаться, оно должно лежать в файле правил проекта или в самом запросе, а не «мы это обсуждали». В-третьих, память привязана к продукту и учётной записи: в другом инструменте или у коллеги её нет, и код, который «работает у меня», у соседа не воспроизводится, потому что у него агент не знает того, что знает ваш.

Правила, скиллы и память на живом примере

Три слова выше — «файл правил», «скиллы», «память» — звучат абстрактно, пока не посмотреть, как они лежат в настоящем репозитории. Возьмём открытый набор скиллов курса, remodov/usecase-pattern-skills: он устроен ровно вокруг бюджета контекста.

Что в контексте всегда. В корне проекта лежит CLAUDE.md — правила проекта на одну-две страницы, которые агент читает в начале каждой сессии. Рядом .claude/rules/ucp-java-core.md или ucp-python-core.md, по языку сервиса: выжимка методологии на сто строк, то, что надо знать до первого символа кода. Это постоянная плата, четыре-пять тысяч токенов в каждом запросе, поэтому туда попадает только то, без чего агент напишет не так.

Что подгружается по требованию: скилл. Скилл — это папка с файлом SKILL.md: короткая шапка, по которой агент решает, нужен ли скилл сейчас, и инструкция, что читать и что делать. Вот шапка и первый шаг настоящего скилла проектирования REST-эндпоинта:

---
name: ucp-api-design
description: Спроектировать новый REST API-эндпоинт или ресурс по требованиям rest-api/* для Java/Spring — OpenAPI-first, нейминг URL, статусы, ошибки ProblemDetails.
when_to_use: Создание новых эндпоинтов, проектирование структуры API, написание OpenAPI-спеки с нуля.
allowed-tools: Read Glob Grep Write Edit
---

1. **Прочитай индекс правил** `.claude/docs/backend/rest-api/spec.md` — компактный список
   всех кодов с формулировками; следуй каждому строго. Полную версию
   `references/java/implementation.md` (примеры, обоснование) читай **точечно по нужному
   разделу**, когда индекса не хватает — не целиком.
2. **Уточни требования** ...

Скилл в примере написан под Java и Spring, но устроен он одинаково для любого языка: шапка, индекс правил, справочник по требованию. Смотреть стоит на две вещи. В контексте каждой сессии лежит только список шапок всех скиллов, по строке на скилл; тело попадает туда в момент, когда скилл выбран. И сам скилл управляет контекстом дальше: велит прочитать короткий индекс правил и запрещает тянуть полный справочник целиком. Корпус требований в .claude/docs/ — сотни страниц, в окно он не влезет, а индекс на две тысячи токенов влезает всегда.

Память. Агент для кода ведёт её как картотеку: файл-индекс MEMORY.md, где по строке на факт, и по отдельному файлу на каждый факт с шапкой — имя, однострочное описание, тип (о пользователе, обратная связь, проект, ссылка). В сессию попадает только индекс; карточка читается, когда факт понадобился. Так выглядит одна из них:

---
name: project_cones_currency
description: "Шишки — внутренняя валюта ИИ-ментора в кабинете: правила, где живёт код, что решено с владельцем"
type: project
---
База 20 шишек в день, потолок 200; задача +2, тест +10, фаза +20 ...

Отсюда и практика работы с такой памятью: факт на карточку, устаревшие карточки удалять, а то, что обязано соблюдаться всегда, переносить из памяти в CLAUDE.md — память агент может не открыть, правила он читает каждый раз.

Больше контекста ≠ лучше

Соблазн — вывалить в модель всё: весь репозиторий, всю документацию, всю переписку. На практике это вредит:

  • Разбавляется важное. Среди 50 файлов нужные три теряются; модель хуже находит релевантное в шуме. Это иногда называют «потерянным в середине» — то, что лежит в середине огромного контекста, модель учитывает хуже, чем начало и конец.

Из последнего свойства следует практика, которая стоит ноль и заметно помогает.

Важное — в начало и в конец. Инструкции и ограничения кладут в начало запроса, а самое главное требование повторяют в конце, после материалов. Это не суеверие: конец запроса — ближайший к ответу текст, и модель опирается на него сильнее всего. Отсюда рабочая форма длинного запроса: задача → материалы → «напоминаю: сделай то-то, не трогай то-то, ответ в таком-то виде».

Материалы — с подписями. Когда в запросе несколько файлов или документов, каждый начинают строкой «файл такой-то» (или тегом), а не склеивают в одну простыню. Модель тогда может сослаться на источник, а вы — проверить, откуда она взяла утверждение.

Требование — рядом с материалом, к которому относится. Если правило касается конкретного файла, его пишут рядом с этим файлом, а не в общем списке из десяти пунктов на две страницы выше.

Длинный разговор — повторять ограничения. В сессии на час ограничение, названное в первой реплике, к тридцатой уже далеко в середине и, возможно, потеряно при сжатии истории. Ключевые запреты («миграции не трогай», «не меняй публичный контракт») повторяют явно, когда переходят к новому шагу. Это же лечится тем, что такие правила кладут в файл настроек агента, который читается каждый раз, — тогда они всегда в начале контекста.

  • Дороже и медленнее. Каждый лишний токен — это деньги и время.
  • Растёт риск ошибки. Больше противоречивого материала — больше шанс, что модель зацепится не за то.

Правило продукт-инженера: давать релевантный контекст, а не максимальный. Три нужных файла лучше пятидесяти «на всякий случай».

Как управляют контекстом

Управление контекстом — ключевой навык работы с ИИ:

  • Отбирайте релевантное — только те куски кода, документации, требований, что относятся к задаче.
  • Давайте факты, а не «вспомни сам» — заземление на данные прямо в контексте резко снижает галлюцинации.
  • Подтягивайте контекст по требованию через вызов инструментов: вместо того чтобы залить всё сразу, дайте модели возможность достать нужное (поиск по коду, запрос к базе) в момент, когда оно понадобилось.
  • Не тащите бесконечную историю — на длинных диалогах начинайте новый с кратким резюме нужного.
  • Автоматизируйте повторяющееся — то, что модель должна знать всегда (стиль, правила проекта), выносят в правила, скиллы и память проекта (как это настраивают), чтобы не вставлять руками каждый раз.

Что уходит наружу вместе с контекстом

Первый вопрос, который задаёт служба безопасности, когда узнаёт, что команда работает с агентом: а что именно уезжает к поставщику модели? Ответ прямой и его стоит знать до разговора, а не во время.

Уезжает всё, что попало в контекст: файлы, которые агент прочитал, вывод команд, который он видел, ваши реплики, содержимое ошибок и трасс, имена переменных и путей. Плюс то, что подставляется автоматически, — правила проекта, описания инструментов. То есть при работе с агентом по закрытому коду за границу процесса уходит именно закрытый код, а не абстрактное «мы спросили ИИ».

Что из этого следует практически:

  • Секреты не должны попадать в контекст. Не потому, что поставщик злоумышленник, а потому, что это второй экземпляр секрета в чужой системе. Файлы с ключами — в исключения агента, вывод команд, печатающих переменные окружения, — не в диалог. И правило: секрет, засветившийся в диалоге, считают утёкшим и меняют.
  • Персональные данные в контексте — это передача персональных данных обработчику со всеми требованиями закона; настоящие данные пользователей в запросы не кладут, для отладки берут обезличенные.
  • У поставщиков разные условия по обучению на ваших данных. У корпоративных тарифов обычно записано, что данные не используются для обучения и удаляются через срок; у бесплатных — часто наоборот. Это первое, что читают перед внедрением, и это же главный аргумент в разговоре с безопасностью.
  • Есть варианты, где данные не выходят из контура: модель в вашем облаке или открытая модель на своём железе. Качество ниже, цена владения выше, и для части команд это единственно допустимый путь.

Подробный разбор с тем, как это обсуждают с безопасностью и что писать в правилах команды, — в разделе «Глубже» статьи про агентов.

Что это значит на практике

Контекст — это рабочая память модели, и качество ответа во многом определяется тем, что вы в неё положили. Продукт-инженер думает не «какой вопрос задать», а «что должно быть перед глазами у модели, чтобы она ответила хорошо»: нужные файлы, факты, правила — и ничего лишнего. Управлять контекстом важнее, чем красиво формулировать запрос.

Дополнительно: при первом чтении можно пропустить

Глубже: когда контекст кончился: сжатие, перезапуск с резюме и его признакирасширенное

«Начинайте новый диалог» это совет, а не механика. Что происходит, когда история перестаёт помещаться, зависит от инструмента, и полезно знать три вещи.

Сжатие. Агенты для кода не дают окну переполниться: заранее, при приближении к пределу, они просят модель пересказать историю в короткое резюме (задача, что сделано, какие решения приняты, какие файлы тронуты) и продолжают с ним вместо полной истории. Это сжатие, и оно происходит без вашего участия. После него агент помнит выводы, но не детали: точные формулировки требований, которые вы дали в начале, аргументы отвергнутых вариантов, содержимое файлов, которые он читал.

Признаки, что сжатие уже случилось. Агент переспрашивает то, что вы говорили час назад. Меняется стиль ответов или имена в коде. Он повторно читает файлы, которые уже читал. Забывает ограничение («не трогай миграции»), которое соблюдал. Инструменты обычно сообщают о сжатии строкой в журнале, и её стоит замечать: после неё ключевые ограничения повторяют явно.

Перезапуск с резюме. Надёжнее, чем полагаться на автоматическое сжатие, завершить сессию самому в удобной точке: попросить агента записать резюме в файл (задача, сделано, осталось, решения и почему, известные проблемы), проверить его глазами, и начать новую сессию с этого файла. Резюме, написанное до переполнения, полнее того, что модель напишет в спешке у предела. То же правило для долгих задач: план и заметки живут в файле в репозитории, а не в истории диалога, и переживают любое сжатие.

Как не доходить до предела. Задача на сессию одна и небольшая; вывод команд и содержимое больших файлов не вываливают в диалог целиком, а просят агента посмотреть нужную часть; подзадачи, которые дают много шума (поиск по репозиторию, чтение документации), отдают отдельным субагентам, чей контекст не попадает в основной. Об этом статьи про настройку агента и работу с ним.

Глубже: обучить модель на нашем коде или дать контекстрасширенное

Через неделю работы с агентом каждый спрашивает: а можно обучить модель на нашем репозитории, чтобы она его знала? Ответ почти всегда «не нужно», и понимание причины экономит месяцы.

Дообучение меняет параметры модели на ваших примерах. Оно хорошо учит форме: стиль ответов, формат вывода, жаргон узкой области, поведение, которое трудно описать инструкцией. Оно плохо учит фактам: модель после дообучения на коде не «помнит» ваш репозиторий как справочник, а лишь чуть охотнее пишет в его стиле; конкретные сигнатуры и правила она по-прежнему достраивает. И дообученная модель устаревает в момент следующего коммита, а переобучение стоит денег и требует набора примеров, размеченных людьми.

Контекст решает ту же задачу дешевле и точнее: правила проекта в файле настроек агента, которые он читает каждую сессию; инструменты поиска по коду, которыми он достаёт нужные файлы по требованию; для базы знаний поиск по документам с подмешиванием найденного в промпт (о нём статья про RAG в следующей фазе). Всё это обновляется вместе с репозиторием и не требует ни данных, ни обучения.

Когда дообучение всё же оправдано: тысячи примеров одного формата (разметка обращений по вашей схеме), где нужна маленькая быстрая модель вместо большой с длинной инструкцией; или стиль, который инструкцией не передать. Для «чтобы агент знал наш код» ответ один: контекст.

Коротко

  • Контекст это всё, что модель видит за один вызов; между разговорами памяти нет, у окна есть предел, у ответа свой потолок.
  • Больше контекста не значит лучше: важное разбавляется, растут цена и ошибки; давать релевантное и подтягивать по требованию через инструменты.
  • Когда история не влезает, агент сжимает её в резюме сам; признаки сжатия это переспросы и забытые ограничения; надёжнее перезапуск с резюме в файле и заметки в репозитории.
  • «Обучить на нашем коде» почти всегда не нужно: дообучение учит форме, а не фактам, и устаревает с первым коммитом; факты дают контекстом, правилами и поиском.
  • Окно в 200 тысяч токенов — это 40–50 файлов по 300 строк, а не весь проект; расходуется оно быстрее всего выводом команд, который остаётся в истории.
  • Против «потерянного в середине»: инструкции в начало, главное требование повторить в конце после материалов, материалы подписать, ограничения повторять в длинной сессии.
  • «Память» продуктов — это подстановка текста программой вокруг модели: сохранённые факты не исчезают сами и тоже занимают контекст, поэтому важное держат в файле правил, а не в памяти.
  • В контекст попадает и уезжает к поставщику всё, что агент прочитал: секреты в диалоге считают утёкшими, персональные данные не кладут, условия про обучение на ваших данных читают заранее.
  • Правила проекта и ядро методологии лежат в контексте всегда (тысячи токенов), скилл подгружается по шапке и сам велит читать индекс, а не справочник, память — картотека: индекс в сессии, карточки по требованию.

Что почитать дальше

Контекст можно наполнять не только вручную, но и давать модели доставать нужное самой — через вызов инструментов. А когда модель в цикле сама решает, что достать и что сделать, — это уже агенты.