تطبيق AgentApplication في Microsoft 365 Agents SDK

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

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

كيفية عمل AgentApplication

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

Channel → Hosting layer → AgentApplication → Your handlers

تعمل طبقات المعالجة في عامل تم إنشاؤه باستخدام Agents SDK على النحو التالي:

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

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

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

الأنشطة

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

المسارات

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

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

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

عند وصول نشاط، يقوم النظام بتقييم المسارات بالترتيب حتى يعثر على تطابق. بشكل افتراضي، يتم تشغيل مسار واحد فقط.

حالة الدوران

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

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

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

تحويل السياق

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

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

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. Rank: ضمن كل مجموعة نوع مسار، يقوم النظام بطلب المسارات حسب قيمة الرتبة الخاصة بها. يتم تقييم القيم الرقمية الدنيا أولا.

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

ثابت Value المعنى
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، يتم إجهاض التبادل ولا يتم تشغيل أي مسارات. عندما يعيد OnAfterTurnfalse، لا يتم حفظ حالة الدوران.

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

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

  • نطاق المحادثة: للبيانات المشتركة عبر جميع عمليات التبادلات في محادثة
  • نطاق المستخدم: للبيانات لكل مستخدم
  • النطاق المؤقت: للبيانات التي تحتاج فقط إلى الوجود أثناء الدوران الحالي
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. راجع استخدام موفري التخزين في وكيلك.

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