LangChainで推奨されるLCEL記法の概要を理解する2024-02-01LangChainで推奨されるLCEL記法の概要を理解する記事タイトルとURLをコピーするこんにちは。CLIが好きな福島です。解説イメージ図コンポーネントとはコンポーネントの種類インターフェース同期メソッド非同期メソッドLCELのメリット終わりにはじめに今回は今更かも知れませんが、LangChainで推奨されているLCEL記法に関する概要をまとめます。LangChain固有のChainを簡単に構築するための宣言型の記法となります。特徴的なのが、Unixのパイプ演算子のイメージで「|」を使える点です。https://python.langchain.com/docs/expression_language/具体例LCEL記法の具体例は、以下の通りです。ChatPromptTemplate.from_template("{topic}に関するジョークを言ってください。print(result)上記の実行結果は以下の通りです。ジョークの内容には触れないのですが、要はLLMにアイスクリームのジョークを教えてと質問した回答が出力されます。アイスクリームに関するジョークです。床!床が一番冷たくなる解説ポイントは、以下の箇所です。ここのprompt、chat、StrOutputParser()のことをコンポーネントと呼びます。コンポーネントについては後述します。StrOutputParser()これは以下のように実行することができます。"アイスクリーム"})ここから私のイメージで少し解説します。chainを定義した際は、処理は実行されるのではなく、セットされるイメージです。ドミノで言うと、ドミノを立てている状態です。セット(実行可能な状態に)しているイメージであることがポイントかなと思います。実際にchainの中身をprintすると以下のように表示され、セットされているイメージが伝わるかなと思います。(実際の出力結果に改行を入れてます。template='{topic}に関するジョークを言ってください。last=StrOutputParser()そして、chain.invokeすることでセットしたchainがドミノが倒れるように実行されます。"アイスクリーム"}がpromptに渡さるpromptの出力結果がchatに渡されるchatの出力結果がStrOutputParser()に渡されるStrOutputParser()が出力結果となるとなります。イメージ図chainの実行イメージ図を以下に載せておりますので、理解の一助になれば幸いです。コンポーネントとはLCELを理解する上で重要なキーワードとして、コンポーネントがあります。LCELでは、Unixのパイプ演算子のイメージで「|」を使えると説明しましたが、もちろん何でも「|」で連結することはできません。何が連結できるのかと言うと、コンポーネントになります。コンポーネントの種類コンポーネントには以下の種類があります。https://python.langchain.com/docs/expression_language/interfaceインターフェース各コンポーネントには、共通のインターフェースが用意されております。今のところ私は基本的にinvokeを使い、ストリーミング出力が必要な場合、streamを使うことがほとんどですが、パフォーマンスが求められる場合は、非同期メソッドが必要になってくると思います。応答のチャンクをストリームバックします。入力のリストでチェーンを呼び出します。最終応答に加えて、発生した中間ステップをストリームバックします。私が感じたLCELの1番のメリットは、LangChain内の処理が透明化されたことです。共通のインターフェースによりコンポーネントごとの実行結果を手軽に確認できます。LLMのアプリ開発が楽になる反面、どういった処理が行われているのかブラックボックスとなっており、分かりづらかった印象です。極端な例ですが、ConversationalRetrievalChainというクラスがあります。何をしているか名前から推測できそうですが、どういった処理が行われるのか理解できるでしょうか。question})実際に上記のクラスは、以下の処理を行いますが、実行結果としては生成された回答のみが出力されます。オプションによって処理の過程を見ることができますが、それでも少し分かりづらさがあり、理解をするのも苦労しました。(私の力量不足やRAGに関する知識不足もあったのですが。)会話履歴を踏まえてquestionを言い換えるretrieverから参考情報を取得する参考情報を加えた質問をLLMに行うこれがLCELでは、それぞれの処理をコンポーネント化(分割)し、最終的に「|」を使ってChainを組むことで同様の処理を実現します。以下のコードは前処理をだいぶ省いており、それぞれのコンポーネント(変数)に何が定義されているか分からないかと思いますが、直感的に理解できそうではないでしょうか。question})上記のコードの処理内容は以下の通りです。コンポーネントは共通のインターフェースを持っているため、chainの定義を少し変更することで各コンポーネントの実行結果を手軽に確認できます。question})少し理解が深められたら、RunnablePassthrough.assignを使うことで最終的な実行結果に各コンポーネントの実行結果を含めることもできます。この辺りはまた別のブログでまとめたいと思います。https://python.langchain.com/docs/expression_language/how_to/passthrough終わりに今回は、LangChainにおけるLCEL記法の概要についてまとめました。LCELも理解するまでに時間は必要ですが、理解することでだいぶ使いやすく感じると思います。どなたかのお役に立てれば幸いです。2009-引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。