エージェントの基本的なアイデアは、LLMを使用して実行する一連のアクションを選択することです。チェーンでは、アクションのシーケンスがコードでハードコードされます。エージェントでは、アクションとその順序を決定するための推論エンジンとして言語モデルが使用されます。
主要なコンポーネントは以下です:
これは次にどのステップを実行するかを決定するクラスです。言語モデルとプロンプトによって動作します。このプロンプトには以下のような内容が含まれることがあります:
- エージェントの個性(特定の方法で応答させるために役立つ)
- エージェントのバックグラウンドコンテキスト(実行されるタスクのコンテキストに関する追加情報)
- より良い推論を呼び起こすプロンプティング戦略(最も有名で広く使用されるのはReActです)
LangChainでは、いくつかの異なる種類のエージェントを提供していますが、これらのエージェントを(1)と(2)のパーツでカスタマイズすることがおそらく必要になるでしょう。エージェントの種類の完全なリストについては「エージェントの種類」を参照してください。
ツールは、エージェントが呼び出す関数です。ここでの2つの重要な考慮事項があります:
- エージェントに適切なツールへのアクセスを提供すること
- ツールをエージェントに最も役立つ方法で説明すること
どちらがなければ、構築しようとしているエージェントは動作しません。エージェントに正しいツールセットへのアクセスを提供しないと、目標を達成することはできません。ツールを適切に説明しないと、エージェントはそれらを正しく使用する方法を知りません。
LangChainは開始には幅広いツールセットを提供していますが、独自のツール(カスタムな説明を含む)を定義することも簡単です。ツールの完全なリストについてはこちらを参照してください。
エージェントがアクセスするツールのセットは、単一のツールよりも重要な場合があります。そのため、LangChainは特定の目的を達成するために必要なツールのグループであるツールキットの概念を提供しています。通常、ツールキットには約3〜5つのツールが含まれます。
LangChainは開始には幅広いツールキットを提供しています。ツールキットの完全なリストについてはこちらを参照してください。
エージェントエグゼキュータはエージェントの実行時です。これがエージェントを呼び出し、選択したアクションを実行します。このランタイムの擬似コードは以下の通りです:
next_action = agent.get_action(...)
while next_action != AgentFinish:
observation = run(next_action)
next_action = agent.get_action(..., next_action, observation)
return next_action
これは単純に見えるかもしれませんが、このランタイムは
- エージェントが非存在するツールを選択した場合、
- ツールエラーが発生した場合、
- エージェントがツール呼び出しに解析できない出力を生成した場合など、いくつかの複雑さを処理します。
- また、全レベルでのログ記録および観測(エージェントの決定、ツール呼び出し)がstdoutまたはLangSmithに行われます。
AgentExecutorクラスはLangChainでサポートされている主要なエージェントランタイムです。ただし、より実験的なランタイムもサポートしています。これには以下が含まれます:
- プランと実行エージェント
- Baby AGI
- Auto GPT
エージェントの構築を始める方法について説明します。LangChainエージェントクラスを使用しますが、特定のコンテキストを与えるためにカスタマイズする方法を示します。それからカスタムツールを定義し、最後にすべてを標準のLangChain AgentExecutor
で実行します。
OpenAIFunctionsAgent
を使用します。これは最も簡単で最適なエージェントです。ただし、ChatOpenAI
モデルを使用する必要があります。異なる言語モデルを使用したい場合は、ReActエージェントを使用することをお勧めします。
このガイドでは、エージェントにカスタムツールへのアクセスを提供するカスタムエージェントを構築します。エージェントまたはツールのいずれかをカスタマイズする必要があると思われるため、この例を選択しました。エージェントに提供するツールは、単語の長さを計算するツールです。これはトークン化のためLLMsがミスすることがある実際の問題です。まず、メモリなしで作成しますが、その後メモリを追加する方法も示します。メモリは会話を有効にするために必要です。
まず、エージェントを制御する言語モデルを読み込みましょう。
from langchain.chat_models import ChatOpenAI
llm = ChatOpenAI(temperature=0)
次に、使用するツールを定義しましょう。渡された単語の長さを計算する非常に単純なPython関数を書いてみましょう。
from langchain.agents import tool
@tool
def get_word_length(word: str) -> int:
"""単語の長さを返す"""
return len(word)
tools = [get_word_length]
次に、プロンプトを作成します。カスタムのSystemMessage
を含むプロンプトを作成するために、OpenAIFunctionsAgent.create_prompt
ヘルパー関数を使用できます。
from langchain.schema import SystemMessage
from langchain.agents import OpenAIFunctionsAgent
system_message = SystemMessage(content="あなたは非常に強力なアシスタントですが、単語の長さを計算するのは得意ではありません。")
prompt = OpenAIFunctionsAgent.create_prompt(system_message=system_message)
これらの要素を組み合わせると、エージェントを作成できます。
agent = OpenAIFunctionsAgent(llm=llm, tools=tools, prompt=prompt)
最後に、エージェントのランタイムであるAgentExecutor
を作成します。
from langchain.agents import AgentExecutor
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
それでは、テストしてみましょう!
agent_executor.run("educaという単語には何文字ありますか?")
これでエージェントができました!ただし、このエージェントは状態を持っていないため、以前の対話について何も覚えていません。これを修正して、メモリを追加しましょう。
これを行うには、2つのことを行う必要があります:
- プロンプトにメモリ変数を格納する場所を追加
- エージェントエグゼキュータにメモリを追加(エージェントではなく、外側のチェーンに追加することに注意)
まず、プロンプトにメモリの場所を追加します。これは、キーが「chat_history」のメッセージ用のプレースホルダを追加することで行います。
from langchain.prompts import MessagesPlaceholder
MEMORY_KEY = "chat_history"
prompt = OpenAIFunctionsAgent.create_prompt(
system_message=system_message,
extra_prompt_messages=[MessagesPlaceholder(variable_name=MEMORY_KEY)]
)
次に、メモリオブジェクトを作成します。ConversationBufferMemory
を使用してこれを行います。重要なのは、memory_key
を「chat_history
」と設定し、return_messages
を設定することです(これにより、文字列ではなくメッセージが返されます)。
from langchain.memory import ConversationBufferMemory
memory = ConversationBufferMemory(memory_key=MEMORY_KEY, return_messages=True)
それでは、すべてを組み合わせましょう!
agent = OpenAIFunctionsAgent(llm=llm, tools=tools, prompt=prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, memory=memory, verbose=True)
agent_executor.run("educaという単語には何文字ありますか?")
agent_executor.run("それは本当の単語ですか?")
これでエージェントにメモリが追加されました。