Skip to content

Conversation

@moonbox3
Copy link
Contributor

Motivation and Context

Workflows supports the ability to be run as an agent using .as_agent(). There is an issue doing this for magentic orchestrations (leads to an error). Also, making sure to add support for SequentialBuilder and ConcurrentBuilder.

Description

Contribution Checklist

  • The code builds clean without any errors or warnings
  • The PR follows the Contribution Guidelines
  • All unit tests pass, and I have added new tests where possible
  • Is this a breaking change? If yes, add "[BREAKING]" prefix to the title of the PR.

Copilot AI review requested due to automatic review settings October 15, 2025 04:00
@markwallace-microsoft markwallace-microsoft added documentation Improvements or additions to documentation python labels Oct 15, 2025
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR fixes orchestration pattern workflows (Magentic, Sequential, and Concurrent) when used as agents via .as_agent(). The main issue was that workflow start executors couldn't properly handle different input types when wrapped as agents.

  • Enhanced WorkflowAgent with flexible input type handling and payload encoding for different workflow types
  • Added support for MagenticBuilder, SequentialBuilder, and ConcurrentBuilder when running as agents
  • Added comprehensive test coverage and sample code demonstrating the patterns

Reviewed Changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
python/packages/core/agent_framework/_workflows/_agent.py Enhanced WorkflowAgent with sophisticated input type resolution and payload encoding system
python/packages/core/agent_framework/_workflows/_magentic.py Added as_agent() method to MagenticWorkflow for consistency
python/packages/core/tests/workflow/test_workflow_agent.py Added comprehensive tests for Sequential, Concurrent, and Magentic builders as agents
python/samples/getting_started/workflows/agents/*.py Added sample implementations demonstrating each orchestration pattern as an agent
python/samples/getting_started/workflows/README.md Updated documentation table to include new workflow-as-agent samples

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

@markwallace-microsoft
Copy link
Member

markwallace-microsoft commented Oct 15, 2025

Python Test Coverage

Python Test Coverage Report •
FileStmtsMissCoverMissing
packages/core/agent_framework/_workflows
   _agent.py3227277%55, 59, 63–66, 94–95, 120–121, 145–148, 151–152, 158, 161, 164, 192–193, 199, 203–206, 212, 216, 219–222, 224–228, 230–232, 234–235, 237, 241, 246, 371, 379–385, 402, 443, 445, 450, 452, 493, 506–507, 513, 525, 557, 564, 585, 592, 596, 598–600, 607
   _magentic.py96924275%61–70, 75, 79–90, 319, 344, 349–350, 426, 430, 444, 450, 465, 545, 558, 575, 584–585, 587–589, 591, 602, 669–673, 676–680, 760–763, 766–770, 772–774, 781, 820, 867, 903–905, 907, 1014, 1050, 1059–1061, 1079–1080, 1082–1083, 1102, 1147, 1150, 1178, 1181, 1186, 1194–1198, 1204, 1222–1223, 1234–1236, 1238, 1240, 1248–1253, 1255, 1259–1260, 1263–1266, 1268–1269, 1275–1277, 1280–1281, 1286–1287, 1295, 1307–1308, 1319, 1331, 1343–1346, 1375–1376, 1381–1383, 1417, 1441, 1458, 1474, 1484, 1498, 1557, 1563–1564, 1578, 1580, 1583, 1585–1586, 1589–1590, 1594, 1597, 1618, 1653–1654, 1656, 1660–1661, 1679–1680, 1690–1691, 1706, 1711–1714, 1793–1794, 1798, 1813, 1817–1818, 1822–1823, 1840, 1843, 1867–1868, 1879, 1882–1889, 1897–1901, 1910–1911, 1965–1967, 2004, 2015, 2026–2028, 2062–2064, 2075–2077, 2090–2092, 2095–2096, 2116, 2120, 2124–2125, 2142, 2149, 2152, 2156, 2161, 2198–2201, 2212–2215, 2227–2230, 2239–2242, 2257, 2261
TOTAL10463173283% 

Python Unit Test Overview

Tests Skipped Failures Errors Time
1279 98 💤 0 ❌ 0 🔥 27.738s ⏱️

return self._workflow

def as_agent(self, name: str | None = None) -> "WorkflowAgent":
"""Expose the underlying workflow as a WorkflowAgent."""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this doc string we should note the message type conversion behavior rather than making this behavior implicit.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to call out the message type conversion? This is simply creating a "WorkflowAgent". We don't call anything specifically even in the workflow's method:

def as_agent(self, name: str | None = None) -> WorkflowAgent:
        """Create a WorkflowAgent that wraps this workflow.

        Args:
            name: Optional name for the agent. If None, a default name will be generated.

        Returns:
            A WorkflowAgent instance that wraps this workflow.
        """
        # Import here to avoid circular imports
        from ._agent import WorkflowAgent

        return WorkflowAgent(workflow=self, name=name)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought user may want to understand how their input messages are being handled under the hood to become input to the first executor/agent? The Workflow.as_agent() method requires the first executor to handle list[ChatMessage] type as part of the original WorkflowAgent constructor and raises error if not the case. It may be helpful to document these transformations so when user reading the doc, they can see the underlying behavior if they need to understand it. It doesn't need to be very detailed but having it avoids guesswork, as the new behavior is more like "parsing" an input type rather than simply passing it along.


raise ValueError("Workflow's start executor cannot be adapted to agent chat inputs.")

def _candidate_adapters_from_input_types(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I know these are "private" methods, but it'd still be beneficial to have some comments for them. It'd make reading and understanding the code so much easier for others and potential AI. And maybe copilot could detect errors if the method doesn't do what the description says.

adapters.append(adapter)
return adapters

def _flatten_type_annotation(self, annotation: Any) -> list[Any]:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we need to support the cases when the input executor cannot handle chat message.


return None

def _adapter_for_specialized_class(self, message_cls: type[Any]) -> Callable[[list[ChatMessage]], Any] | None:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this is probably ok, but I have observed that we have too many lower-level modules taking dependencies on higher level modules. At some point, we should think about some of the design choices we are making to make the code a bit organized.


return None

def _build_magentic_start_adapter(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here too, though it's not taking a dependency on the MagenticBuilder but it's logically related. I don't think this module should know anything about the Magentic orchestration.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I may abandon this PR. I'm working on introducing the base group chat pattern, which the Magentic pattern will extend (as we talked about a while back). There's too much custom "MagenticWorkflow" handling that I don't like as well. I expect we will be able to move away from these custom methods.

@moonbox3
Copy link
Contributor Author

Going to close this PR in favor of a new one that does more cleanup for orchestrations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Python: as_agent() fails for Magentic workflow

4 participants