Стриминг и callbacks
Урок о том, как отдавать ответ по частям и наблюдать за работой цепочки.
Стриминг — выдача ответа модели по мере генерации, токен за токеном, а не целиком в конце.
Зачем стриминг
Длинный ответ модель генерирует несколько секунд. Если ждать его целиком, пользователь смотрит на пустой экран. Стриминг отдаёт текст по кусочкам сразу, как в чат-интерфейсах: воспринимаемая скорость растёт, даже если общее время то же. В LCEL любой Runnable поддерживает stream() без дополнительного кода.
for chunk in chain.stream({"term": "индекс"}):
print(chunk, end="", flush=True)Идею «выдавать по частям» моделирует генератор в чистом Python:
def stream_words(text):
for word in text.split():
yield word + " "
for piece in stream_words("ответ приходит по частям"):
print(piece, end="")
print()Вывод:
ответ приходит по частям
Callbacks: наблюдаемость
Callbacks — это хуки на события выполнения: начало и конец работы цепочки, вызов модели, использование инструмента, поступление токена. Через них логируют, считают токены и стоимость, отправляют события в систему трейсинга.
from langchain_core.callbacks import BaseCallbackHandler
class TokenCounter(BaseCallbackHandler):
def __init__(self):
self.tokens = 0
def on_llm_new_token(self, token, **kwargs):
self.tokens += 1
counter = TokenCounter()
chain.invoke({"term": "индекс"}, config={"callbacks": [counter]})
print(counter.tokens)Как работает под капотом
Стриминг возможен потому, что API провайдера может отдавать ответ частями (chunked), а LCEL прокидывает эти куски сквозь всю цепочку, объединяя по пути. Callbacks работают как система событий: на каждом значимом шаге исполнитель вызывает соответствующий метод у зарегистрированных обработчиков (on_llm_start, on_llm_new_token, on_tool_end и т.д.). Это неинвазивный способ добавить логирование и метрики, не переписывая саму цепочку.
Частые ошибки
- Стримить туда, где собирается весь ответ. Если на выходе стоит парсер, ждущий полный JSON, стриминг по токенам теряет смысл.
- Тяжёлая логика в callback. Хуки вызываются часто (на каждый токен) — не делайте в них дорогих операций.
- Считать стриминг ускорением модели. Он улучшает воспринимаемую скорость, а не реальное время генерации.
Итог
- Стриминг отдаёт ответ по токенам, улучшая воспринимаемую скорость.
- Любой LCEL-Runnable поддерживает
stream()из коробки. - Callbacks — хуки на события для логирования, подсчёта токенов и трейсинга.
- В callback не кладут тяжёлую логику: они вызываются очень часто.