Skip to content

.NET: [Bug]: Magentic Workflow always seems to run into maximum round count limit. #6173

@Abalast8899

Description

@Abalast8899

Description

I have been testing the Magnetic Workflow to see if it is relevant to my upcoming projects.

For this, I used a slightly modified version of the one found in this repository and added a simple agent.

However, regardless of how simple the task is or how high I set it, it always seems to reach the maximum round count. The same problem occurs when using the example found in the repository.

My prompt was simple: 'Show me the status of Light.'

I would have assumed that such a simple task would not require so many turns, and that I would receive a compiled result instead of always encountering this problem.

The models used for this workflow were ChatGPT-4.1.mini and 5.mini

Code Sample

public async Task<AIAgent> LoadLocalSmartLightAgent()
        {

            return CreateAgent(
                "smart_light_agent",
                "You are a task-focused assistant for LightSystem. For a light-status request, call the available light tool once, report the current state of each relevant light, and then stop. If no specific light is named, report the status of all monitored lights. Do not ask follow-up questions, do not offer extra actions, and do not continue the conversation unless required information is missing. End your final response with the exact marker STATUS_COMPLETE.",
                "Specialist agent for light system",
                [AIFunctionFactory.Create(LightsPlugin.GetLightsAsync), AIFunctionFactory.Create(LightsPlugin.ChangeStateAsync)]);
        }

        public static Workflow CreateSimpleMagenticWorkflow(AIAgent masterAgnet, List<AIAgent> participants,
            string description = "Coordinates a researcher and coder to solve a complex analytical task.", 
            bool requiredPlanSignoff = true,
            int maxRounds = 10,
            int maxStalls = 3,
            int maxResets = 2,
            string name = "Magentic Orchestration Workflow")
        {
            #pragma warning disable MAAIW001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
            Workflow workflow = new MagenticWorkflowBuilder(masterAgnet)
                .AddParticipants(participants)
                .WithDescription(description)
                .RequirePlanSignoff(requiredPlanSignoff)
                .WithMaxRounds(maxRounds)
                .WithMaxStalls(maxStalls)
                .WithMaxResets(maxResets)
                .WithName(name)
                .Build();

            return workflow;
        }


public static async Task HandleMagenticWorkflowAsync(Workflow workflow, string TaskPrompt)
    {
        await using StreamingRun run = await InProcessExecution.RunStreamingAsync(
            workflow,
            new List<ChatMessage> { new(ChatRole.User, TaskPrompt) });

        await run.TrySendMessageAsync(new TurnToken(emitEvents: true));

        string? lastResponseId = null;
        WorkflowOutputEvent? finalOutput = null;
        string? lastNextSpeaker = null;
        int repeatedNextSpeakerCount = 0;
        int convergenceNudgeCount = 0;
        string? currentStreamResponseId = null;
        string? currentStreamExecutorId = null;
        string currentStreamResponseText = string.Empty;
        string lastSpecialistAnswer = string.Empty;

        await foreach (WorkflowEvent workflowEvent in run.WatchStreamAsync())
        {
            switch (workflowEvent)
            {
                case RequestInfoEvent requestInfoEvent:
                    try
                    {
                        ExternalResponse response = HandleRequest.HandleExternalRequest(requestInfoEvent.Request);
                        await run.SendResponseAsync(response);
                    }
                    catch (NotSupportedException ex)
                    {
                        Console.ForegroundColor = ConsoleColor.Yellow;
                        Console.WriteLine($"[Magentic External Request] Unsupported request type: {requestInfoEvent.Request.PortInfo.RequestType}. {ex.Message}");
                        Console.ResetColor();
                    }
                    break;

                case AgentResponseUpdateEvent updateEvent:
                    string responseId = updateEvent.Update.ResponseId ?? updateEvent.Update.MessageId ?? updateEvent.ExecutorId;
                    if (!string.Equals(responseId, currentStreamResponseId, StringComparison.Ordinal))
                    {
                        if (!string.IsNullOrWhiteSpace(currentStreamResponseText)
                            && !string.IsNullOrWhiteSpace(currentStreamExecutorId)
                            && !currentStreamExecutorId.Contains("MagenticOrchestrator", StringComparison.OrdinalIgnoreCase))
                        {
                            lastSpecialistAnswer = currentStreamResponseText.Trim();
                        }

                        currentStreamResponseId = responseId;
                        currentStreamExecutorId = updateEvent.ExecutorId;
                        currentStreamResponseText = string.Empty;
                    }

                    if (!string.IsNullOrEmpty(updateEvent.Update.Text))
                    {
                        currentStreamResponseText += updateEvent.Update.Text;
                    }

                    WriteStreamingUpdate(updateEvent, ref lastResponseId);
                    break;

                case MagenticPlanCreatedEvent planCreated:
                    WriteMagenticMessage("Initial Plan", planCreated.FullTaskLedger.Text);
                    break;

                case MagenticReplannedEvent replanned:
                    WriteMagenticMessage("Replanned", replanned.FullTaskLedger.Text);
                    break;

                case MagenticProgressLedgerUpdatedEvent progressUpdated:
                    MagenticProgressLedger ledger = progressUpdated.ProgressLedger;
                    WriteMagenticMessage("Progress Ledger", FormatProgressLedger(ledger));

                    if (!ledger.IsRequestSatisfied)
                    {
                        if (string.Equals(lastNextSpeaker, ledger.NextSpeaker, StringComparison.OrdinalIgnoreCase))
                        {
                            repeatedNextSpeakerCount++;
                        }
                        else
                        {
                            lastNextSpeaker = ledger.NextSpeaker;
                            repeatedNextSpeakerCount = 1;
                        }

                        bool shouldSendNudge = repeatedNextSpeakerCount >= 3
                            && repeatedNextSpeakerCount % 2 == 1
                            && convergenceNudgeCount < 3;

                        if (shouldSendNudge)
                        {
                            string evidence = string.IsNullOrWhiteSpace(lastSpecialistAnswer)
                                ? "No specialist evidence captured yet."
                                : $"Latest specialist evidence:\n{lastSpecialistAnswer}";

                            string nudge =
                                "Host convergence guidance: Do not ask the same participant repeatedly for semantically equivalent information. " +
                                "If current evidence is sufficient, synthesize and provide the final user-facing answer now and stop delegating. " +
                                "Only delegate again if a concrete missing field is identified.\n\n" +
                                evidence;

                            bool accepted = await run.TrySendMessageAsync(new List<ChatMessage>
                            {
                                new(ChatRole.System, nudge)
                            });

                            if (!accepted)
                            {
                                accepted = await run.TrySendMessageAsync(new List<ChatMessage>
                                {
                                    new(ChatRole.User, nudge)
                                });
                            }

                            Console.ForegroundColor = ConsoleColor.DarkYellow;
                            Console.WriteLine($"[Magentic Convergence Nudge] sent={accepted}, count={convergenceNudgeCount + (accepted ? 1 : 0)}, repeats={repeatedNextSpeakerCount}, nextSpeaker={ledger.NextSpeaker}");
                            Console.ResetColor();

                            if (accepted)
                            {
                                convergenceNudgeCount++;
                            }
                        }
                    }
                    break;

                case WorkflowOutputEvent outputEvent when outputEvent.Is<List<ChatMessage>>():
                    finalOutput = outputEvent;
                    break;

                case WorkflowErrorEvent workflowError:
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.Error.WriteLine(workflowError.Exception?.ToString() ?? "Unknown workflow error occurred.");
                    Console.ResetColor();
                    break;

                case ExecutorFailedEvent executorFailed:
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.Error.WriteLine($"Executor '{executorFailed.ExecutorId}' failed with {(executorFailed.Data is null ? "unknown error" : $"exception {executorFailed.Data}")}.");
                    Console.ResetColor();
                    break;
            }
        }

        if (!string.IsNullOrWhiteSpace(currentStreamResponseText)
            && !string.IsNullOrWhiteSpace(currentStreamExecutorId)
            && !currentStreamExecutorId.Contains("MagenticOrchestrator", StringComparison.OrdinalIgnoreCase))
        {
            lastSpecialistAnswer = currentStreamResponseText.Trim();
        }

        if (finalOutput?.As<List<ChatMessage>>() is { } transcript)
        {
            Console.WriteLine();
            Console.WriteLine(new string('=', 80));
            Console.WriteLine();
            Console.WriteLine("Final Conversation Transcript:");
            Console.WriteLine();

            foreach (ChatMessage message in transcript)
            {
                Console.WriteLine($"{message.AuthorName ?? message.Role.ToString()}: {message.Text}");
                Console.WriteLine();
            }

            bool hitRoundLimit = transcript.Any(m =>
                !string.IsNullOrWhiteSpace(m.Text)
                && (m.Text.Contains("maximum round count", StringComparison.OrdinalIgnoreCase)
                    || m.Text.Contains("Task execution stopped", StringComparison.OrdinalIgnoreCase)));

            if (hitRoundLimit && !string.IsNullOrWhiteSpace(lastSpecialistAnswer))
            {
                Console.ForegroundColor = ConsoleColor.DarkCyan;
                Console.WriteLine("[Host Fallback Answer]");
                Console.WriteLine(lastSpecialistAnswer);
                Console.ResetColor();
                Console.WriteLine();
            }
        }
    }

    private static void WriteMagenticMessage(string title, string? content)
    {
        Console.WriteLine();
        Console.WriteLine($"[Magentic {title}]");
        Console.WriteLine(content);
    }

    private static string FormatProgressLedger(MagenticProgressLedger ledger) =>
        string.Join(Environment.NewLine,
            $"Request satisfied: {ledger.IsRequestSatisfied}",
            $"In loop: {ledger.IsInLoop}",
            $"Making progress: {ledger.IsProgressBeingMade}",
            $"Next speaker: {ledger.NextSpeaker}",
            $"Instruction: {ledger.InstructionOrQuestion}");

    private static void PauseIfInteractive()
    {
        if (Console.IsInputRedirected || Console.IsOutputRedirected)
        {
            return;
        }

        Console.Write("Press Enter to continue...");
        Console.ReadLine();
        Console.WriteLine();
    }

    private static void WriteStreamingUpdate(AgentResponseUpdateEvent updateEvent, ref string? lastResponseId)
    {
        string responseId = updateEvent.Update.ResponseId ?? updateEvent.Update.MessageId ?? updateEvent.ExecutorId;
        if (!string.Equals(responseId, lastResponseId, StringComparison.Ordinal))
        {
            if (lastResponseId is not null)
            {
                Console.WriteLine();
                Console.WriteLine();
            }

            Console.Write($"- {updateEvent.ExecutorId}: ");
            lastResponseId = responseId;
        }

        if (!string.IsNullOrEmpty(updateEvent.Update.Text))
        {
            Console.Write(updateEvent.Update.Text);
        }
    }

Error Messages / Stack Traces

Task Execution stopped due to hitting the maximum round count limit.

Package Versions

Microsoft.Agents.AI" Version="1.7.0"

.NET Version

.NET10

Additional Context

No response

Metadata

Metadata

Assignees

Labels

Type

No fields configured for Bug.

Projects

Status

In Review

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions