ReActエージェント

知識

初めに

私はAIのキャラクターを作成しています。その参考になるかなと思い近年の自律型AIエージェントの代表的な仕組みの一つ「ReActエージェント」について調べ、その内容をまとめてみました。

ReActエージェントとは

従来のLLMは内部知識や、与えられた知識のみに基づき解答の生成をする「自己完結型推論」の生成が中心でした。ReActエージェントはこのLLMに対して生成器から実際に行動を洗濯・指示することで、「作業者」としての役割を実行できるようになりました。

ReActエージェントの仕組み

ReActエージェントでは大きな特徴は下記項目があります。

  • ループ構造
  • 外部プログラムの呼び出し機能

ループ機能

ReActエージェントはある動作を繰り返し実行することで目標行動を達成させています。

  1. ユーザーが目標を提示
  2. LLMが現在状態を確認、次の行動を言語化
  3. LLMは事前に取得した使用可能な外部プログラム一覧から、次の行動に利用する外部プログラムを呼び出します。
  4. 外部プログラムが動き、実行結果をLLMへ渡します。
  5. 実行結果を確認してLLMが現在の状況を更新します。
  6. 2~6をユーザーが提示した目標を達成するまで繰り返します。
  7. 6.の結果をユーザーに提示します。
sequenceDiagram
    participant User as ユーザー
    participant Orchestrator as オーケストレーター
    participant LLM as LLM
    participant Action as 実行アクション

    User->>Orchestrator: 目標を入力
    Orchestrator->>LLM: 目標 + 現在状態 + 使用可能なアクション一覧を送信

    LLM-->>Orchestrator: 次に実行するアクション名 + 引数を返す
    Orchestrator->>Action: アクション名 + 引数を渡して実行
    Action-->>Orchestrator: 実行結果を返す

    Orchestrator->>LLM: 目標 + 現在状態 + 実行結果を送信
    LLM-->>Orchestrator: 次のアクション、または最終回答を返す

    alt まだ目標未達成
        Orchestrator->>Action: 次のアクション名 + 引数を渡して実行
        Action-->>Orchestrator: 実行結果を返す
        Orchestrator->>LLM: 更新された状態 + 実行結果を送信
    else 目標達成
        Orchestrator-->>User: 最終結果を返す
    end

LLMは現在の状態や次の行動を毎回読み取ることで目標に対して近づき、達成まで至ることを可能としています

一方デメリットとして毎回のLLM呼び出しが発生しているためトークン消費量が多いという欠点があります。このため、トークン上限を設け、停止条件として設定しその条件まで繰り返すといった構造も見られます。

外部プログラムの呼び出し機能

外部プログラムの呼び出しは下記手順で行われます

  1. LLMへ外部プログラム一覧を提示
  2. 外部プログラムの呼び出し
  3. 外部プログラムの実行
  4. 実行結果の提出

1. LLMへ外部プログラム一覧を提示

LLMは自分が構築環境でどのような行動が可能か知りません。LLMには次行動の言語化前に実施できる行動(外部プログラム)できる一覧表を提示する必要があります

例として、天気情報を取得する外部プログラムの場合、下記のようなツールを渡します。

{
  "name": "get_weather",
  "description": "指定された場所の現在の天気を取得する",
  "parameters": {
    "type": "object",
    "properties": {
      "location": { "type": "string", "description": "都市名 (例: 東京)" }
    }
  }
}

この情報を元にLLMは「現在の天気を取得したい場合、get_weatherを呼べばいい」と理解します。

2.外部プログラムの呼び出し

ユーザーが「今日の東京の天気は何?」と質問した場合、LLMは下記のような出力を行います。

{
  "tool": "get_weather",
  "arguments": { "location": "Tokyo" }
}

これは「get_weather」関数に「Tokyo」という変数を入れて実行するような指示を書いています。LLMの役割はここまでとなります。

3.外部プログラムの実行

2.で出力された内容を外部プログラムを統括するプログラムであるオーケストレーターが受け取り、指定された関数に変数を入れて実行します。

このため正確にはLLMはツールを実行せず、実行したい内容をオーケストラに渡すことで実質的な実行を可能としています。

下記ソースコードはあくまで例のためif文で記載していますが、実際のツール呼び出しはツール一覧からの検索を行います。

import json
import test_tool  # ツールをモジュールとしてインポート

def run_orchestrator(llm_output_json: str):

    # 1. LLMの出力(JSON)をパース
    instruction = json.loads(llm_output_json)
    tool_name = instruction.get("tool")
    args = instruction.get("arguments")

    # 2. ツール名に基づいて実行する関数を選択(実際は索引)
    if tool_name == "get_weather":

        # 3. 関数を実際に呼び出す
        observation = test_tool.get_weather(location=args["location"])
        
        # 4. 実行結果(Observation)をLLMに返す準備
        return observation

4.実行結果の提出

get_weather関数の実行結果が出力したのち、それをLLMに渡し、ツールを実行した場合の実行結果として入力に含め、現在の状況を更新します。

一度のツール呼び出しで終わらないこともあるため、この動作は目標達成、または停止条件まで繰り返されます。

まとめ

近年話題の自律型エージェントと言われる技術の一つにこのReActエージェントの仕組みが挙げられます。強みとして継続的な目標達成が可能となる一方、消費トークンが多くあることが問題です。

コメント

タイトルとURLをコピーしました