見出し画像

Octomind社がLangChainをやめた理由

こんにちはChoimirai Companyのサンミンです。

0  はじめに

DifyがLangChainと決別し、独自の「Model Runtime」を採用したのが2024年の1月。

LangChainの導入を慎重に考えるべきだとする声は1年前から出ている。Nvidiaの研究者でありながら、XでもAIやロボット研究について積極的に発信されているJim FanさんはLangChainの導入について、教育現場やすでに定着しているワークフローを作ることには向いているけどそれより洗練されたシステムを作るのであれば、自らフレームワークを設計するのがいい、と。

LangChain is fantastic for education and well-established workflows that work out of the box. But you are better off building your own pipeline for anything beyond. - Jim Fan(Nvidiaの研究者、2023年7月10日)

また、PerplexityのCEOであるAravindさんは、これまでLangChainを使用したことがなく、今後も使用する可能性は低いでしょうと指摘。その具体的な理由は、最適でない点、モデルが急速に変化し、それに迅速に適応する必要があること、デバッグの難しさ、カスタマイズの制約、そして明確なパフォーマンスや抽象化の利点がないことです、と。

We have never and will likely never use LangChain. - Aravind Srinivas(PerplexityのCEO、2023年7月9日)

こうした流れの中で、1年間LangChainを利用してたOctomind社が今年に入って、LangChainを使うことをやめたと発表(2024年6月13日)。その理由が今後LangChainの導入を検討されている方に参考になると思いましたので、GPT-4o miniによる日本語訳をシェアします。

1  英文記事: Why we no longer use LangChain for building our AI agents

2  日本語訳: なぜ私たちはAIエージェントの構築にLangChainを使用しないのか

抽象化が害をもたらすとき:LangChainをプロダクションで使用した教訓と私たちがすべきだったこと

Octomindでは、複数のLLMを使用したAIエージェントを利用し、Playwrightでエンドツーエンドのテストを自動的に作成・修正しています。数ヶ月前までは、LangChainフレームワークを使用していました。

この記事では、LangChainとの苦闘と、その堅苦しい高レベルの抽象化をモジュール式のビルディングブロックに置き換えたことで、私たちのコードベースがどのように簡素化され、チームがより幸せで生産的になったかを共有します。

背景

私たちは2023年初頭からLangChainをプロダクションで12ヶ月以上使用し、2024年にそれを取りやめました。

LangChainは2023年に私たちにとって最良の選択肢のように思えました。印象的なコンポーネントやツールのリストがあり、その人気は急上昇しました。「アイデアから作業コードへ、午後のうちに移行できる」と約束されていました。しかし、私たちの要件がより洗練されるにつれて、LangChainは生産性の源ではなく、摩擦の原因となっていきました。

その堅苦しさが明らかになると、私たちはすぐにLangChainの内部に潜り込み、システムの低レベルの動作を改善しようとしました。しかし、LangChainは多くの詳細を意図的に抽象化しているため、必要な低レベルのコードを書くことがしばしば難しかったり不可能だったりしました。

初期フレームワークの危険性

AIやLLMは急速に変化する分野であり、新しい概念やアイデアが週ごとに登場しています。そのため、LangChainのように複数の新興技術を基にしたフレームワークを設計することは、時の試練に耐える抽象化を作るのが非常に難しいのです。

私がLangChainのようなフレームワークを構築しようとしたとしても、うまくいかなかったでしょう。後から振り返って間違いを指摘するのは簡単ですが、この記事の目的はLangChainのコア開発者や貢献者を不当に批判することではありません。皆が最善を尽くしています。

よく設計された抽象化を作ることは難しいです - 要件がよく理解されていても。しかし、流動的な状態にあるコンポーネント(例えばエージェント)をモデル化する場合、低レベルのビルディングブロックに対してのみ抽象化を使用する方が安全です。

LangChainの抽象化の問題

LangChainは、私たちのシンプルな要件がその使用前提に合致していたときには役立ちました。しかし、その高レベルの抽象化はすぐに私たちのコードを理解しにくくし、保守が難しくなりました。私たちのチームがLangChainを理解し、デバッグするのに費やす時間が機能を構築する時間と同じくらいになったとき、それは良い兆候ではありませんでした。

LangChainの抽象化アプローチの問題は、英語の単語をイタリア語に翻訳するという簡単な例で示すことができます。

こちらはOpenAIパッケージを使用したPythonの例です:

from openai import OpenAI

client = OpenAI(api_key="<your_api_key>")
text = "hello!"
language = "Italian"

messages = [
    {"role": "system", "content": "You are an expert translator"},
    {"role": "user", "content": f"Translate the following from English into {language}"},
    {"role": "user", "content": f"{text}"},
]

response = client.chat.completions.create(model="gpt-4o", messages=messages)
result = response.choices[0].message.content

これは理解しやすいシンプルなコードで、単一のクラスと1つの関数呼び出しが含まれています。残りは標準的なPythonです。

これをLangChainのバージョンと対比してみましょう:

from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate

os.environ["OPENAI_API_KEY"] = "<your_api_key>"
text = "hello!"
language = "Italian"

prompt_template = ChatPromptTemplate.from_messages(
    [("system", "You are an expert translator"),
     ("user", "Translate the following from English into {language}"),
     ("user", "{text}")]
)

parser = StrOutputParser()
chain = prompt_template | model | parser
result = chain.invoke({"language": language, "text": text})

このコードは大まかに同じことをしていますが、類似点はここまでです。

私たちは今、3つのクラスと4つの関数呼び出しを持っています。しかし、最も懸念すべき点は、3つの新しい抽象化が導入されたことです:

  • プロンプトテンプレート:LLMにプロンプトを提供する

  • 出力パーサー:LLMからの出力を処理する

  • チェーン:LangChainの「LCEL構文」がPythonの`|`演算子をオーバーライドする

LangChainが達成したのは、目に見える利点なしにコードの複雑さを増加させただけです。

LangChainが達成したのは、
目に見える利点なしにコードの複雑さを増加させただけです。

このコードは初期段階のプロトタイプには適しているかもしれません。しかし、プロダクションで使用する場合、各コンポーネントは合理的に理解される必要があり、実際の使用条件下で予期せぬ問題を引き起こさないようにする必要があります。与えられたデータ構造に従い、それらの抽象化に基づいてアプリケーションを設計しなければなりません。

もう一つの抽象化の比較をPythonで見てみましょう。今回はAPIからJSONを取得する場合です。

組み込みのhttpパッケージを使用すると:

import http.client
import json

conn = http.client.HTTPSConnection("api.example.com")
conn.request("GET", "/data")
response = conn.getresponse()
data = json.loads(response.read().decode())
conn.close()

requestsパッケージを使用すると:

import requests

response = requests.get("https://api.example.com/data")
data = response.json()

勝者は明らかです。これが良い抽象化の感触です。

確かに、これらは些細な例です。しかし、私の言いたいことは、良い抽象化はコードを簡素化し、それを理解するために必要な認知負荷を軽減するということです。

LangChainは、詳細を隠すことで少ないコードで多くのことを実現しようとします。しかし、これがシンプルさや柔軟性のコストである場合、抽象化はその価値を失います。

LangChainは他の抽象化の上に抽象化を使用する傾向があるため、APIを正しく使用するためにネストされた抽象化の観点で考えなければならないことがよくあります。これは、巨大なスタックトレースを理解し、あなたが書いていない内部フレームワークコードをデバッグすることにつながり、新機能の実装を妨げます。

LangChainが私たちの開発チームに与えた影響

私たちのアプリケーションは、テストケースの発見、Playwrightテストの生成、自動修正など、さまざまなタスクを実行するためにAIエージェントを多く使用しています。

シーケンシャルな単一エージェントのアーキテクチャから、より複雑なものに移行したいとき、LangChainは制約要因となりました。例えば、サブエージェントを生成し、元のエージェントと相互作用させることや、複数の専門エージェントが互いに相互作用することです。

別の例では、ビジネスロジックやLLMからの出力に基づいて、エージェントがアクセスできるツールの可用性を動的に変更する必要がありました。しかし、LangChainはエージェントの状態を外部から観察する方法を提供していないため、私たちはLangChainエージェントの限られた機能に合わせて実装の範囲を縮小することになりました。

LangChainを取り除いた後、私たちは要件をLangChainに適した解決策に翻訳する必要がなくなりました。単にコードを書くことができるようになったのです。

では、LangChainでない場合、どのフレームワークを使用すべきでしょうか?もしかしたら、フレームワーク自体が不要かもしれません。

AIアプリケーションを構築するためにフレームワークは必要ですか?

LangChainは初期段階でLLMの機能を提供してくれたため、私たちはアプリケーションの構築に集中できました。しかし、振り返ってみると、フレームワークなしで長期的に考えた方が良かったでしょう。

LangChainの長いコンポーネントリストは、LLMを活用したアプリケーションの構築が複雑であるという印象を与えます。しかし、ほとんどのアプリケーションが必要とするコアコンポーネントは通常次の通りです:

  • LLM通信のためのクライアント

  • 関数呼び出しのための関数/ツール

  • RAG用のベクターデータベース

  • トレースや評価などのための可観測性プラットフォーム

残りは、これらのコンポーネントを補助するもの(例えば、ベクターデータベース用のチャンク化や埋め込み)や、ファイル管理やアプリケーション状態のデータ永続化やキャッシングなどの通常のアプリケーションタスクです。

フレームワークなしでAI開発の旅を始めると、独自のツールボックスを整えるのに時間がかかり、初期の学習や調査が必要になります。しかし、これは十分に価値のある投資であり、あなたとあなたのアプリケーションの未来にとって良い時間の使い方です。なぜなら、あなたが活動する分野の基本を学んでいるからです。

ほとんどの場合、LLMの使用はシンプルで明確です。主にシーケンシャルなコードを書き、プロンプトを反復し、出力の質と予測可能性を向上させることになります。大多数のタスクはシンプルなコードと比較的小さな外部パッケージのコレクションで達成できます。

エージェントを使用する場合でも、ビジネスロジックに基づいてエージェントの状態や応答を処理するための予め決められたシーケンシャルフローでのシンプルなエージェント間通信を超えることはあまりないでしょう。これを実装するためにフレームワークは必要ありません。

エージェントの分野は急速に進化しており、興味深いユースケースが増えていますが、エージェントの使用パターンが確立されるまで、今のところシンプルな方法を保つことをお勧めします。

ビルディングブロックを使って迅速かつスリムに

生産に不適切なコードを出荷していないと仮定すれば、チームが革新し、反復する速度が成功の最も重要な指標です。AI分野の多くの開発は、実験やプロトタイピングによって推進されています。

しかし、フレームワークは通常、確立された使用パターンに基づいて構造を強制するために設計されています。これは、LLMを活用したアプリケーションにはまだ存在しないものです。新しいアイデアをフレームワーク固有のコードに翻訳しなければならないことは、反復の速度を制限します。

ビルディングブロックアプローチは、慎重に選択された外部パッケージを使ったシンプルな低レベルのコードを好み、アーキテクチャをスリムに保つことで、開発者が解決しようとしている問題に集中できるようにします。

ビルディングブロックとは、包括的に理解されていて、変わる可能性が低いシンプルなものです。例えば、ベクターデータベースです。これは、基本的な機能セットを持つ既知のモジュラーコンポーネントであり、簡単に交換・置き換えが可能です。あなたのコードベースはスリムで適応可能である必要があり、学習速度と各反復サイクルから得られる価値を最大化する必要があります。

. . .

まとめ

私たちがLangChainとの課題を思慮深く公平に説明し、フレームワークから完全に離れることが私たちのチームにとってどれほど有益であったかを伝えられたことを願っています。

現在のモジュール式ビルディングブロックを使用した戦略は、最小限の抽象化でより迅速に開発できるようになり、摩擦も少なくなりました。

この記事が気に入ったらサポートをしてみませんか?