Ветвление и комбинирование цепочек

Урок про сложные потоки: параллельные ветки, проброс данных и встраивание своих функций.

RunnableParallel — компонент, который запускает несколько веток на одном входе и собирает их результаты в словарь.

Когда линейной цепочки мало

Часто данные нужно не просто прогнать по прямой, а разветвить: например, на одном входе одновременно посчитать краткое резюме и список ключевых слов. Или пробросить исходный вопрос дальше вместе с найденным контекстом. Для этого в LCEL есть несколько служебных Runnable.

КомпонентНазначение
RunnableParallelнесколько веток параллельно, результат — словарь
RunnablePassthroughпробрасывает вход дальше без изменений
RunnableLambdaвстраивает обычную Python-функцию в цепочку
from langchain_core.runnables import RunnableParallel, RunnablePassthrough

# на одном вопросе: пробросить сам вопрос и подмешать контекст
setup = RunnableParallel(
    question=RunnablePassthrough(),
    context=retriever,   # вернёт релевантные документы
)
# дальше: setup | prompt | model | parser

Идею «параллельных веток на одном входе» можно показать на чистом Python:

def parallel(x, branches):
    return {name: fn(x) for name, fn in branches.items()}

result = parallel("langchain", {
    "length": len,
    "upper": str.upper,
    "first": lambda s: s[0],
})
print(result)

Вывод:

{'length': 9, 'upper': 'LANGCHAIN', 'first': 'l'}

Как работает под капотом

RunnableParallel получает один вход и раздаёт его всем веткам, а их результаты складывает в словарь по ключам. RunnablePassthrough — это тождественная функция: вернуть вход как есть (удобно, когда нужно протащить исходные данные сквозь шаг, который их меняет). RunnableLambda оборачивает вашу функцию так, чтобы она стала полноценным звеном с invoke(). Именно эта тройка делает возможной классическую RAG-сборку, где вопрос идёт в retriever и одновременно сохраняется для промпта.

Частые ошибки

  • Терять исходный ввод. Если шаг преобразует данные, а дальше нужен оригинал — пробрасывайте его через RunnablePassthrough.
  • Путать словарь-результат. После RunnableParallel следующий шаг получает словарь; ключи должны совпасть с плейсхолдерами промпта.
  • Прятать тяжёлую логику в RunnableLambda. Лямбды удобны, но сложную обработку лучше вынести в именованную функцию.

Итог

  • RunnableParallel запускает ветки параллельно и собирает словарь результатов.
  • RunnablePassthrough пробрасывает вход без изменений.
  • RunnableLambda встраивает обычную функцию в цепочку.
  • Эта тройка — основа RAG-сборки «вопрос + контекст → промпт».
Проверьте себя
1. Что делает RunnableParallel?
AЗапускает несколько веток на одном входе и собирает результаты в словарь
BУдаляет историю диалога
CУскоряет одну ветку за счёт GPU
DПарсит JSON
2. Зачем нужен RunnablePassthrough?
AЧтобы обнулить вход
BЧтобы пробросить вход дальше без изменений (например, сохранить исходный вопрос)
CЧтобы вызвать модель дважды
DЧтобы загрузить документы