工具使用設計模式 (Tool Use Design Pattern)
原文:Microsoft AI Agents for Beginners · MIT 授權
為什麼工具這麼重要?
如果 LLM 只能根據訓練資料回答,它的能力就被鎖在模型內部。加上工具後,Agent 可以即時查詢股票價格、更新 CRM、操作檔案系統——從「語言模型」變成真正的「行動引擎」。本課深入探討 Tool Use 設計模式:如何讓 AI Agent 選擇並使用外部工具來達成目標。
學習目標
- 定義 Tool Use 設計模式及其目的
- 辨識適合使用此模式的場景
- 理解實作所需的關鍵元件
- 認識建構可信賴 Agent 時的安全考量
什麼是 Tool Use 設計模式?
Tool Use 設計模式的核心是賦予 LLM 與外部工具互動的能力。工具是 Agent 可執行的程式碼——可以是一個簡單的函數(如計算機),也可以是第三方服務的 API 呼叫(如股價查詢、天氣預報)。在 AI Agent 的脈絡中,工具是由 Agent 根據模型生成的 Function Call來執行的。
適用場景
AI Agent 可以運用工具完成複雜任務、擷取資訊或做出決策。Tool Use 模式常用於需要與外部系統動態互動的情境:
| 場景 | 說明 | 範例 |
|---|---|---|
| 動態資訊擷取 | 查詢外部 API 或資料庫取得即時資料 | 查詢 SQLite 資料庫做分析、抓取股價或天氣 |
| 程式碼執行與解譯 | 執行程式碼解決數學問題、產生報表或模擬 | 讓 Agent 跑 Python 解數學方程式 |
| 工作流程自動化 | 整合排程器、郵件服務、資料管線 | 自動化每日報表產出並寄送郵件 |
| 客服支援 | 與 CRM、工單系統、知識庫互動 | 查詢客戶訂單狀態、自動建立工單 |
| 內容生成與編輯 | 使用文法檢查、摘要、安全評估工具 | 寫完文章後自動檢查錯字與敏感內容 |
實作 Tool Use 的關鍵元件
以下元件共同構成 Tool Use 設計模式的基礎:
| 元件 | 說明 |
|---|---|
| 函數/工具 Schema | 所有可用工具的詳細定義,包含函數名稱、用途、必要參數和預期輸出。讓 LLM 理解有哪些工具可用,以及如何構造有效的呼叫請求。 |
| 函數執行邏輯 | 管理工具何時被呼叫、如何被呼叫。可能包含規劃模組、路由機制或條件流程,動態決定工具使用方式。 |
| 訊息處理系統 | 管理使用者輸入、LLM 回應、工具呼叫和工具輸出之間的對話流程。 |
| 工具整合框架 | 將 Agent 連接到各種工具的基礎設施,無論是簡單函數還是複雜的外部服務。 |
| 錯誤處理與驗證 | 處理工具執行失敗、驗證參數、管理非預期回應的機制。 |
| 狀態管理 | 追蹤對話脈絡、先前的工具互動記錄和持久性資料,確保多回合互動的一致性。 |
深入 Function / Tool Calling
Function Calling 是讓 LLM 與工具互動的主要方式。你可能常看到「Function」和「Tool」交替使用——因為「函數(可重用的程式碼區塊)」就是 Agent 用來執行任務的「工具」。流程如下:
- 將包含所有可用函數描述的 Schema 傳送給 LLM
- LLM 根據使用者請求,從 Schema 中選擇最合適的函數,回傳函數名稱與參數
- 程式執行被選中的函數,取得結果
- 將結果回傳給 LLM,LLM 用此資訊回覆使用者
實作 Function Calling 的三要素
- 支援 Function Calling 的 LLM 模型——非所有模型都支援,需確認(Azure OpenAI 支援)
- 包含函數描述的 Schema——JSON 格式,定義函數名稱、用途和參數
- 每個函數的實作程式碼——真正執行任務的邏輯
範例:查詢城市當前時間
步驟一:初始化支援 Function Calling 的 LLM 客戶端(Azure OpenAI Responses API)
from openai import OpenAI
client = OpenAI(
base_url=f"{os.environ['AZURE_OPENAI_ENDPOINT'].rstrip('/')}/openai/v1/",
api_key=os.environ["AZURE_OPENAI_API_KEY"],
)
deployment_name = os.environ["AZURE_OPENAI_DEPLOYMENT"]
步驟二:建立函數 Schema 並傳送給 LLM
# 定義工具 Schema(Responses API 扁平格式)
tools = [
{
"type": "function",
"name": "get_current_time",
"description": "查詢指定城市的當前時間",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "城市名稱,例如 San Francisco",
},
},
"required": ["location"],
},
}
]
messages = [{"role": "user", "content": "舊金山現在幾點?"}]
# 第一次 API 呼叫:要求模型使用工具
response = client.responses.create(
model=deployment_name,
input=messages,
tools=tools,
tool_choice="auto",
store=False,
)
messages += response.output
# 模型回傳的是一個 Function Call,不是最終答案!
# [ResponseFunctionToolCall(
# arguments='{"location":"San Francisco"}',
# call_id='call_pOsKdUlqvdyttYB67MOj434b',
# name='get_current_time',
# type='function_call'
# )]
步驟三:實作函數邏輯並執行
def get_current_time(location):
"""查詢指定城市的當前時間"""
location_lower = location.lower()
for key, timezone in TIMEZONE_DATA.items():
if key in location_lower:
current_time = datetime.now(ZoneInfo(timezone)).strftime("%I:%M %p")
return json.dumps({"location": location, "current_time": current_time})
return json.dumps({"location": location, "current_time": "unknown"})
# 處理 Tool Call
tool_calls = [item for item in response.output if item.type == "function_call"]
if tool_calls:
for tool_call in tool_calls:
if tool_call.name == "get_current_time":
function_args = json.loads(tool_call.arguments)
time_response = get_current_time(location=function_args.get("location"))
messages.append({
"type": "function_call_output",
"call_id": tool_call.call_id,
"output": time_response,
})
# 第二次 API 呼叫:取得最終回覆
final_response = client.responses.create(
model=deployment_name,
input=messages,
tools=tools,
store=False,
)
print(final_response.output_text)
# → 舊金山現在是上午 09:24。
關鍵理解:Function Calling 是「兩段式」流程——LLM 第一次只回傳要呼叫哪個函數 + 什麼參數,你的程式碼實際執行函數後,再把結果送回 LLM,LLM 才生成最終的自然語言回覆。這不是單次請求能完成的。
使用 Agentic 框架實作 Tool Use
雖然可以從頭實作 Function Calling,但如第 2 課所介紹,Agentic 框架提供了預建的構件來簡化開發。
Microsoft Agent Framework
Microsoft Agent Framework 是開源的 AI Agent 框架,直接使用 Python @tool 裝飾器就能定義工具,框架會自動處理模型與程式碼之間的來回通訊:
import os
from agent_framework import tool
from agent_framework.foundry import FoundryChatClient
from azure.identity import AzureCliCredential
@tool(approval_mode="never_require")
def get_current_time(location: str) -> str:
"""查詢指定城市的當前時間"""
... # 實作邏輯
# 建立客戶端
provider = FoundryChatClient(
project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"],
model=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"],
credential=AzureCliCredential(),
)
# 一行建立 Agent 並執行
agent = provider.as_agent(
name="TimeAgent",
instructions="使用可用工具回答問題。",
tools=get_current_time
)
response = await agent.run("現在幾點了?")
使用 @tool 裝飾器的優勢
- 自動序列化:框架自動將函數和參數轉換為 Schema 傳給 LLM
- 自動 Tool Call 處理:不需手動解析 Function Call、呼叫工具、處理回應
- 程式碼簡潔:一行定義工具,一行建立 Agent,大幅減少樣板程式碼
Microsoft Foundry Agent Service
Microsoft Foundry Agent Service 是較新的全託管 Agent 框架,專為企業應用設計,不需管理底層運算和儲存資源。相較於直接使用 LLM API,它提供以下優勢:
| 優勢 | 說明 |
|---|---|
| 自動工具呼叫 | 不需手動解析 Tool Call、呼叫工具、處理回應——全部在伺服器端完成 |
| 安全託管資料 | 不需自行管理對話狀態,使用 Thread 儲存所有需要的資訊 |
| 開箱即用的工具 | 內建 Bing、Azure AI Search、Azure Functions 等工具可直接使用 |
Foundry Agent Service 內建工具分類
| 類別 | 工具 | 用途 |
|---|---|---|
| 知識工具 (Knowledge Tools) |
Bing Search 接地 | 以 Bing 搜尋結果為 LLM 提供即時資訊 |
| File Search | 在檔案中搜尋相關內容 | |
| Azure AI Search | 在 Azure AI Search 索引中查詢 | |
| 行動工具 (Action Tools) |
Function Calling | 呼叫自定義函數 |
| Code Interpreter | 執行程式碼(Python)進行資料分析 | |
| OpenAPI 定義工具 | 透過 OpenAPI 規格整合第三方 API | |
| Azure Functions | 呼叫 Azure Functions 無伺服器函數 |
實戰範例:Contoso 銷售資料分析 Agent
假設你是 Contoso 的銷售專員,想建立一個對話式 Agent 來回答銷售資料相關問題。以下使用 Foundry Agent Service 的程式碼展示如何結合自定義函數和內建 Code Interpreter:
import os
from azure.ai.projects import AIProjectClient
from azure.identity import DefaultAzureCredential
from fetch_sales_data_functions import fetch_sales_data_using_sqlite_query
from azure.ai.projects.models import ToolSet, FunctionTool, CodeInterpreterTool
project_client = AIProjectClient.from_connection_string(
credential=DefaultAzureCredential(),
conn_str=os.environ["PROJECT_CONNECTION_STRING"],
)
# 初始化工具集
toolset = ToolSet()
# 加入自定義函數工具
fetch_data_function = FunctionTool(fetch_sales_data_using_sqlite_query)
toolset.add(fetch_data_function)
# 加入內建 Code Interpreter 工具
code_interpreter = CodeInterpreterTool()
toolset.add(code_interpreter)
# 建立 Agent——LLM 會自動根據使用者請求選擇合適的工具
agent = project_client.agents.create_agent(
model="gpt-5-mini",
name="my-agent",
instructions="You are helpful agent",
toolset=toolset
)
關鍵設計決策:Toolset 的概念讓 Agent 同時擁有「自定義函數」和「內建工具」。LLM 會根據請求內容自動選擇——問 SQL 查詢就用 fetch_sales_data_using_sqlite_query,需要資料分析就用 Code Interpreter。你不需要手寫路由邏輯。
安全性考量
⚠️ 常見安全疑慮:SQL Injection
LLM 動態生成的 SQL 可能帶來 SQL Injection 或惡意操作(如 DROP TABLE)的風險。這些顧慮是合理的,但可透過以下方式有效緩解:
| 防護措施 | 具體做法 |
|---|---|
| 唯讀權限 | 對大多數資料庫,將應用程式設定為唯讀(SELECT)角色。PostgreSQL 或 Azure SQL 可指派 read-only role。 |
| 安全執行環境 | 在企業場景中,資料通常從營運系統萃取轉換到唯讀資料倉儲,確保資料安全且效能最佳化。 |
| 友善的 Schema | 設計對 LLM 友善的資料結構,簡化查詢複雜度,降低錯誤 SQL 的可能性。 |
✅ 安全最佳實踐
- 資料庫層級防護:永遠在資料庫層設定存取權限,不要依賴應用程式層的過濾
- 只讀優先:除非必要,Agent 不應有寫入權限
- 審計日誌:記錄所有 Agent 執行的 SQL 查詢,便於事後追查
範例程式碼
- Python:Agent Framework
- .NET:Agent Framework
本課重點回顧
- Tool Use = Function Calling 核心機制:LLM 選函數 → 程式執行 → 結果回傳 LLM → 自然語言回覆
- 六大關鍵元件:Tool Schema、執行邏輯、訊息處理、整合框架、錯誤處理、狀態管理
- 框架簡化一切:Microsoft Agent Framework 的
@tool裝飾器、Foundry Agent Service 的 Toolset,大幅降低實作複雜度 - 安全性不可忽視:永遠在資料庫層設定唯讀權限,不要信任 LLM 生成的 SQL
- Toolset 是思維升級:不要把每個工具分開管——讓 LLM 自己從 Toolset 中選擇最合適的工具