Function Calling(ツール呼び出し)をする会話型エージェントの構築
国立大学の情報工学科4年です。AIの研究室に所属しており、卒業研究で社会課題に対してAI、LLM、マルチエージェントといったものを用いて解決手法を実験・検証し、Webアプリケーションにまで仕上げたいと考えています。これまでテーマ選定に向け論文を読んできましたが、今回は実際にそれを実現する面にフォーカスした学習過程を投稿させていただきます。『LLMのプロンプトエンジニアリング GitHub Copilotを生んだ開発者が教える生成AIアプリケーション開発』(オライリー・ジャパン)を読み、その内容をもとに落とし込んだ理解と実装を記述しています。
Python3の仮想環境を構築
python3 -m venv venv
Python3の仮想環境を有効化
source venv/bin/activate
ライブラリのインストール
pip install openai
OpenAI Platformで取得したAPIキーを設定
export OPENAI_API_KEY="sk-〇〇〇〇〇〇〇〇〇〇〇〇"
プログラム実行
python3 conversational_agent.py
会話型エージェントのプログラム
import random,json
from openai import OpenAI
from openai.types.chat import ChatCompletionMessage
def get_room_temp():
return str(random.randint(60,80))
def set_room_temp(temp):
return "DONE"
def process_messages(client,messages):
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=messages,
tools=tools,
)
response_message = response.choices[0].message
messages.append(response_message)
if response_message.tool_calls:
for tool_call in response_message.tool_calls:
function_name = tool_call.function.name
function_to_call = available_functions[function_name]
function_args = json.loads(tool_call.function.arguments)
function_response = function_to_call(**function_args)
messages.append(
{
"tool_call_id": tool_call.id,
"role": "tool",
"name": function_name,
"content": function_response,
}
)
return messages
def run_conversation(client):
messages = [
{
"role": "system",
"content": "あなたは、役に立つサーモスタットアシスタントです",
}
]
while True:
user_input = input(">>")
if user_input == "":
break
messages.append(
{
"role": "user",
"content": user_input,
}
)
while True:
new_messages = process_messages(client,messages)
# messages = new_messages
last_message = new_messages[-1]
if not isinstance(last_message, ChatCompletionMessage):
continue
if last_message.content is not None:
print(last_message.content)
if last_message.tool_calls is None:
break
return messages
tools = [
{
"type": "function",
"function": {
"name": "get_room_temp",
"description": "華氏で部屋の温度を取得",
},
},
{
"type": "function",
"function": {
"name": "set_room_temp",
"description": "華氏で部屋の温度を設定",
"parameters": {
"type": "object",
"properties": {
"temp": {
"type": "integer",
"description": "華氏での望ましい部屋の温度",
},
},
"required": ["temp"],
},
},
}
]
available_functions = {
"get_room_temp": get_room_temp,
"set_room_temp": set_room_temp,
}
client = OpenAI()
run_conversation(client)
会話型エージェント(LLMアプリケーション)との会話
get_room_temp()を呼び出す
ユーザー:「部屋の温度を教えて」とメッセージをアプリケーションに送る
→ アプリケーション:ユーザーメッセージを追加し、プロンプトを作成。LLMに送る。
→ LLM:get_room_temp() を補完。アプリケーションに送る。
→ アプリケーション:ツール呼び出しを解析。LLMが出力した関数を呼び出し、ツールを呼び出す(ツール評価のリクエスト)
→ ツール:リクエストされた処理を実行(ツール評価のレスポンス)。数値(温度)をアプリケーションに返す。
→ アプリケーション:プロンプトにツール呼び出しを追加。LLMに送る。
→ LLM:アシスタントメッセージ(「部屋の温度は71°Fです。」)を補完。 アプリケーションに送る。
→ アプリケーション:スレッドにアシスタントメッセージを追加。ユーザーにアシスタントメッセージ(「部屋の温度は71°Fです。」)を送る。
set_room_temp()を呼び出す
ツールを呼ぶべきでない質問
ツールを呼ばずに、普通のLLM回答になる。
境界ケース
LLMが正しく判断できるか試す
気づき
さっき部屋の温度を68度に変更したのに、部屋の温度が71度になってしまった。原因は、get_room_temp()関数で乱数を返してるから。
def get_room_temp():
return str(random.randint(60,80))
ここから学んだことは、「LLM ≠ 記憶」ということ。LLMは会話履歴は持てる。でも、”アプリケーションの状態”は別で管理する必要がある。
自分の研究におけるマルチエージェントでも
- 過去の議論
- 信頼度
- 参照データ
をどこかに保存する必要がある。
つまり、
ツール呼び出し
↓
状態管理
↓
エージェント
という流れになる。調べた結果、AIエージェント設計での重要な考え方だと知った。
アプリケーションで温度を保存するために、ツール定義部分を変更
room_temp = 72
def get_room_temp():
return str(room_temp)
def set_room_temp(temp):
global room_temp
room_temp = temp
return "DONE"
これで再度確認。
温度がアプリケーションで保持されてることを確認!
修正後のプログラム
import random,json
from openai import OpenAI
from openai.types.chat import ChatCompletionMessage
#ツール定義
room_temp = 72
def get_room_temp():
return str(room_temp)
def set_room_temp(temp):
global room_temp
room_temp = temp
return "DONE"
#メッセージ処理機能
def process_messages(client,messages):
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=messages,
tools=tools,
)
response_message = response.choices[0].message
messages.append(response_message)
if response_message.tool_calls:
for tool_call in response_message.tool_calls:
function_name = tool_call.function.name
function_to_call = available_functions[function_name]
function_args = json.loads(tool_call.function.arguments)
function_response = function_to_call(**function_args)
messages.append(
{
"tool_call_id": tool_call.id,
"role": "tool",
"name": function_name,
"content": function_response,
}
)
return messages
def run_conversation(client):
messages = [
{
"role": "system",
"content": "あなたは、役に立つサーモスタットアシスタントです",
}
]
while True:
user_input = input(">>")
if user_input == "":
break
messages.append(
{
"role": "user",
"content": user_input,
}
)
while True:
new_messages = process_messages(client,messages)
# messages = new_messages
last_message = new_messages[-1]
if not isinstance(last_message, ChatCompletionMessage):
continue
if last_message.content is not None:
print(last_message.content)
if last_message.tool_calls is None:
break
return messages
#OpenAIがプロンプトでツールの関数を表現できるようにJSONスキーマでツールを定義
tools = [
{
"type": "function",
"function": {
"name": "get_room_temp",
"description": "華氏で部屋の温度を取得",
},
},
{
"type": "function",
"function": {
"name": "set_room_temp",
"description": "華氏で部屋の温度を設定",
"parameters": {
"type": "object",
"properties": {
"temp": {
"type": "integer",
"description": "華氏での望ましい部屋の温度",
},
},
"required": ["temp"],
},
},
}
]
#OpenAIがツール呼び出しを処理できるように、関数をPythonで定義
available_functions = {
"get_room_temp": get_room_temp,
"set_room_temp": set_room_temp,
}
client = OpenAI()
run_conversation(client)
OpenAIプラットフォームのトークン消費は、4.99 Credit baranceのまま。モデル「gpt-4o-mini」はコストが低いらしい。今回の会話型エージェントの仕組みを学ぶためのプログラム程度なら十分。






