見出し画像

LangChain を使用したツール呼び出し

この記事は、2024/4/11 LangChain blog 掲載記事の日本語翻訳です。

TLDR : tool_callsに新しいAIMessage属性を導入しています。信頼性の高いツール呼び出しのための API を公開する LLM プロバイダーが増えています。新しい属性の目的は、ツール呼び出しを操作するための標準インターフェースを提供することです。これは完全な下位互換性があり、ネイティブ ツール呼び出しサポートを備えたすべてのモデルでサポートされています。これらの最新機能にアクセスするには、自社langchain_coreおよびパートナーのパッケージのバージョンをアップグレードする必要があります。

YouTube のチュートリアル

Python:

JS:


イントロ

大規模言語モデル (LLM) は、ツール呼び出し機能を介して外部データ ソースと対話できます。ツール呼び出しは、開発者が LLM を活用してデータベース、ファイル、API などの外部リソースにアクセス、対話、操作できる高度なアプリケーションを構築できる強力な手法です。

プロバイダーは、ネイティブ ツール呼び出し機能をモデルに導入しています。これは実際にはどのようになるかというと、LLM がプロンプトにオートコンプリートを提供するときに、プレーン テキストに加えてツール呼び出しのリストを返すことができるということです。 OpenAI は約 1 年前にこれを「関数呼び出し」で初めてリリースし、11 月にはすぐに「ツール呼び出し」に進化しました。それ以来、他のモデル プロバイダーも続いています。Gemini (12 月)、Mistral (2 月)、Fireworks (3 月)、Togetter (3 月)、Groq (4 月)、Cohere (4 月)、Anthropic (4 月) 。

これらのプロバイダーはすべて、わずかに異なるインターフェースを公開しています (特に、OpenAI、Anthropic、Gemini の 3 つの最高パフォーマンス モデルには互換性がありません)。これらのプロバイダー間の切り替えを簡単にするために、ツール呼び出し用の標準化されたインターフェースを求めるコミュニティからの要望を聞いており、本日リリースできることに興奮しています。

標準インターフェースは次のもので構成されます。

  • ChatModel.bind_tools(): ツール定義をモデル呼び出しに添付するメソッド。

  • AIMessage.tool_callsAIMessage:作成することを決定したモデルを呼び出すツールに簡単にアクセスするための、モデルから返される属性。

  • create_tool_calling_agent()bind_tools: を実装して返す任意のモデルで動作するエージェント コンストラクターtool_calls。

これらの各コンポーネントを見てみましょう。

ChatModel.bind_tools(...)

モデルがツールを使用できるようにするには、どのツールが利用可能であるかをモデルに伝える必要があります。これを行うには、ツール引数のスキーマを含むツール定義のリストをモデルに渡すことを指定します。ツール定義の正確な形式はモデル プロバイダーによって異なります。OpenAI は「name」、「description」、および「parameters」キーを含む辞書を予期しますが、Anthropic は「name」、「description」、および「input_schema」を予期します。

ChatModel.bind_toolsは、すべてのツール呼び出しモデルによって実装される標準インターフェースを提供し、モデルで使用できるツールを指定できます。生のツール定義 (辞書) だけでなく、ツール定義の派生元となるオブジェクト (つまり、Pydantic クラス、LangChain ツール、および任意の関数) も渡すことができます。これにより、任意のツール呼び出しモデルで使用できる汎用ツール定義を簡単に作成できます。

from langchain_anthropic import ChatAnthropic
from langchain_core.pydantic_v1 import BaseModel, Field
from langchain_core.tools import tool

# ✅ Pydantic class
class multiply(BaseModel):
    """Return product of 'x' and 'y'."""
    x: float = Field(..., description="First factor")
    y: float = Field(..., description="Second factor")
    
# ✅ LangChain tool
@tool
def exponentiate(x: float, y: float) -> float:
    """Raise 'x' to the 'y'."""
    return x**y
    
# ✅ Function

def subtract(x: float, y: float) -> float:
    """Subtract 'x' from 'y'."""
    return y-x
    
# ✅ OpenAI-format dict
# Could also pass in a JSON schema with "title" and "description" 
add = {
  "name": "add",
  "description": "Add 'x' and 'y'.",
  "parameters": {
    "type": "object",
    "properties": {
      "x": {"type": "number", "description": "First number to add"},
      "y": {"type": "number", "description": "Second number to add"}
    },
    "required": ["x", "y"]
  }
}

llm = ChatAnthropic(model="claude-3-sonnet-20240229", temperature=0)

# Whenever we invoke `llm_with_tool`, all three of these tool definitions
# are passed to the model.
llm_with_tools = llm.bind_tools([multiply, exponentiate, add, subtract])

別のツール呼び出しモデルを使用したい場合、コードは非常に似たものになります。

from langchain_openai import ChatOpenAI

llm = ChatOpenAI(model="gpt-4-turbo", temperature=0)
llm_with_tools = llm.bind_tools([multiply, exponentiate, add, subtract])

それでは、通話はどのllm_with_toolsようなものになるでしょうか?そこでAIMessage.tool_calls登場します。

AIMessage.tool_calls

ツール呼び出しモデルを使用する前は、モデルによって返されるツール呼び出しは、モデル プロバイダーの API に応じてAIMessage.additional_kwargsまたは のいずれかで検出されAIMessage.content、プロバイダー固有の形式に従っていました。つまり、さまざまなモデルの出力からツールの呼び出しを抽出するには、カスタム ロジックが必要になります。AIMessage.tool_callsモデル ツールの呼び出しを取得するための標準化されたインターフェースが提供されるようになりました。したがって、バインドされたツールを使用してモデルを呼び出すと、次の形式の出力が得られます。

llm_with_tools.invoke([
	("system", "You're a helpful assistant"), 
	("human", "what's 5 raised to the 2.743"),
])

# 👀 Notice the tool_calls attribute 👀

# -> AIMessage(
# 	  content=..., 
# 	  additional_kwargs={...},
# 	  tool_calls=[{'name': 'exponentiate', 'args': {'y': 2.743, 'x': 5.0}, 'id': '54c166b2-f81a-481a-9289-eea68fc84e4f'}]
# 	  response_metadata={...}, 
# 	  id='...'
#   )

ここで、 には、ツールの呼び出しがある場合に設定される属性AIMessageがあり、ツール呼び出しの標準インターフェースに従います。tool_calls: List[ToolCall]

class ToolCall(TypedDict):
  name: str
  args: Dict[str, Any]
	id: Optional[str]

つまり、Anthropic、OpenAI、Gemini などを呼び出している場合でも、ツール呼び出しがある場合はAIMessage.tool_calls常にToolCall.

ストリーミングされたツール呼び出しチャンクと無効なツール呼び出しを処理するために追加された属性が他にもいくつかあります。詳細については、ここのツール呼び出しドキュメントを参照してください。

create_tool_calling_agent()

LLM ツール呼び出し機能の最も強力かつ明白な用途の 1 つは、エージェントを構築することです。 LangChain には、create_openai_tools_agent()OpenAI ツール呼び出し API に準拠したツール呼び出しモデルを備えたエージェントを簡単に構築できるコンストラクターがすでにありますが、これは Anthropic や Gemini などのモデルでは機能しません。新しいbind_tools()およびインターフェースのおかげで、あらゆるツール呼び出しモデルで動作する がtool_calls追加されました。create_tool_calling_agent()

from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import ConfigurableField
from langchain_core.tools import tool
from langchain.agents import create_tool_calling_agent, AgentExecutor

@tool
def multiply(x: float, y: float) -> float:
    """Multiply 'x' times 'y'."""
    return x * y

@tool
def exponentiate(x: float, y: float) -> float:
    """Raise 'x' to the 'y'."""
    return x**y

@tool
def add(x: float, y: float) -> float:
    """Add 'x' and 'y'."""
    return x + y

prompt = ChatPromptTemplate.from_messages([
    ("system", "you're a helpful assistant"), 
    ("human", "{input}"), 
    ("placeholder", "{agent_scratchpad}"),
])

tools = [multiply, exponentiate, add]


llm = ChatAnthropic(model="claude-3-sonnet-20240229", temperature=0)


agent = create_tool_calling_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)

agent_executor.invoke({"input": "what's 3 plus 5 raised to the 2.743. also what's 17.24 - 918.1241", })

代わりに VertexAI を使用することもできます

from langchain_google_vertexai import ChatVertexAI

llm = ChatVertexAI(
	model="gemini-pro", 
	temperature=0, 
	convert_system_message_to_human=True
)
agent = create_tool_calling_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)

agent_executor.invoke({"input": "what's 3 plus 5 raised to the 2.743. also what's 17.24 - 918.1241", })

または OpenAI

llm = ChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0)

agent = create_tool_calling_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)

agent_executor.invoke({"input": "what's 3 plus 5 raised to the 2.743. also what's 17.24 - 918.1241", })

新しいエージェントに関する完全なドキュメントについては、ここを参照してください。

LangGraph

LangGraphをまだチェックアウトしていない場合は、必ずチェックアウトする必要があります。これは、任意のエージェント フローおよびマルチエージェント フローの構築を容易にする LangChain の拡張機能です。ご想像のとおり、新しいtool_callsインターフェースを使用すると、LangGraph エージェントまたはフローを構築する際の作業も簡素化されます。LangGraph エージェントでのtool_callsの使用方法の詳細なチュートリアルについては、こちらのノートブックを参照してください。

with_structured_output

私たちは最近、モデルから構造化された出力を取得するための ChatModel.with_structured_output() インターフェースをリリースしました。正確な実装はモデルプロバイダによって異なりますが、with_structured_output は、それをサポートするほとんどのモデルのためのツールコールの上に構築されています。内部では、with_structured_output は bind_tools を使って、与えられた構造化出力スキーマをモデルに渡します。

with_structured_outputでは、バインディング ツールや読み取りツール呼び出しを直接使用するのではなく、どのような場合に使用する必要があるのでしょうか?

with_structured_outputは、常に指定したスキーマで構造化された出力を返します。これは、LLMに特定のスキーマにマッチする情報を強制的に出力させたい場合に便利です。これは情報抽出タスクに便利です。

bind_toolsは、より一般的なもので、特定のツールを選択することができます! これは、LLMがどのように応答するかについて、より柔軟性を持たせたい場合に便利です。例えば、エージェント・アプリケーションでは、どのツールを呼び出すかを選択する必要がありますが、ユーザーにも応答する必要があります。

結論

ネイティブ ツール呼び出し機能を LLM に導入する傾向は今後も続くと予想されます。標準化されたツール呼び出しインターフェースにより、LangChain ユーザーの時間と労力が節約され、異なる LLM プロバイダー間をより簡単に切り替えられるようになることを願っています。

langchain_core新しいインターフェースを活用するには、必ず自社およびパートナーのパッケージのバージョンを更新してください。

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