Partager via


Utilisation d’outils de fonction avec un agent

Cette étape du tutoriel vous montre comment utiliser des outils de fonction avec un agent, où l’agent est basé sur le service Azure OpenAI Chat Completion.

Important

Tous les types d’agents ne prennent pas en charge les outils de fonction. Certains peuvent uniquement prendre en charge les outils intégrés personnalisés, sans permettre à l’appelant de fournir leurs propres fonctions. Cette étape utilise un ChatClientAgent, qui prend en charge les outils de fonction.

Prerequisites

Pour connaître les prérequis et l’installation des packages NuGet, consultez l’étape Créer et exécuter un agent simple dans ce tutoriel.

Créer l’agent avec des outils de fonction

Les outils de fonction sont simplement du code personnalisé que vous souhaitez que l’agent puisse appeler si nécessaire. Vous pouvez transformer n’importe quelle méthode C# en outil de fonction à l’aide de la AIFunctionFactory.Create méthode pour créer une AIFunction instance à partir de la méthode.

Si vous devez fournir des descriptions supplémentaires sur la fonction ou ses paramètres à l’agent, afin qu’il puisse choisir plus précisément entre différentes fonctions, vous pouvez utiliser l’attribut System.ComponentModel.DescriptionAttribute sur la méthode et ses paramètres.

Voici un exemple d’outil de fonction simple qui simule l'obtention de la météo pour un emplacement donné. Il est décoré avec des attributs de description pour fournir des descriptions supplémentaires sur lui-même et son paramètre d’emplacement à l’agent.

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.";

Lors de la création de l’agent, vous pouvez maintenant fournir l’outil de fonction à l’agent en passant une liste d’outils à la AsAIAgent méthode.

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)]);

Avertissement

DefaultAzureCredential est pratique pour le développement, mais nécessite une considération minutieuse en production. En production, envisagez d’utiliser des informations d’identification spécifiques (par exemple ManagedIdentityCredential) pour éviter les problèmes de latence, la détection involontaire des informations d’identification et les risques de sécurité potentiels liés aux mécanismes de secours.

Maintenant, vous pouvez simplement exécuter l'agent comme d'habitude, et l'agent sera en mesure d'appeler la fonction GetWeather si besoin.

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

Conseil / Astuce

Consultez les exemples .NET pour obtenir des exemples exécutables complets.

Important

Tous les types d’agents ne prennent pas en charge les outils de fonction. Certains peuvent uniquement prendre en charge les outils intégrés personnalisés, sans permettre à l’appelant de fournir leurs propres fonctions. Cette étape utilise des agents créés via des clients de messagerie instantanée, qui prennent en charge les outils fonctionnels.

Prerequisites

Pour connaître les prérequis et l’installation des packages Python, consultez l’étape Créer et exécuter un agent simple dans ce tutoriel.

Créer l’agent avec des outils de fonction

Les outils de fonction sont simplement du code personnalisé que vous souhaitez que l’agent puisse appeler si nécessaire. Vous pouvez transformer n’importe quelle fonction Python en fonction outil en la transmettant au paramètre tools lors de la création de l’agent.

Si vous devez fournir des descriptions supplémentaires sur la fonction ou ses paramètres à l’agent, afin qu’il puisse choisir plus précisément entre différentes fonctions, vous pouvez utiliser les annotations de type Python avec Annotated et Pydantic Field pour fournir des descriptions.

Voici un exemple d’outil de fonction simple qui simule l'obtention de la météo pour un emplacement donné. Il utilise des annotations de type pour fournir des descriptions supplémentaires sur la fonction et son paramètre d’emplacement à l’agent.

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."

Vous pouvez également utiliser le @tool décorateur pour spécifier explicitement le nom et la description de la fonction :

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."

Si vous ne spécifiez pas les paramètres name et description dans le décorateur @tool, le framework utilisera automatiquement le nom et le docstring de la fonction en tant que secours.

Utiliser des schémas explicites avec @tool

Lorsque vous avez besoin d’un contrôle total sur le schéma exposé au modèle, passez le schema paramètre à @tool. Vous pouvez fournir un modèle Pydantic ou un dictionnaire de schéma JSON brut.

# 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.",

Transmettre le contexte uniquement en temps d'exécution à un outil

Utilisez des paramètres de fonction normaux pour les valeurs que le modèle doit fournir. Utiliser FunctionInvocationContext pour les valeurs runtime uniquement telles que function_invocation_kwargs ou la session active. Le paramètre de contexte injecté est masqué du schéma exposé au modèle.

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"},
    )

Pour plus d’informations sur ctx.kwargs, ctx.session, et l’intergiciel de fonction, consultez Runtime Context.

Créer des outils de déclaration uniquement

Si un outil est implémenté en dehors de l’infrastructure (par exemple, côté client dans une interface utilisateur), vous pouvez le déclarer sans implémentation à l’aide FunctionTool(..., func=None)de . Le modèle peut toujours raisonner et appeler l’outil, et votre application peut fournir le résultat ultérieurement.


# 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"},

Lors de la création de l’agent, vous pouvez maintenant fournir l’outil de fonction à l’agent en le transmettant au tools paramètre.

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
)

Maintenant, vous pouvez simplement exécuter l'agent comme d'habitude, et l'agent sera en mesure d'appeler la fonction get_weather si besoin.

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

asyncio.run(main())

Créer une classe avec plusieurs outils de fonction

Lorsque plusieurs outils partagent des dépendances ou un état mutable, encapsulez-les dans une classe et transmettez des méthodes liées à l’agent. Utilisez des attributs de classe pour les valeurs que le modèle ne doit pas fournir, tels que les clients de service, les indicateurs de fonctionnalité ou l’état mis en cache.

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])

Ce modèle convient bien à un état d’outil durable. Utilisez FunctionInvocationContext à la place lorsque la valeur change par appel.

Prochaines étapes