В руководстве для разработчиков содержится предупреждение о том, что удержание транзакции базы данных открытой на протяжении всего времени ИИ-чата может привести к искажению ответов и перегрузке лежащей в основе СУБД. В заметке, предназначенной для команд, создающих инструменты на базе LLM, говорится, что такую практику «не следует использовать», и предлагаются четыре альтернативных краткосрочных паттерна обеспечения согласованности.

Почему это предупреждение важно

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

Что приводит к длительным транзакциям

  • Многошаговый промптинг — LLM обычно генерируют несколько промптов, прежде чем пользователь увидит ответ.
  • Вызовы инструментов, обращающиеся к базе данных — каждый ход может вызывать хранимую процедуру, SELECT или UPDATE.
  • Неконтролируемая область видимости транзакции — разработчики иногда оборачивают весь чат в блок BEGIN…COMMIT, полагая, что это гарантирует согласованность.

Когда чат затягивается, движок БД должен сохранять исходные версии строк, чтобы транзакция видела стабильное представление. Эти версии занимают место в tempdb, потребляя пространство и ресурсы ввода-вывода. Блокировки, удерживаемые в течение того же периода, препятствуют параллельной записи, а простаивающее соединение может исчерпать пул, заставляя новых вызывающих ждать свободного слота.

Четыре краткосрочных паттерна

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

  1. Операторы в реальном времени (Live statements) — каждый вызов выполняется с уровнем изоляции по умолчанию, видя только те данные, которые были зафиксированы (committed) на момент выполнения. Это самая простая модель; вызывающая сторона принимает тот факт, что данные могли измениться со времени предыдущего шага.
  2. Ограниченные транзакции (Bounded transactions) — разработчик группирует несколько операторов внутри одной короткой транзакции, которая завершается до следующего шага LLM. Это гарантирует атомарность для данной группы без затягивания процесса за пределы вызова инструмента.
  3. Чтение снимков (Snapshot reads) — операция начинается с определенной метки времени снимка (snapshot timestamp), что обеспечивает стабильное представление базы данных на время вызова. Все операции чтения внутри вызова видят одни и те же данные, даже если происходят параллельные записи.
  4. Материализованные отчеты (Materialized reports) — инструмент считывает данные из предварительно сгенерированного, версионного набора результатов, который отражает состояние базы данных в определенный момент времени. Пагинация или дальнейшие вычисления затем выполняются на этом «замороженном» наборе данных.

В SQL Server проверьте, активен ли параметр READ_COMMITTED_SNAPSHOT. Не полагайтесь на то, что название говорит само за себя.

Практические правила для приложений на базе LLM

  • Группируйте необходимое — если вопрос требует получения множества значений, вычислите их за один вызов инструмента, а не отправляйте отдельные запросы, каждый из которых открывает новую транзакцию.
  • Детерминированная пагинация — при представлении результатов на разных страницах используйте стабильный ключ сортировки, курсор или материализованный набор результатов. Никогда не держите транзакцию открытой, пока пользователь прокручивает страницу.
  • Возвращайте подтверждающие данные — вместе с данными включайте метаданные, которые явно описывают модель согласованности: класс согласованности, время начала снимка, точку отсечения отчета, актуальность данных, количество строк, идентификатор базы данных и trace ID.
  • Стресс-тестируйте на конкурентность — имитируйте параллельную запись во время генерации промптов LLM и проверяйте, что приложение корректно выполняет повторные попытки или переходит к резервным сценариям.

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