皆さん、こんにちは。サイオステクノロジー武井です。今回は、生成AIの発展形であり、与えられた課題に対して自分で思考し、行動を決定し、実行する「AIエージェント」について、一筆したためたいと思います。
AIエージェントとは?
トム・クルーズの誕生日にケーキをプレゼントしたいです。彼の年齢分のろうそくを購入するための金額を計算してください。ろうそくは一本100円です。
AIエージェントを使わない場合と使った場合との比較
AIエージェントを実現するプロンプトエンジニアリング「ReAct」
- まず、10個のりんごを5人で割り算します。
 - 10 ÷ 5 = 2 です。
 - それぞれ2個ずつ持つことになります。
 
ReActの手法

質問
トム・クルーズの誕生日にケーキをプレゼントしたいです。彼の年齢分のろうそくを購入するための金額を計算してください。ろうそくは一本100円です。
思考 (1回目)
行動 (1回目)
思考 (2回目)
行動 (2回目)
思考 (3回目)
最終回答
Reactを実現するためのプロンプト
次の質問にできる限り答えてください。次のツールにアクセスできます。– 検索ツール:検索するときに使います– 計算ツール:計算するときに使います。次のフォーマットを使用します。質問:回答する必要がある入力質問思考:次に何をすべきかを常に考える行動:実行するアクションは、「計算ツール」「検索ツール」のいずれかである必要があります。行動の入力:アクションへの入力観察:行動の結果…(この思考/行動/行動の入力/観察はN回繰り返すことができます)最終的な答えがわかったら以下を出力します。思考:今、最終的な答えが分かりました最終回答:元の入力質問に対する最終回答計算ツールを使うときの行動の入力は、計算式を入力してください。例:52 * 100例:100 – 1質問:{question}さぁ始めましょう。思考:
次の質問にできる限り答えてください。次のツールにアクセスできます。– 検索ツール:検索するときに使います– 計算ツール:計算するときに使います。
次のフォーマットを使用します。質問:回答する必要がある入力質問思考:次に何をすべきかを常に考える行動:実行するアクションは、「計算ツール」「検索ツール」のいずれかである必要があります。行動の入力:アクションへの入力観察:行動の結果…(この思考/行動/行動の入力/観察はN回繰り返すことができます)最終的な答えがわかったら以下を出力します。思考:今、最終的な答えが分かりました最終回答:元の入力質問に対する最終回答
計算ツールを使うときの行動の入力は、計算式を入力してください。例:52 * 100例:100 – 1
質問:トム・クルーズの誕生日にケーキをプレゼントしたいです。彼の年齢分のろうそくを購入するための金額を計算してください。ろうそくは一本100円です。さぁ始めましょう。思考:
行動(1回目)の結果
行動(2回目)の結果
ReActの実装
from openai import AzureOpenAI
from duckduckgo_search import DDGS
# OpenAI APIの初期化
aoai_endpoint = "https://hogehoge.openai.azure.com/"
aoai_api_version = "2024-06-01"
aoai_api_key = "XXXXXX"
aoai_embedding_model_name = "text-embedding-ada-002-deploy"
aoai_chat_model_name = "gpt-4-deploy"
template = """
次の質問にできる限り答えてください。次のツールにアクセスできます。
- 検索ツール:検索するときに使います
- 計算ツール:計算するときに使います。
次のフォーマットを使用します。
質問:回答する必要がある入力質問
思考:次に何をすべきかを常に考える
行動:実行するアクションは、「計算ツール」「検索ツール」のいずれかである必要があります。
行動の入力:アクションへの入力
観察:行動の結果
...(この思考/行動/行動の入力/観察はN回繰り返すことができます)
最終的な答えがわかったら以下を出力します。
思考:今、最終的な答えが分かりました
最終回答:元の入力質問に対する最終回答
計算ツールを使うときの行動の入力は、計算式を入力してください。
例:52 * 100
例:100 - 1
質問:{question}
さぁ始めましょう。
思考:
"""
question = "トム・クルーズの誕生日にケーキをプレゼントしたいです。彼の年齢分のろうそくを購入するための金額を計算してください。ろうそくは一本本100円です。"
message = template.format(question=question)
final_observation = ""
while True:
    # 推論と行動のプロセス
    openai_client = AzureOpenAI(azure_endpoint=aoai_endpoint, api_key=aoai_api_key, api_version = aoai_api_version)
    response = openai_client.chat.completions.create(
        model=aoai_chat_model_name,
        messages=[
            {"role": "assistant", "content": message},
        ],
        stop=["観察:"]
    )
    result = response.choices[0].message.content.strip()
    # resultの変数の最後の行に、つまりresultの変数を改行で分割した配列の最後の要素に「最終回答:」がある場合、終了する。
    if "最終回答:" in result.split("\n")[-1]:
        final_observation = result.split("最終回答:")[1].strip()
        break
    # resultの"行動"と"行動の入力"を取得し、それにあったツールを実行する。
    if "行動" in result:
        action = result.split("行動の入力:")[0].split("行動:")[1].strip()
        action_input = result.split("行動の入力:")[1].strip()
        
        observation = ""
        if action in "検索ツール":
            # 検索ツール実行
            search_results = DDGS().text(action_input, max_results=5)
            for search_result in search_results:
                observation = observation + search_result["body"] + "\n"
        elif action in "計算ツール":
            # 計算ツール実行
            observation = str(eval(action_input))
    message = message + "\n行動:" + action + "\n行動の入力:" + action_input + "\n観察:" + observation + "\n思考:"
    print(message)
print("\n最終的な観察:")
print(final_observation)
pip install openai duckduckgo-search
Azure OpenAPI Serviceの初期化
# OpenAI APIの初期化
aoai_endpoint = "https://hogehoge.openai.azure.com/"
aoai_api_version = "2024-06-01"
aoai_api_key = "XXXXXX"
aoai_embedding_model_name = "text-embedding-ada-002-deploy"
aoai_chat_model_name = "gpt-4-deploy"
プロンプトのテンプレートの設定
template = """
次の質問にできる限り答えてください。次のツールにアクセスできます。
- 検索ツール:検索するときに使います
- 計算ツール:計算するときに使います。
次のフォーマットを使用します。
質問:回答する必要がある入力質問
...(省略)...
質問:{question}
さぁ始めましょう。
思考:
"""
question = "トム・クルーズの誕生日にケーキをプレゼントしたいです。彼の年齢分のろうそくを購入するための金額を計算してください。ろうそくは一本本100円です。"
message = template.format(question=question)
回答の取得
final_observation = ""
while True:
    # 推論と行動のプロセス
    openai_client = AzureOpenAI(azure_endpoint=aoai_endpoint, api_key=aoai_api_key, api_version = aoai_api_version)
    response = openai_client.chat.completions.create(
        model=aoai_chat_model_name,
        messages=[
            {"role": "assistant", "content": message},
        ],
        stop=["観察:"]
    )
    result = response.choices[0].message.content.strip()
最終回答のチェック
    if "最終回答:" in result.split("\n")[-1]:
        final_observation = result.split("最終回答:")[1].strip()
        break
ツールの実行
    if "行動" in result:
        action = result.split("行動の入力:")[0].split("行動:")[1].strip()
        action_input = result.split("行動の入力:")[1].strip()
        
        observation = ""
        if action in "検索ツール":
            # 検索ツール実行
            search_results = DDGS().text(action_input, max_results=5)
            for search_result in search_results:
                observation = observation + search_result["body"] + "\n"
        elif action in "計算ツール":
            # 計算ツール実行
            observation = str(eval(action_input))
    message = message + "\n行動:" + action + "\n行動の入力:" + action_input + "\n観察:" + observation + "\n思考:"
最終的な観察結果の表示
print("\n最終的な観察:")
print(final_observation)
Function calling
Function callingのプロンプト
Function callingのプロンプトを見てみます。
{
  "messages": [
    {
      "role": "system",
      "content": "あなたは役立つアシスタントです。"
    },
    {
      "role": "user",
      "content": "トム・クルーズの誕生日にケーキをプレゼントしたいです。彼の年齢分のろうそくを購入するための金額を計算してください。ろうそくは一本100円です。"
    }
  ],
  "functions": [ ⋯ ①
    {
      "name": "search_web", ⋯ ②
      "description": "指定されたクエリでWebを検索します。", ⋯ ③
      "parameters": { ⋯ ④
        "type": "object",
        "properties": {
          "query": {
            "type": "string",
            "description": "検索するクエリ"
          }
        },
        "required": [
          "query"
        ]
      }
    },
    {
      "name": "calculate",
      "description": "与えられた計算式に基づき計算を行います。",
      "parameters": {
        "type": "object",
        "properties": {
          "formula": {
            "type": "string",
            "description": "計算式(例: 52 * 100、100 - 1)"
          }
        },
        "required": [
          "formula"
        ]
      }
    }
  ],
  "function_call": "auto"
}
①のfuntionsは、関数を定義するフィールドです。ここに関数を定義してきます。
②のnameは、関数名を指定します。
③のdescriptionは、関数の説明を指定します。この内容は超重要で、ユーザーの質問とこの関数の説明を比較して、LLMは関数の実施を決定します。
④のparametersは、関数に与える引数になります。関数の引数はJSONの形で与えられ、propertiesフィールドで定義した内容がそのまま引数になります。例えば、上記の内容であれば、{ “query”: “検索するクエリ”}という形式で引数が与えられます。
そして、以下がプロンプトに対するレスポンスになります。
{
  "choices": [
    {
      "content_filter_results": {},
      "finish_reason": "function_call",
      "index": 0,
      "logprobs": null,
      "message": {
        "content": null, ⋯ ①
        "function_call": { ⋯ ②
          "arguments": "{\n  \"query\": \"トム・クルーズの年齢\"\n}",
          "name": "search_web"
        },
        "role": "assistant"
      }
    }
  ],
…(省略)…
}
①の「content」フィールドには、本来質問に対する回答が返ってくるはずですが、Function callingで実施すべき関数がある場合は、nullとなります。
②では、そのかわりにfunction_callというフィールドが登場し、そこのargumentsに関数の引数、nameに実施すべき関数名が入っています。
これらの情報を見て、実施すべき関数を決定します。
シンプルなFunction callingの実装
import requests
import json
from duckduckgo_search import DDGS
# Azure OpenAI Serviceの設定
AOAI_ENDPOINT = "https://hogehoge.openai.azure.com/" # Azure OpenAI Serviceのエンドポイント
AOAI_API_VERSION = "2024-06-01" # Azure OpenAI ServiceのAPIバージョン
AOAI_API_KEY = "XXXXXXX" # Azure OpenAI ServiceのAPIキー
AOAI_CHAT_MODEL_NAME = "gpt-4-deploy" # Azure OpenAI Serviceのデプロイモデル名
# リクエストURL
url = f"{AOAI_ENDPOINT}openai/deployments/{AOAI_CHAT_MODEL_NAME}/chat/completions?api-version={AOAI_API_VERSION}"
# リクエストヘッダー
headers = {
    "Content-Type": "application/json",
    "api-key": AOAI_API_KEY
}
# 最初のリクエストデータ: Web検索関数を自動呼び出し
data = {
    "messages": [
        {"role": "system", "content": "あなたは役立つアシスタントです。"},
        {"role": "user", "content": "トム・クルーズの現在の年齢を調べて。"}
    ],
    "functions": [
        {
            "name": "search_web",
            "description": "指定されたクエリでWebを検索します。",
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {
                        "type": "string",
                        "description": "検索するクエリ"
                    }
                },
                "required": ["query"]
            }
        },
        {
            "name": "calculate",
            "description": "与えられた計算式に基づき計算を行います。",
            "parameters": {
                "type": "object",
                "properties": {
                    "formula": {
                        "type": "string",
                        "description": "計算式(例: 52 * 100、100 - 1)"
                    }
                },
                "required": ["formula"]
            }
        }
    ]
}
# 指定されたクエリでWebを検索する関数
def search_web(query: str):
    final_result = ""
    search_results = DDGS().text(query, max_results=10)
    for search_result in search_results:
        final_result = final_result + search_result["body"] + "\n"
    return final_result
# 与えられた計算式に基づき計算を行う関数
def calculate(formula: str): 
    return eval(formula)
# リクエストを送信
response = requests.post(url, headers=headers, data=json.dumps(data))
# 結果を出力
result = response.json()
# 関数を実行する。
message = result["choices"][0]["message"]["function_call"]
function_name = message["name"]
arguments = json.loads(message["arguments"])  # 文字列を辞書に変換
print(f"実行された関数: {function_name}")
print(f"関数に渡された引数: {arguments}")
func = globals()[function_name]
result = func(**arguments)
print(f"関数の実行結果: {result}")
Azure OpenAI Serviceの設定
# Azure OpenAI Serviceの設定
AOAI_ENDPOINT = "https://hogehoge.openai.azure.com/" # Azure OpenAI Serviceのエンドポイント
AOAI_API_VERSION = "2024-06-01" # Azure OpenAI ServiceのAPIバージョン
AOAI_API_KEY = "XXXXXXX" # Azure OpenAI ServiceのAPIキー
AOAI_CHAT_MODEL_NAME = "gpt-4-deploy" # Azure OpenAI Serviceのデプロイモデル名
リクエストURL
url = f"{AOAI_ENDPOINT}openai/deployments/{AOAI_CHAT_MODEL_NAME}/chat/completions?api-version={AOAI_API_VERSION}"
リクエストヘッダー
# リクエストヘッダー
headers = {
    "Content-Type": "application/json",
    "api-key": AOAI_API_KEY
}
メッセージの定義
# 最初のリクエストデータ: Web検索関数を自動呼び出し
data = {
    "messages": [
        {"role": "system", "content": "あなたは役立つアシスタントです。"},
        {"role": "user", "content": "トム・クルーズの現在の年齢を調べて。"}
    ],
関数の定義
    "functions": [
        {
            "name": "search_web",
            "description": "指定されたクエリでWebを検索します。",
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {
                        "type": "string",
                        "description": "検索するクエリ"
                    }
                },
                "required": ["query"]
            }
        },
        {
            "name": "calculate",
            "description": "与えられた計算式に基づき計算を行います。",
            "parameters": {
                "type": "object",
                "properties": {
                    "formula": {
                        "type": "string",
                        "description": "計算式(例: 52 * 100、100 - 1)"
                    }
                },
                "required": ["formula"]
            }
        }
    ]
指定されたクエリでWebを検索する関数
# 指定されたクエリでWebを検索する関数
def search_web(query: str):
    final_result = ""
    search_results = DDGS().text(query, max_results=10)
    for search_result in search_results:
        final_result = final_result + search_result["body"] + "\n"
    return final_result
与えられた計算式に基づき計算を行う関数
# 与えられた計算式に基づき計算を行う関数
def calculate(formula: str): 
    return eval(formula)
リクエストの送信
# リクエストを送信
response = requests.post(url, headers=headers, data=json.dumps(data))
# 結果を出力
result = response.json()
関数の実行
# 関数を実行する。
message = result["choices"][0]["message"]["function_call"]
function_name = message["name"]
arguments = json.loads(message["arguments"]) # 文字列を辞書に変換
print(f"実行された関数: {function_name}")
print(f"関数に渡された引数: {arguments}")
func = globals()[function_name]
result = func(**arguments)
print(f"関数の実行結果: {result}")
実行された関数: search_web
関数に渡された引数: {'query': 'トム・クルーズ現在の年齢'}
関数の実行結果: トム・クルーズのサイエントロジーに対する開けた態度は、...(以下略)...
実行された関数: calculate
関数に渡された引数: {'formula': '52 * 100'}
関数の実行結果: 5200
Function callingでAIエージェント
処理の流れ
Function callingによるAIエージェントは、何回もLLMにリクエストを送ります。最終結果がでていないとLLMが判断した場合は、実行すべき関数をレスポンスとして返し、アプリケーションはその関数に対応したアプリケーションの実装を実行します。LLMが最終結果がでたと判断したら処理は終了です。「思考」と「行動」を繰り返すReActと同じ流れです。
1回目の処理 (概念図)
トム・クルーズの誕生日にケーキをプレゼントしたいです。彼の年齢分のろうそくを購入するための金額を計算してください。ろうそくは一本本100円です。
1回目の処理 (プロンプト)
{
  "messages": [
    {
      "role": "system",
      "content": "あなたは役立つアシスタントです。"
    },
    {
      "role": "user",
      "content": "トム・クルーズの誕生日にケーキをプレゼントしたいです。彼の年齢分のろうそくを購入するための金額を計算してください。ろうそくは一本100円です。"
    }
  ],
  "functions": [ ⋯ ①
    {
      "name": "search_web", ⋯ ②
      "description": "指定されたクエリでWebを検索します。", ⋯ ③
      "parameters": { ⋯ ④
        "type": "object",
        "properties": {
          "query": {
            "type": "string",
            "description": "検索するクエリ"
          }
        },
        "required": [
          "query"
        ]
      }
    },
    {
      "name": "calculate",
      "description": "与えられた計算式に基づき計算を行います。",
      "parameters": {
        "type": "object",
        "properties": {
          "formula": {
            "type": "string",
            "description": "計算式(例: 52 * 100、100 - 1)"
          }
        },
        "required": [
          "formula"
        ]
      }
    }
  ],
  "function_call": "auto"
}
①のfuntionsは、関数を定義するフィールドです。ここに関数を定義してきます。
②のnameは、関数名を指定します。
③のdescriptionは、関数の説明を指定します。この内容は超重要で、ユーザーの質問とこの関数の説明を比較して、LLMは関数の実施を決定します。
④のparametersは、関数に与える引数になります。関数の引数はJSONの形で与えられ、propertiesフィールドで定義した内容がそのまま引数になります。例えば、上記の内容であれば、{ “query”: “検索するクエリ”}という形式で引数が与えられます。
1回目の処理 (レスポンス)
{
  "choices": [
    {
      "content_filter_results": {},
      "finish_reason": "function_call",
      "index": 0,
      "logprobs": null,
      "message": {
        "content": null, ⋯ ①
        "function_call": { ⋯ ②
          "arguments": "{\n  \"query\": \"トム・クルーズの年齢\"\n}",
          "name": "search_web"
        },
        "role": "assistant"
      }
    }
  ],
…(省略)…
}
①の「content」フィールドには、本来質問に対する回答が返ってくるはずですが、Function callingで実施すべき関数がある場合は、nullとなります。
②では、そのかわりにfunction_callというフィールドが登場し、そこのargumentsに関数の引数、nameに実施すべき関数名が入っています。
ここでは、LLMはプロンプトにて与えられた質問と関数の内容から判断して、まずはトム・クルーズの年齢をsearch_web(インターネットを検索する関数)を使って調べて、その後に計算をする関数(calculate)を使って、トムの年齢とろうそく1本あたりの値段をかけるという推論をしました。その結果、まず一発目に実施する関数はsearch_webであり、その引数は「トム・クルーズの年齢」というレスポンスを返したという結果になります。
2回目の処理 (概念図)
最初に段取りした「トム・クルーズの年齢分のろうそくの合計金額を計算するためには、まずWebを検索して、トム・クルーズの年齢を調べて、その後に計算を行う必要がある」という流れのうち、最初の段取りである「トム・クルーズの年齢を調べる」はすでに完了しているんだな。では、その実行結果を見て、トム・クルーズの現在の年齢を割り出して、次にろうそくの合計金額の計算を行おう。
2回目の処理 (プロンプト)
{
  "messages": [
    {
      "role": "system",
      "content": "あなたは役立つアシスタントです。"
    },
    {
      "role": "user",
      "content": "トム・クルーズの誕生日にケーキをプレゼントしたいです。彼の年齢分のろうそくを購入するための金額を計算してください。ろうそくは一本100円です。"
    },
    { ⋯ ①
      "content": null,
      "function_call": {
        "arguments": "{\n  \"query\": \"トム・クルーズの年齢\"\n}",
        "name": "search_web"
      },
      "role": "assistant"
    },
    { ⋯ ②
      "role": "function",
      "name": "search_web",
      “content”: “トム・クルーズ. トム・クルーズ (英: Tom Cruise, 1962年 7月3日 - )、…(以下省略)…  ],
    }
  ],
  "functions": [
    {
      "name": "search_web",
      "description": "指定されたクエリでWebを検索します。",
      "parameters": {
        "type": "object",
        "properties": {
          "query": {
            "type": "string",
            "description": "検索するクエリ"
          }
        },
        "required": [
          "query"
        ]
      }
    },
    …(以下省略)…  
  ],
  "function_call": "auto"
}
2回目の処理 (レスポンス)
{
  "choices": [
    {
      "content_filter_results": {},
      "finish_reason": "function_call",
      "index": 0,
      "logprobs": null,
      "message": {
        "content": null, ⋯ ①
        "function_call": { ⋯ ②
          "arguments": "{\n  \"formula\": \"62 * 100\"\n}",
          "name": "calculate"
        },
        "role": "assistant"
      }
    }
  ],
  …(省略)…
}
①の「content」フィールドには、本来質問に対する回答が返ってくるはずですが、Function callingで実施すべき関数がある場合は、nullとなります。
②では、そのかわりにfunction_callというフィールドが登場し、そこのargumentsに関数の引数、nameに実施すべき関数名が入っています。
ここでは、先程のプロンプト内にあったsearch_webの実行結果(トム・クルーズ. トム・クルーズ (英: Tom Cruise, 1962年 7月3日 – )、…(以下省略)… )を受けて、LLMはトム・クルーズの年齢は62歳だと判別し、次に実行するべき処理は、年齢 ✕ ろうそく一本あたりの値段だと思考し、その内容のfunction_callを返しています。
3回目の処理 (概念図)
3回目の処理 (プロンプト)
{
  "messages": [
    {
      "role": "system",
      "content": "あなたは役立つアシスタントです。"
    },
    {
      "role": "user",
      "content": "トム・クルーズの誕生日にケーキをプレゼントしたいです。彼の年齢分のろうそくを購入するための金額を計算してください。ろうそくは一本100円です。"
    },
    {
      "content": null,
      "function_call": {
        "arguments": "{\n  \"query\": \"トム・クルーズの年齢\"\n}",
        "name": "search_web"
      },
      "role": "assistant"
    },
    {
      "role": "function",
      "name": "search_web",
      “content”: “トム・クルーズ. トム・クルーズ (英: Tom Cruise, 1962年 7月3日 - )、…(以下省略)…  ],
    },
    { ⋯ ①
      "content": null,
      "function_call": {
        "arguments": "{\n  \"formula\": \"62 * 100\"\n}",
        "name": "calculate"
      },
      "role": "assistant"
    },
    { ⋯ ②
      "role": "function",
      "name": "calculate",
      "content": "6000"
    }
  ],
  "functions": [
    …(以下省略)…  
  ],
  "function_call": "auto"
}
3回目の処理 (レスポンス)
{
  "choices": [
    {
      …(省略)…
      "message": {
        "content": "トム・クルーズは62歳なので、彼の年齢分のろうそくを購入するためには6200円が必要です。", ⋯ ①
        "role": "assistant"
      }
    }
  ],
  …(省略)…
}
Function callingによるAIエージェントの実装
import requests
import json
from duckduckgo_search import DDGS
# Azure OpenAI Serviceの設定
AOAI_ENDPOINT = "https://hogehoge.openai.azure.com/" # Azure OpenAI Serviceのエンドポイント
AOAI_API_VERSION = "2024-06-01" # Azure OpenAI ServiceのAPIバージョン
AOAI_API_KEY = "XXXXXXXX" # Azure OpenAI ServiceのAPIキー
AOAI_CHAT_MODEL_NAME = "gpt-4-deploy" # Azure OpenAI Serviceのデプロイモデル名
# リクエストURL
url = f"{AOAI_ENDPOINT}openai/deployments/{AOAI_CHAT_MODEL_NAME}/chat/completions?api-version={AOAI_API_VERSION}"
# リクエストヘッダー
headers = {
    "Content-Type": "application/json",
    "api-key": AOAI_API_KEY
}
# 初期のメッセージ
messages = [
    {"role": "system", "content": "あなたは役立つアシスタントです。"},
    {"role": "user", "content": "トム・クルーズの誕生日にケーキをプレゼントしたいです。彼の年齢分のろうそくを購入するための金額を計算してください。ろうそくは一本本100円です。"}
]
# 関数の定義
functions = [
    {
        "name": "search_web",
        "description": "指定されたクエリでWebを検索します。",
        "parameters": {
            "type": "object",
            "properties": {
                "query": {
                    "type": "string",
                    "description": "検索するクエリ"
                }
            },
            "required": ["query"]
        }
    },
    {
        "name": "calculate",
        "description": "与えられた計算式に基づき計算を行います。",
        "parameters": {
            "type": "object",
            "properties": {
                "formula": {
                    "type": "string",
                    "description": "計算式(例: 52 * 100、100 - 1)"
                }
            },
            "required": ["formula"]
        }
    }
]
# 指定されたクエリでWebを検索する関数
def search_web(query: str):
    final_result = ""
    search_results = DDGS().text(query, max_results=10)
    for search_result in search_results:
        final_result = final_result + search_result["body"] + "\n"
    return final_result
# 与えられた計算式に基づき計算を行う関数
def calculate(formula: str):
    return str(eval(formula))
# メインループ
while True:
    # リクエストデータの準備
    data = {
        "messages": messages,
        "functions": functions,
        "function_call": "auto"
    }
    
    # リクエストを送信
    response = requests.post(url, headers=headers, data=json.dumps(data))
    result = response.json()
    
    # アシスタントの返信を取得
    assistant_message = result["choices"][0]["message"]
    messages.append(assistant_message)  # アシスタントのメッセージを履歴に追加
    
    # アシスタントが関数を呼び出したか確認
    if "function_call" in assistant_message:
        function_call = assistant_message["function_call"]
        function_name = function_call["name"]
        arguments = json.loads(function_call["arguments"])  # 文字列を辞書に変換
        
        # 関数を実行
        func = globals()[function_name]
        function_response = func(**arguments)
        print(f"実行された関数: {function_name}")
        print(f"関数に渡された引数: {arguments}")
        print(f"関数の実行結果: {function_response}")        
        # 関数の応答をメッセージに追加
        messages.append({
            "role": "function",
            "name": function_name,
            "content": function_response
        })
    else:
        # 最終的な回答が得られた場合
        final_observation = assistant_message["content"]
        print(f"最終回答: {final_observation}")
        break  # ループを終了
メッセージの定義
messages = [
    {"role": "system", "content": "あなたは役立つアシスタントです。"},
    {"role": "user", "content": "トム・クルーズの誕生日にケーキをプレゼントしたいです。彼の年齢分のろうそくを購入するための金額を計算してください。ろうそくは一本本100円です。"}
]
関数の定義
functions = [
    {
        "name": "search_web",
        "description": "指定されたクエリでWebを検索します。",
        "parameters": {
            "type": "object",
            "properties": {
                "query": {
                    "type": "string",
                    "description": "検索するクエリ"
                }
            },
            "required": ["query"]
        }
    },
    {
        "name": "calculate",
        "description": "与えられた計算式に基づき計算を行います。",
        "parameters": {
            "type": "object",
            "properties": {
                "formula": {
                    "type": "string",
                    "description": "計算式(例: 52 * 100、100 - 1)"
                }
            },
            "required": ["formula"]
        }
    }
]
関数の実装
# 指定されたクエリでWebを検索する関数
def search_web(query: str):
    final_result = ""
    search_results = DDGS().text(query, max_results=10)
    for search_result in search_results:
        final_result = final_result + search_result["body"] + "\n"
    return final_result
# 与えられた計算式に基づき計算を行う関数
def calculate(formula: str):
    return str(eval(formula))
メインループ
# メインループ
while True:
    # リクエストデータの準備
    data = {
        "messages": messages,
        "functions": functions,
        "function_call": "auto"
    }
リクエストの送信
    # リクエストを送信
    response = requests.post(url, headers=headers, data=json.dumps(data))
    result = response.json()
アシスタントの返信を取得
    # アシスタントの返信を取得
    assistant_message = result["choices"][0]["message"]
    messages.append(assistant_message) # アシスタントのメッセージを履歴に追加
関数の実行
    # アシスタントが関数を呼び出したか確認
    if "function_call" in assistant_message:
        function_call = assistant_message["function_call"]
        function_name = function_call["name"]
        arguments = json.loads(function_call["arguments"])  # 文字列を辞書に変換
        
        # 関数を実行
        func = globals()[function_name]
        function_response = func(**arguments)
        print(f"実行された関数: {function_name}")
        print(f"関数に渡された引数: {arguments}")
        print(f"関数の実行結果: {function_response}")
        # 関数の応答をメッセージに追加
        messages.append({
            "role": "function",
            "name": function_name,
            "content": function_response
        })
    else:
        # 最終的な回答が得られた場合
        final_observation = assistant_message["content"]
        print(f"最終回答: {final_observation}")
        break  # ループを終了
{
    "content": None,
    "function_call": {
        "name": "search_web",
        "arguments": "{\"query\": \"トム・クルーズの年齢\"}"
    },
    "role": "assistant"
}
{
    "content": "トム・クルーズは現在60歳なので、彼の年齢分のろうそくを購入するには6000円必要です。",
    "role": "assistant"
}
実行された関数: search_web
関数に渡された引数: {'query': 'トム・クルーズの年齢'}
関数の実行結果: トム・クルーズのサイエントロジーに対する開けた態度は、...(以下略)...
実行された関数: calculate
関数に渡された引数: {'formula': '60 * 100'}
関数の実行結果: 6000
最終回答: トム・クルーズの年齢は60歳なので、彼の年齢分のろうそくを購入するためには6000円が必要です。
LangChainでAIエージェント
from langchain_openai import AzureChatOpenAI
from langchain.agents import tool
from langchain_community.tools import DuckDuckGoSearchRun
from langchain.callbacks import StdOutCallbackHandler
from langchain_core.prompts import MessagesPlaceholder, ChatPromptTemplate
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain.memory import ConversationBufferWindowMemory
# Azure OpenAI Serviceの設定
AOAI_ENDPOINT = "https://hogehoge.openai.azure.com/" # Azure OpenAI Serviceのエンドポイント
AOAI_API_VERSION = "2024-06-01" # Azure OpenAI ServiceのAPIバージョン
AOAI_API_KEY = "XXXXXXX" # Azure OpenAI ServiceのAPIキー
AOAI_CHAT_MODEL_NAME = "gpt-4-deploy" # Azure OpenAI Serviceのデプロイモデル名
# LLMの初期化
llm = AzureChatOpenAI(
    azure_endpoint=AOAI_ENDPOINT,
    api_key=AOAI_API_KEY,
    api_version=AOAI_API_VERSION,
    openai_api_type="azure",
    azure_deployment=AOAI_CHAT_MODEL_NAME)
# 指定されたクエリでWebを検索する関数
@tool
def search_web(query: str):
    """
    指定されたクエリでWebを検索します。
    """
    search = DuckDuckGoSearchRun()
    return search.run(query)
# 与えられた計算式に基づき計算を行う関数
@tool
def calculate(formula: str):
    """
    与えられた計算式に基づき計算を行います。
    """
    return str(eval(formula))
tools = [search_web, calculate]
prompt = ChatPromptTemplate.from_messages([
    ("system", "あなたは役立つアシスタントです。"),
    MessagesPlaceholder(variable_name="chat_history"),
    ("user", "{input}"),
    MessagesPlaceholder(variable_name="agent_scratchpad")
])
agent = create_tool_calling_agent(llm, tools, prompt)
exucutor = AgentExecutor(
    agent=agent,
    tools=tools,
    verbose=True,
    memory=ConversationBufferWindowMemory(
            return_messages=True,
            memory_key="chat_history",
            k=10
        )
)
exucutor.invoke(
    {"input": "トム・クルーズの誕生日にケーキをプレゼントしたいです。彼の年齢分のろうそくを購入するための金額を計算してください。ろうそくは一本本100円です。"},
    callback_handler=StdOutCallbackHandler()
)
Azure OpenAI Serviceの設定
# Azure OpenAI Serviceの設定
AOAI_ENDPOINT = "https://hogehoge.openai.azure.com/" # Azure OpenAI Serviceのエンドポイント
AOAI_API_VERSION = "2024-06-01" # Azure OpenAI ServiceのAPIバージョン
AOAI_API_KEY = "XXXXXXX" # Azure OpenAI ServiceのAPIキー
AOAI_CHAT_MODEL_NAME = "gpt-4-deploy" # Azure OpenAI Serviceのデプロイモデル名
LLMの初期化
# LLMの初期化
llm = AzureChatOpenAI(
    azure_endpoint=AOAI_ENDPOINT,
    api_key=AOAI_API_KEY,
    api_version=AOAI_API_VERSION,
    openai_api_type="azure",
    azure_deployment=AOAI_CHAT_MODEL_NAME)
指定されたクエリでWebを検索する関数
# 指定されたクエリでWebを検索する関数
@tool
def search_web(query: str):
    """
    指定されたクエリでWebを検索します。
    """
    search = DuckDuckGoSearchRun()
    return search.run(query)
与えられた計算式に基づき計算を行う関数
# 与えられた計算式に基づき計算を行う関数
@tool
def calculate(formula: str):
    """
    与えられた計算式に基づき計算を行います。
    """
    return str(eval(formula))
関数のリスト化
tools = [search_web, calculate]
プロンプトの定義
prompt = ChatPromptTemplate.from_messages([
    ("system", "あなたは役立つアシスタントです。"),
    MessagesPlaceholder(variable_name="chat_history"),
    ("user", "{input}"),
    MessagesPlaceholder(variable_name="agent_scratchpad")
])
エージェントの作成
agent = create_tool_calling_agent(llm, tools, prompt)
AgentExcecutorの定義
exucutor = AgentExecutor(
    agent=agent,
    tools=tools,
    verbose=True,
    memory=ConversationBufferWindowMemory(
            return_messages=True,
            memory_key="chat_history",
            k=10
        )
)
エージェントの実行
exucutor.invoke(
    {"input": "トム・クルーズの誕生日にケーキをプレゼントしたいです。彼の年齢分のろうそくを購入するための金額を計算してください。ろうそくは一本本100円です。"},
    callback_handler=StdOutCallbackHandler()
)
> Entering new AgentExecutor chain...
まず、トム・クルーズの現在の年齢を知る必要があります。それを知るためには、インターネットで彼の誕生日を調べる必要があります。
Action: duckduckgo-search
Action Input: 'Tom Cruise birth date'Tom Cruise. Thomas Cruise Mapother IV (born July 3, 1962) is an ...(以下略)...
Action: duckduckgo-search
Action Input: 'today's date'Details about today's date with count of days, weeks, and months, Sun and Moon cycles, Zodiac signs and holidays. ...(以下略)...
Action: duckduckgo-search
Action Input: 'current date'Details about today's date with count of days, weeks, and months, Sun and Moon cycles, Zodiac signs and holidays. Tuesday September 24, 2024 . ...(以下略)...
Action: Calculator
Action Input: {'operation': 'subtract', 'operands': [2024, 1962]}Answer: 62トム・クルーズは今年で62歳になります。次に、ろうそくの価格とトム・クルーズの年齢を掛けることで、必要なろうそくの総額を計算します。
Action: Calculator
Action Input: {'operation': 'multiply', 'operands': [62, 100]}Answer: 6200トム・クルーズの年齢分のろうそくを購入するためには、6200円が必要です。
Final Answer: 6200円が必要です。














