mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python: [BREAKING] Python: Make executor ID required, improvements around handling rehydrating checkpoints (#832)
* Make executor ID required, improvements around handling rehydrating checkpoints. * Duplicate executor validation added * fix remaining issues --------- Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
7cd45e313b
commit
aba094b5cf
@@ -1,108 +1,34 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
# import asyncio
|
||||
|
||||
# from agent_framework.foundry import FoundryChatClient
|
||||
# from agent_framework import AgentRunUpdateEvent, WorkflowBuilder, WorkflowCompletedEvent
|
||||
# from azure.identity.aio import AzureCliCredential
|
||||
|
||||
# """
|
||||
# Sample: Agents in a workflow with streaming
|
||||
|
||||
# A Writer agent generates content, then a Reviewer agent critiques it.
|
||||
# The workflow uses streaming so you can observe incremental AgentRunUpdateEvent chunks as each agent produces tokens.
|
||||
|
||||
# Purpose:
|
||||
# Show how to wire chat agents directly into a WorkflowBuilder pipeline where agents are auto wrapped as executors.
|
||||
|
||||
# Demonstrate:
|
||||
# - Automatic streaming of agent deltas via AgentRunUpdateEvent.
|
||||
# - A simple console aggregator that groups updates by executor id and prints them as they arrive.
|
||||
# - A final WorkflowCompletedEvent that contains the reviewer outcome after both agents finish.
|
||||
|
||||
# Prerequisites:
|
||||
# - Foundry Agent Service configured, along with the required environment variables.
|
||||
# - Authentication via azure-identity. Use AzureCliCredential and run az login before executing the sample.
|
||||
# - Basic familiarity with WorkflowBuilder, edges, events, and streaming runs.
|
||||
# """
|
||||
|
||||
|
||||
# async def main():
|
||||
# """Build and run a simple two node agent workflow: Writer then Reviewer."""
|
||||
# # Create the Foundry chat client.
|
||||
# async with (
|
||||
# AzureCliCredential() as credential,
|
||||
# FoundryChatClient(async_credential=credential).create_agent(
|
||||
# name="Writer",
|
||||
# instructions=(
|
||||
# "You are an excellent content writer.You create new content and edit contents based on the feedback."
|
||||
# ),
|
||||
# ) as writer_agent,
|
||||
# FoundryChatClient(async_credential=credential).create_agent(
|
||||
# name="Reviewer",
|
||||
# instructions=(
|
||||
# "You are an excellent content reviewer."
|
||||
# "Provide actionable feedback to the writer about the provided content."
|
||||
# "Provide the feedback in the most concise manner possible."
|
||||
# ),
|
||||
# ) as reviewer_agent,
|
||||
# ):
|
||||
# # Build the workflow using the fluent builder.
|
||||
# # Set the start node and connect an edge from writer to reviewer.
|
||||
# workflow = WorkflowBuilder().set_start_executor(writer_agent).add_edge(writer_agent, reviewer_agent).build()
|
||||
|
||||
# # Stream events from the workflow. We aggregate partial token updates per executor for readable output.
|
||||
# completed_event: WorkflowCompletedEvent | None = None
|
||||
# last_executor_id = None
|
||||
|
||||
# async for event in workflow.run_stream(
|
||||
# "Create a slogan for a new electric SUV that is affordable and fun to drive."
|
||||
# ):
|
||||
# if isinstance(event, AgentRunUpdateEvent):
|
||||
# # AgentRunUpdateEvent contains incremental text deltas from the underlying agent.
|
||||
# # Print a prefix when the executor changes, then append updates on the same line.
|
||||
# eid = event.executor_id
|
||||
# if eid != last_executor_id:
|
||||
# if last_executor_id is not None:
|
||||
# print()
|
||||
# print(f"{eid}:", end=" ", flush=True)
|
||||
# last_executor_id = eid
|
||||
# print(event.data, end="", flush=True)
|
||||
# elif isinstance(event, WorkflowCompletedEvent):
|
||||
# # Terminal event with the final reviewer output.
|
||||
# completed_event = event
|
||||
|
||||
# # Print the final consolidated reviewer result.
|
||||
# if completed_event:
|
||||
# print("\n===== Final Output =====")
|
||||
# print(completed_event.data)
|
||||
|
||||
# """
|
||||
# Sample Output:
|
||||
|
||||
# writer_agent: Charge Up Your Journey. Fun, Affordable, Electric.
|
||||
# reviewer_agent: Clear message, but consider highlighting SUV specific benefits
|
||||
# (space, versatility) for stronger impact. Try more vivid language to evoke
|
||||
# excitement. Example: "Big on Space. Big on Fun. Electric for Everyone."
|
||||
# ===== Final Output =====
|
||||
# Clear message, but consider highlighting SUV specific benefits (space, versatility)
|
||||
# for stronger impact. Try more vivid language to evoke excitement. Example:
|
||||
# "Big on Space. Big on Fun. Electric for Everyone."
|
||||
# """
|
||||
|
||||
|
||||
# if __name__ == "__main__":
|
||||
# asyncio.run(main())
|
||||
|
||||
import asyncio
|
||||
from collections.abc import Awaitable, Callable
|
||||
from contextlib import AsyncExitStack
|
||||
from typing import Any
|
||||
from collections.abc import Awaitable, Callable
|
||||
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from agent_framework import AgentRunUpdateEvent, WorkflowBuilder, WorkflowCompletedEvent
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.identity.aio import AzureCliCredential
|
||||
|
||||
"""
|
||||
Sample: Agents in a workflow with streaming
|
||||
|
||||
A Writer agent generates content, then a Reviewer agent critiques it.
|
||||
The workflow uses streaming so you can observe incremental AgentRunUpdateEvent chunks as each agent produces tokens.
|
||||
|
||||
Purpose:
|
||||
Show how to wire chat agents directly into a WorkflowBuilder pipeline where agents are auto wrapped as executors.
|
||||
|
||||
Demonstrate:
|
||||
- Automatic streaming of agent deltas via AgentRunUpdateEvent.
|
||||
- A simple console aggregator that groups updates by executor id and prints them as they arrive.
|
||||
- A final WorkflowCompletedEvent that contains the reviewer outcome after both agents finish.
|
||||
|
||||
Prerequisites:
|
||||
- Foundry Agent Service configured, along with the required environment variables.
|
||||
- Authentication via azure-identity. Use AzureCliCredential and run az login before executing the sample.
|
||||
- Basic familiarity with WorkflowBuilder, edges, events, and streaming runs.
|
||||
"""
|
||||
|
||||
|
||||
async def create_foundry_agent() -> tuple[Callable[..., Awaitable[Any]], Callable[[], Awaitable[None]]]:
|
||||
"""Helper method to create a Foundry agent factory and a close function.
|
||||
|
||||
+17
-12
@@ -1,27 +1,31 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
import sys
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
from agent_framework import (
|
||||
# Ensure local getting_started package can be imported when running as a script.
|
||||
_SAMPLES_ROOT = Path(__file__).resolve().parents[3]
|
||||
if str(_SAMPLES_ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(_SAMPLES_ROOT))
|
||||
|
||||
from agent_framework import ( # noqa: E402
|
||||
ChatMessage,
|
||||
Executor,
|
||||
FunctionCallContent,
|
||||
FunctionResultContent,
|
||||
Role,
|
||||
)
|
||||
from agent_framework.openai import OpenAIChatClient
|
||||
from agent_framework import (
|
||||
Executor,
|
||||
RequestInfoExecutor,
|
||||
RequestInfoMessage,
|
||||
RequestResponse,
|
||||
Role,
|
||||
WorkflowAgent,
|
||||
WorkflowBuilder,
|
||||
WorkflowContext,
|
||||
handler,
|
||||
)
|
||||
|
||||
from samples.getting_started.workflow.agents.workflow_as_agent_reflection_pattern import (
|
||||
from agent_framework.openai import OpenAIChatClient # noqa: E402
|
||||
from getting_started.workflow.agents.workflow_as_agent_reflection_pattern import ( # noqa: E402
|
||||
ReviewRequest,
|
||||
ReviewResponse,
|
||||
Worker,
|
||||
@@ -56,8 +60,9 @@ class HumanReviewRequest(RequestInfoMessage):
|
||||
class ReviewerWithHumanInTheLoop(Executor):
|
||||
"""Executor that always escalates reviews to a human manager."""
|
||||
|
||||
def __init__(self, worker_id: str, request_info_id: str) -> None:
|
||||
super().__init__()
|
||||
def __init__(self, worker_id: str, request_info_id: str, reviewer_id: str | None = None) -> None:
|
||||
unique_id = reviewer_id or f"{worker_id}-reviewer"
|
||||
super().__init__(id=unique_id)
|
||||
self._worker_id = worker_id
|
||||
self._request_info_id = request_info_id
|
||||
|
||||
@@ -96,8 +101,8 @@ async def main() -> None:
|
||||
# Create executors for the workflow.
|
||||
print("Creating chat client and executors...")
|
||||
mini_chat_client = OpenAIChatClient(ai_model_id="gpt-4.1-nano")
|
||||
worker = Worker(chat_client=mini_chat_client)
|
||||
request_info_executor = RequestInfoExecutor()
|
||||
worker = Worker(id="sub-worker", chat_client=mini_chat_client)
|
||||
request_info_executor = RequestInfoExecutor(id="request_info")
|
||||
reviewer = ReviewerWithHumanInTheLoop(worker_id=worker.id, request_info_id=request_info_executor.id)
|
||||
|
||||
print("Building workflow with Worker ↔ Reviewer cycle...")
|
||||
|
||||
+18
-8
@@ -4,9 +4,19 @@ import asyncio
|
||||
from dataclasses import dataclass
|
||||
from uuid import uuid4
|
||||
|
||||
from agent_framework import AgentRunResponseUpdate, ChatClientProtocol, ChatMessage, Contents, Role
|
||||
from agent_framework import (
|
||||
AgentRunResponseUpdate,
|
||||
AgentRunUpdateEvent,
|
||||
ChatClientProtocol,
|
||||
ChatMessage,
|
||||
Contents,
|
||||
Executor,
|
||||
Role,
|
||||
WorkflowBuilder,
|
||||
WorkflowContext,
|
||||
handler,
|
||||
)
|
||||
from agent_framework.openai import OpenAIChatClient
|
||||
from agent_framework import AgentRunUpdateEvent, Executor, WorkflowBuilder, WorkflowContext, handler
|
||||
from pydantic import BaseModel
|
||||
|
||||
"""
|
||||
@@ -54,8 +64,8 @@ class ReviewResponse:
|
||||
class Reviewer(Executor):
|
||||
"""Executor that reviews agent responses and provides structured feedback."""
|
||||
|
||||
def __init__(self, chat_client: ChatClientProtocol) -> None:
|
||||
super().__init__()
|
||||
def __init__(self, id: str, chat_client: ChatClientProtocol) -> None:
|
||||
super().__init__(id=id)
|
||||
self._chat_client = chat_client
|
||||
|
||||
@handler
|
||||
@@ -106,8 +116,8 @@ class Reviewer(Executor):
|
||||
class Worker(Executor):
|
||||
"""Executor that generates responses and incorporates feedback when necessary."""
|
||||
|
||||
def __init__(self, chat_client: ChatClientProtocol) -> None:
|
||||
super().__init__()
|
||||
def __init__(self, id: str, chat_client: ChatClientProtocol) -> None:
|
||||
super().__init__(id=id)
|
||||
self._chat_client = chat_client
|
||||
self._pending_requests: dict[str, tuple[ReviewRequest, list[ChatMessage]]] = {}
|
||||
|
||||
@@ -189,8 +199,8 @@ async def main() -> None:
|
||||
print("Creating chat client and executors...")
|
||||
mini_chat_client = OpenAIChatClient(ai_model_id="gpt-4.1-nano")
|
||||
chat_client = OpenAIChatClient(ai_model_id="gpt-4.1")
|
||||
reviewer = Reviewer(chat_client=chat_client)
|
||||
worker = Worker(chat_client=mini_chat_client)
|
||||
reviewer = Reviewer(id="reviewer", chat_client=chat_client)
|
||||
worker = Worker(id="worker", chat_client=mini_chat_client)
|
||||
|
||||
print("Building workflow with Worker ↔ Reviewer cycle...")
|
||||
agent = (
|
||||
|
||||
Reference in New Issue
Block a user