mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python: Improve the workflow getting started samples (#570)
* Wip: samples * wip - samples * Updates to workflow getting started samples * Checkpointing enhancements * Cleanup * PR feedback * Updates * Sample updates * Updates * Revamp samples, improve doc strings and code comments * Cleanup unused comment * Formatting cleanup * wip * Further work on samples. Allow agent to be specified as edge. * Cleanup * Typing cleanup * Sample updates --------- Co-authored-by: Chris <66376200+crickman@users.noreply.github.com> Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
cd0587c5f6
commit
518fd447fd
@@ -0,0 +1,89 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
from typing import Any
|
||||
|
||||
from agent_framework.workflow import (
|
||||
Executor,
|
||||
WorkflowBuilder,
|
||||
WorkflowCompletedEvent,
|
||||
WorkflowContext,
|
||||
handler,
|
||||
)
|
||||
|
||||
"""
|
||||
Sample: Sequential workflow with streaming.
|
||||
|
||||
Two custom executors run in sequence. The first converts text to uppercase,
|
||||
the second reverses the text and completes the workflow. The run_stream loop prints events as they occur.
|
||||
|
||||
Purpose:
|
||||
Show how to define explicit Executor classes with @handler methods, wire them in order with
|
||||
WorkflowBuilder, and consume streaming events. Demonstrate typed WorkflowContext[T] for outputs,
|
||||
ctx.send_message to pass intermediate values, and ctx.add_event to signal completion with a WorkflowCompletedEvent.
|
||||
|
||||
Prerequisites:
|
||||
- No external services required.
|
||||
"""
|
||||
|
||||
|
||||
class UpperCaseExecutor(Executor):
|
||||
"""Converts an input string to uppercase and forwards it.
|
||||
|
||||
Concepts:
|
||||
- @handler methods define invokable steps.
|
||||
- WorkflowContext[str] indicates this step emits a string to the next node.
|
||||
"""
|
||||
|
||||
@handler
|
||||
async def to_upper_case(self, text: str, ctx: WorkflowContext[str]) -> None:
|
||||
"""Transform the input to uppercase and send it downstream."""
|
||||
result = text.upper()
|
||||
# Pass the intermediate result to the next executor in the chain.
|
||||
await ctx.send_message(result)
|
||||
|
||||
|
||||
class ReverseTextExecutor(Executor):
|
||||
"""Reverses the incoming string and completes the workflow.
|
||||
|
||||
Concepts:
|
||||
- Use ctx.add_event to publish a WorkflowCompletedEvent when the terminal result is ready.
|
||||
- The terminal node does not forward messages further.
|
||||
"""
|
||||
|
||||
@handler
|
||||
async def reverse_text(self, text: str, ctx: WorkflowContext[Any]) -> None:
|
||||
"""Reverse the input string and emit a completion event."""
|
||||
result = text[::-1]
|
||||
await ctx.add_event(WorkflowCompletedEvent(result))
|
||||
|
||||
|
||||
async def main():
|
||||
"""Build a two step sequential workflow and run it with streaming to observe events."""
|
||||
# Step 1: Create executor instances.
|
||||
upper_case_executor = UpperCaseExecutor(id="upper_case_executor")
|
||||
reverse_text_executor = ReverseTextExecutor(id="reverse_text_executor")
|
||||
|
||||
# Step 2: Build the workflow graph.
|
||||
# Order matters. We connect upper_case_executor -> reverse_text_executor and set the start.
|
||||
workflow = (
|
||||
WorkflowBuilder()
|
||||
.add_edge(upper_case_executor, reverse_text_executor)
|
||||
.set_start_executor(upper_case_executor)
|
||||
.build()
|
||||
)
|
||||
|
||||
# Step 3: Stream events for a single input.
|
||||
# The stream will include executor invoke and completion events, plus the final WorkflowCompletedEvent.
|
||||
completion_event = None
|
||||
async for event in workflow.run_stream("hello world"):
|
||||
print(f"Event: {event}")
|
||||
if isinstance(event, WorkflowCompletedEvent):
|
||||
completion_event = event
|
||||
|
||||
if completion_event:
|
||||
print(f"Workflow completed with result: {completion_event.data}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,85 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
|
||||
from agent_framework.workflow import WorkflowBuilder, WorkflowCompletedEvent, WorkflowContext, executor
|
||||
|
||||
"""
|
||||
Sample: Foundational sequential workflow with streaming using function-style executors.
|
||||
|
||||
Two lightweight steps run in order. The first converts text to uppercase.
|
||||
The second reverses the text and completes the workflow. Events are printed as they arrive from run_stream.
|
||||
|
||||
Purpose:
|
||||
Show how to declare executors with the @executor decorator, connect them with WorkflowBuilder,
|
||||
pass intermediate values using ctx.send_message, and signal completion with ctx.add_event by emitting a
|
||||
WorkflowCompletedEvent. Demonstrate how streaming exposes ExecutorInvokeEvent and WorkflowCompletedEvent
|
||||
for observability.
|
||||
|
||||
Prerequisites:
|
||||
- No external services required.
|
||||
"""
|
||||
|
||||
|
||||
# Step 1: Define methods using the executor decorator.
|
||||
@executor(id="upper_case_executor")
|
||||
async def to_upper_case(text: str, ctx: WorkflowContext[str]) -> None:
|
||||
"""Transform the input to uppercase and forward it to the next step.
|
||||
|
||||
Concepts:
|
||||
- The @executor decorator registers this function as a workflow node.
|
||||
- WorkflowContext[str] indicates that this node emits a string payload downstream.
|
||||
"""
|
||||
result = text.upper()
|
||||
|
||||
# Send the intermediate result to the next executor in the workflow graph.
|
||||
await ctx.send_message(result)
|
||||
|
||||
|
||||
@executor(id="reverse_text_executor")
|
||||
async def reverse_text(text: str, ctx: WorkflowContext[str]) -> None:
|
||||
"""Reverse the input and complete the workflow with the final result.
|
||||
|
||||
Concepts:
|
||||
- Terminal nodes publish a WorkflowCompletedEvent using ctx.add_event.
|
||||
- No further messages are forwarded after completion.
|
||||
"""
|
||||
result = text[::-1]
|
||||
|
||||
# Emit the terminal event that carries the final output for this run.
|
||||
await ctx.add_event(WorkflowCompletedEvent(result))
|
||||
|
||||
|
||||
async def main():
|
||||
"""Build a two-step sequential workflow and run it with streaming to observe events."""
|
||||
# Step 2: Build the workflow with the defined edges.
|
||||
# Order matters. upper_case_executor runs first, then reverse_text_executor.
|
||||
workflow = WorkflowBuilder().add_edge(to_upper_case, reverse_text).set_start_executor(to_upper_case).build()
|
||||
|
||||
# Step 3: Run the workflow and stream events in real time.
|
||||
completion_event = None
|
||||
async for event in workflow.run_stream("hello world"):
|
||||
# You will see executor invoke and completion events, and then the final WorkflowCompletedEvent.
|
||||
print(f"Event: {event}")
|
||||
if isinstance(event, WorkflowCompletedEvent):
|
||||
# The WorkflowCompletedEvent contains the final result.
|
||||
completion_event = event
|
||||
|
||||
# Print the final result after the streaming loop concludes.
|
||||
if completion_event:
|
||||
print(f"Workflow completed with result: {completion_event.data}")
|
||||
|
||||
"""
|
||||
Sample Output:
|
||||
|
||||
Event: ExecutorInvokeEvent(executor_id=upper_case_executor)
|
||||
Event: ExecutorCompletedEvent(executor_id=upper_case_executor)
|
||||
Event: ExecutorInvokeEvent(executor_id=reverse_text_executor)
|
||||
Event: WorkflowCompletedEvent(data=DLROW OLLEH)
|
||||
Event: ExecutorCompletedEvent(executor_id=reverse_text_executor)
|
||||
Workflow completed with result: DLROW OLLEH
|
||||
"""
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user