mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Design doc draft (#5)
* wip * wip * wip * wip * wip * wip * Update docs/design/main.md Co-authored-by: Evan Mattson <35585003+moonbox3@users.noreply.github.com> * Update docs/design/main.md Co-authored-by: Evan Mattson <35585003+moonbox3@users.noreply.github.com> * wip * wip * wip * wip * wip * wip * wip * wip * wip * update * update * update * wip * wip * wip * wip * address comment * update * add custom agent example * address comment * update code teaser * Update docs/design/main.md Co-authored-by: Evan Mattson <35585003+moonbox3@users.noreply.github.com> * update * address comments * update guardrails * address some of mark's comments * add new separate sections for agents and workflows * update agent doc * Update agent.md Co-authored-by: Jack Gerrits <jackgerrits@users.noreply.github.com> * add foundry agent doc * wip * refine the component registration interface with agent runtime * update * workflows * update * update * Update * Update * update * Update design doc to remove runtime * Update * Update * Update * update * Add eval section notes (#9) * add notes on eval * remove duplicate title * update docs * update docs * save updates before merge * update evaluation script * Update agents.md * update workflows * Update Co-authored-by: Jack Gerrits <jackgerrits@users.noreply.github.com> * update workflow * Updated design doc * Update * Update * update * update * Update * update * update * Update * update * Update with agent abstraction alternatives * Update discussion * Update * update * Update * Update * Update * Update --------- Co-authored-by: Evan Mattson <35585003+moonbox3@users.noreply.github.com> Co-authored-by: Jack Gerrits <jackgerrits@users.noreply.github.com> Co-authored-by: Victor Dibia <chuvidi2003@gmail.com>
This commit is contained in:
co-authored by
Evan Mattson
Jack Gerrits
Victor Dibia
parent
d0531bb93b
commit
6089446f04
@@ -0,0 +1,201 @@
|
||||
# Workflow
|
||||
|
||||
The design goal is to create workflows that can be specified in a declarative
|
||||
way to allow for easy creation and modification without needing to change the
|
||||
underlying code.
|
||||
|
||||
## `Workflow` is Agent
|
||||
|
||||
A `Workflow` is an agent composed of other agents. It follows the same interface
|
||||
as an agent. This allows for nested workflows, where a workflow can contain other
|
||||
workflows.
|
||||
|
||||
## Agents in a `Workflow`
|
||||
|
||||
Each agent (or a `Workflow`) in a `Workflow` has a thread on which it will
|
||||
always run. The thread may be privated, or shared among some or all of the agents.
|
||||
|
||||
When do agents share a `thread`?
|
||||
|
||||
- When an agent is called through handoff or as a tool by another agent, the caller
|
||||
agent's thread may be shared with the callee agent.
|
||||
|
||||
When do agents not share a `thread`?
|
||||
|
||||
- When a set of worker agents are called through a "fan-out" and "fan-in" pattern, where the worker
|
||||
agents are called in parallel and the results are combined by an aggregator agent.
|
||||
|
||||
Thread sharing can be configured through the `Workflow`'s constructor.
|
||||
By default, each agent has its own private thread and no sharing.
|
||||
|
||||
See [Threads](threads.md) for more details on how threads work.
|
||||
|
||||
## `Workflow` from control flow graph
|
||||
|
||||
A `Workflow` can be created from a control flow graph of agents.
|
||||
The graph is a directed graph where each node is an agent and each edge
|
||||
is a transition between agents. The graph can contain loops
|
||||
and conditional transitions.
|
||||
|
||||
The control flow graph specifies the order in which agents are called
|
||||
and the conditions under which they are called.
|
||||
|
||||
```python
|
||||
# Create agent instances.
|
||||
agent1 = MCPAgent(
|
||||
model_client="OpenAIChatCompletionClient",
|
||||
mcp_server=["MCPServer1", "MCPServer2"],
|
||||
)
|
||||
agent2 = MCPAgent(
|
||||
model_client="OpenAIChatCompletionClient",
|
||||
mcp_server=["MCPServer3", "MCPServer4"],
|
||||
)
|
||||
agent3 = MCPAgent(
|
||||
model_client="OpenAIChatCompletionClient",
|
||||
mcp_server="MCPServer5",
|
||||
)
|
||||
|
||||
# Create a directed graph of agents with conditional loops and transitions.
|
||||
# The graph builder validates the graph.
|
||||
graph = GraphBuilder() \
|
||||
.add_agent(agent1) \
|
||||
.add_agent(agent2) \
|
||||
.add_agent(agent3) \
|
||||
.add_loop(agent1, agent2, conditions=Any(...)) \
|
||||
.add_transition(agent2, agent3, conditions=Any(..., All(...))]) \
|
||||
.build()
|
||||
|
||||
# Create a workflow from the graph.
|
||||
workflow = Workflow(graph=graph)
|
||||
```
|
||||
|
||||
## `Workflow` from message router
|
||||
|
||||
By default, each message is delivered to an _inbox_ of every agent in a `Workflow`.
|
||||
When an agent is called, the inbox is cleared and the messages are added
|
||||
to the thread that is used by the agent.
|
||||
|
||||
If multiple agents share a thread, each message is added exactly once to the thread.
|
||||
|
||||
To customize the message flow, we can configure how each inbox behaves.
|
||||
Each agent's inbox can be configured to only accept messages from a specific sender(s).
|
||||
We can also configure the inbox batch size, time-to-live for messages in the inbox
|
||||
and various other parameters that controls how the inbox is processed.
|
||||
|
||||
The configuration of agents' inboxes is done using a `Router` object,
|
||||
which can be built using a `RouterBuilder` object.
|
||||
|
||||
```python
|
||||
graph = ...
|
||||
|
||||
router = RouterBuilder() \
|
||||
.add_route(source=agent1, target=agent2) \ # Agent2 will receive messages from agent1.
|
||||
.add_route(source=[agent1, agent2], target=agent3, batch_size=10, ttl="1h") \ # Agent3 will receive messages from agent1 and agent2, with a batch size of 10 and a time-to-live of 1 hour.
|
||||
.add_route(source=Router.ANY, target=agent4) \ # Agent4 will receive all messages.
|
||||
).build()
|
||||
|
||||
# Create a workflow from the graph and router.
|
||||
workflow = Workflow(graph=graph, router=router)
|
||||
```
|
||||
|
||||
You can also skip the graph all together and just create a workflow from the router.
|
||||
In this case, all agents will run concurrently to process the messages delivered
|
||||
to their inboxes, according to the inbox rules.
|
||||
|
||||
```python
|
||||
# Create a workflow from the router.
|
||||
workflow = Workflow(router=router)
|
||||
```
|
||||
|
||||
The validation of the router is done as part of the workflow creation, to ensure
|
||||
that no gap exists in the routing, and warning for cascading routes.
|
||||
|
||||
## Run `Workflow`
|
||||
|
||||
It is the same as running an agent.
|
||||
|
||||
```python
|
||||
# Create a message batch to send to the workflow.
|
||||
# The run context is used to pass in the event channel and other context
|
||||
# shared by the agents.
|
||||
thread = [
|
||||
Message("Hello"),
|
||||
Message("Can you find the file 'foo.txt' for me?"),
|
||||
]
|
||||
context = RunContext(event_channel="console")
|
||||
result = await workflow.run(thread, context=context)
|
||||
```
|
||||
|
||||
## `Workflow` has a final response
|
||||
|
||||
A `Workflow` is expected to have a final response, which is the final response in the
|
||||
result of the last agent in the workflow. The final response is returned as part of the
|
||||
`Result` object returned by the `run` method.
|
||||
|
||||
This is to ensure the workflow can be used in the same way as an agent.
|
||||
|
||||
## Stopping `Workflow`
|
||||
|
||||
A `workflow` may run indefinitely, so it is important to have a way to stop it.
|
||||
|
||||
```python
|
||||
# Use a stopping condition to stop the workflow when the condition is met.
|
||||
# Detail design TBD.
|
||||
condition = StopCondition(
|
||||
condition=Any(...),
|
||||
timeout="1h",
|
||||
)
|
||||
workflow = Workflow(graph=graph, stop_condition=condition)
|
||||
```
|
||||
|
||||
TBD.
|
||||
|
||||
## `Workflow` can be stateless
|
||||
|
||||
The workflow state is kept in the thread object as input to the `run` method.
|
||||
If not provided, the workflow will create new sub-threads for each agent
|
||||
in the workflow for their private threads, otherwise, the workflow will
|
||||
use the provided sub-thread.
|
||||
|
||||
```python
|
||||
# Create a workflow with a graph and router.
|
||||
workflow = Workflow(graph=graph, router=router, stop_condition=condition)
|
||||
|
||||
# Create a new thread.
|
||||
thread = [
|
||||
Message("Hello"),
|
||||
Message("Can you find the file 'foo.txt' for me?"),
|
||||
]
|
||||
|
||||
# Run the workflow.
|
||||
result = await workflow.run(thread, context=context)
|
||||
|
||||
# Update the thread with new messages from the user.
|
||||
thread = result.thread + [
|
||||
Message("Can you find the file 'bar.txt' for me?"),
|
||||
]
|
||||
|
||||
# Resume the workflow from where it left off.
|
||||
result = await workflow.run(thread, context=context)
|
||||
```
|
||||
|
||||
Read more about [Threads](threads.md) for more details on threads.
|
||||
|
||||
## Pre-defined workflows
|
||||
|
||||
The framework ships with a few pre-defined workflows for common orchestration
|
||||
patterns. These workflows can be used as-is or as a starting point for
|
||||
new developers, however, when using them, you should be aware of the underlying
|
||||
implementation and move on to custom workflows when a limit is reached.
|
||||
|
||||
The pre-defined workflows are:
|
||||
- `Sequential`: A sequential workflow that calls each agent in order,
|
||||
its message flow can be configured separately.
|
||||
- `MapReduce`: A map-reduce workflow that splits a task into smaller
|
||||
tasks, runs them in parallel and then combines the results.
|
||||
- `RoundRobinGroupChat`: agents are called in a round-robin fashion in a loop.
|
||||
- `SelectorGroupChat`: agents are selected on each iteration by the workflow's built-in
|
||||
LLM based selector.
|
||||
- `Swarm`: use handoffs.
|
||||
|
||||
The predefined workflows are implemented as subclasses of the `Workflow` class.
|
||||
Reference in New Issue
Block a user