OpenAI · GPT-6 Astra

GPT-6 Astra Function Calling 實戰 2026:讓 AI 呼叫 API、資料庫和自訂工具

2026.09.11 · 約 15 分鐘閱讀

Function Calling 不難,難在工具迴圈寫錯、工具權限沒有隔離,以及把 Chat Completions 當預設擋死了託管工具。 下文給出 HTTP API 工具 schema、完整工具迴圈、資料庫唯讀模式、函式工具 vs 託管工具對比、決策矩陣和 7 步驗收。

開發者在螢幕前除錯 GPT-6 Astra Function Calling 工具呼叫迴圈

GPT-6 Astra 的工具呼叫(Function Calling)是 AI Agent 的核心能力:模型決定要呼叫哪個工具、傳什麼參數,執行環境負責副作用,再把結果送回模型繼續推理。這條鏈路寫對了,Astra 能自主查詢 API、讀資料庫、執行自訂邏輯;寫錯了,要麼工具迴圈死結,要麼工具權限失控。本文不重複「Function Calling 是什麼」——見 協議對照;也不重複「怎麼拿 Key」——見 API 呼叫教學。本文只把從「能跑」到「能安全上線」的三個核心工具類型拆開講清楚。

Responses
工具呼叫唯一主路徑
function_call
output 事件類型
allowlist
資料庫工具安全基線
必須用 Responses API
工具呼叫、託管工具(網頁搜尋、程式碼直譯器、Computer Use 等)和非同步 tool 全部要走 Responses API。把工具焊在 Chat Completions 上,接託管工具時會直接 400。文字探測已經成功的前提下,本文所有程式碼走 Responses。

第一步:定義工具 schema

工具 schema 是一段 JSON 物件,告訴模型「這個工具叫什麼、做什麼、接受什麼參數」。以呼叫天氣 HTTP API 的工具為例:

工具 schema 定義(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,不能假設模型只會呼叫一次工具。

完整工具迴圈(Responses API)
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 序列化為字串。

安全:allowlist 工具,隔離執行
工具能做什麼,執行環境說了算。對 name 欄位做白名單過濾;參數用型別校驗;HTTP 工具加逾時和域名白名單;資料庫工具只執行預設 SQL,不接受模型拼的 SQL 字串。會寫磁碟或跑命令的工具,放到遠端 Mac 上隔離執行。

第三步:資料庫唯讀工具

資料庫工具的安全底線:只讀(不給 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"],
    },
}

生產環境還需要:連線池、查詢逾時、結果行數硬限制、敏感欄位遮罩。工具傳回給模型的應該是摘要,不是整張表的 raw dump。

函式工具 vs 託管工具

維度函式工具(type: function)託管工具(OpenAI 運營)
執行位置你的執行環境(本機 / 遠端 Mac)OpenAI 基礎設施
適用場景私有 API、內部資料庫、自訂業務邏輯網頁搜尋、程式碼直譯器、shell、Computer Use
開發成本需自己寫 schema + 執行邏輯 + 工具迴圈只寫工具名,OpenAI 負責執行
計費僅 tokentoken + 工具附加費
隔離要求寫操作需隔離執行環境OpenAI 負責沙箱,仍需考量資料安全

兩類工具可以混用:同一次請求可以同時傳函式工具和託管工具。混用時,託管工具的傳回結果自動進入對話歷史,不需要手動提交;函式工具的結果必須手動透過 function_call_output 提交。

決策矩陣

如果你需要用這個理由
呼叫公司內網 REST API函式工具內網對 OpenAI 基礎設施不開放
查詢內部資料庫(唯讀)函式工具 + allowlist SQL私有資料,需自己控制存取權限
搜尋公開網頁最新資訊託管 web_search_previewOpenAI 負責爬取和排名,無需維護
讓模型執行 Python 程式碼託管 code_interpreter在 OpenAI 沙箱執行,不消耗本機資源
讓模型操作桌面 / 瀏覽器託管 computer_use + 隔離 MacComputer 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 定價

常見陷阱

  1. 工具迴圈只跑一輪:用 if 而不是 while,拿不到最終文字回答。
  2. 提交結果時不帶歷史:input 只傳 tool_results,模型不知道上下文。
  3. 把模型輸出直接拼進 SQL / shell:始終用 allowlist + 參數化查詢。
  4. 工具逾時沒設:下游服務卡住後整個 Agent 會話掛死。
  5. 工具傳回整張表:結果進上下文,行數過多觸發 272K 升檔費率。

落地步驟:7 步

  1. 確認文字通路已成功(見 API 呼叫教學);工具基於文字通路,不要跳步。
  2. 寫第一個函式工具 schema,description 清晰,parameters 有 required,additionalProperties false。
  3. 實作工具執行函式,HTTP 工具加逾時,DB 工具用 allowlist + 參數化查詢。
  4. 用 Responses API 發帶 tools 參數的請求,確認 response.output 出現 function_call 事件。
  5. 實作 while 迴圈,正確提交 function_call_output,call_id 一一對應。
  6. 按工具類型決定是否引入託管工具(web_search、code_interpreter),混用時注意託管工具結果無需手動提交。
  7. 寫磁碟、跑命令、開瀏覽器的工具移到遠端 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 隔離。從 租用頁定價頁 查看節點,帳戶問題走 說明中心

延伸閱讀

工具要呼叫真實 API?放到隔離的 Cloud Mac 上再跑

本機呼叫 HTTP API 可以;一旦工具要寫磁碟、改倉庫或開瀏覽器,執行環境不該和日常桌面共用。遠端 Mac 按會話隔離工作區,適合把 Astra Function Calling 從「能跑」推進到「能安全上線」。

立即訂購 · 查看定價

GPT-6 Astra Function Calling

工具要呼叫真實 API?放到隔離的 Cloud Mac 上再跑

Cloud Mac · isolated agent runtime

立即訂購
Mac 立即訂購