はじめに
LangChain 1.0がリリースされました。
詳細は公式Blogを確認いただくのがいいかと思います。
また、主な変化点は以下のドキュメントにも掲載されています。
ドキュメント含めて大きく変更されており、またαリリースの際よりも組み込みMiddlewareも増えてましたので、個人的に気になる内容をピックアップしてDatabricks Free Edition上で検証していきます。
この回は、Agentsページにある「Dynamic Model (Selection)」です。
名前の通り、動的に利用するモデルを選択する機能です。
これを実際に実装・実行して試してみます。
Dynamic Model Selection(動的なモデルの選択)
LangChainのエージェントにおいて、処理の実行状況等に応じて動的にモデルを切り替えるにはLangChain1.0から追加されたMiddlewareの機能を使います。
実際に処理の長さに応じてモデルを切り替えるサンプルを実行してみます。
まずはノートブックを作成して必要なパッケージをインストール。
TracingのためにMLflowもインストールします。
%pip install -U langchain>=1.0.0 langchain_openai>=1.0.0 mlflow
%restart_python
次に利用するモデルを2種セットアップします。
それぞれ、通常時に使うモデルと、エージェントの実行履歴が長くなってきたときに使うモデルです。
from langchain.chat_models import init_chat_model
import mlflow
mlflow.langchain.autolog()
creds = mlflow.utils.databricks_utils.get_databricks_host_creds()
llm_config = {"api_key": creds.token, "base_url": creds.host + "/serving-endpoints"}
# 2種のモデルを準備
basic_model = init_chat_model("openai:databricks-gpt-oss-20b", **llm_config)
advanced_model = init_chat_model("openai:databricks-gpt-oss-120b", **llm_config)
次がこの記事の重要部分で、モデルを動的に切り替えるためのMiddlewareを準備します。
Middlewareの詳細は以下の公式ドキュメントを確認いただければと思いますが、
ざっくり言えばエージェントの各ノード前後に割り込み処理を追加し、エージェントの挙動をカスタマイズする機能です。
今回はデコレータを使って、LLMのクエリ実行処理に動的なモデル選択をラップします。
エージェントのノード実行が3回より多い場合、advanced_modelで設定したモデルを利用するようにします。
from langchain.agents.middleware import wrap_model_call, ModelRequest, ModelResponse
@wrap_model_call
def dynamic_model_selection(request: ModelRequest, handler) -> ModelResponse:
"""会話の複雑さに基づいてモデルを選択します。"""
message_count = len(request.state["messages"])
if message_count > 3:
# 処理履歴が長い場合は高度なモデルを使用する
model = advanced_model
else:
model = basic_model
request.model = model
return handler(request)
最後に、動的モデル変更のミドルウェアを組み込んだエージェントの作成・実行を行います。
from langchain.agents import create_agent
from pprint import pprint
def get_weather(city: str) -> str:
"""指定した都市の天気を取得します。"""
return f"It's always sunny in {city}!"
agent = create_agent(
model=basic_model,
tools=[get_weather],
middleware=[dynamic_model_selection],
)
print("---- simple ---")
input = {"messages": [{"role": "user", "content": "SFの天気は?"}]}
result = agent.invoke(input)
print("answer:", result["messages"][-1].content)
print("model:", result["messages"][-1].response_metadata["model_name"])
print("---- complex ---")
input = {"messages": [{"role": "user", "content": "東京・大阪・福岡の天気を教えてください。"}]}
result = agent.invoke(input)
print("answer:", result["messages"][-1].content)
print("model:", result["messages"][-1].response_metadata["model_name"])
実行結果は以下の通り。
---- simple ---
answer: 気象庁によると、現在のサン フランシスコの天気は「常に晴れ」します。🌞
model: gpt-oss-20b-080525
---- complex ---
answer: 以下の3都市の天気情報です。
- **東京**:晴天です。
- **大阪**:晴天です。
- **福岡**:晴天です。
model: gpt-oss-120b-080525
MLflowのトレースを見るに、最初のinvokeでは以下のように3メッセージ分のステップしか実行されていないため、全てbasic_modelで設定したモデルのみ利用されています。
二つ目のクエリでは、7ステップ実行されています。
そのため、後半のLLMへのクエリ実行においてはadvanced_modelで指定したモデルが利用されていました。
このように、動的なモデル選択のロジックを定義した関数を用意することで、簡単にエージェント中のモデル選択を動的にすることができます。
まとめ
LangChainエージェントの動的モデル選択を試してみました。
LangGraphでも簡単に設定することはできたのですが、Middlewareの概念が組み込まれたことでより使いやすくなったのではないかと思います。
LangChainのエージェントは、このMiddlewareがかなり重要な機能になっているようで、このあたりを理解することが重要だと思いました。
この後もMiddleware関連の検証を記事として上げていく予定です。


