0Pricing
AI Engineering Academy · Урок

Сохранение истории чата в Redis и PostgreSQL

Храните историю диалога отдельно с помощью RedisChatMessageHistory и PostgresChatMessageHistory, чтобы память сохранялась после перезапуска приложения и масштабировалась на несколько экземпляров.

«Сохранение истории чата в Redis и PostgreSQL» — бесплатный урок AI Engineering Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения AI Engineering Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс AI Engineering Academy содержит 4 уроков всего.

Зачем сохранять историю чатов во внешнем хранилище?

История разговора в памяти исчезает при перезапуске приложения или его масштабировании на несколько экземпляров. Внешнее сохранение решает три проблемы: история переживает сбои, несколько экземпляров сервера используют одну и ту же историю, а прошлые сеансы можно загружать для вернувшихся пользователей. Redis и PostgreSQL — два наиболее популярных хранилища для этой задачи.

Интерфейс истории сообщений чата LangChain

LangChain определяет интерфейс BaseChatMessageHistory с двумя методами: add_message(message) и messages (свойство). Любое хранилище, реализующее этот интерфейс, можно подключить как память для любой цепочки LangChain. Реализации для Redis и PostgreSQL соответствуют этому интерфейсу.

from langchain_community.chat_message_histories import RedisChatMessageHistory

# Each unique session_id gets its own history namespace
history = RedisChatMessageHistory(
    session_id='user:alice:session:42',
    url='redis://localhost:6379'
)

# Add messages
history.add_user_message('What is RAG?')
history.add_ai_message('RAG stands for Retrieval-Augmented Generation...')

# Retrieve stored messages
for msg in history.messages:
    print(msg.type, ':', msg.content[:50])

Подключение истории Redis к цепочке

Оберните цепочку в RunnableWithMessageHistory и передайте фабричную функцию, которая возвращает подходящий объект истории для каждого сеанса. LangChain автоматически загружает историю перед каждым вызовом и сохраняет новые сообщения после каждого ответа.

from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_community.chat_message_histories import RedisChatMessageHistory
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder

llm = ChatOpenAI(model='gpt-4o-mini')
prompt = ChatPromptTemplate.from_messages([
    ('system', 'You are a helpful assistant.'),
    MessagesPlaceholder(variable_name='history'),
    ('human', '{input}')
])

chain = prompt | llm

chain_with_history = RunnableWithMessageHistory(
    chain,
    lambda session_id: RedisChatMessageHistory(
        session_id=session_id,
        url='redis://localhost:6379'
    ),
    input_messages_key='input',
    history_messages_key='history'
)

Вызов цепочки с идентификатором сеанса

При вызове цепочки, обёрнутой историей сообщений, передайте идентификатор сеанса в словаре config внутри configurable. LangChain автоматически направляет каждый вызов в правильное хранилище истории, поэтому у Alice и Bob полностью независимые истории разговоров.

# First turn for session alice-001
response = chain_with_history.invoke(
    {'input': 'My name is Alice.'},
    config={'configurable': {'session_id': 'alice-001'}}
)
print(response.content)

# Second turn — the chain remembers Alice's name from Redis
response = chain_with_history.invoke(
    {'input': 'What is my name?'},
    config={'configurable': {'session_id': 'alice-001'}}
)
print(response.content)  # Should say 'Your name is Alice.'

Проектирование ключей Redis для истории чата

Redis хранит сообщения каждого сеанса в списке, доступном по ключу, сформированному на основе идентификатора сеанса. Используйте структурированную схему ключей, например chat:<app>:<user_id>:<session_id>, чтобы избежать конфликтов ключей между приложениями и обеспечить эффективный поиск по пользователю. Устанавливайте TTL для ключей старых сеансов, чтобы хранилище не росло без ограничений.

import redis

r = redis.Redis.from_url('redis://localhost:6379')

# List all chat sessions for user alice
keys = r.keys('message_store:alice-*')
print('Active sessions:', len(keys))

# Set a 30-day TTL on a session to auto-expire old history
for key in keys:
    r.expire(key, 60 * 60 * 24 * 30)

Проектирование таблицы истории чата в PostgreSQL

Для хранения данных в SQL создайте таблицу со столбцами для идентификатора сеанса, роли сообщения, содержимого и временной метки. PostgreSQL идеально подходит, если историю чата необходимо запрашивать, проверять или объединять с данными учётных записей пользователей. Пакет langchain_postgres предоставляет готовую реализацию.

-- SQL table for storing chat messages
CREATE TABLE chat_messages (
    id          SERIAL PRIMARY KEY,
    session_id  TEXT NOT NULL,
    role        TEXT NOT NULL,   -- 'human' or 'ai'
    content     TEXT NOT NULL,
    created_at  TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_chat_session ON chat_messages (session_id, created_at);

PostgresChatMessageHistory в LangChain

Класс PostgresChatMessageHistory из langchain_postgres работает так же, как версия для Redis, но хранит сообщения в вашей базе данных PostgreSQL. Передайте строку подключения и идентификатор сеанса — класс автоматически создаст таблицу и сериализует сообщения.

from langchain_postgres import PostgresChatMessageHistory
import psycopg

CONNECTION_STRING = 'postgresql://user:pass@localhost:5432/mydb'

# Create history object for a session
history = PostgresChatMessageHistory(
    table_name='chat_messages',
    session_id='alice-001',
    connection=psycopg.connect(CONNECTION_STRING)
)

history.add_user_message('Tell me about embeddings.')
history.add_ai_message('Embeddings are dense vector representations...')

print(f'Stored {len(history.messages)} messages')

Загрузка предыдущих сеансов для вернувшихся пользователей

Когда пользователь возвращается в ваше приложение после его закрытия, вы можете восстановить последний сеанс, загрузив историю из Redis или PostgreSQL. Покажите пользователю краткое описание того, на чём он остановился, или незаметно продолжите разговор, добавив сохранённую историю в следующий запрос.

def get_or_create_history(user_id: str, session_id: str):
    history = RedisChatMessageHistory(
        session_id=f'{user_id}:{session_id}',
        url='redis://localhost:6379'
    )
    if history.messages:
        print(f'Resuming session with {len(history.messages)} messages.')
    else:
        print('Starting a new session.')
    return history

Обрезка истории в базе данных

В длительных сеансах могут накопиться тысячи сообщений. Реализуйте стратегию обрезки: удаляйте сообщения старше заданного порога или оставляйте в базе данных только последние N сообщений. Это ускоряет извлечение данных и предотвращает переполнение контекстного окна при загрузке истории.

# Keep only the last 20 messages per session in PostgreSQL
CLEAN_SQL = '''
    DELETE FROM chat_messages
    WHERE session_id = %s
    AND id NOT IN (
        SELECT id FROM chat_messages
        WHERE session_id = %s
        ORDER BY created_at DESC
        LIMIT 20
    )
'''

def trim_session(conn, session_id: str, keep_last: int = 20):
    with conn.cursor() as cur:
        cur.execute(CLEAN_SQL, (session_id, session_id))
    conn.commit()

Выбор между Redis и PostgreSQL

Оба хранилища готовы к использованию в промышленной среде, но обладают разными преимуществами. Redis обеспечивает чтение за доли миллисекунды, поэтому идеально подходит для чатов реального времени, где важнее всего задержка. PostgreSQL поддерживает полноценные SQL-запросы, транзакции ACID и легко интегрируется с существующей базой данных пользователей для аналитики и аудита. Во многих промышленных системах Redis используется как основной кэш, а PostgreSQL — для надёжного архивного хранения.

Безопасность: изоляция историй в многопользовательских системах

В многопользовательских приложениях необходимо гарантировать, что пользователи не смогут получить доступ к историям друг друга. Всегда включайте идентификатор пользователя в ключ сеанса, проверяйте принадлежность идентификатора сеанса аутентифицированному пользователю перед загрузкой истории и никогда не раскрывайте необработанные ключи сеансов в URL или файлах cookie на стороне клиента.

  • Хороший ключ: chat:app1:user:42:session:abc
  • Плохой ключ: session:abc (легко угадать, отсутствует область пользователя)

Быстрая проверка

Проверьте, насколько хорошо вы поняли сохранение истории чата во внешнем хранилище.

Итоги урока

В этом уроке вы узнали, что RedisChatMessageHistory хранит историю чата в Redis с ключами для отдельных сеансов, PostgresChatMessageHistory обеспечивает долговременное хранение с возможностью выполнения SQL-запросов, а RunnableWithMessageHistory подключает любое хранилище к цепочке через фабрику session_id. Далее мы рассмотрим фреймворк ReAct для создания агентов, способных рассуждать и действовать.

Часто задаваемые вопросы

Урок «Сохранение истории чата в Redis и PostgreSQL» бесплатный?

Да — полный текст урока «Сохранение истории чата в Redis и PostgreSQL» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс AI Engineering Academy, подпишись на CoddyKit PRO. Курс AI Engineering Academy содержит 4 уроков всего.

Чему я научусь в уроке «Сохранение истории чата в Redis и PostgreSQL»?

Храните историю диалога отдельно с помощью RedisChatMessageHistory и PostgresChatMessageHistory, чтобы память сохранялась после перезапуска приложения и масштабировалась на несколько экземпляров. Ты практикуешь AI Engineering Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать AI Engineering Academy?

Предыдущий опыт не требуется. AI Engineering Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.

Сколько времени занимает урок «Сохранение истории чата в Redis и PostgreSQL»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке AI Engineering Academy?

Да. Каждый урок AI Engineering Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Зачем бессостояниным LLM нужна внешняя память
  2. Память буфера и окна
  3. Память сводки и усечение с учётом токенов
  4. Сохранение истории чата в Redis и PostgreSQL
← Назад к AI Engineering Academy