Гайд · TNWS AI

Как хранить историю диалога через Sessions в OpenAI Agents SDK

7 мин

Практический гайд по Sessions в OpenAI Agents SDK: SQLiteSession, раздельные session ID, ограничение истории, исправление последнего хода и тест изоляции пользователей.

Задача и применимость

Этот гайд решает конкретную задачу: сохранить контекст между отдельными запусками агента, не пересылая вручную весь список сообщений и не смешивая диалоги разных пользователей. Материал рассчитан на разработчика, который уже умеет запускать Python, но хочет получить воспроизводимую интеграцию, а не демонстрацию «ответ пришёл — значит всё готово». Ниже есть рабочий каркас, реалистичный контрольный пример, негативные сценарии и критерии приёмки.

Сведения сверены с официальной документацией 12 сентября 2026 года. В статье намеренно нет неподтверждённых цен, обещаний доступности из конкретной страны и результатов чужих тестов. Такие параметры зависят от аккаунта, региона, модели и даты. Перед production-развёртыванием повторите smoke-тест из своего окружения.

Что подтверждено официально

  1. SQLiteSession("conversation_123") хранит историю конкретной сессии; файловый путь вторым аргументом делает SQLite-хранилище постоянным.
  2. Runner.run(..., session=session) перед вызовом модели получает историю, а после завершения сохраняет новые пользовательские сообщения, ответы и вызовы инструментов.
  3. Сессию нельзя одновременно сочетать в одном run с conversation_id, previous_response_id или auto_previous_response_id.
  4. SessionSettings(limit=N) через RunConfig.session_settings ограничивает число последних извлекаемых элементов, не удаляя остальное из хранилища.
  5. Методы get_items(), add_items(), pop_item() и clear_session() позволяют проверить и обслуживать память явно.

Первоисточник: официальная документация. Это ссылка на интерфейс, использованный в примере, а не на пересказ стороннего блога. Сохраните дату проверки в change log проекта: при обновлении SDK сравнение станет быстрее.

Что подготовить

Нужны Python-окружение, официальный SDK, ключ нужного сервиса в переменной окружения и небольшой тестовый набор. Ключ нельзя вставлять в браузерный JavaScript, мобильное приложение, публичный notebook или репозиторий. Если код выполняется на сервере, выдайте процессу минимально необходимые права и предусмотрите отзыв секрета.

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

Пошаговая настройка

  1. Установите пакет openai-agents, задайте OPENAI_API_KEY в окружении и не помещайте ключ в исходник.
  2. Создайте Agent(name="Support", instructions="Помогай по заказам; не выдумывай номер заказа.").
  3. Сформируйте session ID на сервере из внутренних идентификаторов пользователя и диалога: например u_481:ticket_9032. Не принимайте этот ключ напрямую из URL без проверки владельца.
  4. Для локального прототипа создайте SQLiteSession(session_id, "support_memory.db"); в нескольких процессах выберите Redis/SQLAlchemy из документированных реализаций.
  5. Первый раз вызовите await Runner.run(agent, "Мой заказ A-104 задержан", session=session), второй — await Runner.run(agent, "Какой номер заказа я назвал?", session=session).
  6. Для длинного чата передайте run_config=RunConfig(session_settings=SessionSettings(limit=50)) и отдельно проверьте, что важные сведения не оказались за границей окна.
  7. При исправлении последнего вопроса сначала дважды вызовите await session.pop_item() — удалить ответ и исходный вопрос — затем отправьте исправленный текст.
  8. После завершения тикета используйте await session.clear_session() только по подтверждённому запросу и журналируйте операцию без содержимого сообщений.

Не объединяйте все проверки в один логический флаг. Отдельно фиксируйте транспортный успех, корректность схемы, бизнес-валидацию и качество содержимого. Тогда по журналу видно, сломался ли HTTP, изменился ли SDK, модель выбрала неверное действие или постусловие не выполнено.

Копируемый шаблон

from agents import Agent, Runner, SQLiteSession, RunConfig, SessionSettings

agent = Agent(name="Support", instructions="Отвечай только по данным диалога. Если номера заказа нет, запроси его.")
session = SQLiteSession("u_481:ticket_9032", "support_memory.db")

first = await Runner.run(agent, "Заказ A-104 задержан", session=session)
second = await Runner.run(
    agent, "Какой номер заказа я назвал?", session=session,
    run_config=RunConfig(session_settings=SessionSettings(limit=50)),
)
print(second.final_output)

Переменные модели и провайдера специально вынесены в окружение там, где их доступность может меняться. Подставляйте только модель, которая видна вашему проекту и подходит задаче по официальной карточке. Если пример вызывает внешнее действие, замените обработчик на stub до завершения тестов.

Реалистичный пример входа и ожидаемого результата

Вход: Ход 1: «Заказ A-104 задержан». Ход 2 в новом вызове Runner: «Какой номер заказа я назвал?»

Ожидаемый результат: Во втором ответе присутствует A-104. При той же проверке с новой сессией u_999:ticket_1 агент должен сообщить, что номер не указан, а не раскрыть A-104.

Сохраните этот кейс как regression fixture. Сравнивать весь текст побуквенно обычно не нужно: проверяйте обязательные сущности, типы, порядок побочных эффектов и запретные утверждения. Если результат недетерминирован, выполните несколько прогонов и рассматривайте любое нарушение инварианта как дефект интеграции.

Проверка по уровням

1. Транспорт

Проверьте код ответа, таймаут и идентификатор запроса, если провайдер его возвращает. Ошибки авторизации и неверные параметры не следует повторять с backoff: сначала исправьте конфигурацию. Для временных 429/5xx используйте ограниченное число повторов с jitter и идемпотентностью.

2. Контракт

Убедитесь, что обязательные поля присутствуют и имеют документированные типы. Логируйте только безопасную выжимку: имя операции, модель, длительность, статус и размеры. Не записывайте ключи, полный пользовательский текст, документы или персональные данные «для отладки».

3. Смысл

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

4. Побочные эффекты

Если инструмент меняет данные, сначала валидируйте право пользователя и состояние ресурса, затем используйте идемпотентный ключ. После таймаута перепроверьте фактический статус до повтора. Так сеть не превратит один запрос в две оплаты, две рассылки или два удаления.

Негативные тесты

  1. Удалите ключ из окружения: приложение должно завершиться понятной ошибкой до отправки пользовательских данных.
  2. Укажите несуществующую модель: ошибка не должна превращаться в пустой «успешный» ответ.
  3. Передайте вход без обязательного значения и проверьте, что слой приложения его отклоняет.
  4. Имитируйте таймаут после отправки запроса. Повтор допускается только после проверки идемпотентности.
  5. Подмените тип одного поля в mock-ответе: контрактный тест обязан сработать.
  6. Запустите запрещённый или чужой идентификатор: интеграция не должна выполнять действие только потому, что его предложила модель.

Рабочая приёмка

Минимальная приёмка состоит из журнала теста, сохранённой версии зависимостей и таблицы «вход → инварианты → результат». Для каждой ошибки определите владельца: транспорт обслуживает platform-команда, схему — разработчик интеграции, бизнес-правила — продуктовый сервис, качество — владелец данных. Это предотвращает ситуацию, когда некорректный ответ неделями считают «особенностью нейросети».

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

Чек-лист финальной проверки

  • Использована официальная документация, проверенная 12 сентября 2026 года.
  • Секрет хранится в переменной окружения и не попадает в клиентский код или логи.
  • Модель/провайдер доступны именно в рабочем аккаунте.
  • Обычный пример возвращает обязательные поля и значения.
  • Граничный и запрещённый примеры дают контролируемый результат.
  • Числа, даты, идентификаторы и отрицания сверяются с источником.
  • Повтор запроса ограничен и безопасен для побочных эффектов.
  • Версия SDK зафиксирована, а контрактный тест запускается в CI.
  • Пользователь видит понятную ошибку вместо ложного успеха.

Ограничения

  • SQLite удобен для одного локального приложения, но не является автоматическим решением для нескольких реплик сервиса.
  • Ограничение извлечённой истории может скрыть ранние факты; это надо тестировать на длинных диалогах.
  • Session ID — граница доступа к данным. Ошибка авторизации превращается в утечку чужой переписки.

Кроме перечисленного, результат зависит от выбранной модели и входных данных. Не переносите вывод одного smoke-теста на весь поток. Для чувствительных решений используйте человеко-машинный процесс: модель предлагает или извлекает данные, код проверяет контракт, а уполномоченный сервис или сотрудник подтверждает действие.

FAQ

Как понять, что интеграция действительно работает?

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

Можно ли сразу использовать пример в production?

Нет. Пример показывает подтверждённый интерфейс API, но в production нужны секреты в хранилище, таймауты, ограничение повторов, авторизация, журнал решений и тесты на данных вашего домена.

Почему в статье нет фиксированной цены?

Цены, квоты и доступность меняются. На 12 сентября 2026 года в этом руководстве используются только интерфейсы из официальной документации; стоимость проверяйте непосредственно в кабинете и актуальном прайс-листе перед запуском.

Что делать, если поле ответа отличается?

Сначала зафиксируйте версию SDK и сырой ответ без секретов. Затем сверяйте его с официальной документацией и типами установленной версии. Не маскируйте несовместимость универсальным try/catch, который возвращает пустой результат.

Официальный первоисточник

Частые вопросы

Как понять, что интеграция действительно работает?

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

Можно ли сразу использовать пример в production?

Нет. Пример показывает подтверждённый интерфейс API, но в production нужны секреты в хранилище, таймауты, ограничение повторов, авторизация, журнал решений и тесты на данных вашего домена.

Почему в статье нет фиксированной цены?

Цены, квоты и доступность меняются. На 12 сентября 2026 года в этом руководстве используются только интерфейсы из официальной документации; стоимость проверяйте непосредственно в кабинете и актуальном прайс-листе перед запуском.

Что делать, если поле ответа отличается?

Сначала зафиксируйте версию SDK и сырой ответ без секретов. Затем сверяйте его с официальной документацией и типами установленной версии. Не маскируйте несовместимость универсальным try/catch, который возвращает пустой результат.

Читайте также

Комментарии

Пока тихо. Скажите первое слово