GPT-6 Astra 的工具呼叫(Function Calling)是 AI Agent 的核心能力:模型決定要呼叫哪個工具、傳什麼參數,執行環境負責副作用,再把結果送回模型繼續推理。這條鏈路寫對了,Astra 能自主查詢 API、讀資料庫、執行自訂邏輯;寫錯了,要麼工具迴圈死結,要麼工具權限失控。本文不重複「Function Calling 是什麼」——見 協議對照;也不重複「怎麼拿 Key」——見 API 呼叫教學。本文只把從「能跑」到「能安全上線」的三個核心工具類型拆開講清楚。
第一步:定義工具 schema
工具 schema 是一段 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 列出必填欄位,可選欄位加在 properties 裡但不進 required。additionalProperties: false 降低模型傳入意外欄位的機率。多個工具放在同一個 list 裡,模型根據對話決定呼叫哪個(或同時呼叫多個,迴圈需要處理並行 tool call)。
第二步:完整工具迴圈
工具迴圈的核心:發請求 → 檢查 output 裡是否有 function_call → 執行 → 把結果塞回 input → 再次請求,直到沒有新的 function_call 為止。迴圈必須是 while-true,不能假設模型只會呼叫一次工具。
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" 的項。第二,提交結果時,input 要傳 response.output + tool_results,帶上歷史,模型才知道自己問了什麼。第三,call_id 必須和工具結果的 call_id 對應。第四,工具傳回值用 json.dumps 序列化為字串。
name 欄位做白名單過濾;參數用型別校驗;HTTP 工具加逾時和域名白名單;資料庫工具只執行預設 SQL,不接受模型拼的 SQL 字串。會寫磁碟或跑命令的工具,放到遠端 Mac 上隔離執行。
第三步:資料庫唯讀工具
資料庫工具的安全底線:只讀(不給 INSERT / UPDATE / DELETE 權限)、參數化查詢(防注入)、SQL 白名單(防模型構造惡意查詢)。
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"],
},
}
生產環境還需要:連線池、查詢逾時、結果行數硬限制、敏感欄位遮罩。工具傳回給模型的應該是摘要,不是整張表的 raw dump。
函式工具 vs 託管工具
| 維度 | 函式工具(type: function) | 託管工具(OpenAI 運營) |
|---|---|---|
| 執行位置 | 你的執行環境(本機 / 遠端 Mac) | OpenAI 基礎設施 |
| 適用場景 | 私有 API、內部資料庫、自訂業務邏輯 | 網頁搜尋、程式碼直譯器、shell、Computer Use |
| 開發成本 | 需自己寫 schema + 執行邏輯 + 工具迴圈 | 只寫工具名,OpenAI 負責執行 |
| 計費 | 僅 token | token + 工具附加費 |
| 隔離要求 | 寫操作需隔離執行環境 | OpenAI 負責沙箱,仍需考量資料安全 |
兩類工具可以混用:同一次請求可以同時傳函式工具和託管工具。混用時,託管工具的傳回結果自動進入對話歷史,不需要手動提交;函式工具的結果必須手動透過 function_call_output 提交。
決策矩陣
| 如果你需要 | 用這個 | 理由 |
|---|---|---|
| 呼叫公司內網 REST API | 函式工具 | 內網對 OpenAI 基礎設施不開放 |
| 查詢內部資料庫(唯讀) | 函式工具 + allowlist SQL | 私有資料,需自己控制存取權限 |
| 搜尋公開網頁最新資訊 | 託管 web_search_preview | OpenAI 負責爬取和排名,無需維護 |
| 讓模型執行 Python 程式碼 | 託管 code_interpreter | 在 OpenAI 沙箱執行,不消耗本機資源 |
| 讓模型操作桌面 / 瀏覽器 | 託管 computer_use + 隔離 Mac | Computer Use 需要可見桌面;隔離執行避免誤操作 |
| 執行任意 shell 命令(寫磁碟) | 函式工具 + 遠端 Mac 隔離 | 寫權限必須在可銷毀環境,不上日常桌面 |
推薦組合
A — 個人開發者: 本機 + 函式工具(HTTP API,不寫磁碟)。工具 schema 寫好,迴圈跑通,日誌打出 call_id。先把單工具迴圈跑穩,再接多工具或並行 tool call。
B — 小團隊: 函式工具掛內部服務,API 加 token 鑑權;資料庫工具只讀,加連線池和查詢逾時;託管工具按任務需要開啟。工具執行邏輯單元測試覆蓋,傳回值做 schema 校驗。寫磁碟的場景放遠端 Mac。
C — 企業: 所有函式工具經 API 閘道代理,工具名和參數做稽核日誌;資料庫唯讀 replica,工具層再加 Row-Level Security;Computer Use 放到可銷毀的隔離 Mac 節點。帳戶和交付邊界見 說明中心,節點月費見 Mac mini 定價。
常見陷阱
- 工具迴圈只跑一輪:用 if 而不是 while,拿不到最終文字回答。
- 提交結果時不帶歷史:input 只傳 tool_results,模型不知道上下文。
- 把模型輸出直接拼進 SQL / shell:始終用 allowlist + 參數化查詢。
- 工具逾時沒設:下游服務卡住後整個 Agent 會話掛死。
- 工具傳回整張表:結果進上下文,行數過多觸發 272K 升檔費率。
落地步驟:7 步
- 確認文字通路已成功(見 API 呼叫教學);工具基於文字通路,不要跳步。
- 寫第一個函式工具 schema,description 清晰,parameters 有 required,additionalProperties false。
- 實作工具執行函式,HTTP 工具加逾時,DB 工具用 allowlist + 參數化查詢。
- 用 Responses API 發帶 tools 參數的請求,確認 response.output 出現 function_call 事件。
- 實作 while 迴圈,正確提交 function_call_output,call_id 一一對應。
- 按工具類型決定是否引入託管工具(web_search、code_interpreter),混用時注意託管工具結果無需手動提交。
- 寫磁碟、跑命令、開瀏覽器的工具移到遠端 Mac 隔離執行,會話結束銷毀節點。
FAQ
Function Calling 必須用 Responses API 嗎?
是的。工具呼叫、託管工具和非同步 tool 必須走 Responses API。
一次呼叫可以定義多少個工具?
沒有硬性小數字上限,但工具越多提示 token 越貴,只傳入相關工具。
資料庫工具怎麼防止 SQL 注入?
使用參數化查詢(? 占位符),配合預設 SQL 白名單,絕不把模型輸出直接拼接成 SQL。
函式工具和託管工具怎麼選?
私有 API 或內部 DB 用函式工具;網頁搜尋、程式碼執行、Computer Use 走託管工具;兩類可混用。
工具呼叫結果可以串流輸出嗎?
工具呼叫本身是結構化事件;工具結果提交後的最終回答可以串流。先驗證非串流工具迴圈成功,再叠加串流。
總結
GPT-6 Astra Function Calling 的三個核心:工具 schema 的 description 和 parameters 寫清楚;工具迴圈用 while,提交結果時帶上歷史;資料庫工具加 allowlist 和參數化查詢,寫權限的工具放到遠端 Mac 隔離。從 租用頁 和 定價頁 查看節點,帳戶問題走 說明中心。