AgentApplication فِي Microsoft 365 Agents SDK

AgentApplication هي الكتلة المركزية للعامل المبني باستخدام مجموعة تطوير العوامل. AgentApplication هي نقطة الدخول لجميع الأنشطة الواردة، بما فِي ذلك رسائل المستخدمين، وأحداث دورة حياة المحادثة، وتفاعلات البطاقات التكيفية، واستدعاءات OAuth.

عامل هو، فِي جوهره، AgentApplication. تقوم بتكوينها باستخدام معالجات تصف ما يفعله عاملك. تعود مجموعة تطوير البرمجيات (SDK) إلى إدارة التوجيه، وإدارة الحالات، والبنية التحتية اللازمة لتشغيلها.

كيفية عمل AgentApplication

تبدأ دورة حياة كل عامل عندما ترسل قناة (مثل Microsoft Teams أو خدمة روبوت أو عميل مخصص) نشاطًا إلى نقطة نهاية عامل الخاص بك. AgentApplication يتوسط هذه الدورة الحياتية:

Channel → Hosting layer → AgentApplication → Your handlers

طبقات المعالجة فِي عامل مبني باستخدام SDK للعوامل تعمل كما يلي:

  1. تستقبل طبقة الاستضافة طلب HTTP وتُصادق عليه.
  2. يقوم AgentApplication بمعالجة النشاط الوارد عبر خط الأنابيب الخاص به.
  3. يتم استدعاء المعالجات وفقًا للمسارات المطابقة.

يقوم العامل بتحميل حالة التبادل قبل تشغيل المعالجات. بعد ذلك، يحفظ العامل حالة التبادل.

المفاهيم الأساسية

الأنشطة

كل شيء فِي حزمة تطوير العوامل يتدفق كنشاط. النشاط هو رسالة منظمة تمثل شيئًا حدث. النشاط له نوع، مثل رسالة، حدث، استدعاء، conversationUpdate، وهكذا. يحمل بيانات ذات صلة بذلك النوع. AgentApplication يستقبل الأنشطة ويوجهها إلى المعالج المناسب.

المسارات

يقوم مسار بإقران محدد مع معالج. يحدد المحدد ما إذا كان المسار يتطابق مَعَ النشاط الحالي. يقوم المعالج بتنفيذ منطقك عندما يتطابق المسار.

سجل المسارات عند تكوين عاملِك. يمكن للمسارات أن تتطابق مع:

  • رسالة تحتوي على نص محدد أو تطابق تعبيرًا منتظمًا
  • أي نشاط مِنْ نوع معين
  • أحداث دورة حياة المحادثة (إضافة عضو، إزالة عضو)
  • إجراءات البطاقات التكيفية
  • الشروط المخصصة

عندما يصل النشاط، يقيم النظام المسارات بالترتيب حتى يجد تطابقًا. افتراضيًا، يتم تنفيذ مسار واحد فقط.

حالة التفاعل

AgentApplication يدير حالة _التبادل — تخزين منظم مقسم إلى نطاقات:

نوع النطاق الوصف
المحادثة مشترك بين جميع المستخدمين في المحادثة، ويستمر بين التبادلات
المُسْتَخْدِم محصورة بمستخدم واحد عبر جميع المحادثات
درجة الحرارة التبادل الحالي فقط — لا يستمر إطلاقًا

يحمّل النظام الحالة تلقائيًا قبل تشغيل معالجاتك، ويحفظها تلقائيًا بعد ذلك.

سياق تحويل المحادثة

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

البرامج الوسيطة

AgentApplication يدعم مسار البرامج الوسيطة. البرنامج الوسيط هو سلسلة من المكونات التي تعالج كل تبادل قبل تشغيل المعالجات وبعد تشغيلها. يمكن للبرمجيات الوسيطة فحص أو تحويل أو قطع تدفق النشاط. تشمل الاستخدامات الشائعة التسجيل، وفحوصات المصادقة، وتطبيع الطلبات.

إنشَاء مندوب

قم بإنشاء فئة فرعية مِنْ AgentApplication وسجّل معالجاتك فِي المُنشئ. يقوم إطار الاستضافة تلقائياً بحقن AgentApplicationOptions.

public class MyAgent : AgentApplication
{
    public MyAgent(AgentApplicationOptions options) : base(options)
    {
        OnConversationUpdate(ConversationUpdateEvents.MembersAdded, WelcomeAsync);
        OnActivity(ActivityTypes.Message, OnMessageAsync, rank: RouteRank.Last);
    }

    private async Task WelcomeAsync(ITurnContext context, ITurnState state, CancellationToken ct)
    {
        foreach (var member in context.Activity.MembersAdded)
        {
            if (member.Id != context.Activity.Recipient.Id)
            {
                await context.SendActivityAsync("Hello! How can I help you?", cancellationToken: ct);
            }
        }
    }

    private async Task OnMessageAsync(ITurnContext context, ITurnState state, CancellationToken ct)
    {
        await context.SendActivityAsync($"You said: {context.Activity.Text}", cancellationToken: ct);
    }
}

سجِّل العامل فِي Program.cs:

WebApplicationBuilder builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpClient();
builder.Services.AddSingleton<IStorage, MemoryStorage>();
builder.Services.AddAgent<MyAgent>();
builder.Services.AddAgentAspNetAuthentication(builder.Configuration);

WebApplication app = builder.Build();

app.UseAuthentication();
app.UseAuthorization();
app.MapAgentApplicationEndpoints(requireAuth: !app.Environment.IsDevelopment());

app.Run();

سجّل معالجات النشاط

معالجة الرسائل

طابِق الرسائل بناءً على النص المطابق تمامًا (من دون تمييز بين الأحرف الكبيرة والصغيرة):

OnMessage("help", async (context, state, ct) =>
{
    await context.SendActivityAsync("Here's what I can do...", cancellationToken: ct);
});

مطابقة الرسائل باستخدام تعبير منتظم:

OnMessage(new Regex(@"^order\s+\d+$", RegexOptions.IgnoreCase), async (context, state, ct) =>
{
    await context.SendActivityAsync("Looking up your order...", cancellationToken: ct);
});

معالجة تحديثات المحادثة

سجّل معالجات أحداث دورة حياة المحادثة مثل انضمام الأعضاء أو مغادرتهم.

OnConversationUpdate(ConversationUpdateEvents.MembersAdded, async (context, state, ct) =>
{
    foreach (var member in context.Activity.MembersAdded)
    {
        if (member.Id != context.Activity.Recipient.Id)
        {
            await context.SendActivityAsync("Welcome!", cancellationToken: ct);
        }
    }
});

OnConversationUpdate(ConversationUpdateEvents.MembersRemoved, async (context, state, ct) =>
{
    // Called when participants leave the conversation
});

عالِج أي نوع مِنْ الأنشطة.

مطابقة أي نشاط حسب سلسلة نوعه للتحكم الكامل فِي التوجيه.

OnActivity(ActivityTypes.Message, async (context, state, ct) =>
{
    // Handles all message activities
});

OnActivity(ActivityTypes.Event, async (context, state, ct) =>
{
    // Handles event activities
});

استخدم ActivityTypes الثوابت بدلاً مِنْ السلاسل النصية الثابتة.

التحكم فِي ترتيب تقييم المسارات

يقوم النظام بفرز المسارات فِي ترتيب تقييم ثابت عند تسجيلها، وليس أثناء وقت التشغيل. يستخدم هذا التصنيف مستويين:

  1. نوع المسار: يجمع النظام المسارات حسب النوع، ويقيّم دائماً الأنواع ذات الأولوية الأعلى قبل الأنواع ذات الأولوية الأقل، بغض النظر عن الرتبة:

    الأولوية نوع المسار
    1 (الأعلى) مسارات استدعاء قائمة على العاملين
    2 استدعاء المسارات (إجراءات بطاقة الموائمة المفتوحة، واستدعاءات OAuth، وغيرها من عمليات الاستدعاء الحساسة للوقت)
    3 مسارات قائمة على العاملين
    4 (الأدنى) جميع المسارات الأخرى
  2. الرتبة: داخل كل مجموعة مِنْ أنواع المسار، يرتب النظام المسارات حسب قيمة رتبتها. يتم تقييم القيم الرقمية الأدنى أولاً.

استخدم RouteRank الثوابت لتحديد الرتبة عند تسجيل معالج:

ثابت القيمة‬ المعنى
RouteRank.First 0 يتم تقييمه قبل جميع المسارات الأخرى ضمن مجموعته
RouteRank.Unspecified 32767 الافتراضي عندما لا يتم تحديد رتبة
RouteRank.Last 65535 يُقيَّم بعد جميع المسارات الأخرى فِي مجموعته.

بشكل افتراضي، يتوقف التقييم عند أول مسار مطابق. استخدم RouteRank.Last كمسار احتياطي شامل لمعالجة أي شيء لا يتطابق مَعَ مسار أكثر تحديدًا.

// Specific handlers use the default rank
OnMessage("status", HandleStatusAsync);
OnMessage("help", HandleHelpAsync);

// Catch-all — handles anything not matched above
OnActivity(ActivityTypes.Message, HandleUnknownMessageAsync, rank: RouteRank.Last);

خطافات دورة حياة التبادل

سجّل منطقًا يُنفَّذ فِي كل دورة، قبل أو بعد مطابقة المسار. هذه الخطافات مفيدة للتسجيل والاهتمامات العرضية، ومعالجة الأخطاء.

OnBeforeTurn(async (context, state, ct) =>
{
    logger.LogInformation("Turn started: {Type}", context.Activity.Type);
    return true; // Return false to abort the turn
});

OnAfterTurn(async (context, state, ct) =>
{
    logger.LogInformation("Turn completed");
    return true; // Return false to skip state saving
});

OnTurnError(async (context, state, exception, ct) =>
{
    logger.LogError(exception, "Turn error");
    await context.SendActivityAsync("Something went wrong. Please try again.", cancellationToken: ct);
});

عندما يقوم OnBeforeTurn بإرجاع false، يتم إجهاض التبادل ولا يتم تشغيل أي مسارات. عند OnAfterTurn العودة false، لا يتم حفظ حالة الدور.

استخدم حالة الدور

يقوم العامل تلقائيًا بتحميل حالة التبادل قبل تشغيل المعالجات، ثم يحفظها. كائن حالة الدورة الذي يُمرر إلى معالجاتك يمنحك الوصول إلى النطاقات المختلفة حتى تتمكن مِنْ قراءة وكتابة بيانات تبقى عبر الدورات أو تكون مؤقتة للدورة الحالية:

  • نطاق المحادثة: للبيانات المشتركة عبر جميع عمليات التبادلات في محادثة
  • نطاق المستخدم: لبيانات كل مستخدم
  • نطاق المؤقت: للبيانات التي توجد فقط أثناء الدور الحالي
OnActivity(ActivityTypes.Message, async (context, state, ct) =>
{
    // Conversation scope — persisted per conversation
    var count = state.Conversation.GetValue<int>("messageCount", () => 0);
    state.Conversation.SetValue("messageCount", count + 1);

    // User scope — persisted per user
    var name = state.User.GetValue<string>("displayName");

    // Temp scope — current turn only
    state.Temp.SetValue("parsedInput", context.Activity.Text?.Trim());

    await context.SendActivityAsync($"Message #{count + 1}: {context.Activity.Text}", cancellationToken: ct);
});

إشعار

استخدم MemoryStorageللتطوير والاختبار. لنشر الإنتاج، خاصة النشر الذي يعمل على عدة مثلات، استخدم مزود تخزين دائم مثل Azure Cosmos DB أو مساحة تخزين Azure Blob. راجع موفري التخزين فِي عاملك.

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