スキップしてメイン コンテンツに移動

【LangChainの真髄】LCEL記法マスターガイド - パイプ演算子で書く美しいコード

【LangChainの真髄】LCEL記法マスターガイド - パイプ演算子で書く美しいコード

はじめに

過去の記事で何度か登場している prompt | llm | parser という不思議な書き方。これはPython標準の演算子ではなく、LangChainが独自に定義した LCEL (LangChain Expression Language) という宣言型の記述スタイルです。

「普通のPythonコードで書けばいいじゃん」と思うなかれ。LCELをマスターすると、並列処理やストリーミング、フォールバック(エラー時の代替処理)といった高度な機能を、驚くほど簡潔かつ宣言的に実装できるようになります。

LCEL (LangChain Expression Language) とは

Concept

チェーンコンポーネントを Unixパイプライン風に `|` 演算子でつなぎ合わせ、データの流れを直感的に記述するためのシンタックスシュガーです。

従来(v0.1以前)の LLMChain クラスなどは隠蔽され、現在のLangChainでは、ほぼ全てのコンポーネントが Runnable プロトコルを実装しており、このLCEL記法に対応しています。

Unixパイプのように書く

基本形は以下です。

chain = prompt | llm | output_parser
  1. 辞書を入力として受け取る。
  2. prompt がそれをPromptValue(メッセージオブジェクト)に変換。
  3. llm がそれを受け取り、ChatMessageを返す。
  4. 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になれると、以前の冗長な書き方には戻れなくなります。コード量が減り、可読性が上がり、機能も増える。使わない手はありません。

Debug

チェーンが長くなるとデバッグが難しくなりますが、次回のテーマである「LangSmith」を使えば、チェーン内部のデータの流れを完全に可視化できます。

次回は、ブラックボックスになりがちなLLMアプリの挙動を追跡・評価するための開発者ツール「LangSmith」について紹介します。

このブログ記事はAIを利用して自動生成されました。

コメント