Главная
» Домены
»
Как исправить потерю памяти агента LangChain в длинных диалогах
Как исправить потерю памяти агента LangChain в длинных диалогах
Если агент LangChain забывает детали во время длинного диалога, сначала исправьте архитектуру, прежде чем увеличивать контекстное окно модели. В текущих агентах в стиле LangChain v1 непрерывность диалога строится на двух отдельных уровнях: чекпоинтер для краткосрочного состояния с привязкой к потоку и хранилище для долгосрочной информации, которая должна сохраняться между потоками. Для длинных диалогов возникает третья проблема: управление контекстом, обычно включающее обрезку или суммаризацию старых сообщений, чтобы они не перегружали модель.
Это руководство следует официальной документации LangChain, проверенной 11 сентября 2026 года. Текущая документация рекомендует использовать langchain.agents.create_agent для новых агентов и описывает персистентность LangGraph как базовую систему памяти. Старые примеры на основе ConversationChain, ConversationBufferMemory или initialize_agent могут все еще встречаться в устаревших материалах, но руководство по миграции LangChain v1 перенесло устаревшие цепочки и другую deprecated-функциональность в пакет langchain-classic. См. официальное руководство по миграции LangChain v1.
Иллюстрация, созданная ИИ: Симптом прост: факт был предоставлен ранее, но более поздний ответ больше его не использует. Иллюстрация является концептуальной, а не снимком интерфейса LangChain.
Что на самом деле означает «потеря памяти» в LangChain
Прежде чем менять код, разделите три проблемы, которые часто выглядят одинаково с точки зрения пользователя.
Симптом
Вероятная причина
Правильный уровень для исправления
Агент забывает информацию после перезапуска сервера
Состояние хранилось только в оперативной памяти процесса
Постоянный чекпоинтер или хранилище
Агент забывает информацию между двумя запросами в одном чате
Отсутствие чекпоинтера или использование разных thread_id
Персистентность потока
Агент помнит ранние реплики в хранилище, но перестает использовать их в очень длинных чатах
Контекст модели стал слишком большим или шумным
Суммаризация, обрезка, поиск
Агент помнит предпочтение в одном чате, но не в новом
Факт существует только в состоянии потока
Долгосрочное хранилище
Документация LangChain по краткосрочной памяти определяет краткосрочную память как состояние в рамках одного потока. Документация по долгосрочной памяти определяет долгосрочную память как информацию, которая сохраняется между различными диалогами и сессиями.
Иллюстрация, созданная ИИ: Думайте о краткосрочной памяти как о состоянии одного потока диалога. Текущий LangChain реализует эту непрерывность через чекпоинтер, а не через устаревшие классы памяти, часто показываемые в старых руководствах.
Что вам нужно перед началом
Вам нужно текущее приложение LangChain/LangGraph, интеграция с моделью и место для сохранения состояния. Для локального эксперимента достаточно InMemorySaver. Для продакшена используйте чекпоинтер с базой данных. Официальная документация LangChain показывает использование PostgreSQL через отдельный пакет langgraph-checkpoint-postgres.
Четко разделяйте четыре идентификатора:
ID диалога или чата: идентификатор, который ваше приложение предоставляет пользователям.
thread_id: ключ персистентности LangGraph, используемый для возобновления состояния одного потока.
ID пользователя: постоянная идентичность, используемая для пространства имен долгосрочных воспоминаний.
Ключ памяти: ключ для одного постоянного элемента внутри пространства имен хранилища.
Они не должны автоматически быть одним и тем же значением. У одного пользователя может быть много потоков, а один поток может содержать много фактов.
Шаг 1: Воспроизведите сбой с помощью теста из двух запросов
Начните с минимально возможного теста. Попросите агента запомнить уникальную деталь, затем вызовите его снова и спросите об этой детали. Не тестируйте память одним вызовом invoke(), потому что модель видит все в этом одном запросе, даже если персистентность нарушена.
config = {"configurable": {"thread_id": "debug-thread-001"}}
agent.invoke(
{"messages": [{"role": "user", "content": "Запомни, что кодовое имя моего проекта — Juniper."}]},
config,
)
result = agent.invoke(
{"messages": [{"role": "user", "content": "Какое кодовое имя у моего проекта?"}]},
config,
)
Если второй запрос забывает «Juniper», проверьте конфигурацию чекпоинтера и фактический thread_id, прежде чем менять промпты.
Шаг 2: Добавьте чекпоинтер для памяти в рамках одного потока
Чекпоинтер сохраняет снимки состояния графа агента. LangGraph использует его для краткосрочной памяти, восстановления после прерываний, процессов с участием человека (human-in-the-loop) и отказоустойчивости. Текущее руководство по персистентности описывает чекпоинтеры как привязанные к потоку и указывает, что приложение получает доступ к состоянию, передавая thread_id. См. официальное руководство по персистентности LangGraph.
InMemorySaver отлично подходит для проверки того, что ваша схема работы с потоками работает, но он хранит чекпоинты в оперативной памяти. LangGraph явно предупреждает, что MemorySaver/InMemorySaver не сохраняются между перезапусками процесса.
Шаг 3: Используйте один и тот же thread_id для одного и того же диалога
Самая распространенная ошибка на уровне приложения — создание нового thread_id при каждом HTTP-запросе. База данных может работать идеально, но каждый запрос начинает новый поток LangGraph.
Например, предположим, что ваш фронтенд имеет ID чата chat_8bf4. Детерминированно отобразите это значение на поток LangGraph и переиспользуйте его для каждого хода в этом чате. Новый чат должен получать новый ID потока.
Иллюстрация, созданная ИИ: Персистентность не снимает ограничение контекстного окна модели. Стабильный поток может содержать больше истории, чем модель должна получать при каждом вызове.
Не используйте один постоянный thread_id для всех чатов, принадлежащих одному пользователю. Это объединит несвязанные диалоги в один поток состояния. Если вы используете PostgreSQL, текущие рекомендации по устранению неполадок LangGraph также указывают, что thread_id должен быть короче 255 символов; UUID или детерминированный хеш безопаснее, чем огромный сериализованный объект.
Шаг 4: Замените хранение в памяти перед выходом в продакшен
Как только тест из двух запросов проходит, протестируйте перезапуск процесса. Сохраните факт, остановите приложение, запустите его снова, а затем запросите факт с тем же ID потока. Если вы все еще используете InMemorySaver, забывание является ожидаемым поведением.
Официальная документация по краткосрочной памяти показывает продакшен-настройку на базе PostgreSQL с использованием PostgresSaver:
from langchain.agents import create_agent
from langgraph.checkpoint.postgres import PostgresSaver
DB_URI = "postgresql://user:password@db-host/app"
with PostgresSaver.from_conn_string(DB_URI) as checkpointer:
checkpointer.setup()
agent = create_agent(
model="your-provider:your-model",
tools=[],
checkpointer=checkpointer,
)
Для настройки пакета, документированной LangChain в настоящее время, см. Краткосрочная память. Не помещайте реальные учетные данные базы данных непосредственно в исходный код; используйте вашу обычную систему управления секретами.
Иллюстрация, созданная ИИ: Один особенно важный пункт — локальное хранилище процесса: чекпоинтер в памяти намеренно теряется после перезапуска, поэтому тесты на перезапуск должны входить в набор тестов памяти.
Шаг 5: Управляйте длинными историями вместо отправки всего навсегда
Контекстное окно — это объем входного и выходного контекста, который модель может обработать за один вызов. Чекпоинтинг может сохранить очень длинный диалог в хранилище, но это не означает, что каждое историческое сообщение должно вечно отправляться обратно в модель.
Руководство LangChain по краткосрочной памяти гласит, что длинные истории могут превышать контекстное окно модели, и что даже модели, способные принять полную историю, могут отвлекаться на устаревший или нерелевантный контент, что приводит к увеличению задержки и стоимости. Документированные стратегии включают обрезку, удаление, суммаризацию или применение пользовательской политики.
Используйте суммаризацию, когда старые детали все еще важны
SummarizationMiddleware — это текущая встроенная опция для замены старой истории компактным резюме при сохранении последних сообщений. Его триггер может основываться на количестве токенов, количестве сообщений или доле контекста модели.
Приведенные выше числа — это пример политики, а не универсальные настройки. Выбирайте пороги после измерения ваших собственных промптов, выводов инструментов, лимитов контекста модели, задержки и качества резюме. См. документацию по встроенному middleware LangChain для текущих поддерживаемых опций триггера и сохранения.
Иллюстрация, созданная ИИ: Тестируйте воспроизведение после достаточного количества ходов, чтобы активировать вашу политику обрезки или суммаризации; короткий чат может скрывать ошибки длинного контекста.
Не обрезайте сообщения инструментов вслепую
Если вы реализуете пользовательское удаление или обрезку, сохраняйте корректную последовательность сообщений. LangChain предупреждает, что многие провайдеры требуют, чтобы сообщение ассистента, содержащее вызовы инструментов, следовало за соответствующими сообщениями с результатами инструментов. Удаление одной половины этой пары может привести к ошибкам провайдера или путанице в поведении модели.
Шаг 6: Переместите постоянные факты в долгосрочное хранилище
Хранилище (store) — это уровень персистентности LangGraph для данных, определяемых приложением, вне состояния графа одного потока. Текущая документация LangChain использует хранилища для информации, которая должна быть доступна между диалогами, такой как предпочтения пользователей, факты или общие знания приложения.
Элементы долгосрочного хранилища — это JSON-документы, организованные по пространству имен и ключу. Практичное пространство имен часто содержит идентификатор пользователя или организации:
Это отличается от сохранения всего транскрипта. Храните информацию, которую ваш продукт намеренно считает постоянной. Если факт является частным или регулируемым, применяйте ваши обычные политики хранения, авторизации, шифрования и удаления, а не предполагайте, что «память агента» освобождена от них.
Иллюстрация, созданная ИИ: Эта иллюстрация использует широкие концептуальные метки, а не буквальные текущие имена API. Для нового кода LangChain v1 используйте различие между чекпоинтером и хранилищем, описанное в тексте и официальной документации.
Используйте хранилище на базе базы данных в продакшене
Официальное руководство по долгосрочной памяти показывает как InMemoryStore, так и PostgresStore, и явно отмечает, что реализация в памяти должна быть заменена хранилищем на базе базы данных для продакшена. Оно также перечисляет интеграции хранилищ помимо PostgreSQL. Используйте бэкенд, который соответствует вашему развертыванию и операционным требованиям, а не выбирайте векторную базу данных только потому, что в названии есть слово «память».
Добавляйте семантический поиск только при необходимости нечеткого воспроизведения
Хранилища LangGraph можно настроить с индексом, чтобы store.search() мог извлекать элементы по семантическому сходству. Это полезно, когда у вас много воспоминаний и вы не знаете точный ключ. Для небольшого набора структурированных предпочтений прямой поиск по пространству имен/ключу часто проще и детерминированнее.
Шаг 7: Сделайте пути чтения и записи памяти явными
Сохранение долгосрочного элемента не гарантирует, что агент будет его использовать. Приложению все еще нужен путь извлечения. Текущие агенты LangChain позволяют инструментам получать доступ к предоставленному хранилищу через ToolRuntime.
from dataclasses import dataclass
from langchain.tools import tool, ToolRuntime
@dataclass
class Context:
user_id: str
@tool
def get_response_style(runtime: ToolRuntime[Context]) -> str:
store = runtime.store
if store is None:
return "No memory store configured"
namespace = ("users", runtime.context.user_id, "preferences")
item = store.get(namespace, "response_style")
return item.value["value"] if item else "default"
Вы также можете создавать динамические промпты или middleware, которые читают состояние и постоянную память перед вызовом модели. Важное правило проектирования состоит в том, что путь извлечения должен быть наблюдаемым и тестируемым. «Информация существует где-то в базе данных» — недостаточно.
Иллюстрация, созданная ИИ: Полезный регрессионный тест запрашивает ранее указанный факт после многих ходов и проверяет, что ответ поступает из предполагаемого уровня памяти, а не из случайно дублированного текста промпта.
Шаг 8: Тестируйте четыре границы памяти отдельно
Надежный набор тестов памяти должен охватывать больше, чем «модель однажды вспомнила мое имя». Используйте как минимум эти четыре случая:
Тест
Ожидаемый результат
Два вызова, один ID потока
Информация с привязкой к потоку доступна
Два вызова, разные ID потоков
Краткосрочная история потока не утекает
Перезапуск приложения, тот же ID потока с постоянным чекпоинтером
Состояние потока может возобновиться
Новый поток, тот же пользователь с долгосрочным хранилищем
Могут быть воспроизведены только намеренно сохраненные постоянные факты
Затем добавьте тест длинного диалога, превышающий ваш порог суммаризации. Убедитесь, что важные постоянные факты сохраняются, последние последовательности вызовов инструментов остаются корректными, а размер промпта остается в пределах вашего целевого бюджета.
Иллюстрация, созданная ИИ: Относитесь к этому как к концептуальному чек-листу QA. Текущая архитектура LangChain v1 должна проверяться с использованием официальных API чекпоинтеров, хранилищ и middleware, а не примеров устаревших классов памяти.
Минимальная продакшен-архитектура
Для многих агентных приложений надежная дизайн выглядит так:
API получает user_id, conversation_id и новое сообщение пользователя.
Приложение отображает conversation_id на стабильный thread_id LangGraph.
Постоянный чекпоинтер восстанавливает состояние потока.
Долгосрочное хранилище извлекает только постоянные факты пользователя или приложения, необходимые для запроса.
Суммаризация или обрезка удерживают историю, видимую моделью, в пределах измеренного бюджета контекста.
Агент выполняет инструменты и модель.
Чекпоинтер фиксирует обновленное состояние потока.
Только одобренные факты записываются в долгосрочное хранилище.
Если вы развертываете через LangGraph Agent Server, текущее руководство по персистентности говорит, что сервер автоматически обрабатывает инфраструктуру персистентности, поэтому не дублируйте этот уровень, не проверив модель развертывания.
Частые ошибки, из-за которых память кажется сломанной
Генерация нового thread_id для каждого запроса
Это создает новое состояние диалога каждый ход. Логируйте ID потока рядом с ID чата вашего приложения и проверяйте переиспользование.
Использование InMemorySaver в многорабочем или перезапускаемом сервисе
Состояние в локальной оперативной памяти исчезает вместе с процессом и может не разделяться между воркерами. Используйте постоянный бэкенд для непрерывности в продакшене.
Предположение, что чекпоинтер решает проблему контекстного окна
Чекпоинтер сохраняет состояние; он не гарантирует, что постоянно растущий транскрипт полезен для модели. Добавьте явную политику управления контекстом.
Помещение каждого исторического факта в промпт
Больше контекста не означает автоматически лучший контекст. Извлекайте информацию, релевантную текущему ходу, и отдельно сохраняйте недавнюю непрерывность диалога.
Отношение к резюме как к идеальной базе данных
Резюме — это сжатые представления, сгенерированные моделью. Если факт должен быть точным — идентификатор счета, контрактное ограничение, одобренное пользователем предпочтение или состояние рабочего процесса — храните его как структурированные данные, а не надейтесь, что он переживет повторную суммаризацию.
Смешивание краткосрочных и долгосрочных областей
История потока не должна молча становиться глобальным профилем пользователя. И наоборот, предпочтение пользователя, предназначенное для сопровождения пользователя между чатами, не должно жить только в одном потоке.
Копирование руководств по памяти до v1 без проверки импортов
Если пример начинается с устаревших цепочек или старых классов памяти, сравните его с текущим руководством по миграции v1 и документации по памяти перед использованием в новом приложении.
Чек-лист отладки
Убедитесь, что агент был создан с чекпоинтером.
Логируйте и сравнивайте thread_id между последовательными запросами.
Проверяйте сохраненное состояние потока, прежде чем обвинять модель.
Перезапустите процесс и повторите тест с тем же потоком.
Замените InMemorySaver на постоянный чекпоинтер для продакшена.
Измеряйте рост сообщений/токенов в длинных чатах.
Включите суммаризацию или обрезку до того, как история станет чрезмерной.
Сохраняйте корректными последовательности вызовов/результатов инструментов при удалении сообщений.
Перемещайте факты между потоками в долгосрочное хранилище с пространством имен.
Тестируйте новый поток для того же пользователя, чтобы проверить намеренное долгосрочное воспроизведение.
Тестируйте другого пользователя, чтобы проверить изоляцию памяти.
Отслеживайте, какие элементы памяти были извлечены для каждого ответа.
Итог
Потеря памяти агента LangChain редко решается одним большим контекстным окном. Сначала сделайте состояние потока постоянным с помощью чекпоинтера и стабильного thread_id. Затем контролируйте длинные истории с помощью обрезки или SummarizationMiddleware. Наконец, поместите факты, которые должны переживать диалоги, в долгосрочное хранилище с пространством имен и извлекайте их намеренно.
Это разделение дает вам нечто гораздо более полезное, чем «память»: систему, которую можно перезапускать, масштабировать, тестировать, аудировать и анализировать, когда пользователь спрашивает: «Почему агент забыл?»