Open3

LangChainことはじめ

KazuKazu

LCEL

LCELはLangChainを利用する際に使う、記法・表現言語(Expression Language)のこと。この記法で複数ステップのLLM API呼び出しなどを実装することで、以下のようなメリットがあります。

  • 並列実行ができるのでレスポンス速度が最適化される
  • 途中の任意の部分の再試行ができる
  • 最終出力までの中間生成物へのアクセスができるためデバッグが容易になる
  • LangSmithを使用してトレースができる

pipe() もしくは RunnableSequence.from([])を使用してチェーンを構築・実行する

LCELとしてpipe()RunnableSequence.from([])の二つの方法があり、この二つに差はないと理解しており好みや状況によって利用しやすい方を採用するのが良いかと思います。個人的にはRunnableSequence.from([])の方が好きです。

プロンプト、モデル、ツール、リトリーバー、パーサーを、pipe()の場合は.で繋いでいったり、 RunnableSequence.from([])の場合は配列の中に格納します。それをユーザープロンプトをインプットとしてinvokeすることでトリガーします。

https://js.langchain.com/docs/expression_language/why

エージェントを使用することでよりカスタマイズされた処理の実行準備・実行をする

LLM、プロンプト、ツールの三つを用意できれば、それらをエージェントに突っ込むことでより複雑な処理だったり外部APIを実行することができます。単なるチェーンではなくエージェント(とツール)を使用することで、より何かに特化した処理を実現することができます。エージェントは、LLM、プロンプト、ツールの三つの引数が必須となっていて、プロンプトからどのツールを使って出力するかを決定する責務があります。なお、エージェントは、どのツールを使うかを決定するだけで、実行する力はありません。

https://js.langchain.com/docs/modules/agents/quick_start

実行するのはAgentExecutorで、以下のようにAgentExecutorを使用することでエージェンを実行します。

ファイル名
const agent = await createOpenAIToolsAgent({
  llm,
  tools,
  prompt,
});

const agentExecutor = new AgentExecutor({
  agent,
  tools,
});

const result = await agentExecutor.invoke({
  input: "what is LangChain?",
});

https://api.js.langchain.com/functions/langchain_agents.createOpenAIToolsAgent.html

KazuKazu

Tools: DynamicToolの挙動についての注意点

Tools

特定の機能や処理を実行するためのコンポーネントです。外部APIを実行したり、何かに特化した処理だったりを行うときにはこのツールを使用します。

DynamicToolの挙動についての注意点

ツールのインスタンスであるDynamicToolの引数として設定できるdescriptionプロパティは、func関数に渡されるデータをプロンプトで定義する際に重要です。つまり、descriptionの定義によってfunc関数に渡されるtextの内容を変えることができます。 ここ分かっていなくてデバッグに時間かかった。

例えば、agentExecutor.invokeinputに「PDFを解釈してくれる機能はある?https://github.com/langchain-ai/langchainjs 」という入力があった場合、

  • descriptionが「このツールは引数でGitHubリポジトリのURLを受け取り...」と定義されている場合は、functextには「https://github.com/langchain-ai/langchainjs 」のみが入ります。

  • descriptionが「このツールは引数でGitHubリポジトリのURLとそのリポジトリに関する質問を受け取り...」と定義されている場合は、「PDFを解釈してくれる機能はある?https://github.com/langchain-ai/langchainjs 」という全文がtextとして入ります。

descriptionのプロンプトによって、変数に何を受け取れるのかを制御できるので自由度が高くてよき!複数のToolを束ねてエージェントを構築するのだけど、Toolの実行順序を制御できるのかは気になったので後で調べてみる。Toolの中でも優先順位があるケースってありそうだよねって思った。

hogeTool.ts
export const hogeTool = async ({
  vectorStore,
  llm,
  name = "hogeTool",
  description = defaultDescription,
  k = 5,
  callbacks = [],
}: {
  vectorStore: PineconeStore;
  llm: ChatOpenAI<ChatOpenAICallOptions>;
  name?: string;
  description?: string;
  k?: number;
  callbacks?: BaseCallbackHandler[];
}) => {
  const hogeTool = new DynamicTool({
    name,
    description,
    func: async (text, runManager) => {
      console.log("text", text);

      // 任意の処理

      return "hoge";
    },
  });

  return hogeTool;
};

https://js.langchain.com/docs/modules/agents/tools/dynamic

KazuKazu

agent vs chain

agentの使用

createOpenAIToolsAgentAgentExecutorを使用するアプローチは、複数のツールやプロセスを組み合わせてエージェントを構成し、そのエージェントを実行することで質問応答やその他のタスクを処理する。この方法は、複雑な処理や複数のステップを組み合わせる場合に適している。

chainの使用

RunnableSequenceを使用するアプローチは、一連の処理をステップバイステップで定義し、それらを連鎖させて実行することで質問応答を行う。この方法は、処理の流れを明確に定義し、各ステップの入出力を細かく制御したい場合に適している。各ステップは、前のステップの出力を次のステップの入力として使用し、この連鎖を通じて最終結果を出す。

どちらの方法も、LLMを使用したシステムを実装するための機能ですが、使用する方法はプロジェクトの要件によって異なると思います。例えば、より柔軟な処理や複数のツールを組み合わせたい場合はagentを、処理の流れを細かく制御したい場合はchainを選択すると良いと思われる。