Гайд · TNWS AI

Как показывать ответ нейросети потоком в Gradio ChatInterface

4 мин—

Показываем частичный ответ LLM без его перезаписи: генератор yield, накопление текста и тест остановки.

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

Русскоязычный вопрос: «как сделать потоковый ответ llm в gradio chatinterface». Подходит для Gradio ChatInterface с Python-генератором и для модельного API, который действительно возвращает части текста. API и версия модели проверяются на вашей инсталляции отдельно; пример с фрагментами позволяет сначала проверить интерфейс вообще без доступа к весам. Документация Gradio проверена 20 сентября 2026 года.

Что подтверждено производителем

Руководство Gradio показывает fn-генератор с yield: каждый новый yield заменяет текущий видимый текст сообщения. Кнопка Stop во время потоковой генерации останавливает выполнение генератора; это не означает автоматической отмены удалённого запроса на уровне провайдера.

Порядок действий

  1. Сначала подготовьте фиксированный список фрагментов и проверьте видимое обновление интерфейса.
  2. В функции сохраняйте накопленный текст вместо выдачи только текущего токена.
  3. Подключите поток своего модельного API, если он поддерживается выбранным checkpoint.
  4. Нажмите Stop в середине теста и проверьте, что дальнейшие фрагменты больше не появляются.
  5. Проведите отдельный тест завершения удалённого вызова и обработки сетевого разрыва.

Готовый шаблон

Сохраните пример в отдельном файле Python. В коде нет реального секрета или скрытого вызова модели: если подключён внешний API, его адрес и параметры указаны явно. Установите gradio в отдельном окружении; дополнительные пакеты требуются только там, где они импортированы в примере.

import time
import gradio as gr

def stream_reply(message, history):
    answer = "Сначала проверю источник. Ответ: " + message.strip()
    accumulated = ""
    for word in answer.split():
        accumulated += (" " if accumulated else "") + word
        yield accumulated
        time.sleep(0.1)

gr.ChatInterface(stream_reply, stop_btn=True,
                 title="Проверка потока").launch(server_name="127.0.0.1")

Вход и ожидаемый результат

Вход «доставка по будням» даёт последовательные видимые состояния: «Сначала», затем «Сначала проверю», затем полную строку с исходным текстом. Это детерминированная проверка потокового UI, не обещание фактического анализа документа. У настоящей LLM ответ и границы токенов зависят от модели.

Проверка контракта

Стриминг решает проблему ощущения задержки, но сам по себе не делает генерацию быстрее. Измеряйте два времени: до первого осмысленного фрагмента и до полного ответа. Если удалённый API возвращает одно готовое тело, разбивка этого тела на слова лишь имитирует поток после уже законченной генерации. Для реального выигрыша нужен upstream-стрим: перебирайте поступающие delta, добавляйте их к накопленной строке и выдавайте текущее целое значение через yield. Разделяйте измерение транспортной задержки и скорости самой модели.

Работа с данными пользователя

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

Что проверить перед развёртыванием

Кнопка Stop относится к генератору Gradio. При интеграции с внешним inference-сервером проверьте, освобождает ли закрытие генератора его сетевое соединение и фактически ли прекращается работа модели; интерфейсную отмену нельзя считать гарантией уменьшения расходов. Предусмотрите контекстный менеджер потока и закрывайте ресурсы при отмене. Если пользователь начинает новый запрос, не смешивайте накопитель двух запросов в глобальной переменной. Локальная переменная в функции принадлежит отдельному вызову и поэтому безопаснее для такого простого сценария.

Готовый шаблон интеграции: «Для каждого поступившего delta.content добавь непустой фрагмент в buffer; после добавления выдай yield buffer; в finally закрой поток». Отдельно задайте правила модерации: если полный ответ должен пройти проверку до показа, передавать токены сразу нельзя. Компромисс — буферизовать целые предложения или отключить поток для регулируемых ответов. Для короткой классификации из одного слова потоковое UI может только усложнить интерфейс без реального удобства.

Для длинного ответа полезно проверять ещё и корректность Unicode: кириллическое слово может поступить по частям из сетевого потока, а конкатенацию надо выполнять после корректного декодирования транспорта. Учитывайте ситуацию, когда первый фрагмент пустой: не показывайте «готово» до финального события. Снимите скринкаст контрольного теста с остановкой на середине и сравните с серверным журналом. Если удалённый сервер продолжает вычислять после ухода пользователя, ограничивайте расходы на стороне инференса отдельно, не меняя поведение UI наугад.

Критерии приёмки

  1. Последний кадр включает все ранее выведенные слова.
  2. Stop прекращает появление новых кадров в браузере.
  3. Обрыв upstream не показывает ложное завершение ответа.

Типичные ошибки и чего не делать

  • Не принимать UI Stop за доказательство отмены серверного инференса.
  • Не перезаписывать весь ответ последним токеном.
  • Не смешивать буферы двух пользователей через глобальное состояние.

FAQ

Нужен ли async def?

Нет, обычный генератор с yield поддерживается.

Ускоряет ли yield модель?

Нет, он ускоряет показ промежуточного результата.

Что делать с обрывом?

Показать статус ошибки и закрыть upstream-поток.

Официальные источники

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

Нужен ли async def?

Нет, обычный генератор с yield поддерживается.

Ускоряет ли yield модель?

Нет, он ускоряет показ промежуточного результата.

Что делать с обрывом?

Показать статус ошибки и закрыть upstream-поток.

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

Комментарии

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