استخدام أدوات الدالة مع عامل

توضح لك خطوة البرنامج التعليمي هذه كيفية استخدام أدوات الدالة مع عامل، حيث يتم بناء العامل على خدمة إكمال دردشة OpenAI Azure.

Important

لا تدعم كافة أنواع الوكلاء أدوات الدالة. قد يدعم البعض الأدوات المضمنة المخصصة فقط، دون السماح للمتصل بتوفير وظائفه الخاصة. تستخدم ChatClientAgentهذه الخطوة ، والتي تدعم أدوات الدالة.

المتطلبات الأساسية

للحصول على المتطلبات الأساسية وتثبيت حزم NuGet، راجع خطوة إنشاء وتشغيل عامل بسيط في هذا البرنامج التعليمي.

إنشاء العامل باستخدام أدوات الدالة

أدوات الدالة هي مجرد تعليمة برمجية مخصصة تريد أن يتمكن العامل من الاتصال بها عند الحاجة. يمكنك تحويل أي أسلوب C# إلى أداة دالة، باستخدام AIFunctionFactory.Create الأسلوب لإنشاء AIFunction مثيل من الأسلوب .

إذا كنت بحاجة إلى توفير أوصاف إضافية حول الدالة أو معلماتها للعامل، بحيث يمكن الاختيار بشكل أكثر دقة بين الدالات المختلفة، يمكنك استخدام السمة System.ComponentModel.DescriptionAttribute على الأسلوب ومعلماته.

هنا مثال على أداة وظيفة بسيطة مزيفة الحصول على الطقس لموقع معين. تم تزيينه بسمات الوصف لتوفير أوصاف إضافية حول نفسه ومعلمة موقعه للعامل.

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

عند إنشاء العامل، يمكنك الآن توفير أداة الوظيفة للعامل، عن طريق تمرير قائمة الأدوات إلى 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)]);

تحذير

DefaultAzureCredential مناسب للتنمية ولكنه يتطلب دراسة متأنية في الإنتاج. في الإنتاج، ضع في اعتبارك استخدام بيانات اعتماد محددة (على سبيل المثال، ManagedIdentityCredential) لتجنب مشكلات زمن الانتقال، وبحث بيانات الاعتماد غير المقصودة، والمخاطر الأمنية المحتملة من الآليات الاحتياطية.

الآن يمكنك فقط تشغيل العامل كالمعتاد، وسيكون العامل قادرا على استدعاء أداة الدالة GetWeather عند الحاجة.

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

Tip

راجع نماذج .NET للحصول على أمثلة كاملة قابلة للتشغيل.

Important

لا تدعم كافة أنواع الوكلاء أدوات الدالة. قد يدعم البعض الأدوات المضمنة المخصصة فقط، دون السماح للمتصل بتوفير وظائفه الخاصة. تستخدم هذه الخطوة العوامل التي تم إنشاؤها عبر عملاء الدردشة، والتي تدعم أدوات الوظائف.

المتطلبات الأساسية

للحصول على المتطلبات الأساسية وتثبيت حزم Python، راجع خطوة إنشاء وتشغيل عامل بسيط في هذا البرنامج التعليمي.

إنشاء العامل باستخدام أدوات الدالة

أدوات الدالة هي مجرد تعليمة برمجية مخصصة تريد أن يتمكن العامل من الاتصال بها عند الحاجة. يمكنك تحويل أي دالة Python إلى أداة دالة عن طريق تمريرها إلى معلمة العامل tools عند إنشاء العامل.

إذا كنت بحاجة إلى توفير أوصاف إضافية حول الدالة أو معلماتها للعامل، بحيث يمكن الاختيار بشكل أكثر دقة بين الدالات المختلفة، يمكنك استخدام التعليقات التوضيحية لنوع Python مع Annotated و Pydantic Field لتوفير الأوصاف.

هنا مثال على أداة وظيفة بسيطة مزيفة الحصول على الطقس لموقع معين. يستخدم التعليقات التوضيحية للنوع لتوفير أوصاف إضافية حول الدالة ومعلمة موقعها للعامل.

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

يمكنك أيضا استخدام @tool مصمم الديكور لتحديد اسم الدالة ووصفها بشكل صريح:

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

إذا لم تحدد name المعلمتين و description في @tool المصمم، فسيستخدم إطار العمل تلقائيا اسم الدالة و docstring كعمليات احتياطية.

استخدام المخططات الصريحة مع @tool

عندما تحتاج إلى التحكم الكامل في المخطط المكشوف للنموذج، قم بتمرير المعلمة schema إلى @tool. يمكنك توفير إما نموذج Pydantic أو قاموس مخطط JSON الخام.

from pydantic import BaseModel, Field

# 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.",
)
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 for a trusted, non-sensitive tool.
# This receives lightweight top-level checks only; use Pydantic for runtime enforcement.
get_current_time_schema = {
    "type": "object",
    "properties": {
        "timezone": {"type": "string", "description": "The timezone to get the current time for", "default": "UTC"},
    },
}


@tool(

تمرير سياق وقت التشغيل فقط إلى أداة

استخدم معلمات الدالة العادية للقيم التي يجب أن يوفرها النموذج. استخدم FunctionInvocationContext لقيم وقت التشغيل فقط مثل function_invocation_kwargs أو جلسة العمل الحالية. يتم إخفاء معلمة السياق التي تم إدخالها من المخطط المكشوف للنموذج.

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

لمزيد من التفاصيل حول ctx.kwargsو ctx.sessionو ودالة الوسيطة، راجع سياق وقت التشغيل.

إنشاء أدوات الإعلان فقط

إذا تم تنفيذ أداة خارج إطار العمل (على سبيل المثال، من جانب العميل في واجهة المستخدم)، يمكنك الإعلان عنها دون تنفيذ باستخدام FunctionTool(..., func=None). لا يزال بإمكان النموذج التفكير في الأداة واستدعاءها، ويمكن للتطبيق الخاص بك توفير النتيجة لاحقا.

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

تحليل نتائج الأدوات المخصصة

قم بتعيين result_parser على FunctionTool أو @tool عندما تحتاج إلى تحويل قيمة إرجاع أولية إلى سلسلة أو قائمة من Content العناصر. إذا قام محلل مخصص برفع استثناء، فإن استدعاء مباشر ينشر الاستثناء، بينما يرجع استدعاء الدالة التلقائي نتيجة خطأ أداة عادية include_detailed_errorsويكرم . لا يتم استخدام قيمة الإرجاع الأولية كقيمة احتياطية، لذا قم بمعالجة أخطاء التحويل القابلة للاسترداد داخل المحلل المخصص.

عند إنشاء العامل، يمكنك الآن توفير أداة الوظيفة للعامل، عن طريق تمريرها إلى المعلمة 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
)

الآن يمكنك فقط تشغيل العامل كالمعتاد، وسيكون العامل قادرا على استدعاء أداة الدالة get_weather عند الحاجة.

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

asyncio.run(main())

الحد من استدعاء الأدوات التلقائي

قم بتعيين حدود لعميل الدردشة للتحكم في استدعاء الأداة التلقائية عن طريق الرحلات ذهابا ورابعا وإجمالي استدعاءات الدالة ووقت ساعة الحائط المنقضي:

max_function_calls والإعداد max_duration_seconds الافتراضي إلى None، مما يعني غير محدود. تعيينها إلى قيم موجبة. عندما يصل العميل إلى حد ما، فإنه يتوقف عن استدعاء الأدوات ويطلب من النموذج استجابة نص نهائي.

هذه الحدود هي أفضل جهد ويتم التحقق منها بعد كل دفعة طلب نموذج من استدعاءات الأدوات، بحيث يمكن أن تتجاوز الدفعة حد عدد المكالمات أو المدة. الوقت المستغرق في انتظار عدد الموافقة على الأدوات باتجاه max_duration_seconds.

from agent_framework.openai import OpenAIChatCompletionClient

client = OpenAIChatCompletionClient()
client.function_invocation_configuration.update(
    {
        "max_iterations": 5,
        "max_function_calls": 20,
        "max_duration_seconds": 30.0,
    }
)

التحكم في استدعاء الأداة المتزامنة

يتم تشغيل استدعاءات الدالة التي يتم إرجاعها في رسالة مساعد واحد بشكل متزامن بشكل افتراضي. اضبط allow_concurrent_invocation على False عندما تشترك الأدوات في حالة قابلة للتغيير أو يجب تشغيلها بترتيب النموذج:

client.function_invocation_configuration["allow_concurrent_invocation"] = False

يتحكم هذا الإعداد في تنفيذ أداة من جانب العميل بشكل مستقل عن خيارات جانب الموفر مثل allow_multiple_tool_calls. بالنسبة إلى دفعات الموافقة المدعومة بالجلسة، يمكن أن تصل القرارات بشكل منفصل، ولكن لا يتم تشغيل أي أداة ولا يستأنف النموذج حتى يكون لكل موافقة في الدفعة الأصلية قرار. ثم يتم تشغيل الأدوات المعتمدة بترتيب النموذج الأصلي.

تفاصيل خطأ أداة التحكم

بشكل افتراضي، include_detailed_errors هو False. ترجع حالات فشل تنفيذ الدالة والتحقق من صحة الوسيطة نتائج عامة إلى النموذج والقنوات التسلسلية الأخرى. يظل التشخيص الأصلي متاحا للتعليمات البرمجية للمضيف الموثوق به في Content.exception. Content.to_dict() يستبدل هذا الحقل بعلامة فشل ثابتة وغير حساسة، ولا تعرض تحويلات البروتوكول التشخيص.

اضبط client.function_invocation_configuration["include_detailed_errors"] = True فقط عندما تبقى النتيجة على قناة موثوق بها. يمكن أن يحتوي نص الاستثناء على بيانات حساسة، ويضيف هذا الإعداد هذا النص إلى النتيجة المرئية للقناة.

إنشاء فئة باستخدام أدوات وظائف متعددة

عندما تشترك العديد من الأدوات في تبعيات أو حالة قابلة للتغيير، قم بتضمينها في فئة وتمرير الأساليب المرتبطة إلى العامل. استخدم سمات الفئة للقيم التي يجب ألا يوفرها النموذج، مثل عملاء الخدمة أو علامات الميزات أو الحالة المخزنة مؤقتا.

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

هذا النمط مناسب لحالة الأدوات طويلة الأمد. استخدم FunctionInvocationContext بدلا من ذلك عندما تتغير القيمة لكل استدعاء.

أدوات الدوال

تتيح أدوات الدالة للوكلاء استدعاء دوال Go المخصصة. functool توفر الحزمة طريقة بسيطة لتحديد الأدوات الآمنة من النوع مع إنشاء مخطط تلقائي.

تعريف أداة دالة

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

يحدد توقيع الدالة مخطط إدخال الأداة. context.Context يتم إدخال المعلمة بواسطة إطار العمل ولا تتعرض للنموذج.

أنواع الإدخالات المنظمة

بالنسبة للأدوات ذات معلمات متعددة، حدد البنية:

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

أنشئ وكيلا باستخدام الأدوات

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

استخدام عامل كأداة دالة

يمكن التفاف أي عامل كأداة دالة لاستخدامها من قبل عامل آخر:

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

استخدام أداة shell المحلية

يتضمن tool/shelltool Go SDK لتنفيذ shell المحلي. تتطلب الأداة الموافقة بشكل افتراضي ويمكن إقرانها بموفر سياق البيئة حتى يعرف النموذج عائلة shell الحالية ودليل العمل وإصدارات الأدوات الشائعة.

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

استخدم shelltool.ModeStateless عندما يجب تشغيل كل مكالمة في shell جديدة. استخدم shelltool.ModePersistent فقط عندما تحتاج جلسة عامل واحد إلى حالة shell مثل الدلائل المتغيرة أو متغيرات البيئة المصدرة للاستمرار عبر المكالمات. اضبط AcknowledgeUnsafe: true فقط عند توفير حدود عزل مستقلة ولا تحتاج إلى بوابة الموافقة المضمنة.

استخدام أدوات الدالة مع Harness Agent

يستخدم الوكيل العادي الأدوات التي تمر بها أثناء إنشاء الوكيل، وتؤلف أي موفرين إضافيين أو برامج وسيطة بنفسك. يستخدم عامل Harness نفس أدوات الدالة، ولكنه يعمل مسبقا على تكوين البنية الأساسية لبرنامج ربط العمليات التجارية لاستدعاء الوظيفة، واستمرارية كل استدعاء لكل خدمة، ودعم الموافقة على الأدوات، وقدرات تسخير أخرى.

قم بتمرير أدوات الدالة من خلال HarnessAgentOptions.ChatOptions.Tools عند إنشاء HarnessAgent باستخدام AsHarnessAgent:

using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;

AIAgent agent = chatClient.AsHarnessAgent(new HarnessAgentOptions
{
    ChatOptions = new ChatOptions
    {
        Instructions = "You are a helpful assistant.",
        Tools = [AIFunctionFactory.Create(GetWeather)],
    },
});

AgentSession session = await agent.CreateSessionAsync();
AgentResponse response = await agent.RunAsync(
    "What is the weather like in Amsterdam?",
    session);

HarnessAgent يتم FunctionInvokingChatClient تكوينه تلقائيا. تعيين HarnessAgentOptions.MaximumIterationsPerRequest لتجاوز حد استدعاء الدالة الخاص به؛ يستخدم FunctionInvokingChatClient الافتراضي null الافتراضي. يضيف HostedWebSearchTool التسخير أيضا بشكل افتراضي، لذا قم بتعيين DisableWebSearch = true ما إذا كان يجب أن يعرض العامل الأدوات فقط في ChatOptions.Tools.

قم بتمرير أداة واحدة أو سلسلة من الأدوات إلى معلمة toolscreate_harness_agent:

from agent_framework import create_harness_agent

agent = create_harness_agent(
    client=client,
    agent_instructions="You are a helpful assistant.",
    tools=get_weather,
)

session = agent.create_session()
response = await agent.run(
    "What is the weather like in Amsterdam?",
    session=session,
)
print(response.text)

يقوم المصنع بتكوين استدعاء الدالة التلقائي واستمرار محفوظات استدعاء لكل خدمة. دالات مزينة باستخدام @toolapproval_mode="never_require" بشكل افتراضي. disable_web_search=False يضيف أيضا أداة البحث على الويب الخاصة بالعميل عندما يدعمها العميل؛ تعيين disable_web_search=True إلى حذفه.

يثبت ToolApprovalMiddleware التسخير افتراضيا (disable_tool_auto_approval=False)، ويتطلب AgentSession هذا البرنامج الوسيط لكل تشغيل. قم بالتمرير session=agent.create_session() كما هو موضح، أو قم بتعيينه disable_tool_auto_approval=True بشكل صريح إذا لم تكن بحاجة إلى برنامج وسيط للموافقة على التسخير.

لا يتوفر حاليا تسخير Go المحزم. أضف أدوات الدالة إلى agent.Config.Tools وقم بإنشاء موفري البرامج الوسيطة والسياق المطلوبين مباشرة.

الخطوات التالية

التحكم في توفر الأدوات في وقت التشغيل

يمكنك إضافة أدوات أو إزالتها أثناء تشغيل عامل باستخدام FunctionInvocationContext.add_tools() / remove_tools()، أو استدعاء البوابة عبر برنامج وسيط للوظيفة، أو فرض استدعاء أول محدد باستخدام tool_choice. راجع التحكم في توفر الأداة للأنماط الكاملة.