【LangChainの真髄】LCEL記法マスターガイド - パイプ演算子で書く美しいコード
はじめに
過去の記事で何度か登場している prompt | llm | parser という不思議な書き方。これはPython標準の演算子ではなく、LangChainが独自に定義した
LCEL (LangChain Expression Language) という宣言型の記述スタイルです。
「普通のPythonコードで書けばいいじゃん」と思うなかれ。LCELをマスターすると、並列処理やストリーミング、フォールバック(エラー時の代替処理)といった高度な機能を、驚くほど簡潔かつ宣言的に実装できるようになります。
LCEL (LangChain Expression Language) とは
チェーンコンポーネントを Unixパイプライン風に `|` 演算子でつなぎ合わせ、データの流れを直感的に記述するためのシンタックスシュガーです。
従来(v0.1以前)の LLMChain クラスなどは隠蔽され、現在のLangChainでは、ほぼ全てのコンポーネントが Runnable
プロトコルを実装しており、このLCEL記法に対応しています。
Unixパイプのように書く
基本形は以下です。
chain = prompt | llm | output_parser
- 辞書を入力として受け取る。
promptがそれをPromptValue(メッセージオブジェクト)に変換。llmがそれを受け取り、ChatMessageを返す。output_parserが文字列やJSONに変換して最終出力とする。
データが左から右へと流れていく様が可視化されるため、処理の流れを追うのが非常に楽になります。
Runnableの恩恵(Stream/Batch/Async)
LCELで書かれたチェーンは、自動的に以下の標準メソッドを持ちます。
invoke: 通常実行stream: ストリーミング実行(1文字ずつ返す)batch: 並列バッチ処理(複数の入力を同時に処理)ainvoke: 非同期実行(asyncio対応)
自分で async def を書いてループ処理を作る必要はありません。LangChainが内部で最適化してくれます。
複雑なチェーンの実装例
RAG(検索拡張生成)のような複雑なフローも一目で分かります。
from langchain_core.runnables import RunnablePassthrough
# リトリーバー(検索機)の設定
retriever = vectorstore.as_retriever()
rag_chain = (
{"context": retriever, "question": RunnablePassthrough()}
| prompt
| llm
| parser
)
RunnablePassthrough()
は「入力をそのまま渡す」という意味です。このコードだけで、「質問を検索に回しつつ、結果をプロンプトのcontextに埋め込み、質問自体もquestionに埋め込んで推論する」という複雑な処理が表現されています。
まとめ
LCELになれると、以前の冗長な書き方には戻れなくなります。コード量が減り、可読性が上がり、機能も増える。使わない手はありません。
チェーンが長くなるとデバッグが難しくなりますが、次回のテーマである「LangSmith」を使えば、チェーン内部のデータの流れを完全に可視化できます。
次回は、ブラックボックスになりがちなLLMアプリの挙動を追跡・評価するための開発者ツール「LangSmith」について紹介します。
コメント
コメントを投稿