Freigeben über


Verwenden von Funktionstools mit einem Agent

In diesem Lernprogrammschritt erfahren Sie, wie Sie Funktionstools mit einem Agent verwenden, in dem der Agent auf dem Azure OpenAI-Chat-Abschlussdienst basiert.

Von Bedeutung

Nicht alle Agenttypen unterstützen Funktionstools. Einige unterstützen möglicherweise nur benutzerdefinierte integrierte Tools, ohne dem Aufrufer die Bereitstellung eigener Funktionen zu gestatten. In diesem Schritt wird ein ChatClientAgent verwendet, der Funktionstools unterstützt.

Voraussetzungen

Für Voraussetzungen und die Installation von NuGet-Paketen siehe den Schritt Erstellen und Ausführen eines einfachen Agenten in diesem Tutorial.

Erstellen Sie den Agenten mit Funktionstools

Funktionstools sind nur benutzerdefinierter Code, den der Agent bei Bedarf aufrufen soll. Sie können jede C#-Methode in ein Funktionstool umwandeln, indem Sie die AIFunctionFactory.Create Methode verwenden, um eine AIFunction Instanz aus der Methode zu erstellen.

Wenn Sie zusätzliche Beschreibungen über die Funktion oder deren Parameter für den Agent angeben müssen, damit sie genauer zwischen verschiedenen Funktionen wählen kann, können Sie das System.ComponentModel.DescriptionAttribute Attribut für die Methode und die zugehörigen Parameter verwenden.

Hier ist ein Beispiel für ein einfaches Funktionstool, das das Wetter für einen bestimmten Ort fälscht. Es ist mit Beschreibungsattributen versehen, um zusätzliche Beschreibungen über sich selbst und seinen Standortparameter für den Agent bereitzustellen.

using System.ComponentModel;

[Description("Get the weather for a given location.")]
static string GetWeather([Description("The location to get the weather for.")] string location)
    => $"The weather in {location} is cloudy with a high of 15°C.";

Beim Erstellen des Agents können Sie nun das Funktionstool an den Agent bereitstellen, indem Sie eine Liste der Tools an die AsAIAgent Methode übergeben.

using System;
using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using OpenAI;

AIAgent agent = new AzureOpenAIClient(
    new Uri("https://<myresource>.openai.azure.com"),
    new DefaultAzureCredential())
     .GetChatClient("gpt-4o-mini")
     .AsAIAgent(instructions: "You are a helpful assistant", tools: [AIFunctionFactory.Create(GetWeather)]);

Warnung

DefaultAzureCredential ist praktisch für die Entwicklung, erfordert aber sorgfältige Überlegungen in der Produktion. Berücksichtigen Sie in der Produktion die Verwendung bestimmter Anmeldeinformationen (z. B. ManagedIdentityCredential), um Latenzprobleme, unbeabsichtigte Abfragen von Anmeldeinformationen und potenzielle Sicherheitsrisiken durch Ausweichmechanismen zu vermeiden.

Jetzt können Sie den Agent einfach normal ausführen, und der Agent kann das GetWeather Funktionstool bei Bedarf aufrufen.

Console.WriteLine(await agent.RunAsync("What is the weather like in Amsterdam?"));

Tipp

Vollständige Runnable-Beispiele finden Sie in den .NET-Beispielen .

Von Bedeutung

Nicht alle Agenttypen unterstützen Funktionstools. Einige unterstützen möglicherweise nur benutzerdefinierte integrierte Tools, ohne dem Aufrufer die Bereitstellung eigener Funktionen zu gestatten. In diesem Schritt werden Agents verwendet, die über Chatclients erstellt wurden, die Funktionstools unterstützen.

Voraussetzungen

Die erforderlichen Komponenten und die Installation von Python-Paketen finden Sie im Abschnitt zum Erstellen und Ausführen eines einfachen Agent-Schritts in diesem Lernprogramm.

Erstellen Sie den Agenten mit Funktionstools

Funktionstools sind nur benutzerdefinierter Code, den der Agent bei Bedarf aufrufen soll. Sie können jede Python-Funktion in ein Funktionstool umwandeln, indem Sie sie beim Erstellen des Agents an den Parameter des tools Agents übergeben.

Wenn Sie zusätzliche Beschreibungen zu der Funktion oder ihren Parametern für den Agent angeben müssen, damit sie genauer zwischen verschiedenen Funktionen wählen kann, können Sie die Python-Typanmerkungen mit Annotated und Pydantics Field verwenden, um Beschreibungen bereitzustellen.

Hier ist ein Beispiel für ein einfaches Funktionstool, das das Wetter für einen bestimmten Ort fälscht. Es verwendet Typanmerkungen, um zusätzliche Beschreibungen zu der Funktion und dem Positionsparameter für den Agent bereitzustellen.

from typing import Annotated
from pydantic import Field

def get_weather(
    location: Annotated[str, Field(description="The location to get the weather for.")],
) -> str:
    """Get the weather for a given location."""
    return f"The weather in {location} is cloudy with a high of 15°C."

Sie können auch den @tool Dekorierer verwenden, um den Namen und die Beschreibung der Funktion explizit anzugeben:

from typing import Annotated
from pydantic import Field
from agent_framework import tool

@tool(name="weather_tool", description="Retrieves weather information for any location")
def get_weather(
    location: Annotated[str, Field(description="The location to get the weather for.")],
) -> str:
    return f"The weather in {location} is cloudy with a high of 15°C."

Wenn Sie die Parameter name und description im @tool Dekorateur nicht angeben, verwendet das Framework automatisch den Funktionsnamen und den Docstring als Fallbacks.

Verwenden expliziter Schemas mit @tool

Wenn Sie die vollständige Kontrolle über das Schema benötigen, das für das Modell verfügbar gemacht wird, übergeben Sie den schema Parameter an @tool. Sie können entweder ein Pydantisches Modell oder ein unformatiertes JSON-Schemawörterbuch bereitstellen.

# Load environment variables from .env file
load_dotenv()


# Approach 1: Pydantic model as explicit schema
class WeatherInput(BaseModel):
    """Input schema for the weather tool."""

    location: Annotated[str, Field(description="The city name to get weather for")]
    unit: Annotated[str, Field(description="Temperature unit: celsius or fahrenheit")] = "celsius"


@tool(
    name="get_weather",
    description="Get the current weather for a given location.",
    schema=WeatherInput,
    approval_mode="never_require",
    """Get the current weather for a location."""
    return f"The weather in {location} is 22 degrees {unit}."


# Approach 2: JSON schema dictionary as explicit schema
get_current_time_schema = {
    "type": "object",
    "properties": {
        "timezone": {"type": "string", "description": "The timezone to get the current time for", "default": "UTC"},
    },
}


@tool(
    name="get_current_time",
    description="Get the current time in a given timezone.",

Übergeben des reinen Laufzeitkontexts an ein Tool

Verwenden Sie normale Funktionsparameter für Werte, die das Modell bereitstellen soll. Verwenden Sie FunctionInvocationContext für nur zur Laufzeit verfügbare Werte wie function_invocation_kwargs oder die aktuelle Sitzung. Der eingefügte Kontextparameter ist vom Schema ausgeblendet, das für das Modell verfügbar gemacht wird.

import asyncio
from typing import Annotated

from agent_framework import FunctionInvocationContext, tool
from agent_framework.openai import OpenAIResponsesClient
from dotenv import load_dotenv
from pydantic import Field
# Define the function tool with explicit invocation context.
# The context parameter can also be declared as an untyped ``ctx`` parameter.
@tool(approval_mode="never_require")
def get_weather(
    location: Annotated[str, Field(description="The location to get the weather for.")],
    ctx: FunctionInvocationContext,
) -> str:
    """Get the weather for a given location."""
    # Extract the injected argument from the explicit context
    user_id = ctx.kwargs.get("user_id", "unknown")

    # Simulate using the user_id for logging or personalization
    print(f"Getting weather for user: {user_id}")

    return f"The weather in {location} is cloudy with a high of 15°C."


async def main() -> None:
    agent = OpenAIResponsesClient().as_agent(
        name="WeatherAgent",
        instructions="You are a helpful weather assistant.",
        tools=[get_weather],
    )

    # Pass the runtime context explicitly when running the agent.
    response = await agent.run(
        "What is the weather like in Amsterdam?",
        function_invocation_kwargs={"user_id": "user_123"},
    )

Weitere Details zu ctx.kwargs, ctx.session und der Funktions-Middleware finden Sie unter Runtime Context.

Erstellung von Tools nur zur Deklaration

Wenn ein Tool außerhalb des Frameworks implementiert wird (z. B. clientseitig in einer Benutzeroberfläche), können Sie es ohne Implementierung deklarieren.FunctionTool(..., func=None) Das Modell kann das Tool weiterhin begründen und aufrufen, und Ihre Anwendung kann das Ergebnis später bereitstellen.


# Load environment variables from .env file
load_dotenv()

# A declaration-only tool: the schema is sent to the LLM, but the framework
# has no implementation to execute. The caller must supply the result.
get_user_location = FunctionTool(
    name="get_user_location",
    func=None,
    description="Get the user's current city. Only the client application can resolve this.",
    input_model={
        "type": "object",
        "properties": {
            "reason": {"type": "string", "description": "Why the location is needed"},

Beim Erstellen des Agents können Sie nun das Funktionstool an den Agent bereitstellen, indem Sie ihn an den tools Parameter übergeben.

import asyncio
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential

agent = AzureOpenAIChatClient(credential=AzureCliCredential()).as_agent(
    instructions="You are a helpful assistant",
    tools=get_weather
)

Jetzt können Sie den Agent einfach normal ausführen, und der Agent kann das get_weather Funktionstool bei Bedarf aufrufen.

async def main():
    result = await agent.run("What is the weather like in Amsterdam?")
    print(result.text)

asyncio.run(main())

Erstellen einer Klasse mit mehreren Funktionstools

Wenn mehrere Tools Abhängigkeiten oder den veränderbaren Zustand gemeinsam nutzen, schließen Sie sie in eine Klasse um und übergeben gebundene Methoden an den Agent. Verwenden Sie Klassenattribute für Werte, die das Modell nicht bereitstellen sollte, z. B. Dienstclients, Featurekennzeichnungen oder zwischengespeicherten Zustand.

import asyncio
from typing import Annotated

from agent_framework import tool
from agent_framework.openai import OpenAIResponsesClient
from dotenv import load_dotenv
class MyFunctionClass:
    def __init__(self, safe: bool = False) -> None:
        """Simple class with two tools: divide and add.

        The safe parameter controls whether divide raises on division by zero or returns `infinity` for divide by zero.
        """
        self.safe = safe

    def divide(
        self,
        a: Annotated[int, "Numerator"],
        b: Annotated[int, "Denominator"],
    ) -> str:
        """Divide two numbers, safe to use also with 0 as denominator."""
        result = "∞" if b == 0 and self.safe else a / b
        return f"{a} / {b} = {result}"

    def add(
        self,
        x: Annotated[int, "First number"],
        y: Annotated[int, "Second number"],
    ) -> str:
        return f"{x} + {y} = {x + y}"


async def main():
    # Creating my function class with safe division enabled
    tools = MyFunctionClass(safe=True)
    # Applying the tool decorator to one of the methods of the class
    add_function = tool(description="Add two numbers.")(tools.add)

    agent = OpenAIResponsesClient().as_agent(
        name="ToolAgent",
        instructions="Use the provided tools.",
    )
    print("=" * 60)
    print("Step 1: Call divide(10, 0) - tool returns infinity")
    query = "Divide 10 by 0"
    response = await agent.run(
        query,
        tools=[add_function, tools.divide],
    )
    print(f"Response: {response.text}")
    print("=" * 60)
    print("Step 2: Call set safe to False and call again")
    # Disabling safe mode to allow exceptions
    tools.safe = False
    response = await agent.run(query, tools=[add_function, tools.divide])

Dieses Muster eignet sich gut für den langlebigen Zustand eines Werkzeugs. Verwenden Sie FunctionInvocationContext stattdessen, wenn sich der Wert per Aufruf ändert.

Nächste Schritte