[LangChain.js LCEL入門] LCELを使ったChainを作る
LangChainを利用したLLMアプリ構築では、LangChain Expression Language(LCEL)の理解が重要です。LCELはLangChainを操作するための表現言語であり、複数ステップのAPI呼び出しを実装する際に役立ちます。LCELの書き方はPythonとJSで異なり、LangChain.jsではRunnableSequenceやpipe()関数を使って処理を記述します。それぞれのメソッドは異なる使い方があり、状況や好みによって選択することができます。LangChain.jsを使用して動作させた結果は正常に文字列データが取得できることが確認されています。
目次
LangChainを利用して LLMアプリを構築する上で、「LangChain Expression Language(LCEL)」の理解は欠かせません。今回はLangChain.jsでLCELを使った処理を作る方法を簡単に紹介します。
LCELとは
LCELはLangChainを利用する際に使う、記法・表現言語(Expression Language)です。ドキュメントの説明によると、この記法で複数ステップのLLM API呼び出しなどを実装することで、次のような恩恵を受けることができます。
- ストリームでのレスポンス(1トークン目の返却)が最適化される
- 並列実行やリトライの最適化
- 入出力の値を制御したり、途中ステップでのデータ処理やログ出力などのハンドリング
RunnableSequenceに配列で投入する
LangChainのPythonとJSでは、LCELの書き方が少し異なります。LangChain.jsの場合、基本的なLCELの書き方は、次のようなコードになります。
app.get('/chat', async c => {
const chat = new FakeListChatModel({
responses: ["I'll callback later.", "You 'console' them!"],
});
const prompt = PromptTemplate.fromTemplate(
`Given the user question below.
Question: {question}
Answer:`
)
const chain = RunnableSequence.from([
prompt,
chat,
new StringOutputParser()
])
const result = await chain.invoke({question: "Who are you?"})
return c.json(result)
})
RunnableSequence.from
に配列でプロンプトや利用するモデル、出力のパーサーを渡します。特徴的なのは、この際「配列に渡すリソースの順序に指定はない」ことです。そのため、この例ではプロンプト・モデル・パーサーの順番に渡していますが、ここに別のChainを差し込んだり、入力値の制御を行う関数を追加することも可能です。
Pipeでつなぐ書き方もできる
もう一つの書き方は、pipe()
関数を使う方法です。こちらはプロンプトに対して、モデルや出力パーサーなどを追加していきます。
app.get('/chat', async c => {
const chat = new FakeListChatModel({
responses: ["I'll callback later.", "You 'console' them!"],
});
const prompt = PromptTemplate.fromTemplate(
`Given the user question below.
Question: {question}
Answer:`
)
const chain = prompt.pipe(chatCloudflare).pipe(new StringOutputParser())
const result = await chain.invoke({question: "Who are you?"})
return c.json(result)
})
どちらの方法が良いとかではなく、状況や好みに応じて使い分けていくことができる様子です。
pipeを使うときは、メソッドチェーン方式でやるべき・・・かも?
pipe形式で実装する場合、次のような複数回にわけて呼び出すパターンのユースケースがでてくると思います。
const chain2 = prompt.pipe(chatCloudflare)
chain2.pipe(new StringOutputParser())
ただ、この実装方法では、後に追加したStringOutputParser
が機能しませんでした。。
{
"lc": 1,
"type": "constructor",
"id": [
"langchain_core",
"messages",
"AIMessage"
],
"kwargs": {
"content": "I am LLaMA, an AI assistant developed by Meta AI that can understand and respond to human input in a conversational manner. I am trained on a massive dataset of text from the internet and can generate human-like responses to a wide range of topics and questions, including the one you just asked me! 😊",
"additional_kwargs": {}
}
}
具体的な理由は、コードを追えていないので不明ですが、LCELの書き方が原則「1行でまとめて書く」書き方の様子なので、その辺りが関係しているのかもしれません。
Cloudflare Workers AIを使って動かしてみる
LCEL(Chain)を実装できましたので、Workers AIモデルを使って動かしてみましょう。
const chatCloudflare = new ChatCloudflareWorkersAI({
model: "@cf/meta/llama-2-7b-chat-int8", // Default value
cloudflareAccountId: c.env.CLOUDFLARE_ACCOUNT_ID,
cloudflareApiToken: c.env.CLOUDFLARE_API_TOKEN,
});
それぞれの回答結果がこちらです。ちゃんと文字列のみを取得できていることがわかります。
{
"sequenceResult": "I am LLaMA, an AI assistant developed by Meta AI that can understand and respond to human input in a conversational manner. I am trained on a massive dataset of text from the internet and can generate human-like responses to a wide range of topics and questions, including the one you just asked me! 😊",
"pipeResult": "I am LLaMA, an AI assistant developed by Meta AI that can understand and respond to human input in a conversational manner. I am trained on a massive dataset of text from the internet and can generate human-like responses to a wide range of topics and questions, including the one you just asked me! 😊"
}
まとめ
LangChainのLCELは、PythonとJavaScriptで少し書き方が異なります。JavaScript版(LangChain.js)では、「配列またはpipe()
で利用するリソースをつないでいく」と考えていくと良いかもしれません。