この内容は、以下ページをAIで機械翻訳したものです。
エージェントミドルウェア
LangChain は約3年間エージェント抽象化を提供してきました。現在では同じコア抽象化を持つエージェントフレームワークが数百存在します。これらはすべて、元の LangChain エージェントが抱えていた同じ欠点を抱えています。すなわち、必要に応じてコンテキストエンジニアリングを十分に制御できないため、非自明なユースケースでは抽象化から離れざるを得ません。LangChain 1.0 では、この問題を解決すると考える新しいエージェント抽象化(Middleware
)を導入します。
コアエージェントの構成要素は非常にシンプルです:
- モデル
- プロンプト
- ツールのリスト
コアエージェントアルゴリズムも同様にシンプルです。ユーザーはまずエージェントに入力メッセージを渡し、エージェントはループ内でツールを呼び出し、AI とツールのメッセージを状態に追加し続け、ツールを呼び出さないと判断した時点で最終的に終了します。
このエージェントは 2022 年 11 月に LangChain に実装され、過去 3 年間で同様の抽象化を持つフレームワークが数百登場しました。
同時に、基本的なエージェント抽象化を立ち上げるのは簡単ですが、実運用に耐える柔軟性を持たせるのは難しいです。
このブログでは以下を取り上げます:
- 本番環境で信頼できる抽象化にするのがなぜ難しいのか
- ここ1年ほどで信頼性を高めるために取った取り組み
- LangChain 1.0 で導入する新しい
Middleware
抽象化――これが最も柔軟で構成可能なエージェント抽象化になると考えています
この抽象化を本番環境に持ち込むのが難しい理由
では、なぜこれらのフレームワークで信頼できるエージェントを構築するのが依然として難しいのでしょうか? 複雑さが一定レベルに達したときに、フレームワークから離れてカスタムコードに切り替える人が多いのはなぜでしょうか?
答えはコンテキストエンジニアリングです。モデルに入力されるコンテキストが出力を決定します。モデル(すなわちエージェント)をより信頼できるものにするためには、モデルに何を入力するかを完全にコントロールしたいのです。シンプルなエージェント状態とループは入門には最適ですが、エージェントの性能限界に挑むにつれて、状態の一部を変更したくなるでしょう。
複雑さが増すにつれて、次のような点をより細かく制御したくなります:
- エージェントの「状態」にメッセージ以外の情報も含めたい
- モデルに渡す内容を正確にコントロールしたい
- 実行するステップの順序を細かく制御したい
信頼性向上に向けた取り組み
過去 2 年間で、コンテキストエンジニアリングをよりサポートできるようエージェント抽象化を改良してきました。実施した主な変更は(大まかな順序です):
- ユーザーがランタイム設定(接続文字列や読み取り専用ユーザー情報など)を指定できるようにした
- 任意の状態スキーマを指定できるようにし、ユーザーまたはエージェントが更新可能にした
- 文字列ではなく関数でプロンプトを返すようにし、動的プロンプトを実現した
- 関数でメッセージリスト全体を返すようにし、モデルへ送るメッセージを完全に制御できるようにした
- 「モデル呼び出し前フック(pre model hook)」を指定できるようにし、状態更新や別ノードへの遷移を実行できるようにした(長い会話の要約などに利用可能)
- 「モデル呼び出し後フック(post model hook)」を指定できるようにし、状態更新や別ノードへの遷移を実行できるようにした(ヒューマン・イン・ザ・ループやガードレールに利用可能)
- 各呼び出しで使用するモデルを返す関数を指定できるようにし、動的なモデル切替やツール呼び出しを実現した
これにより、コンテキストエンジニアリングのカスタマイズ性と制御性が大幅に向上しました。
しかし同時に、エージェントに渡すパラメータが多数になり、相互依存関係も増えて調整が困難になりました。さらに、これらのパラメータの組み合わせや、すぐに試せるオフ・ザ・シェルフ版のバリエーションを提供するのも難しくなっていました。
LangChain 1.0 での取り組み
LangChain 1.0 では、コアエージェントループを変更する手段として Middleware
の概念を導入します。
コアエージェントループは依然として「モデルノード」と「ツールノード」から構成されますが、Middleware によって次のことが指定可能になります:
-
before_model
:モデル呼び出し前に実行され、状態更新や別ノードへの遷移ができる -
after_model
:モデル呼び出し後に実行され、状態更新や別ノードへの遷移ができる -
modify_model_request
:モデル呼び出し前に実行され、そのリクエストに対してツール、プロンプト、メッセージリスト、モデル、モデル設定、出力フォーマット、ツール選択を動的に変更できる
エージェントに複数の Middleware を付与できます。実行順序は Web サーバーのミドルウェアと同様で、モデル呼び出しへ向かう際は順次(before_model
→ modify_model_request
)に、戻る際は逆順(after_model
)に処理されます。
Middleware はカスタム状態スキーマやツールも指定可能です。
開発者がすぐに使えるオフ・ザ・シェルフ版 Middleware を提供するとともに、コミュニティが作成した Middleware の一覧も管理します。以前から開発者は LangGraph エージェントに差し込むノード集合を求めていましたが、これがまさにその実装です。
Middleware は異なるエージェント抽象化を統一する手段にもなります。現在、Supervisor、Swarm、BigTool、DeepAgents、Reflection など、個別の LangGraph エージェントが存在しますが、Middleware を用いればこれらのアーキテクチャをすべて再現できることを確認済みです。
LangChain 1.0α で試す
最新の LangChain 1.0 α リリース(Python と JavaScript)で Middleware を試すことができます。これは LangChain 1.0 の最大の新機能なので、ぜひフィードバックをお願いします。
この α リリースと同時に、内部エージェントで既に使用している 3 つの Middleware 実装も公開します:
-
Human-in-the-loop:
Middleware.after_model
を利用し、ツール呼び出し時にヒューマン・イン・ザ・ループの割り込みを簡単に追加できる -
Summarization:
Middleware.before_model
を利用し、メッセージが一定量を超えたら要約を自動実行する -
Anthropic Prompt Caching:
Middleware.modify_model_request
を利用し、メッセージに特別なプロンプトキャッシュタグを付与する