Używanie narzędzi funkcjonalnych z agentem

W tym kroku samouczka pokazano, jak używać narzędzi funkcjonalnych z agentem, gdzie agent jest oparty na usłudze Azure OpenAI Chat Completion.

Ważne

Nie wszystkie typy agentów obsługują funkcjonalne narzędzia. Niektóre mogą obsługiwać tylko konfigurowalne wbudowane narzędzia, nie zezwalając wywołującym na użycie własnych funkcji. W tym kroku używany jest element ChatClientAgent, który obsługuje narzędzia funkcji.

Wymagania wstępne

Aby uzyskać wymagania wstępne i zainstalować pakiety NuGet, zobacz krok Tworzenie i uruchamianie prostego agenta w tym samouczku.

Tworzenie agenta za pomocą narzędzi funkcji

Narzędzia funkcji to kod niestandardowy, który ma być wywoływany przez agenta w razie potrzeby. Dowolną metodę języka C# można przekształcić w funkcję narzędzia, używając metody AIFunctionFactory.Create, aby utworzyć wystąpienie AIFunction z tej metody.

Jeśli musisz podać agentowi dodatkowe opisy funkcji lub jej parametrów, aby mógł on dokładniej wybierać między różnymi funkcjami, możesz użyć atrybutu System.ComponentModel.DescriptionAttribute dla metody i jej parametrów.

Oto przykład prostego narzędzia funkcjonalnego, które symuluje pobieranie danych pogodowych dla danej lokalizacji. Jest on ozdobiony atrybutami opisu, aby udostępnić dodatkowe opisy dotyczące samego siebie i jego parametru lokalizacji agentowi.

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

Podczas tworzenia agenta możesz teraz udostępnić narzędzie funkcjonalności agentowi, poprzez przekazanie listy narzędzi do metody AsAIAgent.

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

AIAgent agent = new AIProjectClient(
    new Uri("<your-foundry-project-endpoint>"),
    new DefaultAzureCredential())
     .AsAIAgent(
        model: "gpt-4o-mini",
        instructions: "You are a helpful assistant",
        tools: [AIFunctionFactory.Create(GetWeather)]);

Ostrzeżenie

DefaultAzureCredential jest wygodne do programowania, ale wymaga starannego rozważenia w środowisku produkcyjnym. W środowisku produkcyjnym rozważ użycie określonego poświadczenia (np. ManagedIdentityCredential), aby uniknąć problemów z opóźnieniami, niezamierzonego sondowania poświadczeń i potencjalnych zagrożeń bezpieczeństwa wynikających z mechanizmów awaryjnych.

Teraz możesz po prostu uruchomić agenta jak zwykle, a agent będzie mógł wywołać funkcję GetWeather w razie potrzeby.

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

Tip

Zobacz przykłady dla platformy .NET , aby uzyskać pełne przykłady możliwych do uruchomienia.

Ważne

Nie wszystkie typy agentów obsługują funkcjonalne narzędzia. Niektóre mogą obsługiwać tylko konfigurowalne wbudowane narzędzia, nie zezwalając wywołującym na użycie własnych funkcji. Ten krok korzysta z agentów utworzonych za pośrednictwem klientów czatów, które obsługują narzędzia funkcji.

Wymagania wstępne

Aby uzyskać wymagania wstępne i zainstalować pakiety języka Python, zobacz krok Tworzenie i uruchamianie prostego agenta w tym samouczku.

Tworzenie agenta za pomocą narzędzi funkcji

Narzędzia funkcji to kod niestandardowy, który ma być wywoływany przez agenta w razie potrzeby. Dowolną funkcję języka Python można przekształcić w narzędzie funkcji, przekazując ją do parametru agenta tools podczas tworzenia agenta.

Jeśli potrzebujesz podać dodatkowe opisy dotyczące funkcji lub jej parametrów agentowi, aby mógł dokładniej wybrać między różnymi funkcjami, możesz użyć adnotacji typu w języku Python, korzystając z elementów Annotated oraz narzędzi Pydantic Field do przekazywania opisów.

Oto przykład prostego narzędzia funkcjonalnego, które symuluje pobieranie danych pogodowych dla danej lokalizacji. Używa adnotacji typu, aby udostępnić dodatkowe opisy dotyczące funkcji i parametru lokalizacji dla agenta.

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

Możesz również użyć dekoratora @tool , aby jawnie określić nazwę i opis funkcji:

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

Jeśli nie określisz parametrów name i description w dekoratorze @tool, framework automatycznie użyje nazwy funkcji i jej docstringa jako wartości domyślnych.

Używanie jawnych schematów z @tool

Jeśli potrzebujesz pełnej kontroli nad schematem uwidocznionym w modelu, przekaż parametr schema do @tool. Możesz podać model Pydantic lub pierwotny słownik schematu JSON.

# 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",
)
def get_weather(location: str, unit: str = "celsius") -> str:
    """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.",
    schema=get_current_time_schema,
    approval_mode="never_require",
)
def get_current_time(timezone: str = "UTC") -> str:
    """Get the current time."""

Przekazywanie kontekstu tylko podczas uruchamiania do narzędzia

Użyj normalnych parametrów funkcji dla wartości, które powinien podać model. Użyj FunctionInvocationContext przeznaczone tylko do użycia w trakcie działania, takich jak function_invocation_kwargs lub bieżąca sesja. Wstrzykiwany parametr kontekstu jest ukryty przed schematem ujawnionym modelowi.

import asyncio
from typing import Annotated

from agent_framework import Agent, FunctionInvocationContext, tool
from agent_framework.openai import OpenAIChatClient
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 = Agent(
        client=OpenAIChatClient(),
        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"},
    )

    print(f"Agent: {response.text}")

Aby uzyskać więcej informacji na temat ctx.kwargsoprogramowania pośredniczącego , ctx.sessioni funkcji, zobacz Kontekst środowiska uruchomieniowego.

Tworzenie narzędzi tylko do deklarowania

Jeśli narzędzie jest implementowane poza strukturą (na przykład po stronie klienta w interfejsie użytkownika), można zadeklarować je bez implementacji przy użyciu polecenia FunctionTool(..., func=None). Model nadal może rozumować na temat narzędzia i je wywoływać, a aplikacja może dostarczyć wynik później.

# 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"},
        },
        "required": ["reason"],
    },
)

Podczas tworzenia agenta można teraz udostępnić agentowi narzędzie funkcji, przekazując je jako parametr tools.

import asyncio
import os
from agent_framework.openai import OpenAIChatCompletionClient
from azure.identity import AzureCliCredential

agent = OpenAIChatCompletionClient(
    model=os.environ["AZURE_OPENAI_CHAT_COMPLETION_MODEL"],
    azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
    api_version=os.getenv("AZURE_OPENAI_API_VERSION"),
    credential=AzureCliCredential(),
).as_agent(
    instructions="You are a helpful assistant",
    tools=get_weather
)

Teraz możesz po prostu uruchomić agenta jak zwykle, a agent będzie mógł wywołać funkcję get_weather w razie potrzeby.

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

asyncio.run(main())

Tworzenie klasy z wieloma narzędziami funkcji

Gdy kilka narzędzi współużytkuje zależności lub stan modyfikowalny, zawija je w klasie i przekazuje metody powiązane do agenta. Użyj atrybutów klasy dla wartości, które model nie powinien podawać, takich jak klienci usług, flagi funkcji lub stan buforowany.

import asyncio
from typing import Annotated

from agent_framework import Agent, tool
from agent_framework.openai import OpenAIChatClient
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 = Agent(
        client=OpenAIChatClient(),
        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

Wzorzec ten jest dobrze dopasowany do długotrwałego stanu narzędzia. Użyj FunctionInvocationContext zamiast tego, gdy wartość zmienia się przy każdym wywołaniu.

Narzędzia funkcji

Narzędzia funkcji umożliwiają agentom wywoływanie niestandardowych funkcji Języka Go. Pakiet functool zapewnia prosty sposób definiowania narzędzi bezpiecznych dla typów przy użyciu automatycznego generowania schematu.

Zdefiniuj narzędzie funkcji

import (
    "context"

    "github.com/microsoft/agent-framework-go/tool"
    "github.com/microsoft/agent-framework-go/tool/functool"
)

var weatherTool = functool.MustNew(functool.Config{
    Name:        "weather",
    Description: "Get the current weather for a given location",
}, func(_ context.Context, location string) (string, error) {
    return fmt.Sprintf("The weather in %s is cloudy with a high of 15°C.", location), nil
})

Podpis funkcji określa schemat wejściowy narzędzia. Parametr context.Context jest wstrzykiwany przez platformę i nie jest udostępniany modelowi.

Typy danych wejściowych ze strukturą

W przypadku narzędzi z wieloma parametrami zdefiniuj strukturę:

type WeatherInput struct {
    Location string `json:"location" jsonschema:"description=The city to check weather for"`
    Unit     string `json:"unit" jsonschema:"description=Temperature unit (celsius or fahrenheit),enum=celsius,enum=fahrenheit"`
}

var weatherTool = functool.MustNew(functool.Config{
    Name:        "weather",
    Description: "Get weather for a location",
}, func(_ context.Context, input WeatherInput) (string, error) {
    return fmt.Sprintf("Weather in %s: 15°%s", input.Location, input.Unit), nil
})

Tworzenie agenta za pomocą narzędzi

a := foundryprovider.NewAgent(endpoint, token, foundryprovider.ModelDeployment(model), foundryprovider.AgentConfig{
    Instructions: "You are a helpful assistant.",
    Config: agent.Config{
        Tools: []tool.Tool{weatherTool},
    },
})

resp, err := a.RunText(ctx, "What is the weather like in Amsterdam?").Collect()

Użyj agenta jako narzędzia funkcyjnego

Dowolny agent może być opakowany jako narzędzie funkcji do użycia przez innego agenta:

import "github.com/microsoft/agent-framework-go/tool/agenttool"

weatherAgent := foundryprovider.NewAgent(endpoint, token, foundryprovider.ModelDeployment(model), foundryprovider.AgentConfig{
    Instructions: "You answer questions about the weather.",
    Config: agent.Config{
        Name:        "WeatherAgent",
        Description: "An agent that answers weather questions.",
        Tools:       []tool.Tool{weatherTool},
    },
})

mainAgent := foundryprovider.NewAgent(endpoint, token, foundryprovider.ModelDeployment(model), foundryprovider.AgentConfig{
    Instructions: "You are a helpful assistant who responds in French.",
    Config: agent.Config{
        Tools: []tool.Tool{agenttool.New(weatherAgent, agenttool.Config{})},
    },
})

Użyj lokalnego narzędzia powłoki

SDK Go zawiera tool/shelltool do lokalnego wykonywania poleceń powłoki. Narzędzie domyślnie wymaga zatwierdzenia i może być połączone z dostawcą kontekstu środowiska, dzięki czemu model zna bieżący typ powłoki, katalog roboczy i typowe wersje narzędzi.

import "github.com/microsoft/agent-framework-go/tool/shelltool"

shell, err := shelltool.NewLocal(shelltool.LocalConfig{
    Mode: shelltool.ModeStateless,
})
if err != nil {
    return err
}
defer shell.Close()

envProvider := shelltool.NewEnvironmentProvider(shell, shelltool.EnvironmentProviderConfig{})

a := foundryprovider.NewAgent(endpoint, token, foundryprovider.ModelDeployment(model), foundryprovider.AgentConfig{
    Instructions: "Run shell commands only when needed and summarize the result.",
    Config: agent.Config{
        Tools:            []tool.Tool{shell},
        ContextProviders: []agent.ContextProvider{envProvider},
    },
})

Użyj shelltool.ModeStateless, gdy każde wywołanie ma być uruchamiane w nowej powłoce. Używaj shelltool.ModePersistent tylko wtedy, gdy jedna sesja agenta wymaga stanu powłoki, takiego jak zmienione katalogi lub wyeksportowane zmienne środowiskowe, aby utrwały się między wywołaniami. Ustaw AcknowledgeUnsafe: true tylko wtedy, gdy zapewniasz niezależną granicę izolacji i nie potrzebujesz wbudowanej bramy zatwierdzania.

Tip

Zobacz przykład narzędzi funkcji, przykład agenta jako narzędzia oraz przykład powłoki ze środowiskiem, aby zapoznać się z pełnymi przykładami.

Dalsze kroki

Sterowanie dostępnością narzędzia w czasie działania

Narzędzia można dodawać lub usuwać podczas działania agenta za pomocą FunctionInvocationContext.add_tools() / remove_tools(), kontrolować wywołania za pośrednictwem oprogramowania pośredniczącego funkcji lub wymusić określone pierwsze wywołanie za pomocą tool_choice. Zobacz Kontrolowanie dostępności narzędzi , aby uzyskać pełne wzorce.