إشعار
يتطلب الوصول إلى هذه الصفحة تخويلاً. يمكنك محاولة تسجيل الدخول أو تغيير الدلائل.
يتطلب الوصول إلى هذه الصفحة تخويلاً. يمكنك محاولة تغيير الدلائل.
يسمح تزامن التسليم للوكلاء بنقل عنصر التحكم إلى بعضهم البعض استنادا إلى السياق أو طلب المستخدم. يمكن لكل عامل "تسليم" المحادثة إلى وكيل آخر يتمتع بالخبرة المناسبة، ما يضمن أن الوكيل المناسب يتعامل مع كل جزء من المهمة. وهذا مفيد بشكل خاص في دعم العملاء أو أنظمة الخبراء أو أي سيناريو يتطلب تفويضا ديناميكيا.
داخليا، يتم تنفيذ تزامن التسليم باستخدام مخطط شبكة حيث يتم توصيل العوامل مباشرة دون منسق. يمكن لكل عامل تحديد وقت تسليم المحادثة استنادا إلى قواعد محددة مسبقا أو محتوى الرسائل.
Note
يدعم Agent تنسيق التسليم فقط ويجب على الوكلاء دعم تنفيذ الأدوات المحلية.
الاختلافات بين التسليم ووكيل كأدوات
في حين أن العامل كأدوات يعتبر عادة كنمط متعدد العوامل وقد يبدو مشابها للتسليم للوهلة الأولى، هناك اختلافات أساسية بين الاثنين:
- تدفق التحكم: في تزامن التسليم، يتم تمرير التحكم بشكل صريح بين العوامل استنادا إلى قواعد محددة. يمكن لكل عامل أن يقرر تسليم المهمة بأكملها إلى وكيل آخر. لا توجد سلطة مركزية تدير سير العمل. في المقابل، يتضمن العامل كأدوات وكيلا أساسيا يفوض المهام الفرعية إلى وكلاء آخرين وبمجرد أن يكمل العامل المهمة الفرعية، يعود عنصر التحكم إلى العامل الأساسي.
- ملكية المهمة: في التسليم، يأخذ العامل الذي يتلقى التسليم الملكية الكاملة للمهمة. في عامل كأدوات، يحتفظ العامل الأساسي بالمسؤولية العامة عن المهمة، بينما يتم التعامل مع الوكلاء الآخرين كأدوات للمساعدة في مهام فرعية محددة.
- إدارة السياق: في تنسيق التسليم، يتم تسليم المحادثة إلى وكيل آخر بالكامل. لدى الوكيل المتلقي السياق الكامل لما تم القيام به حتى الآن. في agent-as-tools، يدير العامل الأساسي السياق العام وقد يوفر معلومات ذات صلة فقط لوكلاء الأدوات حسب الحاجة.
ما ستتعلمه
- كيفية إنشاء وكلاء متخصصين لمجالات مختلفة
- كيفية تكوين قواعد التسليم بين الوكلاء
- كيفية إنشاء مهام سير عمل تفاعلية مع توجيه الوكيل الديناميكي
- كيفية التعامل مع المحادثات متعددة الأدوار مع تبديل العامل
- كيفية تنفيذ الموافقة على الأدوات للعمليات الحساسة (HITL)
- كيفية استخدام نقاط التحقق لسير عمل التسليم الدائم
في تزامن التسليم، يمكن للوكلاء نقل التحكم إلى بعضهم البعض بناء على السياق، ما يسمح للتوجيه الديناميكي ومعالجة الخبرة المتخصصة.
إعداد عميل Azure OpenAI
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Azure.AI.Projects;
using Azure.Identity;
using Microsoft.Agents.AI.Workflows;
using Microsoft.Extensions.AI;
using Microsoft.Agents.AI;
// 1) Set up the Azure OpenAI client
var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ??
throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
var client = new AIProjectClient(new Uri(endpoint), new DefaultAzureCredential())
.GetProjectOpenAIClient()
.GetProjectResponsesClient()
.AsIChatClient(deploymentName);
تحذير
DefaultAzureCredential مناسب للتنمية ولكنه يتطلب دراسة متأنية في الإنتاج. في الإنتاج، ضع في اعتبارك استخدام بيانات اعتماد محددة (على سبيل المثال، ManagedIdentityCredential) لتجنب مشكلات زمن الانتقال، وبحث بيانات الاعتماد غير المقصودة، والمخاطر الأمنية المحتملة من الآليات الاحتياطية.
تعريف وكلاءك المتخصصين
إنشاء عوامل خاصة بالمجال وعامل فرز للتوجيه:
// 2) Create specialized agents
ChatClientAgent historyTutor = client.AsAIAgent(new ChatClientAgentOptions
{
Id = "history-tutor",
Name = "history_tutor",
Description = "Specialist agent for historical questions",
ChatOptions = new()
{
Instructions = "You provide assistance with historical queries. Explain important events and context clearly. Only respond about history."
}
});
ChatClientAgent mathTutor = client.AsAIAgent(new ChatClientAgentOptions
{
Id = "math-tutor",
Name = "math_tutor",
Description = "Specialist agent for math questions",
ChatOptions = new()
{
Instructions = "You provide help with math problems. Explain your reasoning at each step and include examples. Only respond about math."
}
});
ChatClientAgent triageAgent = client.AsAIAgent(new ChatClientAgentOptions
{
Id = "triage-agent",
Name = "triage_agent",
Description = "Routes messages to the appropriate specialist agent",
ChatOptions = new()
{
Instructions = "You determine which agent to use based on the user's homework question. ALWAYS handoff to another agent."
}
});
Note
إذا تم التحقق من سير عمل التسليم وإعادة إنشائه لاحقا، أعد استخدام نفس الفريد Id (وإذا تم تعيينه، نفس Name) لكل عامل مشارك. تعد المعرفات الثابتة مهمة بشكل خاص عند تحديد نطاق العوامل أو إعادة إنشائها لكل طلب لأن توجيه التسليم وتوافق نقطة التحقق يعتمدان على هويات العامل الداخلي. لمزيد من المعلومات، راجع إعادة الترطيب من نقاط التحقق.
تكوين قواعد التسليم
تحديد العوامل التي يمكن تسليمها إلى أي وكلاء آخرين:
// 3) Build handoff workflow with routing rules
var workflow = AgentWorkflowBuilder.CreateHandoffBuilderWith(triageAgent)
.WithHandoffs(triageAgent, [mathTutor, historyTutor]) // Triage can route to either specialist
.WithHandoffs([mathTutor, historyTutor], triageAgent) // Both specialists can return to triage
.Build();
تشغيل سير عمل التسليم التفاعلي
التعامل مع المحادثات متعددة الأدوار باستخدام تبديل العامل الديناميكي:
// 4) Process multi-turn conversations
List<ChatMessage> messages = new();
while (true)
{
Console.Write("Q: ");
string userInput = Console.ReadLine()!;
messages.Add(new(ChatRole.User, userInput));
// Execute workflow and process events
await using StreamingRun run = await InProcessExecution.RunStreamingAsync(workflow, messages);
await run.TrySendMessageAsync(new TurnToken(emitEvents: true));
string? lastExecutorId = null;
List<ChatMessage> newMessages = new();
await foreach (WorkflowEvent evt in run.WatchStreamAsync())
{
if (evt is AgentResponseUpdateEvent e)
{
if (e.ExecutorId != lastExecutorId)
{
lastExecutorId = e.ExecutorId;
Console.WriteLine();
Console.WriteLine(e.ExecutorId);
}
Console.Write(e.Update.Text);
}
else if (evt is WorkflowOutputEvent outputEvt)
{
newMessages = outputEvt.As<List<ChatMessage>>()!;
break;
}
}
// Add new messages to conversation history
messages.AddRange(newMessages.Skip(messages.Count));
}
نموذج التفاعل
Q: What is the derivative of x^2?
triage_agent: This is a math question. I'll hand this off to the math tutor.
math_tutor: The derivative of x^2 is 2x. Using the power rule, we bring down the exponent (2) and multiply it by the coefficient (1), then reduce the exponent by 1: d/dx(x^2) = 2x^(2-1) = 2x.
Q: Tell me about World War 2
triage_agent: This is a history question. I'll hand this off to the history tutor.
history_tutor: World War 2 was a global conflict from 1939 to 1945. It began when Germany invaded Poland and involved most of the world's nations. Key events included the Holocaust, Pearl Harbor attack, D-Day invasion, and ended with atomic bombs on Japan.
Q: Can you help me with calculus integration?
triage_agent: This is another math question. I'll route this to the math tutor.
math_tutor: I'd be happy to help with calculus integration! Integration is the reverse of differentiation. The basic power rule for integration is: ∫x^n dx = x^(n+1)/(n+1) + C, where C is the constant of integration.
الوضع المستقل
بشكل افتراضي، يكون تزامن التسليم تفاعليا: عندما يستجيب عامل دون تسليم، يقوم سير العمل بإرجاع عنصر التحكم إليك لإدخال المستخدم التالي. تمكين الوضع المستقل للسماح للعامل بمواصلة العمل دون انتظار إدخال المستخدم. عندما لا يسلم العامل إيقاف التشغيل، يغذيه سير العمل بمطالبة متابعة ويستدعيه مرة أخرى، حتى يتم إيقاف تشغيل العامل، أو استيفاء شرط الإنهاء، أو الوصول إلى حد تحويل كل عامل.
قم بتمكينه عن طريق استدعاء WithAutonomousMode() منشئ التسليم:
var workflow = AgentWorkflowBuilder.CreateHandoffBuilderWith(triageAgent)
.WithHandoffs(triageAgent, [mathTutor, historyTutor])
.WithHandoffs([mathTutor, historyTutor], triageAgent)
.WithAutonomousMode()
.Build();
بشكل افتراضي، يشغل كل عامل ما يصل إلى 50 تحويلا مستقلا، ويستخدم كل استمرار المطالبة "User did not respond. Continue assisting autonomously.". تجاوز حد الدور والمطالبة حسب الحاجة:
var workflow = AgentWorkflowBuilder.CreateHandoffBuilderWith(triageAgent)
.WithHandoffs(triageAgent, [mathTutor, historyTutor])
.WithAutonomousMode(turnLimit: 10, continuationPrompt: "Continue assisting the user.")
.Build();
قم بتمرير قائمة العوامل إلى المعلمة agents لتمكين الوضع المستقل لمجموعة فرعية فقط من المشاركين. العوامل غير الموجودة في القائمة ترجع دائما عنصر التحكم بعد استجابة واحدة:
var workflow = AgentWorkflowBuilder.CreateHandoffBuilderWith(triageAgent)
.WithHandoffs(triageAgent, [mathTutor, historyTutor])
.WithAutonomousMode(agents: [triageAgent]) // Only triageAgent runs autonomously
.Build();
اجمع بين الوضع المستقل وشرط الإنهاء لإيقاف التكرار الحلقي عندما تصل المحادثة إلى حالة معينة:
var workflow = AgentWorkflowBuilder.CreateHandoffBuilderWith(triageAgent)
.WithHandoffs(triageAgent, [mathTutor, historyTutor])
.WithAutonomousMode(turnLimit: 10)
.WithTerminationCondition(conversation => conversation.Any(m => m.Text?.Contains("RESOLVED") == true))
.Build();
خيارات متقدمة: الموافقة على الأداة في مهام سير عمل التسليم
يمكن للوكلاء في سير عمل التسليم استخدام الأدوات التي تتطلب موافقة بشرية قبل تشغيلها؛ مفيد للعمليات الحساسة مثل معالجة المبالغ المستردة أو إجراء عمليات الشراء أو تنفيذ إجراءات لا رجعة فيها. التفاف الدالة الحساسة مع ApprovalRequiredAIFunction. عندما يحاول العامل استدعائه، يتوقف سير العمل مؤقتا ويبعث منه RequestInfoEvent يحتوي على ToolApprovalRequestContent.
تعريف العوامل باستخدام أدوات Approval-Required
ChatClientAgent triageAgent = new(client,
"You are frontline support. Route the customer to the right specialist.",
"triage_agent",
"Routes customers to specialists");
ChatClientAgent refundAgent = new(client,
"You process refund requests.",
"refund_agent",
"Handles refund requests",
[new ApprovalRequiredAIFunction(AIFunctionFactory.Create(ProcessRefund))]);
معالجة طلبات الموافقة على إدخال المستخدم والأدوات
هناك أمران يمكن أن يوقفا سير عمل التسليم مؤقتا: عامل ينهي دوره وينتظر رسالة المستخدم التالية، واستدعاء أداة مطلوب للموافقة. معالجة كليهما في نفس حلقة الحدث؛ الاستجابة للموافقة RequestInfoEvent باستخدام SendResponseAsync، وتوفير رسالة المستخدم التالية عندما يرجع سير العمل عنصر التحكم:
var workflow = AgentWorkflowBuilder.CreateHandoffBuilderWith(triageAgent)
.WithHandoffs(triageAgent, [refundAgent])
.WithHandoffs([refundAgent], triageAgent)
.Build();
List<ChatMessage> messages = [];
while (true)
{
Console.Write("You: ");
string userInput = Console.ReadLine()!;
if (userInput.Equals("exit", StringComparison.OrdinalIgnoreCase))
{
break;
}
messages.Add(new(ChatRole.User, userInput));
await using StreamingRun run = await InProcessExecution.RunStreamingAsync(workflow, messages);
await run.TrySendMessageAsync(new TurnToken(emitEvents: true));
List<ChatMessage> newMessages = [];
await foreach (WorkflowEvent evt in run.WatchStreamAsync())
{
// An approval-required tool call pauses the workflow and emits a RequestInfoEvent.
if (evt is RequestInfoEvent requestEvt &&
requestEvt.Request.TryGetDataAs(out ToolApprovalRequestContent? approval))
{
var toolCall = (FunctionCallContent)approval.ToolCall;
Console.Write($"Approve {toolCall.Name}? (y/n): ");
bool approved = (Console.ReadLine() ?? "n").Trim().Equals("y", StringComparison.OrdinalIgnoreCase);
await run.SendResponseAsync(requestEvt.Request.CreateResponse(approval.CreateResponse(approved)));
}
else if (evt is AgentResponseUpdateEvent update)
{
Console.Write(update.Update.Text);
}
else if (evt is WorkflowOutputEvent outputEvt)
{
newMessages = outputEvt.As<List<ChatMessage>>()!;
break;
}
}
// Control returns here after the agent responds without handing off. Merge the new
// messages into the conversation and loop to collect the next user input.
messages.AddRange(newMessages.Skip(messages.Count));
}
Note
تعمل الموافقة على الأداة خارج CreateHandoffBuilderWith() الصندوق؛ لا يلزم تكوين منشئ إضافي. عندما يستدعي عامل أداة مغلفة ب ApprovalRequiredAIFunction، يتوقف سير العمل مؤقتا تلقائيا ويبعث .RequestInfoEvent يتم استخدام نفس RequestInfoEvent نمط المعالجة عبر التنسيقات؛ راجع العينةGroupChatToolApproval لمشروع كامل قابل للتشغيل.
تحديد بعض الأدوات للعرض التوضيحي
@tool
def process_refund(order_number: Annotated[str, "Order number to process refund for"]) -> str:
"""Simulated function to process a refund for a given order number."""
return f"Refund processed successfully for order {order_number}."
@tool
def check_order_status(order_number: Annotated[str, "Order number to check status for"]) -> str:
"""Simulated function to check the status of a given order number."""
return f"Order {order_number} is currently being processed and will ship in 2 business days."
@tool
def process_return(order_number: Annotated[str, "Order number to process return for"]) -> str:
"""Simulated function to process a return for a given order number."""
return f"Return initiated successfully for order {order_number}. You will receive return instructions via email."
إعداد عميل الدردشة
import os
from agent_framework.foundry import FoundryChatClient
from azure.identity import AzureCliCredential
chat_client = FoundryChatClient(
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
model=os.environ["FOUNDRY_MODEL"],
credential=AzureCliCredential(),
)
تعريف وكلاءك المتخصصين
إنشاء عوامل خاصة بالمجال مع منسق للتوجيه:
# Create triage/coordinator agent
triage_agent = chat_client.as_agent(
instructions=(
"You are frontline support triage. Route customer issues to the appropriate specialist agents "
"based on the problem described."
),
description="Triage agent that handles general inquiries.",
name="triage_agent",
)
# Refund specialist: Handles refund requests
refund_agent = chat_client.as_agent(
instructions="You process refund requests.",
description="Agent that handles refund requests.",
name="refund_agent",
# In a real application, an agent can have multiple tools; here we keep it simple
tools=[process_refund],
)
# Order/shipping specialist: Resolves delivery issues
order_agent = chat_client.as_agent(
instructions="You handle order and shipping inquiries.",
description="Agent that handles order tracking and shipping issues.",
name="order_agent",
# In a real application, an agent can have multiple tools; here we keep it simple
tools=[check_order_status],
)
# Return specialist: Handles return requests
return_agent = chat_client.as_agent(
instructions="You manage product return requests.",
description="Agent that handles return processing.",
name="return_agent",
# In a real application, an agent can have multiple tools; here we keep it simple
tools=[process_return],
)
تكوين قواعد التسليم
إنشاء سير عمل التسليم باستخدام HandoffBuilder:
from agent_framework.orchestrations import HandoffBuilder
# Build the handoff workflow
workflow = (
HandoffBuilder(
name="customer_support_handoff",
participants=[triage_agent, refund_agent, order_agent, return_agent],
termination_condition=lambda conversation: len(conversation) > 0 and "welcome" in conversation[-1].text.lower(),
)
.with_start_agent(triage_agent) # Triage receives initial user input
.build()
)
بشكل افتراضي، يمكن لجميع الوكلاء التسليم لبعضهم البعض. للحصول على توجيه أكثر تقدما، يمكنك تكوين التسليمات:
workflow = (
HandoffBuilder(
name="customer_support_handoff",
participants=[triage_agent, refund_agent, order_agent, return_agent],
termination_condition=lambda conversation: len(conversation) > 0 and "welcome" in conversation[-1].text.lower(),
)
.with_start_agent(triage_agent) # Triage receives initial user input
# Triage cannot route directly to refund agent
.add_handoff(triage_agent, [order_agent, return_agent])
# Only the return agent can handoff to refund agent - users wanting refunds after returns
.add_handoff(return_agent, [refund_agent])
# All specialists can handoff back to triage for further routing
.add_handoff(order_agent, [triage_agent])
.add_handoff(return_agent, [triage_agent])
.add_handoff(refund_agent, [triage_agent])
.build()
)
Note
حتى مع قواعد التسليم المخصصة، لا تزال جميع العوامل متصلة في مخطط شبكة. وذلك لأن الوكلاء بحاجة إلى مشاركة السياق مع بعضهم البعض للحفاظ على محفوظات المحادثات (راجع مزامنة السياق لمزيد من التفاصيل). تحكم قواعد التسليم فقط العملاء الذين يمكنهم تولي المحادثة بعد ذلك.
تشغيل تفاعل عامل التسليم
على عكس التنسيقات الأخرى، يكون التسليم تفاعليا لأن العامل قد لا يقرر التسليم بعد كل منعطف. إذا لم يسلم العامل، يلزم إدخال الإنسان لمتابعة المحادثة. راجع الوضع المستقل لتجاوز هذا المطلب. في التنسيقات الأخرى، بعد استجابة عامل، ينتقل عنصر التحكم إما إلى المنسق أو العامل التالي.
عندما يقرر عامل في سير عمل التسليم عدم التسليم (يتم تشغيل التسليم بواسطة استدعاء أداة خاصة)، يصدر سير العمل WorkflowEvent مع type="request_info" وحمولة HandoffAgentUserRequest تحتوي على أحدث رسائل العامل. يجب على المستخدم الاستجابة لهذا الطلب لمتابعة سير العمل.
from agent_framework import WorkflowEvent
from agent_framework.orchestrations import HandoffAgentUserRequest
# Start workflow with initial user message
events = [event async for event in workflow.run("I need help with my order", stream=True)]
# Process events and collect pending input requests
pending_requests = []
for event in events:
if event.type == "request_info" and isinstance(event.data, HandoffAgentUserRequest):
pending_requests.append(event)
request_data = event.data
print(f"Agent {event.executor_id} is awaiting your input")
# The request contains the most recent messages generated by the
# agent requesting input
for msg in request_data.agent_response.messages[-3:]:
print(f"{msg.author_name}: {msg.text}")
# Interactive loop: respond to requests
while pending_requests:
user_input = input("You: ")
# Send responses to all pending requests
responses = {req.request_id: HandoffAgentUserRequest.create_response(user_input) for req in pending_requests}
# You can also send a `HandoffAgentUserRequest.terminate()` to end the workflow early
events = [event async for event in workflow.run(responses=responses, stream=True)]
# Process new events
pending_requests = []
for event in events:
# Check for new input requests
الوضع المستقل
تم تصميم تزامن التسليم للسيناريوهات التفاعلية حيث يكون الإدخال البشري مطلوبا عندما يقرر العامل عدم التسليم. ومع ذلك، كميزة تجريبية، يمكنك تمكين "الوضع المستقل" للسماح لسير العمل بالاستمرار دون تدخل بشري. في هذا الوضع، عندما يقرر عامل عدم التسليم، يرسل سير العمل تلقائيا استجابة افتراضية (على سبيل المثالUser did not respond. Continue assisting autonomously.) إلى العامل، ما يسمح له بمتابعة المحادثة.
Tip
لماذا يكون تزامن Handoff تفاعليا بطبيعتها؟ على عكس التنسيقات الأخرى حيث يوجد مسار واحد فقط للمتابعة بعد استجابة عامل (على سبيل المثال، العودة إلى المنسق أو العامل التالي)، في تزامن Handoff، لدى العامل خيار إما التسليم إلى عامل آخر أو الاستمرار في مساعدة المستخدم نفسه. ولأن التسليمات يتم تحقيقها من خلال استدعاءات الأداة، إذا لم يستدعي العامل أداة التسليم ولكنه ينشئ استجابة بدلا من ذلك، فلن يعرف سير العمل ما يجب فعله بعد ذلك ولكن للتفويض مرة أخرى إلى المستخدم لمزيد من الإدخال. من غير الممكن أيضا إجبار العامل على التسليم دائما عن طريق مطالبته باستدعاء أداة التسليم لأن العامل لن يكون قادرا على إنشاء استجابات ذات مغزى بخلاف ذلك.
يتم تمكين الوضع المستقل عن طريق استدعاء with_autonomous_mode() على HandoffBuilder. يؤدي هذا إلى تكوين سير العمل للاستجابة تلقائيا لطلبات الإدخال برسالة افتراضية، ما يسمح للعامل بالمتابعة دون انتظار الإدخال البشري.
workflow = (
HandoffBuilder(
name="autonomous_customer_support",
participants=[triage_agent, refund_agent, order_agent, return_agent],
)
.with_start_agent(triage_agent)
.with_autonomous_mode()
.build()
)
يمكنك أيضا تمكين الوضع المستقل على مجموعة فرعية فقط من العوامل عن طريق تمرير قائمة بمثيلات العامل إلى with_autonomous_mode().
workflow = (
HandoffBuilder(
name="partially_autonomous_support",
participants=[triage_agent, refund_agent, order_agent, return_agent],
)
.with_start_agent(triage_agent)
.with_autonomous_mode(agents=[triage_agent]) # Only triage_agent runs autonomously
.build()
)
يمكنك تخصيص رسالة الاستجابة الافتراضية.
workflow = (
HandoffBuilder(
name="custom_autonomous_support",
participants=[triage_agent, refund_agent, order_agent, return_agent],
)
.with_start_agent(triage_agent)
.with_autonomous_mode(
agents=[triage_agent],
prompts={triage_agent.name: "Continue with your best judgment as the user is unavailable."},
)
.build()
)
يمكنك تخصيص عدد المنعطفات التي يمكن للعامل تشغيلها بشكل مستقل قبل طلب إدخال بشري. يمكن أن يمنع هذا سير العمل من العمل إلى أجل غير مسمى دون مشاركة المستخدم.
workflow = (
HandoffBuilder(
name="limited_autonomous_support",
participants=[triage_agent, refund_agent, order_agent, return_agent],
)
.with_start_agent(triage_agent)
.with_autonomous_mode(
agents=[triage_agent],
turn_limits={triage_agent.name: 3}, # Max 3 autonomous turns
)
.build()
)
خيارات متقدمة: الموافقة على الأداة في مهام سير عمل التسليم
يمكن أن تتضمن مهام سير عمل التسليم عوامل بأدوات تتطلب موافقة بشرية قبل التنفيذ. وهذا مفيد للعمليات الحساسة مثل معالجة المبالغ المستردة أو إجراء عمليات الشراء أو تنفيذ إجراءات لا رجعة فيها.
تعريف الأدوات مع الموافقة المطلوبة
from typing import Annotated
from agent_framework import tool
@tool(approval_mode="always_require")
def process_refund(order_number: Annotated[str, "Order number to process refund for"]) -> str:
"""Simulated function to process a refund for a given order number."""
return f"Refund processed successfully for order {order_number}."
إنشاء عوامل باستخدام أدوات Approval-Required
import os
from agent_framework import Agent
from agent_framework.openai import OpenAIChatCompletionClient
from azure.identity import AzureCliCredential
chat_client = 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(),
)
triage_agent = chat_client.as_agent(
instructions=(
"You are frontline support triage. Route customer issues to the appropriate specialist agents "
"based on the problem described."
),
description="Triage agent that handles general inquiries.",
name="triage_agent",
)
refund_agent = chat_client.as_agent(
instructions="You process refund requests.",
description="Agent that handles refund requests.",
name="refund_agent",
tools=[process_refund],
)
order_agent = chat_client.as_agent(
instructions="You handle order and shipping inquiries.",
description="Agent that handles order tracking and shipping issues.",
name="order_agent",
tools=[check_order_status],
)
معالجة طلبات الموافقة على إدخال المستخدم والأدوات
from agent_framework import (
Content,
WorkflowEvent,
)
from agent_framework.orchestrations import HandoffBuilder, HandoffAgentUserRequest
workflow = (
HandoffBuilder(
name="support_with_approvals",
participants=[triage_agent, refund_agent, order_agent],
)
.with_start_agent(triage_agent)
.build()
)
pending_requests: list[WorkflowEvent] = []
# Start workflow
async for event in workflow.run("My order 12345 arrived damaged. I need a refund.", stream=True):
if event.type == "request_info":
pending_requests.append(event)
# Process pending requests - could be user input OR tool approval
while pending_requests:
responses: dict[str, object] = {}
for request in pending_requests:
if isinstance(request.data, HandoffAgentUserRequest):
# Agent needs user input
print(f"Agent {request.executor_id} asks:")
for msg in request.data.agent_response.messages[-2:]:
print(f" {msg.author_name}: {msg.text}")
user_input = input("You: ")
responses[request.request_id] = HandoffAgentUserRequest.create_response(user_input)
elif isinstance(request.data, Content) and request.data.type == "function_approval_request":
# Agent wants to call a tool that requires approval
func_call = request.data.function_call
args = func_call.parse_arguments() or {}
print(f"\nTool approval requested: {func_call.name}")
print(f"Arguments: {args}")
approval = input("Approve? (y/n): ").strip().lower() == "y"
responses[request.request_id] = request.data.to_function_approval_response(approved=approval)
# Send all responses and collect new requests
pending_requests = []
async for event in workflow.run(responses=responses, stream=True):
if event.type == "request_info":
pending_requests.append(event)
elif event.type == "output":
print("\nWorkflow completed!")
مع نقاط التحقق ل Durable Workflows
بالنسبة إلى مهام سير العمل طويلة الأمد حيث قد تحدث الموافقات على الأدوات بعد ساعات أو أيام، استخدم نقاط التحقق:
from agent_framework import FileCheckpointStorage
storage = FileCheckpointStorage(storage_path="./checkpoints")
workflow = (
HandoffBuilder(
name="durable_support",
participants=[triage_agent, refund_agent, order_agent],
checkpoint_storage=storage,
)
.with_start_agent(triage_agent)
.build()
)
# Initial run - workflow pauses when approval is needed
pending_requests = []
async for event in workflow.run("I need a refund for order 12345", stream=True):
if event.type == "request_info":
pending_requests.append(event)
# Process can exit here - checkpoint is saved automatically
# Later: Resume from checkpoint and provide approval
checkpoints = await storage.list_checkpoints(workflow_name="durable_support")
latest = sorted(checkpoints, key=lambda c: c.timestamp, reverse=True)[0]
# Step 1: Restore checkpoint to reload pending requests
restored_requests = []
async for event in workflow.run(checkpoint_id=latest.checkpoint_id, stream=True):
if event.type == "request_info":
restored_requests.append(event)
# Step 2: Send responses
responses = {}
for req in restored_requests:
if isinstance(req.data, Content) and req.data.type == "function_approval_request":
responses[req.request_id] = req.data.to_function_approval_response(approved=True)
elif isinstance(req.data, HandoffAgentUserRequest):
responses[req.request_id] = HandoffAgentUserRequest.create_response("Yes, please process the refund.")
async for event in workflow.run(responses=responses, stream=True):
if event.type == "output":
print("Refund workflow completed!")
نموذج التفاعل
User: I need help with my order
triage_agent: I'd be happy to help you with your order. Could you please provide more details about the issue?
User: My order 1234 arrived damaged
triage_agent: I'm sorry to hear that your order arrived damaged. I will connect you with a specialist.
support_agent: I'm sorry about the damaged order. To assist you better, could you please:
- Describe the damage
- Would you prefer a replacement or refund?
User: I'd like a refund
triage_agent: I'll connect you with the refund specialist.
refund_agent: I'll process your refund for order 1234. Here's what will happen next:
1. Verification of the damaged items
2. Refund request submission
3. Return instructions if needed
4. Refund processing within 5-10 business days
Could you provide photos of the damage to expedite the process?
مزامنة السياق
يعتمد الوكلاء في إطار عمل العامل على جلسات العامل (AgentSession) لإدارة السياق. في تزامن التسليم، لا يشارك الوكلاء نفس مثيل الجلسة، المشاركون مسؤولون عن ضمان تناسق السياق. ولتحقيق ذلك، تم تصميم المشاركين لبث ردودهم أو مدخلات المستخدمين المستلمة إلى جميع الآخرين في سير العمل كلما قاموا بإنشاء استجابة، مع التأكد من أن جميع المشاركين لديهم أحدث سياق بدورهم التالي.
Note
لا يتم بث المحتويات المتعلقة بالأدوات، بما في ذلك استدعاءات أداة التسليم، إلى وكلاء آخرين. تتم مزامنة رسائل المستخدم والوكيل فقط عبر جميع المشاركين.
Tip
لا يشترك الوكلاء في نفس مثيل جلسة العمل لأن أنواع الوكلاء المختلفة قد يكون لها تطبيقات مختلفة للتجريد AgentSession . قد تؤدي مشاركة نفس مثيل الجلسة إلى عدم تناسق في كيفية معالجة كل عامل للسياق والحفاظ عليه.
بعد بث الاستجابة، يتحقق المشارك بعد ذلك من الحاجة إلى تسليم المحادثة إلى وكيل آخر. إذا كان الأمر كذلك، فإنه يرسل طلبا إلى العامل المحدد للاستيلاء على المحادثة. وإلا، فإنه يطلب إدخال المستخدم أو يستمر بشكل مستقل استنادا إلى تكوين سير العمل.
المفاهيم الأساسية
- التوجيه الديناميكي: يمكن للوكلاء تحديد العامل الذي يجب أن يتعامل مع التفاعل التالي بناء على السياق
- AgentWorkflowBuilder.CreateHandoffBuilderWith(): يحدد العامل الأولي الذي يبدأ سير العمل
- WithHandoff() و WithHandoffs(): تكوين قواعد التسليم بين عوامل معينة
- الاحتفاظ بالسياق: يتم الاحتفاظ بمحفوظات المحادثات الكاملة عبر جميع التسليمات
- دعم متعدد الأدوار: يدعم المحادثات المستمرة مع تبديل الوكيل السلس
- الخبرة المتخصصة: يركز كل وكيل على مجاله مع التعاون من خلال التسليمات
- WithAutonomousMode(): يتيح للوكلاء المتابعة دون انتظار إدخال المستخدم، أو حتى حد تحويل لكل عامل أو حتى يتم استيفاء شرط الإنهاء
-
الموافقة على الأداة (HITL): التفاف الأدوات الحساسة مع
ApprovalRequiredAIFunction؛ يتوقف سير العمل مؤقتا ويبعث بToolApprovalRequestContentRequestInfoEvent، والذي تجيب عليه عبرSendResponseAsync
- التوجيه الديناميكي: يمكن للوكلاء تحديد العامل الذي يجب أن يتعامل مع التفاعل التالي بناء على السياق
- HandoffBuilder: إنشاء مهام سير العمل مع تسجيل أداة التسليم التلقائي
- with_start_agent(): يحدد العامل الذي يتلقى إدخال المستخدم أولا
- add_handoff(): تكوين علاقات تسليم محددة بين العوامل
-
الإخراج: يتم تعيين افتراضيا
output_fromعلى جميع المشاركين، بحيث تظهر استجابة كل عامل كحدث"output"(طرفي) (AgentResponseفي وضع عدم البث،AgentResponseUpdateفي وضع الدفق). لتعيين عوامل معينة كمصادر وسيطة بدلا من ذلك، قم بالتمريرintermediate_output_from=[agent_a, agent_b]إلى — يؤدي ذلك ضمنيا إلىHandoffBuilderتخفيض عدد هؤلاء الوكلاء من مجموعة الإخراج الافتراضية بحيث تصبح استجاباتهم أحداثا"intermediate". لا يوجد خطأ في التداخل؛ تخفيض الرتبة صامت ومتعمد. - الاحتفاظ بالسياق: يتم الاحتفاظ بمحفوظات المحادثات الكاملة عبر جميع التسليمات
- دورة الطلب/الاستجابة: يطلب سير العمل إدخال المستخدم، ويعالج الاستجابات، ويستمر حتى يتم استيفاء شرط الإنهاء
-
الموافقة على الأداة: يستخدم
@tool(approval_mode="always_require")للعمليات الحساسة التي تحتاج إلى موافقة بشرية -
معالجة الموافقة على الدالة: عندما يستدعي عامل أداة تتطلب الموافقة، يتم إصدار كائن
Contentبنوع"function_approval_request"؛ استخدمto_function_approval_response(approved=...)للاستجابة -
نقاط التحقق: تمرير
checkpoint_storage=إلىHandoffBuilderلسير العمل الدائم الذي يمكن إيقافه مؤقتا واستئنافه عبر عمليات إعادة تشغيل العملية - الخبرة المتخصصة: يركز كل وكيل على مجاله مع التعاون من خلال التسليمات
Note
سيتوفر الدعم لهذه الميزة قريبا. راجع مستودع Agent Framework Go للحصول على أحدث حالة.
منفذ عامل التسليم
على عكس مهام سير العمل القياسية حيث يتم التفاف العوامل في منفذ عامل للأغراض العامة، يستخدم تزامن التسليم ملفا متخصصا HandoffAgentExecutor. يوسع هذا المنفذ منفذ العامل الأساسي بقدرات خاصة بالتسلم:
- حقن أداة التسليم — يسجل تلقائيا أدوات التسليم على كل عامل استنادا إلى قواعد التسليم المكونة، بحيث يمكن للعامل استدعاء أداة لنقل عنصر التحكم.
- الكشف عن دالة التسليم — يفحص استجابة العامل لاستدعاءات أداة التسليم ويوجه التحكم إلى العامل الهدف.
- تصفية استدعاء الأداة — تقوم بتصفية استدعاءات الوظائف ذات الصلة بالتسليم ونتائج الأداة من محفوظات المحادثات قبل إعادة التوجيه إلى العامل التالي، مما يمنع آليات سير العمل الداخلية من إرباك النموذج.