[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()で利用するリソースをつないでいく」と考えていくと良いかもしれません。

    参考

    広告ここから
    広告ここまで
    Home
    Search
    Bookmark