Microsoft Foundry 代理程式支援函式呼叫,讓你能擴充代理程式並具備自訂功能。 定義一個函式及其名稱、參數和描述,代理程式就可以請求你的應用程式呼叫它。 你的應用執行函式並回傳結果。 客服人員會利用結果,根據系統中準確且即時的數據繼續對話。
這很重要
執行會在建立後 10 分鐘過期。 請在工具產出過期前提交。
你可以在 Microsoft Foundry 入口網站裡用函式工具執行代理程式。 然而,入口網站不支援在代理上新增、移除或更新函式定義。 使用 SDK 或 REST API 來配置功能工具。
使用支援
✔️ (GA) 表示正式上市,✔️(Preview) 表示公開預覽,破折號(-) 表示此功能尚未開放。
| Microsoft Foundry 支援 | Python SDK | C# SDK | JavaScript SDK | Java 開發套件 | REST API | 基本代理程序設定 | 標準代理程序設定 |
|---|---|---|---|---|---|---|---|
| ✔️ | ✔️ (正式發行) | ✔️ (預告) | ✔️ (正式發行) | ✔️ (正式發行) | ✔️ (正式發行) | ✔️ | ✔️ |
先決條件
開始之前,請確定您擁有:
一個 Foundry 專案與一個已部署的模型。
你語言的 SDK 套件:
- Python:
azure-ai-projects(最新) - .NET:
Azure.AI.Extensions.OpenAI(預發行版) - TypeScript:
@azure/ai-projects(最新) - Java:
azure-ai-agents
關於安裝與驗證步驟,請參閱 快速入門。
- Python:
小提示
如果你使用 DefaultAzureCredential,請在執行樣本前,使用 az login 登入。
用函式工具建立代理
函式呼叫遵循以下模式:
- 定義函式工具 — 描述每個函式的名稱、參數與用途。
- 建立代理 — 將代理註冊在貴函式定義中。
- 發送提示詞 — 代理會分析提示詞,必要時請求函式呼叫。
- 執行與返回 — 您的應用程式執行該函式並將輸出回送給代理。
- 取得最終回應 — 代理人利用你的函數輸出來完成回應。
請使用以下程式碼範例建立代理程式,處理函式呼叫,並將工具輸出回傳給代理。
import json
from azure.ai.projects import AIProjectClient
from azure.ai.projects.models import PromptAgentDefinition, Tool, FunctionTool
from azure.identity import DefaultAzureCredential
from openai.types.responses.response_input_param import FunctionCallOutput, ResponseInputParam
def get_horoscope(sign: str) -> str:
"""Generate a horoscope for the given astrological sign."""
return f"{sign}: Next Tuesday you will befriend a baby otter."
# Format: "https://resource_name.ai.azure.com/api/projects/project_name"
PROJECT_ENDPOINT = "your_project_endpoint"
project = AIProjectClient(
endpoint=PROJECT_ENDPOINT,
credential=DefaultAzureCredential(),
)
openai = project.get_openai_client()
# Define a function tool for the model to use
func_tool = FunctionTool(
name="get_horoscope",
parameters={
"type": "object",
"properties": {
"sign": {
"type": "string",
"description": "An astrological sign like Taurus or Aquarius",
},
},
"required": ["sign"],
"additionalProperties": False,
},
description="Get today's horoscope for an astrological sign.",
strict=True,
)
tools: list[Tool] = [func_tool]
agent = project.agents.create_version(
agent_name="MyAgent",
definition=PromptAgentDefinition(
model="gpt-4.1-mini",
instructions="You are a helpful assistant that can use function tools.",
tools=tools,
),
)
# Prompt the model with tools defined
response = openai.responses.create(
input="What is my horoscope? I am an Aquarius.",
extra_body={"agent_reference": {"name": agent.name, "type": "agent_reference"}},
)
input_list: ResponseInputParam = []
# Process function calls
for item in response.output:
if item.type == "function_call":
if item.name == "get_horoscope":
# Execute the function logic for get_horoscope
horoscope = get_horoscope(**json.loads(item.arguments))
# Provide function call results to the model
input_list.append(
FunctionCallOutput(
type="function_call_output",
call_id=item.call_id,
output=json.dumps({"horoscope": horoscope}),
)
)
# Submit function results and get the final response
response = openai.responses.create(
input=input_list,
previous_response_id=response.id,
extra_body={"agent_reference": {"name": agent.name, "type": "agent_reference"}},
)
print(f"Agent response: {response.output_text}")
# Clean up resources
project.agents.delete_version(agent_name=agent.name, agent_version=agent.version)
預期的輸出
下列範例會顯示預期的輸出:
Agent response: Your horoscope for Aquarius: Next Tuesday you will befriend a baby otter.
使用具有函數示例的代理
在這個例子中,你使用帶有代理的局部函數。 利用這些函式來回應使用者問題,給代理提供具體資訊。 此範例中的程式碼是同步的。 若要查看非同步執行範例,請參考 GitHub 上的 Azure SDK for .NET 儲存庫中的 sample code 範例。
using System;
using Azure.AI.Projects;
using Azure.AI.Extensions.OpenAI;
using Azure.Identity;
class FunctionCallingDemo
{
// Define three functions:
// 1. GetUserFavoriteCity always returns "Seattle, WA".
// 2. GetCityNickname handles only "Seattle, WA"
// and throws an exception for other city names.
// 3. GetWeatherAtLocation returns the weather in Seattle, WA.
/// Example of a function that defines no parameters but
/// returns the user's favorite city.
private static string GetUserFavoriteCity() => "Seattle, WA";
/// <summary>
/// Example of a function with a single required parameter
/// </summary>
/// <param name="location">The location to get nickname for.</param>
/// <returns>The city nickname.</returns>
/// <exception cref="NotImplementedException"></exception>
private static string GetCityNickname(string location) => location switch
{
"Seattle, WA" => "The Emerald City",
_ => throw new NotImplementedException(),
};
/// <summary>
/// Example of a function with one required and one optional, enum parameter
/// </summary>
/// <param name="location">Get weather for location.</param>
/// <param name="temperatureUnit">"c" or "f"</param>
/// <returns>The weather in selected location.</returns>
/// <exception cref="NotImplementedException"></exception>
public static string GetWeatherAtLocation(string location, string temperatureUnit = "f") => location switch
{
"Seattle, WA" => temperatureUnit == "f" ? "70f" : "21c",
_ => throw new NotImplementedException()
};
// For each function, create FunctionTool, which defines the function name, description, and parameters.
public static readonly FunctionTool getUserFavoriteCityTool = ResponseTool.CreateFunctionTool(
functionName: "getUserFavoriteCity",
functionDescription: "Gets the user's favorite city.",
functionParameters: BinaryData.FromString("{}"),
strictModeEnabled: false
);
public static readonly FunctionTool getCityNicknameTool = ResponseTool.CreateFunctionTool(
functionName: "getCityNickname",
functionDescription: "Gets the nickname of a city, e.g. 'LA' for 'Los Angeles, CA'.",
functionParameters: BinaryData.FromObjectAsJson(
new
{
Type = "object",
Properties = new
{
Location = new
{
Type = "string",
Description = "The city and state, e.g. San Francisco, CA",
},
},
Required = new[] { "location" },
},
new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }
),
strictModeEnabled: false
);
private static readonly FunctionTool getCurrentWeatherAtLocationTool = ResponseTool.CreateFunctionTool(
functionName: "getCurrentWeatherAtLocation",
functionDescription: "Gets the current weather at a provided location.",
functionParameters: BinaryData.FromObjectAsJson(
new
{
Type = "object",
Properties = new
{
Location = new
{
Type = "string",
Description = "The city and state, e.g. San Francisco, CA",
},
Unit = new
{
Type = "string",
Enum = new[] { "c", "f" },
},
},
Required = new[] { "location" },
},
new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }
),
strictModeEnabled: false
);
// Create the method GetResolvedToolOutput.
// It runs the preceding functions and wraps the output in a ResponseItem object.
private static FunctionCallOutputResponseItem GetResolvedToolOutput(FunctionCallResponseItem item)
{
if (item.FunctionName == getUserFavoriteCityTool.FunctionName)
{
return ResponseItem.CreateFunctionCallOutputItem(item.CallId, GetUserFavoriteCity());
}
using JsonDocument argumentsJson = JsonDocument.Parse(item.FunctionArguments);
if (item.FunctionName == getCityNicknameTool.FunctionName)
{
string locationArgument = argumentsJson.RootElement.GetProperty("location").GetString();
return ResponseItem.CreateFunctionCallOutputItem(item.CallId, GetCityNickname(locationArgument));
}
if (item.FunctionName == getCurrentWeatherAtLocationTool.FunctionName)
{
string locationArgument = argumentsJson.RootElement.GetProperty("location").GetString();
if (argumentsJson.RootElement.TryGetProperty("unit", out JsonElement unitElement))
{
string unitArgument = unitElement.GetString();
return ResponseItem.CreateFunctionCallOutputItem(item.CallId, GetWeatherAtLocation(locationArgument, unitArgument));
}
return ResponseItem.CreateFunctionCallOutputItem(item.CallId, GetWeatherAtLocation(locationArgument));
}
return null;
}
// Format: "https://resource_name.ai.azure.com/api/projects/project_name"
private const string ProjectEndpoint = "your_project_endpoint";
public static void Main()
{
AIProjectClient projectClient = new(endpoint: new Uri(ProjectEndpoint), tokenProvider: new DefaultAzureCredential());
// Create an agent version with the defined functions as tools.
PromptAgentDefinition agentDefinition = new(model: "gpt-4.1-mini")
{
Instructions = "You are a weather bot. Use the provided functions to help answer questions. "
+ "Customize your responses to the user's preferences as much as possible and use friendly "
+ "nicknames for cities whenever possible.",
Tools = { getUserFavoriteCityTool, getCityNicknameTool, getCurrentWeatherAtLocationTool }
};
AgentVersion agentVersion = projectClient.Agents.CreateAgentVersion(
agentName: "myAgent",
options: new(agentDefinition));
// If the local function call is required, the response item is of type FunctionCallResponseItem.
// It contains the function name needed by the Agent. In this case, use the helper method
// GetResolvedToolOutput to get the FunctionCallOutputResponseItem with the function call result.
// To provide the right answer, supply all the response items to the CreateResponse call.
// At the end, output the function's response.
ResponsesClient responseClient = projectClient.OpenAI.GetProjectResponsesClientForAgent(agentVersion.Name);
ResponseItem request = ResponseItem.CreateUserMessageItem("What's the weather like in my favorite city?");
var inputItems = new List<ResponseItem> { request };
string previousResponseId = null;
bool functionCalled = false;
ResponseResult response;
do
{
response = responseClient.CreateResponse(
previousResponseId: previousResponseId,
inputItems: inputItems);
previousResponseId = response.Id;
inputItems.Clear();
functionCalled = false;
foreach (ResponseItem responseItem in response.OutputItems)
{
inputItems.Add(responseItem);
if (responseItem is FunctionCallResponseItem functionToolCall)
{
Console.WriteLine($"Calling {functionToolCall.FunctionName}...");
inputItems.Add(GetResolvedToolOutput(functionToolCall));
functionCalled = true;
}
}
} while (functionCalled);
Console.WriteLine(response.GetOutputText());
// Remove all the resources created in this sample.
projectClient.Agents.DeleteAgentVersion(agentName: agentVersion.Name, agentVersion: agentVersion.Version);
}
}
預期的輸出
下列範例會顯示預期的輸出:
Calling getUserFavoriteCity...
Calling getCityNickname...
Calling getCurrentWeatherAtLocation...
Your favorite city, Seattle, WA, is also known as The Emerald City. The current weather there is 70f.
在 Foundry Agent Service 中,使用函式呼叫有兩種方式。
- 建立
response。 當你需要代理再次呼叫函式時,再建立一個response。 - 建立一個
conversation,然後建立多個對話項目。 每個對話項目對應一個response。
執行範例前,請先設定以下環境變數:
export AGENT_TOKEN=$(az account get-access-token --scope "https://ai.azure.com/.default" --query accessToken -o tsv)
定義代理程式呼叫的函式
首先定義代理程式要呼叫的函式。 當你為代理建立一個要呼叫的函式時,請在 docstring 中描述它的結構和所需的參數。 您可以查看其他 SDK 語言中的範例函式。
建立專員
curl -X POST "$FOUNDRY_PROJECT_ENDPOINT/agents?api-version=v1" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $AGENT_TOKEN" \
-d '{
"name": "<AGENT_NAME>-function-calling",
"description": "Agent with function calling",
"definition": {
"kind": "prompt",
"model": "<MODEL_DEPLOYMENT>",
"instructions": "You are a helpful agent.",
"tools": [
{
"type": "function",
"name": "getCurrentWeather",
"description": "Get the current weather in a location",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "The city and state e.g. San Francisco, CA"},
"unit": {"type": "string", "enum": ["c", "f"]}
},
"required": ["location"]
}
}
]
}
}'
建立對話
curl -X POST "$FOUNDRY_PROJECT_ENDPOINT/openai/v1/conversations" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $AGENT_TOKEN" \
-d '{
"items": [
{
"type": "message",
"role": "user",
"content": [
{
"type": "input_text",
"text": "What'\''s the weather in Dar es Salaam, Tanzania?"
}
]
}
]
}'
將回傳的對話 IDconv_xyz...()保存到下一步。
建立回應
用前一步的 ID 來替換 <CONVERSATION_ID> 。
curl -X POST "$FOUNDRY_PROJECT_ENDPOINT/openai/v1/responses" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $AGENT_TOKEN" \
-d '{
"agent": {"type": "agent_reference", "name": "<AGENT_NAME>-function-calling"},
"conversation": "<CONVERSATION_ID>",
"input": []
}'
預期的輸出
回應包含一個函式呼叫項目,你需要處理它:
{
"output": [
{
"type": "function_call",
"call_id": "call_xyz789",
"name": "getCurrentWeather",
"arguments": "{\"location\": \"Dar es Salaam, Tanzania\", \"unit\": \"c\"}"
}
]
}
當你處理函式呼叫並將輸出回傳給代理後,最終回應會以自然語言呈現天氣資訊。
提交函式呼叫輸出
在本地處理函式呼叫後,將結果回送給代理:
curl -X POST "$FOUNDRY_PROJECT_ENDPOINT/openai/v1/responses" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $AGENT_TOKEN" \
-d '{
"agent": {"type": "agent_reference", "name": "<AGENT_NAME>-function-calling"},
"conversation": "<CONVERSATION_ID>",
"input": [
{
"type": "function_call_output",
"call_id": "<CALL_ID>",
"output": "{\"temperature\": \"30\", \"unit\": \"c\", \"description\": \"Sunny\"}"
}
]
}'
用前一個回應中函式呼叫的值替換<CALL_ID>call_id。 代理人利用函數輸出產生自然語言答案。
請使用以下程式碼範例,使用函式工具建立代理,處理模型中的函式呼叫,並提供函數結果以取得最終回應。
import { DefaultAzureCredential } from "@azure/identity";
import { AIProjectClient } from "@azure/ai-projects";
// Format: "https://resource_name.ai.azure.com/api/projects/project_name"
const projectEndpoint = "your_project_endpoint";
/**
* Define a function tool for the model to use
*/
const funcTool = {
type: "function" as const,
name: "get_horoscope",
description: "Get today's horoscope for an astrological sign.",
strict: true,
parameters: {
type: "object",
properties: {
sign: {
type: "string",
description: "An astrological sign like Taurus or Aquarius",
},
},
required: ["sign"],
additionalProperties: false,
},
};
/**
* Generate a horoscope for the given astrological sign.
*/
function getHoroscope(sign: string): string {
return `${sign}: Next Tuesday you will befriend a baby otter.`;
}
export async function main(): Promise<void> {
// Create AI Project client
const project = new AIProjectClient(projectEndpoint, new DefaultAzureCredential());
const openai = project.getOpenAIClient();
// Create agent with function tools
const agent = await project.agents.createVersion("function-tool-agent", {
kind: "prompt",
model: "gpt-4.1-mini",
instructions: "You are a helpful assistant that can use function tools.",
tools: [funcTool],
});
// Prompt the model with tools defined
const response = await openai.responses.create(
{
input: [
{
type: "message",
role: "user",
content: "What is my horoscope? I am an Aquarius.",
},
],
},
{
body: { agent: { name: agent.name, type: "agent_reference" } },
},
);
console.log(`Response output: ${response.output_text}`);
// Process function calls
const inputList: Array<{
type: "function_call_output";
call_id: string;
output: string;
}> = [];
for (const item of response.output) {
if (item.type === "function_call") {
if (item.name === "get_horoscope") {
// Parse the function arguments
const args = JSON.parse(item.arguments);
// Execute the function logic for get_horoscope
const horoscope = getHoroscope(args.sign);
// Provide function call results to the model
inputList.push({
type: "function_call_output",
call_id: item.call_id,
output: JSON.stringify({ horoscope }),
});
}
}
}
// Submit function results to get final response
const finalResponse = await openai.responses.create(
{
input: inputList,
previous_response_id: response.id,
},
{
body: { agent: { name: agent.name, type: "agent_reference" } },
},
);
// Print the final response
console.log(finalResponse.output_text);
// Clean up
await project.agents.deleteVersion(agent.name, agent.version);
}
main().catch((err) => {
console.error("The sample encountered an error:", err);
});
預期的輸出
下列範例會顯示預期的輸出:
Response output:
Your horoscope for Aquarius: Next Tuesday you will befriend a baby otter.
在 Java 代理中使用函式呼叫
設定
新增相依性至您的pom.xml:
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-ai-agents</artifactId>
<version>2.0.0</version>
</dependency>
用函式工具建立代理
import com.azure.ai.agents.AgentsClient;
import com.azure.ai.agents.AgentsClientBuilder;
import com.azure.ai.agents.ResponsesClient;
import com.azure.ai.agents.models.AgentReference;
import com.azure.ai.agents.models.AgentVersionDetails;
import com.azure.ai.agents.models.AzureCreateResponseOptions;
import com.azure.ai.agents.models.FunctionTool;
import com.azure.ai.agents.models.PromptAgentDefinition;
import com.azure.core.util.BinaryData;
import com.azure.identity.DefaultAzureCredentialBuilder;
import com.openai.models.responses.Response;
import com.openai.models.responses.ResponseCreateParams;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class FunctionCallingExample {
// Format: "https://resource_name.ai.azure.com/api/projects/project_name"
private static final String PROJECT_ENDPOINT = "your_project_endpoint";
public static void main(String[] args) {
AgentsClientBuilder builder = new AgentsClientBuilder()
.credential(new DefaultAzureCredentialBuilder().build())
.endpoint(PROJECT_ENDPOINT);
AgentsClient agentsClient = builder.buildAgentsClient();
ResponsesClient responsesClient = builder.buildResponsesClient();
// Define function parameters
Map<String, BinaryData> parameters = new HashMap<>();
parameters.put("type", BinaryData.fromString("\"object\""));
parameters.put("properties", BinaryData.fromString(
"{\"location\":{\"type\":\"string\",\"description\":\"The city and state, e.g. Seattle, WA\"},"
+ "\"unit\":{\"type\":\"string\",\"enum\":[\"celsius\",\"fahrenheit\"]}}"));
parameters.put("required", BinaryData.fromString("[\"location\"]"));
FunctionTool weatherFunction = new FunctionTool("get_weather", parameters, true);
// Create agent with function tool
PromptAgentDefinition agentDefinition = new PromptAgentDefinition("gpt-4.1-mini")
.setInstructions("You are a weather assistant. Use the get_weather function to retrieve weather information.")
.setTools(Arrays.asList(weatherFunction));
AgentVersionDetails agent = agentsClient.createAgentVersion("function-calling-agent", agentDefinition);
System.out.printf("Agent created: %s (version %s)%n", agent.getName(), agent.getVersion());
// Create a response - the agent will call the function
AgentReference agentReference = new AgentReference(agent.getName())
.setVersion(agent.getVersion());
Response response = responsesClient.createAzureResponse(
new AzureCreateResponseOptions().setAgentReference(agentReference),
ResponseCreateParams.builder()
.input("What is the weather in Seattle?"));
System.out.println("Response: " + response.output());
// Clean up
agentsClient.deleteAgentVersion(agent.getName(), agent.getVersion());
}
}
關於處理工具呼叫並將結果回傳給代理的完整函式呼叫迴圈,請參見 Azure AI 代理 Java SDK 範例 。
驗證函式呼叫的運作情況
請使用以下檢查來確認函式呼叫是否正常:
- 你的第一個回應包含一個輸出元素,將
type設為function_call。 - 你的應用程式會使用回傳的參數來執行所請求的函式。
- 你的應用程式會提交包含
function_call_output項目並參考先前回應的後續回應,客服則回傳一個自然語言的回答。
如果您在 Microsoft Foundry 中使用追蹤功能,請確認工具呼叫是否確實發生。 關於驗證工具調用及控制工具使用,請參閱 Microsoft Foundry Agent Service 中工具使用的最佳實務。
安全性與資料考量
- 將工具參數和輸出視為不可信的輸入。 在使用之前,務必驗證並淨化這些價值。
- 不要在工具輸出中傳遞秘密(API 金鑰、標記、連線字串)。 只回傳模型所需的資料。
- 將
DefaultAzureCredential所使用的身份設置為最低權限。 - 除非您明確有意圖,否則避免副作用。 例如,將功能工具限制為安全操作,或要求變更資料的動作需明確使用者確認。
- 對於長期執行的操作,請立即回傳狀態並實作輪詢。 10 分鐘的運行有效期適用於總經過時間,而非單一函數執行。
故障排除
| 問題 | 可能的原因 | 解決辦法 |
|---|---|---|
| 代理回傳函式呼叫,但沒有最終回應。 | 工具輸出未返回模型。 | 執行這個功能,然後用工具的輸出呼叫responses.create,並用previous_response_id繼續。 |
| 不會發生函式呼叫。 | 功能不在代理程式定義中或名稱不佳。 | 確認功能工具已添加至代理程式。 使用清晰且具描述性的名稱與參數描述。 |
| 參數不是有效的 JSON。 | 模式不匹配或模型產生錯誤資訊。 | 確認 JSON 架構是否使用正確的型別和所需屬性。 在應用程式中優雅地處理解析錯誤。 |
| 遺漏必填欄位。 | Schema 不會強制執行必要的屬性。 | 將 "required": [...] 陣列加入你的參數結構。 設定 strict: true 為更嚴格的驗證。 |
| 工具輸出因過期而失效。 | 執行已過期 (限制 10 分鐘)。 | 迅速傳回工具的輸出。 如為較慢作業,回分開傳回狀態與輪詢。 |
| 函式呼叫時參數錯誤。 | 功能描述模糊。 | 改善功能 description 欄位。 加入詳細的參數描述並附上範例。 |
| 在一個回應呼叫多個功能。 | 模型決定了需要多種功能。 | 處理輸出陣列中的每個函式呼叫。 在一個 responses.create 呼叫中回傳所有結果。 |
| 功能在 Foundry 入口網站中無法顯示。 | Portal 不會執行函式呼叫。 | 透過 SDK 或 REST API 測試函式呼叫。 入口網站顯示代理,但不會啟動功能。 |
清理資源
測試結束後,刪除你建立的資源以避免持續成本。
刪除代理人:
curl -X DELETE "$FOUNDRY_PROJECT_ENDPOINT/agents/<AGENT_NAME>-function-calling?api-version=v1" \
-H "Authorization: Bearer $AGENT_TOKEN"
刪除對話:
curl -X DELETE "$FOUNDRY_PROJECT_ENDPOINT/openai/v1/conversations/<CONVERSATION_ID>" \
-H "Authorization: Bearer $AGENT_TOKEN"