GPT-6 Astra の Function Calling は AI エージェントの中核だ。モデルが呼び出すツールと引数を決定し、ランタイムが副作用を実行し、結果をモデルに返して推論を続ける。ループが正しければ Astra は自律的に API を叩き、データベースを読み、カスタムロジックを実行できる。誤っていればループが詰まり、ツール権限が暴走する。「Function Calling とは何か」は プロトコル比較 に、「API キーの取り方」は API チュートリアル に譲る。本稿では「動く」から「安全に本番稼動できる」までの 3 つのツールパターンを解説する。
ステップ 1:ツールスキーマを定義する
ツールスキーマは「このツールの名前・目的・パラメータ」をモデルに伝える JSON オブジェクトだ。天気 HTTP API を叩く関数ツールの例を示す。
import json
from openai import OpenAI
client = OpenAI()
tools = [
{
"type": "function",
"name": "get_weather",
"description": "Get current weather for a city via HTTP API.",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "City name, e.g. Tokyo, London, Shanghai",
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "Temperature unit. Default celsius.",
},
},
"required": ["city"],
"additionalProperties": False,
},
}
]
description は「何をするか」を動詞句で書く。モデルはここを読んでツールを呼ぶか判断する。必須パラメータは required に列挙し、additionalProperties: false で予期しないキーを排除する。複数ツールは同じリストに入れる。モデルが 1 ターンで複数ツールを並列呼び出しすることもあるため、ループは必ず全 tool_call を処理すること。
ステップ 2:完全なツールループ
リクエスト送信 → output の function_call を検出 → 実行 → 結果を input に追加 → 再送。function_call が消えるまで while で繰り返す。if で 1 回だけ処理すると最終テキスト回答が届かない。
import json
import requests
from openai import OpenAI
client = OpenAI()
def call_weather_api(city: str, unit: str = "celsius") -> dict:
"""Replace with your real HTTP endpoint."""
url = "https://api.example.com/weather"
r = requests.get(url, params={"city": city, "unit": unit}, timeout=5)
r.raise_for_status()
return r.json() # e.g. {"city": "Tokyo", "temp": 22, "condition": "sunny"}
# Initial request — model may call tools
response = client.responses.create(
model="gpt-6-astra",
input="What is the weather in Tokyo right now?",
tools=tools,
reasoning={"effort": "low"},
)
# Tool loop: keep going until model stops calling tools
while True:
tool_calls = [o for o in response.output if o.type == "function_call"]
if not tool_calls:
break # model is done — final answer is in response.output_text
tool_results = []
for call in tool_calls:
args = json.loads(call.arguments)
if call.name == "get_weather":
result = call_weather_api(**args)
else:
result = {"error": f"Unknown tool: {call.name}"}
tool_results.append({
"type": "function_call_output",
"call_id": call.call_id,
"output": json.dumps(result, ensure_ascii=False),
})
# Append history + results and continue
response = client.responses.create(
model="gpt-6-astra",
input=response.output + tool_results,
tools=tools,
reasoning={"effort": "low"},
)
print(response.output_text)
実装上の注意点。① response.output は複数の型が混在するリスト。type == "function_call" だけを抽出する。② 続行時は response.output + tool_results を input に渡す。履歴を省くとモデルがコンテキストを失う。③ call_id は tool_call と tool_result で一致させる。④ 戻り値は json.dumps で文字列化する。
name フィールドを known allowlist で検証し、引数を型チェックし、HTTP ツールにはタイムアウトとドメイン allowlist を設ける。DB ツールは事前定義 SQL のみ実行し、モデル出力を SQL に連結しない。ディスク書き込みやシェル実行を伴うツールはリモート Mac で隔離する。
ステップ 3:読み取り専用データベースツール
安全な DB ツールの 3 要件:読み取り専用権限(INSERT / UPDATE / DELETE を与えない)、パラメータ化クエリ(インジェクション防止)、SQL allowlist(任意クエリを防ぐ)。
import sqlite3
from typing import Any
# Only these parameterized queries can be executed — no raw user SQL
ALLOWED_QUERIES: dict[str, str] = {
"get_order": "SELECT id, status, total, created_at FROM orders WHERE id = ?",
"list_orders": "SELECT id, status, total FROM orders WHERE user_id = ? LIMIT 20",
}
def query_db(query_name: str, params: list[Any]) -> list[dict]:
"""Execute an allowlisted, parameterized read-only query.
Never accepts raw SQL strings from the model.
"""
sql = ALLOWED_QUERIES.get(query_name)
if sql is None:
raise ValueError(f"Query '{query_name}' not in allowlist")
con = sqlite3.connect("orders.db", check_same_thread=False)
con.row_factory = sqlite3.Row
try:
rows = con.execute(sql, params).fetchall()
return [dict(r) for r in rows]
finally:
con.close()
# Schema exposed to the model
db_tool = {
"type": "function",
"name": "query_db",
"description": "Query the orders DB. Only read-only allowlisted queries.",
"parameters": {
"type": "object",
"properties": {
"query_name": {
"type": "string",
"enum": list(ALLOWED_QUERIES.keys()),
"description": "Name of the allowlisted query",
},
"params": {
"type": "array",
"items": {"type": ["string", "number"]},
"description": "Positional parameters for the query",
},
},
"required": ["query_name", "params"],
},
}
本番では接続プール、クエリタイムアウト、行数上限の SQL 組み込み、機密フィールドのマスクが必要。モデルに返すのはサマリーであり、テーブル全体の raw dump ではない。
関数ツール vs ホストツール
| 軸 | 関数ツール(type: function) | ホストツール(OpenAI 運営) |
|---|---|---|
| 実行場所 | 自前のランタイム(ローカル / リモート Mac) | OpenAI インフラ |
| 適用シーン | プライベート API・社内 DB・カスタムロジック | Web 検索・ファイル検索・コード実行・shell・Computer Use |
| 開発コスト | スキーマ・実行関数・ループを自前で実装 | ツール名を指定するだけ、OpenAI が実行 |
| 課金 | トークンのみ | トークン + ツール従量費 |
| 隔離要件 | 書き込み系は実行環境を隔離 | OpenAI がサンドボックスを管理 |
2 種類は同一リクエストで混在可能。ホストツールの結果は自動でコンテキストに入るため手動提出不要。関数ツールの結果は function_call_output で手動提出する。
決定行列
| したいこと | 使うもの | 理由 |
|---|---|---|
| 社内 REST API を呼ぶ | 関数ツール | プライベートネットワークは OpenAI インフラから到達不可 |
| 社内 DB を読む(読み取り専用) | 関数ツール + SQL allowlist | プライベートデータ。アクセス権を自前で管理 |
| 公開 Web 情報をリアルタイムで検索 | ホスト web_search_preview | クロールとランキングは OpenAI 任せ |
| モデルに Python を実行させる | ホスト code_interpreter | OpenAI サンドボックス実行。ローカルリソース不要 |
| モデルにデスクトップ / ブラウザを操作させる | ホスト computer_use + 隔離 Mac | 可視デスクトップが必要。誤操作防止のため隔離 |
| ディスク書き込みを伴う shell コマンドを実行 | 関数ツール + リモート Mac 隔離 | 書き込み権限は破棄可能な環境に限定 |
推奨スタック
A — 個人開発・プロトタイプ: ローカル + 関数ツール(HTTP API、ディスク非書き込み)。スキーマとループを確立し call_id をログ。単一ツールループを安定させてから複数ツールへ。
B — 小チーム・プロダクト: 関数ツールは内部サービスに token 認証でマウント。DB ツールは読み取り専用 + 接続プール + クエリタイムアウト。ホストツールはタスク種別に応じて有効化。ツール実行ロジックはユニットテストでカバー。
C — エンタープライズ: 関数ツールは API ゲートウェイ経由で監査ログ付き。DB は読み取り専用レプリカ + 行レベルセキュリティ。Computer Use は破棄可能な隔離 Mac ノードで実行。ノード月額は 料金ページ、アカウントは ヘルプセンター。
よくある失敗
- ループを 1 回だけ実行(if を使う):最終テキスト回答が届かない。
- 履歴なしで結果を提出:input に tool_results だけ渡し、モデルがコンテキストを失う。
- モデル出力を SQL / シェルに直接連結:allowlist + パラメータ化クエリで防ぐ。
- HTTP ツールのタイムアウトなし:下流サービスがハングするとエージェント全体が止まる。
- ツールが大量データを返す:結果がコンテキストに入り 272K 升档料金を誘発。
アクションプラン:7 ステップ
- まずテキスト経路が通っていることを確認(API チュートリアル参照)。
- 最初の関数ツールスキーマを書く。description を動詞句に、required を設定し、additionalProperties: false。
- ツール実行関数を実装。HTTP ツールにタイムアウト、DB ツールに allowlist + パラメータ化クエリ。
- tools パラメータ付きで Responses リクエストを送り、response.output に function_call が現れることを確認。
- while True ループを実装。call_id を一致させて function_call_output を提出し、履歴を input に含める。
- ホストツール(web_search、code_interpreter)が必要なら追加。その結果は手動提出不要。
- ディスク書き込み・コマンド実行・ブラウザ操作のツールは隔離リモート Mac へ移し、セッション終了後に破棄。
FAQ
Function Calling は Responses API 必須か?
はい。ツール呼び出し・ホストツール・非同期ループは Responses API が必要です。
1 リクエストで定義できるツール数は?
厳密な小さい上限はないが、ツールが増えるほどプロンプトトークンコストが増える。タスクに必要なものだけを渡す。
SQL インジェクションを防ぐには?
? プレースホルダーのパラメータ化クエリと SQL allowlist を組み合わせる。モデル出力を SQL に直接連結しない。
関数ツールとホストツールの使い分けは?
プライベート API・社内 DB・カスタムロジックは関数ツール。Web 検索・コード実行・Computer Use はホストツール。
ツール呼び出し後の最終回答はストリーミング可能か?
可能です。非ストリーミングのツールループを安定させてから stream=True を加えること。
まとめ
GPT-6 Astra Function Calling の要点:明確な description を持つスキーマ、履歴付きで結果を提出する while ループ、allowlist + パラメータ化クエリの DB ツール、そして書き込み系ツールの隔離実行。リモート Mac ノードは レンタルページ と 料金ページ、アカウントは ヘルプセンター。