Методы сохранения и передачи контекста
Когда мы создаём ИИ-агента, важно, чтобы он помнил, о чём вы говорили ранее. Без сохранения контекста каждый запрос будет для него как первый — будто он забывает всё каждые 5 секунд. Это не просто неудобно, это делает агента бесполезным в реальных задачах. Сегодня мы разберём, как сохранять и передавать контекст между запросами — основу «памяти» ИИ-агента в архитектуре MCP.
Вы уже знаете, как устроена базовая структура проекта MCP-сервера на TypeScript. Теперь пришло время научить агента не терять данные между запросами. Мы рассмотрим два основных подхода: файловое хранилище и Redis, покажем, как формируется контекстный объект, и почему выбор метода хранения влияет на стабильность, безопасность и масштабируемость.
Что такое контекст в MCP?
В архитектуре MCP контекст — это структурированный объект, который передаётся в запросе /prompt и содержит всю необходимую информацию для LLM: историю диалога, текущее состояние, метаданные и данные от инструментов. Это не просто «память», а формализованный JSON, который сервер должен корректно обновлять и возвращать.
Пример контекстного объекта:
{
"sessionId": "user_123",
"history": [
{"role": "user", "content": "Создай курс по Python"},
{"role": "assistant", "content": "Какой уровень у аудитории?"}
],
"state": {
"courseTopic": "Python",
"audienceLevel": null
},
"metadata": {
"createdAt": "2026-04-05T10:00:00Z",
"lastActive": "2026-04-05T10:05:00Z"
}
}
Важно: MCP не хранит контекст за вас. Сервер должен сам решить, где и как его сохранять. Клиент присылает
sessionId, а сервер по ней извлекает, обновляет и возвращает контекст.
Метод 1: Файловое хранилище
Файловое хранилище — это простой и понятный способ хранения контекста, особенно на этапе разработки. Мы будем сохранять контекст в .json-файлах, используя sessionId как имя файла.
Пример реализации на TypeScript:
import fs from 'fs';
import path from 'path';
const CONTEXT_DIR = path.join(__dirname, 'contexts');
// Сохранение контекста
function saveContext(sessionId: string, context: any) {
const filePath = path.join(CONTEXT_DIR, `${sessionId}.json`);
fs.writeFileSync(filePath, JSON.stringify(context, null, 2), 'utf-8');
}
// Загрузка контекста
function loadContext(sessionId: string): any {
const filePath = path.join(CONTEXT_DIR, `${sessionId}.json`);
if (fs.existsSync(filePath)) {
const data = fs.readFileSync(filePath, 'utf-8');
return JSON.parse(data);
}
return null; // Новый сеанс
}
✅ Плюсы:
- Простота понимания и отладки
- Не требует внешних зависимостей
- Подходит для прототипирования
❌ Минусы:
- Не масштабируется при высокой нагрузке
- Риск конфликтов при одновременной записи
- Нет автоматической очистки устаревших данных
Совет: Файловое хранилище отлично подходит для локальной разработки, но не для продакшена. В реальных условиях оно может стать узким местом.
Метод 2: Redis — промышленный стандарт
Redis — это in-memory ключ-значение хранилище, идеально подходящее для хранения контекста. Он быстро работает, поддерживает TTL (время жизни ключа), кластеризацию и отлично интегрируется с Docker и облачными платформами.
Установка и подключение:
npm install redis
Пример работы с Redis:
import { createClient } from 'redis';
const client = createClient({
url: 'redis://localhost:6379'
});
await client.connect();
// Сохранение с TTL = 1 час
async function saveContext(sessionId: string, context: any) {
await client.setEx(
`context:${sessionId}`,
3600, // TTL в секундах
JSON.stringify(context)
);
}
// Загрузка
async function loadContext(sessionId: string): Promise<any> {
const data = await client.get(`context:${sessionId}`);
return data ? JSON.parse(data) : null;
}
✅ Плюсы:
- Высокая производительность
- Поддержка TTL — автоматическая очистка
- Масштабируемость и отказоустойчивость
- Совместимость с Docker и облачными средами
❌ Минусы:
- Требует отдельного сервера или контейнера
- Нужно настраивать безопасность и резервное копирование
Особенно актуально в России: Redis часто используется в локальных дата-центрах и частных облаках из-за требований к локализации данных. Это делает его предпочтительным выбором для продакшн-систем в 2026 году.
Сравнение методов
| Критерий | Файловое хранилище | Redis |
|---|---|---|
| Скорость | Средняя | Высокая |
| Масштабируемость | Низкая | Высокая |
| Безопасность | Зависит от файловой системы | Требует настройки (пароли, шифрование) |
| TTL (автоочистка) | Нет | Да |
| Поддержка в Docker | Да | Да |
| Подходит для продакшена | Нет | Да |
Практический пример: сохранение истории диалога
Представим, что наш агент помогает пользователю создать курс. Мы хотим, чтобы он помнил тему и уровень аудитории.
// В обработчике /prompt
app.post('/prompt', async (req, res) => {
const { sessionId, prompt } = req.body;
// Загружаем контекст
let context = await loadContext(sessionId) || {
history: [],
state: {},
metadata: { createdAt: new Date().toISOString() }
};
// Обновляем историю
context.history.push({ role: 'user', content: prompt });
// Обновляем метаданные
context.metadata.lastActive = new Date().toISOString();
// Сохраняем
await saveContext(sessionId, context);
// Передаём в LLM
const response = await callLLM(prompt, context);
context.history.push({ role: 'assistant', content: response });
await saveContext(sessionId, context);
res.json({ response, context });
});
Ошибка новичка: не обновлять контекст после ответа LLM. Без этого агент «забудет» свою последнюю реплику и не сможет поддерживать диалог.
Что дальше?
Мы научились сохранять контекст — но это только начало. В следующей теме мы перейдём к реализации памяти для агента, где добавим асинхронную обработку, работу с историей и метаданными, а также научимся эффективно управлять жизненным циклом данных.
Контекст — это основа, но память — это уже система: она знает, что важно, что можно забыть, и как быстро отвечать, не перегружая LLM. Именно это превращает агента из «бота» в «помощника».
Готовы к следующему шагу? Вперед — к созданию по-настоящему умных ИИ-агентов.