Freigeben über


Funktionsaufrufe mit Azure KI-Agents

Azure AI Agents unterstützt Funktionsaufrufe, mit denen Sie die Struktur von Funktionen an einen Agent beschreiben und dann die Funktionen zurückgeben können, die zusammen mit ihren Argumenten aufgerufen werden müssen.

Hinweis

Läuft 10 Minuten nach der Erstellung ab. Stellen Sie sicher, dass Sie Ihre Toolausgabe vor dem Ablauf übermitteln.

Nutzungssupport

Unterstützung für Azure AI Foundry Python SDK C# SDK JavaScript SDK REST-API Basic-Agent-Setup Standard-Agent-Setup
✔️ ✔️ ✔️ ✔️ ✔️ ✔️

Definieren einer Funktion für ihren Agent, die aufgerufen werden soll

Definieren Sie zunächst eine Funktion für ihren Agent, die aufgerufen werden soll. Wenn Sie eine Funktion für einen Agent zum Aufrufen erstellen, beschreiben Sie die Struktur mit allen erforderlichen Parametern in einer Docstring.When you create a function for an agent to call, you describe its structure with any required parameters in a docstring.

import json
import datetime
from typing import Any, Callable, Set, Dict, List, Optional

def fetch_weather(location: str) -> str:
    """
    Fetches the weather information for the specified location.

    :param location: The location to fetch weather for.
    :return: Weather information as a JSON string.
    """
    # Mock weather data for demonstration purposes
    mock_weather_data = {"New York": "Sunny, 25°C", "London": "Cloudy, 18°C", "Tokyo": "Rainy, 22°C"}
    weather = mock_weather_data.get(location, "Weather data not available for this location.")
    return json.dumps({"weather": weather})

# Define user functions
user_functions = {fetch_weather}

Erstellen eines Clients und eines Agents

Hinweis

Sie finden ein Streamingbeispiel auf GitHub.

import os, time
from azure.identity import DefaultAzureCredential
from azure.ai.projects import AIProjectClient
from azure.ai.agents.models import FunctionTool

# Retrieve the project endpoint from environment variables
project_endpoint = os.environ["PROJECT_ENDPOINT"]

# Initialize the AIProjectClient
project_client = AIProjectClient(
    endpoint=project_endpoint,
    credential=DefaultAzureCredential(),
    api_version="latest",
)

# Initialize the FunctionTool with user-defined functions
functions = FunctionTool(functions=user_functions)

with project_client:
    # Create an agent with custom functions
    agent = project_client.agents.create_agent(
        model=os.environ["MODEL_DEPLOYMENT_NAME"],
        name="my-agent",
        instructions="You are a helpful agent",
        tools=functions.definitions,
    )
    print(f"Created agent, ID: {agent.id}")

Erstellen eines Threads

# Create a thread for communication
thread = project_client.agents.threads.create()
print(f"Created thread, ID: {thread.id}")

# Send a message to the thread
message = project_client.agents.messages.create(
    thread_id=thread.id,
    role="user",
    content="Hello, send an email with the datetime and weather information in New York?",
)
print(f"Created message, ID: {message['id']}")

Erstellen einer Ausführung und Überprüfen der Ausgabe

# Create and process a run for the agent to handle the message
run = project_client.agents.runs.create(thread_id=thread.id, agent_id=agent.id)
print(f"Created run, ID: {run.id}")

# Poll the run status until it is completed or requires action
while run.status in ["queued", "in_progress", "requires_action"]:
    time.sleep(1)
    run = project_client.agents.runs.get(thread_id=thread.id, run_id=run.id)

    if run.status == "requires_action":
        tool_calls = run.required_action.submit_tool_outputs.tool_calls
        tool_outputs = []
        for tool_call in tool_calls:
            if tool_call.name == "fetch_weather":
                output = fetch_weather("New York")
                tool_outputs.append({"tool_call_id": tool_call.id, "output": output})
        project_client.agents.runs.submit_tool_outputs(thread_id=thread.id, run_id=run.id, tool_outputs=tool_outputs)

print(f"Run completed with status: {run.status}")

# Fetch and log all messages from the thread
messages = project_client.agents.messages.list(thread_id=thread.id)
for message in messages:
    print(f"Role: {message['role']}, Content: {message['content']}")

# Delete the agent after use
project_client.agents.delete_agent(agent.id)
print("Deleted agent")

Hinweis

Sie finden ein Streamingbeispiel auf GitHub.

Konfigurieren von Client- und Definieren von Funktionen

Richten Sie zunächst die Konfiguration mit appsettings.json ein, und erstellen Sie eine PersistentAgentsClient.

using Azure;
using Azure.AI.Agents.Persistent;
using Azure.Identity;
using Microsoft.Extensions.Configuration;
using System.Text.Json;

// Load configuration from appsettings.json file
IConfigurationRoot configuration = new ConfigurationBuilder()
    .SetBasePath(AppContext.BaseDirectory)
    .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
    .Build();

// Read necessary configuration values (Project Endpoint and Model Deployment Name)
var projectEndpoint = configuration["ProjectEndpoint"];
var modelDeploymentName = configuration["ModelDeploymentName"];
// Initialize the client to interact with the Azure AI Agents Persistent Client using default credentials
PersistentAgentsClient client = new(projectEndpoint, new DefaultAzureCredential());

Definieren von Funktionen

Definieren Sie die lokalen C#-Funktionen, die Ihr Agent aufrufen kann, sowie deren FunctionToolDefinition um ihren Zweck und ihre Parameter dem Agenten zu beschreiben.

// Function to get the user's favorite city (hardcoded for example)
string GetUserFavoriteCity() => "Seattle, WA";
// Definition for the GetUserFavoriteCity function, describing its purpose to the agent
FunctionToolDefinition getUserFavoriteCityTool = new("getUserFavoriteCity", "Gets the user's favorite city.");

// Function to get a city's nickname based on its location
string GetCityNickname(string location) => location switch
{
    "Seattle, WA" => "The Emerald City",
    // Handle cases where the nickname is not known
    _ => throw new NotImplementedException(), 
};
// Definition for the GetCityNickname function, including parameter description
FunctionToolDefinition getCityNicknameTool = new(
    name: "getCityNickname",
    description: "Gets the nickname of a city, e.g. 'LA' for 'Los Angeles, CA'.",
    // Define the expected parameters (location string)
    parameters: 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 }));

// Function to get weather at a specific location, with an optional temperature unit
string GetWeatherAtLocation(string location, string temperatureUnit = "f") => location switch
{
    "Seattle, WA" => temperatureUnit == "f" ? "70f" : "21c",
    // Handle cases where weather data is not available
    _ => throw new NotImplementedException()
};
// Definition for the GetWeatherAtLocation function, specifying parameters and enum for unit
FunctionToolDefinition getCurrentWeatherAtLocationTool = new(
    name: "getCurrentWeatherAtLocation",
    description: "Gets the current weather at a provided location.",
    // Define expected parameters (location string, optional unit enum)
    parameters: 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 }));

Implementieren der Funktionsausführungslogik

Erstellen Sie eine Hilfsfunktion GetResolvedToolOutput, um RequiredToolCall-Objekte vom Agenten zu verarbeiten. Diese Funktion ruft die entsprechende lokale C#-Funktion auf und gibt die Ausgabe an den Agent zurück.

// Helper function to execute the correct local C# function based on the tool call request from the agent
ToolOutput GetResolvedToolOutput(RequiredToolCall toolCall)
{
    // Check if the required call is a function call
    if (toolCall is RequiredFunctionToolCall functionToolCall)
    {
        // Execute GetUserFavoriteCity if its name matches
        if (functionToolCall.Name == getUserFavoriteCityTool.Name)
        {
            return new ToolOutput(toolCall, GetUserFavoriteCity());
        }
        // Parse the arguments provided by the agent for other functions
        using JsonDocument argumentsJson = JsonDocument.Parse(functionToolCall.Arguments);
        // Execute GetCityNickname if its name matches
        if (functionToolCall.Name == getCityNicknameTool.Name)
        {
            // Extract the 'location' argument
            string locationArgument = argumentsJson.RootElement.GetProperty("location").GetString();
            return new ToolOutput(toolCall, GetCityNickname(locationArgument));
        }
        // Execute GetWeatherAtLocation if its name matches
        if (functionToolCall.Name == getCurrentWeatherAtLocationTool.Name)
        {
            // Extract the 'location' argument
            string locationArgument = argumentsJson.RootElement.GetProperty("location").GetString();
            // Check if the optional 'unit' argument was provided
            if (argumentsJson.RootElement.TryGetProperty("unit", out JsonElement unitElement))
            {
                string unitArgument = unitElement.GetString();
                return new ToolOutput(toolCall, GetWeatherAtLocation(locationArgument, unitArgument));
            }
            // Call without the unit if it wasn't provided
            return new ToolOutput(toolCall, GetWeatherAtLocation(locationArgument));
        }
    }
    // Return null if the tool call type isn't handled
    return null;
}

Agent- und Konversationsthread erstellen

Erstellen Sie nun den PersistentAgentModellbereitstellungsnamen, einen beschreibenden Namen, Anweisungen für das Verhalten und die Liste der FunctionToolDefinitions Modellbereitstellungen. Erstellen Sie dann eine PersistentAgentThread und fügen Sie eine anfängliche Benutzernachricht hinzu, um die Unterhaltung zu starten.

// Create the agent instance
PersistentAgent agent = client.Administration.CreateAgent(
    model: modelDeploymentName, 
    name: "SDK Test Agent - Functions",
    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]);

// Create a new conversation thread for the agent
PersistentAgentThread thread = client.Threads.CreateThread();

// Add the initial user message to the thread
client.Messages.CreateMessage(
    thread.Id,
    MessageRole.User,
    "What's the weather like in my favorite city?");

Ausführen und Verarbeiten von Funktionsaufrufen

Erstellen Sie einen ThreadRun für den Agent im Thread. Abrufen des Abschlussstatus der Ausführung. Wenn der Ausführungsstatus lautet RequiresAction, bedeutet dies, dass der Agent eine Ihrer lokalen Funktionen aufrufen muss. Verwenden Sie das GetResolvedToolOutput Hilfsprogramm, um das Ergebnis der Funktion abzurufen und an die Ausführung zurückzusenden.

// Start a run for the agent to process the messages in the thread
ThreadRun run = client.Runs.CreateRun(thread.Id, agent.Id);

// Loop to check the run status and handle required actions
do
{
    // Wait briefly before checking the status again
    Thread.Sleep(TimeSpan.FromMilliseconds(500));
    // Get the latest status of the run
    run = client.Runs.GetRun(thread.Id, run.Id);

    // Check if the agent requires a function call to proceed
    if (run.Status == RunStatus.RequiresAction
        && run.RequiredAction is SubmitToolOutputsAction submitToolOutputsAction)
    {
        // Prepare a list to hold the outputs of the tool calls
        List<ToolOutput> toolOutputs = [];
        // Iterate through each required tool call
        foreach (RequiredToolCall toolCall in submitToolOutputsAction.ToolCalls)
        {
            // Execute the function and get the output using the helper method
            toolOutputs.Add(GetResolvedToolOutput(toolCall));
        }
        // Submit the collected tool outputs back to the run
        run = client.Runs.SubmitToolOutputsToRun(run, toolOutputs);
    }
}
// Continue looping while the run is in progress or requires action
while (run.Status == RunStatus.Queued
    || run.Status == RunStatus.InProgress
    || run.Status == RunStatus.RequiresAction);

Abrufen und Anzeigen von Ergebnissen

Rufen Sie nach Abschluss der Ausführung alle Nachrichten aus dem Thread ab, um die vollständige Unterhaltung anzuzeigen, einschließlich der endgültigen Antwort des Agents.

// Retrieve all messages from the completed thread, oldest first
Pageable<PersistentThreadMessage> messages = client.Messages.GetMessages(
    threadId: thread.Id,
    order: ListSortOrder.Ascending
);

// Iterate through each message in the thread
foreach (PersistentThreadMessage threadMessage in messages)
{
    // Iterate through content items in the message (usually just one text item)
    foreach (MessageContent content in threadMessage.ContentItems)
    {
        // Process based on content type
        switch (content)
        {
            // If it's a text message
            case MessageTextContent textItem:
                // Print the role (user/agent) and the text content
                Console.WriteLine($"[{threadMessage.Role}]: {textItem.Text}");
                break;
                // Add handling for other content types if necessary (e.g., images)
        }
    }
}

Bereinigen von Ressourcen

Bereinigen Sie schließlich die erstellten Ressourcen, indem Sie den Thread und den Agent löschen.

// Delete the conversation thread
client.Threads.DeleteThread(threadId: thread.Id);
// Delete the agent definition
client.Administration.DeleteAgent(agentId: agent.Id);

Definieren einer Funktion für ihren Agent, die aufgerufen werden soll

Definieren Sie zunächst eine Funktion für ihren Agent, die aufgerufen werden soll. Wenn Sie eine Funktion für einen Agent erstellen, die aufgerufen werden soll, beschreiben Sie deren Struktur mit allen erforderlichen Parametern in einer docstring-Datei.

class FunctionToolExecutor {
    functionTools;

    constructor() {
      this.functionTools = [
        {
          func: this.getUserFavoriteCity,
          ...ToolUtility.createFunctionTool({
            name: "getUserFavoriteCity",
            description: "Gets the user's favorite city.",
            parameters: {},
          }),
        },
        {
          func: this.getCityNickname,
          ...ToolUtility.createFunctionTool({
            name: "getCityNickname",
            description: "Gets the nickname of a city, e.g. 'LA' for 'Los Angeles, CA'.",
            parameters: {
              type: "object",
              properties: {
                location: { type: "string", description: "The city and state, e.g. Seattle, Wa" },
              },
            },
          }),
        },
        {
          func: this.getWeather,
          ...ToolUtility.createFunctionTool({
            name: "getWeather",
            description: "Gets the weather for a location.",
            parameters: {
              type: "object",
              properties: {
                location: { type: "string", description: "The city and state, e.g. Seattle, Wa" },
                unit: { type: "string", enum: ["c", "f"] },
              },
            },
          }),
        },
      ];
    }

    getUserFavoriteCity() {
      return { location: "Seattle, WA" };
    }

    getCityNickname(_location) {
      return { nickname: "The Emerald City" };
    }

    getWeather(_location, unit) {
      return { weather: unit === "f" ? "72f" : "22c" };
    }

    invokeTool(toolCall) {
      console.log(`Function tool call - ${toolCall.function.name}`);
      const args = [];
      if (toolCall.function.parameters) {
        try {
          const params = JSON.parse(toolCall.function.parameters);
          for (const key in params) {
            if (Object.prototype.hasOwnProperty.call(params, key)) {
              args.push(params[key]);
            }
          }
        } catch (error) {
          console.error(`Failed to parse parameters: ${toolCall.function.parameters}`, error);
          return undefined;
        }
      }
      const result = this.functionTools
        .find((tool) => tool.definition.function.name === toolCall.function.name)
        ?.func(...args);
      return result
        ? {
            toolCallId: toolCall.id,
            output: JSON.stringify(result),
          }
        : undefined;
    }

    getFunctionDefinitions() {
      return this.functionTools.map((tool) => {
        return tool.definition;
      });
    }
  }

Erstellen eines Clients und eines Agents

const { AgentsClient, ToolUtility, isOutputOfType } = require("@azure/ai-agents");
const { delay } = require("@azure/core-util");
const { DefaultAzureCredential } = require("@azure/identity");

require("dotenv/config");

const projectEndpoint = process.env["PROJECT_ENDPOINT"];

const client = new AgentsClient(projectEndpoint, new DefaultAzureCredential());
const functionToolExecutor = new FunctionToolExecutor();
  const functionTools = functionToolExecutor.getFunctionDefinitions();
  const agent = await client.createAgent("gpt-4o", {
    name: "my-agent",
    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: functionTools,
  });
  console.log(`Created agent, agent ID: ${agent.id}`);

Erstellen eines Threads

// Create thread
const thread = await client.threads.create();
console.log(`Created Thread, thread ID:  ${thread.id}`);

// Create message
const message = await client.messages.create(
  thread.id,
  "user",
  "What's the weather like in my favorite city?",
);
console.log(`Created message, message ID ${message.id}`);

Erstellen einer Ausführung und Überprüfen der Ausgabe

// Create run
let run = await client.runs.create(thread.id, agent.id);
console.log(`Created Run, Run ID:  ${run.id}`);

while (["queued", "in_progress", "requires_action"].includes(run.status)) {
  await delay(1000);
  run = await client.runs.get(thread.id, run.id);
  console.log(`Current Run status - ${run.status}, run ID: ${run.id}`);
  if (run.status === "requires_action" && run.requiredAction) {
    console.log(`Run requires action - ${run.requiredAction}`);
    if (isOutputOfType(run.requiredAction, "submit_tool_outputs")) {
      const submitToolOutputsActionOutput = run.requiredAction;
      const toolCalls = submitToolOutputsActionOutput.submitToolOutputs.toolCalls;
      const toolResponses = [];
      for (const toolCall of toolCalls) {
        if (isOutputOfType(toolCall, "function")) {
          const toolResponse = functionToolExecutor.invokeTool(toolCall);
          if (toolResponse) {
            toolResponses.push(toolResponse);
          }
        }
      }
      if (toolResponses.length > 0) {
        run = await client.runs.submitToolOutputs(thread.id, run.id, toolResponses);
        console.log(`Submitted tool response - ${run.status}`);
      }
    }
  }
}

console.log(`Run status - ${run.status}, run ID: ${run.id}`);
const messages = client.messages.list(thread.id);
for await (const threadMessage of messages) {
  console.log(
    `Thread Message Created at  - ${threadMessage.createdAt} - Role - ${threadMessage.role}`,
  );
  threadMessage.content.forEach((content) => {
    if (isOutputOfType(content, "text")) {
      const textContent = content;
      console.log(`Text Message Content - ${textContent.text.value}`);
    } else if (isOutputOfType(content, "image_file")) {
      const imageContent = content;
      console.log(`Image Message Content - ${imageContent.imageFile.fileId}`);
    }
  });
}
// Delete agent
await client.deleteAgent(agent.id);
console.log(`Deleted agent, agent ID: ${agent.id}`);

Definieren einer Funktion für ihren Agent, die aufgerufen werden soll

Definieren Sie zunächst eine Funktion für ihren Agent, die aufgerufen werden soll. Wenn Sie eine Funktion für einen Agent erstellen, die aufgerufen werden soll, beschreiben Sie deren Struktur mit allen erforderlichen Parametern in einer docstring-Datei. In den anderen SDK-Sprachen finden Sie Beispielfunktionen.

Einen Agent erstellen

Folgen Sie der Quickstart-Anleitung der REST-API, um die richtigen Werte für die Umgebungsvariablen AGENT_TOKEN, AZURE_AI_FOUNDRY_PROJECT_ENDPOINT und API_VERSION festzulegen.

curl --request POST \
  --url $AZURE_AI_FOUNDRY_PROJECT_ENDPOINT/assistants?api-version=$API_VERSION \
  -H "Authorization: Bearer $AGENT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "instructions": "You are a weather bot. Use the provided functions to answer questions.",
    "model": "gpt-4o-mini",
    tools=[{
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "Get the weather in location",
        "parameters": {
          "type": "object",
          "properties": {
            "location": {"type": "string", "description": "The city name, for example San Francisco"}
          },
          "required": ["location"]
        }
      }
    }]
  }'

Erstellen eines Threads

curl --request POST \
  --url $AZURE_AI_FOUNDRY_PROJECT_ENDPOINT/threads?api-version=$API_VERSION \
  -H "Authorization: Bearer $AGENT_TOKEN" \
  -H "Content-Type: application/json" \
  -d ''

Hinzufügen einer Benutzerfrage zum Thread

curl --request POST \
  --url $AZURE_AI_FOUNDRY_PROJECT_ENDPOINT/threads/thread_abc123/messages?api-version=$API_VERSION \
  -H "Authorization: Bearer $AGENT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
      "role": "user",
      "content": "What is the weather in Seattle?"
    }'

Thread ausführen

curl --request POST \
  --url $AZURE_AI_FOUNDRY_PROJECT_ENDPOINT/threads/thread_abc123/runs?api-version=$API_VERSION \
  -H "Authorization: Bearer $AGENT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "assistant_id": "asst_abc123",
  }'

Abrufen des Ausführungsstatus

curl --request GET \
  --url $AZURE_AI_FOUNDRY_PROJECT_ENDPOINT/threads/thread_abc123/runs/run_abc123?api-version=$API_VERSION \
  -H "Authorization: Bearer $AGENT_TOKEN"

Antwort des Agenten abrufen

curl --request GET \
  --url $AZURE_AI_FOUNDRY_PROJECT_ENDPOINT/threads/thread_abc123/messages?api-version=$API_VERSION \
  -H "Authorization: Bearer $AGENT_TOKEN"