mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python: [BREAKING] Simplify API: ChatAgent -> Agent, ChatMessage -> Message (#3747)
* [BREAKING] Rename ChatAgent -> Agent, ChatMessage -> Message, ChatClientProtocol -> SupportsChatGetResponse Simplify the public API by removing redundant 'Chat' prefix from core types: - ChatAgent -> Agent - RawChatAgent -> RawAgent - ChatMessage -> Message - ChatClientProtocol -> SupportsChatGetResponse Also renamed internal WorkflowMessage (was Message in _runner_context) to avoid collision. No backward compatibility aliases - this is a clean breaking change. * [BREAKING] Rename Agent chat_client parameter to client * Fix rebase issues: WorkflowMessage references and broken markdown links * Fix formatting and lint issues from code quality checks * Fix import ordering in workflow sample files * fixed rebase * Fix test failures: use WorkflowMessage and A2AMessage after ChatMessage→Message rename - Replace Message(data=..., source_id=...) with WorkflowMessage(...) in workflow tests - Fix isinstance check in A2A agent to use A2AMessage instead of Message - Fix import in test_workflow_observability.py (Message→WorkflowMessage) * Fix lint, fmt, and sample errors after ChatMessage→Message rename - Auto-fix 70+ ruff lint issues across samples (ChatMessage→Message refs) - Fix HostedVectorStoreContent→Content.from_hosted_vector_store in file search sample - Fix _normalize_messages→normalize_messages in custom agent sample - Fix context.terminate→raise MiddlewareTermination in middleware samples - Fix with_update_hook→with_transform_hook in override middleware sample - Add TOptions_co import back to custom_chat_client sample - Add noqa for FastAPI File() default in chatkit sample - Fix B023 loop variable capture in weather agent sample * fix: update Agent constructor calls from chat_client to client in declaration-only tool tests * fix: add register_cleanup to devui lazy-loading proxy and type stub * fixed tests and updated new pieces * fix agui typevar * fix merge errors * fix merge conflicts * fiux merge * Remove unused links --------- Co-authored-by: Evan Mattson <evan.mattson@microsoft.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
a4c9e43afb
commit
0521f5bed8
@@ -30,32 +30,29 @@ from agent_framework.orchestrations import (
|
||||
|
||||
| Sample | File | Concepts |
|
||||
| ------------------------------------------------- | ------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------- |
|
||||
| Concurrent Orchestration (Default Aggregator) | [concurrent_agents.py](./concurrent_agents.py) | Fan-out to multiple agents; fan-in with default aggregator returning combined ChatMessages |
|
||||
| Concurrent Orchestration (Default Aggregator) | [concurrent_agents.py](./concurrent_agents.py) | Fan-out to multiple agents; fan-in with default aggregator returning combined Messages |
|
||||
| Concurrent Orchestration (Custom Aggregator) | [concurrent_custom_aggregator.py](./concurrent_custom_aggregator.py) | Override aggregator via callback; summarize results with an LLM |
|
||||
| Concurrent Orchestration (Custom Agent Executors) | [concurrent_custom_agent_executors.py](./concurrent_custom_agent_executors.py) | Child executors own ChatAgents; concurrent fan-out/fan-in via ConcurrentBuilder |
|
||||
| Concurrent Orchestration (Participant Factory) | [concurrent_participant_factory.py](./concurrent_participant_factory.py) | Use participant factories for state isolation between workflow instances |
|
||||
| Concurrent Orchestration (Custom Agent Executors) | [concurrent_custom_agent_executors.py](./concurrent_custom_agent_executors.py) | Child executors own Agents; concurrent fan-out/fan-in via ConcurrentBuilder |
|
||||
| Group Chat with Agent Manager | [group_chat_agent_manager.py](./group_chat_agent_manager.py) | Agent-based manager using `with_orchestrator(agent=)` to select next speaker |
|
||||
| Group Chat Philosophical Debate | [group_chat_philosophical_debate.py](./group_chat_philosophical_debate.py) | Agent manager moderates long-form, multi-round debate across diverse participants |
|
||||
| Group Chat with Simple Function Selector | [group_chat_simple_selector.py](./group_chat_simple_selector.py) | Group chat with a simple function selector for next speaker |
|
||||
| Handoff (Simple) | [handoff_simple.py](./handoff_simple.py) | Single-tier routing: triage agent routes to specialists, control returns to user after each specialist response |
|
||||
| Handoff (Autonomous) | [handoff_autonomous.py](./handoff_autonomous.py) | Autonomous mode: specialists iterate independently until invoking a handoff tool using `.with_autonomous_mode()` |
|
||||
| Handoff (Participant Factory) | [handoff_participant_factory.py](./handoff_participant_factory.py) | Use participant factories for state isolation between workflow instances |
|
||||
| Handoff with Code Interpreter | [handoff_with_code_interpreter_file.py](./handoff_with_code_interpreter_file.py) | Retrieve file IDs from code interpreter output in handoff workflow |
|
||||
| Magentic Workflow (Multi-Agent) | [magentic.py](./magentic.py) | Orchestrate multiple agents with Magentic manager and streaming |
|
||||
| Magentic + Human Plan Review | [magentic_human_plan_review.py](./magentic_human_plan_review.py) | Human reviews/updates the plan before execution |
|
||||
| Magentic + Checkpoint Resume | [magentic_checkpoint.py](./magentic_checkpoint.py) | Resume Magentic orchestration from saved checkpoints |
|
||||
| Sequential Orchestration (Agents) | [sequential_agents.py](./sequential_agents.py) | Chain agents sequentially with shared conversation context |
|
||||
| Sequential Orchestration (Custom Executor) | [sequential_custom_executors.py](./sequential_custom_executors.py) | Mix agents with a summarizer that appends a compact summary |
|
||||
| Sequential Orchestration (Participant Factories) | [sequential_participant_factory.py](./sequential_participant_factory.py) | Use participant factories for state isolation between workflow instances |
|
||||
|
||||
## Tips
|
||||
|
||||
**Magentic checkpointing tip**: Treat `MagenticBuilder.participants` keys as stable identifiers. When resuming from a checkpoint, the rebuilt workflow must reuse the same participant names; otherwise the checkpoint cannot be applied and the run will fail fast.
|
||||
|
||||
**Handoff workflow tip**: Handoff workflows maintain the full conversation history including any `ChatMessage.additional_properties` emitted by your agents. This ensures routing metadata remains intact across all agent transitions. For specialist-to-specialist handoffs, use `.add_handoff(source, targets)` to configure which agents can route to which others with a fluent, type-safe API.
|
||||
**Handoff workflow tip**: Handoff workflows maintain the full conversation history including any `Message.additional_properties` emitted by your agents. This ensures routing metadata remains intact across all agent transitions. For specialist-to-specialist handoffs, use `.add_handoff(source, targets)` to configure which agents can route to which others with a fluent, type-safe API.
|
||||
|
||||
**Sequential orchestration note**: Sequential orchestration uses a few small adapter nodes for plumbing:
|
||||
- `input-conversation` normalizes input to `list[ChatMessage]`
|
||||
- `input-conversation` normalizes input to `list[Message]`
|
||||
- `to-conversation:<participant>` converts agent responses into the shared conversation
|
||||
- `complete` publishes the final output event (type='output')
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import asyncio
|
||||
from typing import Any
|
||||
|
||||
from agent_framework import ChatMessage
|
||||
from agent_framework import Message
|
||||
from agent_framework.azure import AzureOpenAIChatClient
|
||||
from agent_framework.orchestrations import ConcurrentBuilder
|
||||
from azure.identity import AzureCliCredential
|
||||
@@ -14,7 +14,7 @@ Sample: Concurrent fan-out/fan-in (agent-only API) with default aggregator
|
||||
Build a high-level concurrent workflow using ConcurrentBuilder and three domain agents.
|
||||
The default dispatcher fans out the same user prompt to all agents in parallel.
|
||||
The default aggregator fans in their results and yields output containing
|
||||
a list[ChatMessage] representing the concatenated conversations from all agents.
|
||||
a list[Message] representing the concatenated conversations from all agents.
|
||||
|
||||
Demonstrates:
|
||||
- Minimal wiring with ConcurrentBuilder(participants=[...]).build()
|
||||
@@ -29,9 +29,9 @@ Prerequisites:
|
||||
|
||||
async def main() -> None:
|
||||
# 1) Create three domain agents using AzureOpenAIChatClient
|
||||
chat_client = AzureOpenAIChatClient(credential=AzureCliCredential())
|
||||
client = AzureOpenAIChatClient(credential=AzureCliCredential())
|
||||
|
||||
researcher = chat_client.as_agent(
|
||||
researcher = client.as_agent(
|
||||
instructions=(
|
||||
"You're an expert market and product researcher. Given a prompt, provide concise, factual insights,"
|
||||
" opportunities, and risks."
|
||||
@@ -39,7 +39,7 @@ async def main() -> None:
|
||||
name="researcher",
|
||||
)
|
||||
|
||||
marketer = chat_client.as_agent(
|
||||
marketer = client.as_agent(
|
||||
instructions=(
|
||||
"You're a creative marketing strategist. Craft compelling value propositions and target messaging"
|
||||
" aligned to the prompt."
|
||||
@@ -47,7 +47,7 @@ async def main() -> None:
|
||||
name="marketer",
|
||||
)
|
||||
|
||||
legal = chat_client.as_agent(
|
||||
legal = client.as_agent(
|
||||
instructions=(
|
||||
"You're a cautious legal/compliance reviewer. Highlight constraints, disclaimers, and policy concerns"
|
||||
" based on the prompt."
|
||||
@@ -66,7 +66,7 @@ async def main() -> None:
|
||||
if outputs:
|
||||
print("===== Final Aggregated Conversation (messages) =====")
|
||||
for output in outputs:
|
||||
messages: list[ChatMessage] | Any = output
|
||||
messages: list[Message] | Any = output
|
||||
for i, msg in enumerate(messages, start=1):
|
||||
name = msg.author_name if msg.author_name else "user"
|
||||
print(f"{'-' * 60}\n\n{i:02d} [{name}]:\n{msg.text}")
|
||||
|
||||
+19
-19
@@ -4,11 +4,11 @@ import asyncio
|
||||
from typing import Any
|
||||
|
||||
from agent_framework import (
|
||||
Agent,
|
||||
AgentExecutorRequest,
|
||||
AgentExecutorResponse,
|
||||
ChatAgent,
|
||||
ChatMessage,
|
||||
Executor,
|
||||
Message,
|
||||
WorkflowContext,
|
||||
handler,
|
||||
)
|
||||
@@ -20,15 +20,15 @@ from azure.identity import AzureCliCredential
|
||||
Sample: Concurrent Orchestration with Custom Agent Executors
|
||||
|
||||
This sample shows a concurrent fan-out/fan-in pattern using child Executor classes
|
||||
that each own their ChatAgent. The executors accept AgentExecutorRequest inputs
|
||||
that each own their Agent. The executors accept AgentExecutorRequest inputs
|
||||
and emit AgentExecutorResponse outputs, which allows reuse of the high-level
|
||||
ConcurrentBuilder API and the default aggregator.
|
||||
|
||||
Demonstrates:
|
||||
- Executors that create their ChatAgent in __init__ (via AzureOpenAIChatClient)
|
||||
- Executors that create their Agent in __init__ (via AzureOpenAIChatClient)
|
||||
- A @handler that converts AgentExecutorRequest -> AgentExecutorResponse
|
||||
- ConcurrentBuilder(participants=[...]) to build fan-out/fan-in
|
||||
- Default aggregator returning list[ChatMessage] (one user + one assistant per agent)
|
||||
- Default aggregator returning list[Message] (one user + one assistant per agent)
|
||||
- Workflow completion when all participants become idle
|
||||
|
||||
Prerequisites:
|
||||
@@ -37,10 +37,10 @@ Prerequisites:
|
||||
|
||||
|
||||
class ResearcherExec(Executor):
|
||||
agent: ChatAgent
|
||||
agent: Agent
|
||||
|
||||
def __init__(self, chat_client: AzureOpenAIChatClient, id: str = "researcher"):
|
||||
self.agent = chat_client.as_agent(
|
||||
def __init__(self, client: AzureOpenAIChatClient, id: str = "researcher"):
|
||||
self.agent = client.as_agent(
|
||||
instructions=(
|
||||
"You're an expert market and product researcher. Given a prompt, provide concise, factual insights,"
|
||||
" opportunities, and risks."
|
||||
@@ -57,10 +57,10 @@ class ResearcherExec(Executor):
|
||||
|
||||
|
||||
class MarketerExec(Executor):
|
||||
agent: ChatAgent
|
||||
agent: Agent
|
||||
|
||||
def __init__(self, chat_client: AzureOpenAIChatClient, id: str = "marketer"):
|
||||
self.agent = chat_client.as_agent(
|
||||
def __init__(self, client: AzureOpenAIChatClient, id: str = "marketer"):
|
||||
self.agent = client.as_agent(
|
||||
instructions=(
|
||||
"You're a creative marketing strategist. Craft compelling value propositions and target messaging"
|
||||
" aligned to the prompt."
|
||||
@@ -77,10 +77,10 @@ class MarketerExec(Executor):
|
||||
|
||||
|
||||
class LegalExec(Executor):
|
||||
agent: ChatAgent
|
||||
agent: Agent
|
||||
|
||||
def __init__(self, chat_client: AzureOpenAIChatClient, id: str = "legal"):
|
||||
self.agent = chat_client.as_agent(
|
||||
def __init__(self, client: AzureOpenAIChatClient, id: str = "legal"):
|
||||
self.agent = client.as_agent(
|
||||
instructions=(
|
||||
"You're a cautious legal/compliance reviewer. Highlight constraints, disclaimers, and policy concerns"
|
||||
" based on the prompt."
|
||||
@@ -97,11 +97,11 @@ class LegalExec(Executor):
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
chat_client = AzureOpenAIChatClient(credential=AzureCliCredential())
|
||||
client = AzureOpenAIChatClient(credential=AzureCliCredential())
|
||||
|
||||
researcher = ResearcherExec(chat_client)
|
||||
marketer = MarketerExec(chat_client)
|
||||
legal = LegalExec(chat_client)
|
||||
researcher = ResearcherExec(client)
|
||||
marketer = MarketerExec(client)
|
||||
legal = LegalExec(client)
|
||||
|
||||
workflow = ConcurrentBuilder(participants=[researcher, marketer, legal]).build()
|
||||
|
||||
@@ -110,7 +110,7 @@ async def main() -> None:
|
||||
|
||||
if outputs:
|
||||
print("===== Final Aggregated Conversation (messages) =====")
|
||||
messages: list[ChatMessage] | Any = outputs[0] # Get the first (and typically only) output
|
||||
messages: list[Message] | Any = outputs[0] # Get the first (and typically only) output
|
||||
for i, msg in enumerate(messages, start=1):
|
||||
name = msg.author_name if msg.author_name else "user"
|
||||
print(f"{'-' * 60}\n\n{i:02d} [{name}]:\n{msg.text}")
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import asyncio
|
||||
from typing import Any
|
||||
|
||||
from agent_framework import ChatMessage
|
||||
from agent_framework import Message
|
||||
from agent_framework.azure import AzureOpenAIChatClient
|
||||
from agent_framework.orchestrations import ConcurrentBuilder
|
||||
from azure.identity import AzureCliCredential
|
||||
@@ -20,7 +20,7 @@ The workflow completes when all participants become idle.
|
||||
Demonstrates:
|
||||
- ConcurrentBuilder(participants=[...]).with_aggregator(callback)
|
||||
- Fan-out to agents and fan-in at an aggregator
|
||||
- Aggregation implemented via an LLM call (chat_client.get_response)
|
||||
- Aggregation implemented via an LLM call (client.get_response)
|
||||
- Workflow output yielded with the synthesized summary string
|
||||
|
||||
Prerequisites:
|
||||
@@ -29,23 +29,23 @@ Prerequisites:
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
chat_client = AzureOpenAIChatClient(credential=AzureCliCredential())
|
||||
client = AzureOpenAIChatClient(credential=AzureCliCredential())
|
||||
|
||||
researcher = chat_client.as_agent(
|
||||
researcher = client.as_agent(
|
||||
instructions=(
|
||||
"You're an expert market and product researcher. Given a prompt, provide concise, factual insights,"
|
||||
" opportunities, and risks."
|
||||
),
|
||||
name="researcher",
|
||||
)
|
||||
marketer = chat_client.as_agent(
|
||||
marketer = client.as_agent(
|
||||
instructions=(
|
||||
"You're a creative marketing strategist. Craft compelling value propositions and target messaging"
|
||||
" aligned to the prompt."
|
||||
),
|
||||
name="marketer",
|
||||
)
|
||||
legal = chat_client.as_agent(
|
||||
legal = client.as_agent(
|
||||
instructions=(
|
||||
"You're a cautious legal/compliance reviewer. Highlight constraints, disclaimers, and policy concerns"
|
||||
" based on the prompt."
|
||||
@@ -66,16 +66,16 @@ async def main() -> None:
|
||||
expert_sections.append(f"{getattr(r, 'executor_id', 'expert')}: (error: {type(e).__name__}: {e})")
|
||||
|
||||
# Ask the model to synthesize a concise summary of the experts' outputs
|
||||
system_msg = ChatMessage(
|
||||
system_msg = Message(
|
||||
"system",
|
||||
text=(
|
||||
"You are a helpful assistant that consolidates multiple domain expert outputs "
|
||||
"into one cohesive, concise summary with clear takeaways. Keep it under 200 words."
|
||||
),
|
||||
)
|
||||
user_msg = ChatMessage("user", text="\n\n".join(expert_sections))
|
||||
user_msg = Message("user", text="\n\n".join(expert_sections))
|
||||
|
||||
response = await chat_client.get_response([system_msg, user_msg])
|
||||
response = await client.get_response([system_msg, user_msg])
|
||||
# Return the model's final assistant text as the completion result
|
||||
return response.messages[-1].text if response.messages else ""
|
||||
|
||||
@@ -83,7 +83,7 @@ async def main() -> None:
|
||||
# - participants([...]) accepts SupportsAgentRun (agents) or Executor instances.
|
||||
# Each participant becomes a parallel branch (fan-out) from an internal dispatcher.
|
||||
# - with_aggregator(...) overrides the default aggregator:
|
||||
# • Default aggregator -> returns list[ChatMessage] (one user + one assistant per agent)
|
||||
# • Default aggregator -> returns list[Message] (one user + one assistant per agent)
|
||||
# • Custom callback -> return value becomes workflow output (string here)
|
||||
# The callback can be sync or async; it receives list[AgentExecutorResponse].
|
||||
workflow = (
|
||||
|
||||
@@ -4,9 +4,9 @@ import asyncio
|
||||
from typing import cast
|
||||
|
||||
from agent_framework import (
|
||||
Agent,
|
||||
AgentResponseUpdate,
|
||||
ChatAgent,
|
||||
ChatMessage,
|
||||
Message,
|
||||
)
|
||||
from agent_framework.azure import AzureOpenAIChatClient
|
||||
from agent_framework.orchestrations import GroupChatBuilder
|
||||
@@ -17,7 +17,7 @@ Sample: Group Chat with Agent-Based Manager
|
||||
|
||||
What it does:
|
||||
- Demonstrates the new set_manager() API for agent-based coordination
|
||||
- Manager is a full ChatAgent with access to tools, context, and observability
|
||||
- Manager is a full Agent with access to tools, context, and observability
|
||||
- Coordinates a researcher and writer agent to solve tasks collaboratively
|
||||
|
||||
Prerequisites:
|
||||
@@ -36,32 +36,32 @@ Guidelines:
|
||||
|
||||
async def main() -> None:
|
||||
# Create a chat client using Azure OpenAI and Azure CLI credentials for all agents
|
||||
chat_client = AzureOpenAIChatClient(credential=AzureCliCredential())
|
||||
client = AzureOpenAIChatClient(credential=AzureCliCredential())
|
||||
|
||||
# Orchestrator agent that manages the conversation
|
||||
# Note: This agent (and the underlying chat client) must support structured outputs.
|
||||
# The group chat workflow relies on this to parse the orchestrator's decisions.
|
||||
# `response_format` is set internally by the GroupChat workflow when the agent is invoked.
|
||||
orchestrator_agent = ChatAgent(
|
||||
orchestrator_agent = Agent(
|
||||
name="Orchestrator",
|
||||
description="Coordinates multi-agent collaboration by selecting speakers",
|
||||
instructions=ORCHESTRATOR_AGENT_INSTRUCTIONS,
|
||||
chat_client=chat_client,
|
||||
client=client,
|
||||
)
|
||||
|
||||
# Participant agents
|
||||
researcher = ChatAgent(
|
||||
researcher = Agent(
|
||||
name="Researcher",
|
||||
description="Collects relevant background information",
|
||||
instructions="Gather concise facts that help a teammate answer the question.",
|
||||
chat_client=chat_client,
|
||||
client=client,
|
||||
)
|
||||
|
||||
writer = ChatAgent(
|
||||
writer = Agent(
|
||||
name="Writer",
|
||||
description="Synthesizes polished answers from gathered information",
|
||||
instructions="Compose clear and structured answers using any notes provided.",
|
||||
chat_client=chat_client,
|
||||
client=client,
|
||||
)
|
||||
|
||||
# Build the group chat workflow
|
||||
@@ -103,7 +103,7 @@ async def main() -> None:
|
||||
print(data.text, end="", flush=True)
|
||||
elif event.type == "output":
|
||||
# The output of the group chat workflow is a collection of chat messages from all participants
|
||||
outputs = cast(list[ChatMessage], event.data)
|
||||
outputs = cast(list[Message], event.data)
|
||||
print("\n" + "=" * 80)
|
||||
print("\nFinal Conversation Transcript:\n")
|
||||
for message in outputs:
|
||||
|
||||
@@ -5,9 +5,9 @@ import logging
|
||||
from typing import cast
|
||||
|
||||
from agent_framework import (
|
||||
Agent,
|
||||
AgentResponseUpdate,
|
||||
ChatAgent,
|
||||
ChatMessage,
|
||||
Message,
|
||||
)
|
||||
from agent_framework.azure import AzureOpenAIChatClient
|
||||
from agent_framework.orchestrations import GroupChatBuilder
|
||||
@@ -48,7 +48,7 @@ def _get_chat_client() -> AzureOpenAIChatClient:
|
||||
async def main() -> None:
|
||||
# Create debate moderator with structured output for speaker selection
|
||||
# Note: Participant names and descriptions are automatically injected by the orchestrator
|
||||
moderator = ChatAgent(
|
||||
moderator = Agent(
|
||||
name="Moderator",
|
||||
description="Guides philosophical discussion by selecting next speaker",
|
||||
instructions="""
|
||||
@@ -75,10 +75,10 @@ Finish when:
|
||||
|
||||
In your final_message, provide a brief synthesis highlighting key themes that emerged.
|
||||
""",
|
||||
chat_client=_get_chat_client(),
|
||||
client=_get_chat_client(),
|
||||
)
|
||||
|
||||
farmer = ChatAgent(
|
||||
farmer = Agent(
|
||||
name="Farmer",
|
||||
description="A rural farmer from Southeast Asia",
|
||||
instructions="""
|
||||
@@ -91,10 +91,10 @@ Share your perspective authentically. Feel free to:
|
||||
- Use concrete examples from your experience
|
||||
- Keep responses thoughtful but concise (2-4 sentences)
|
||||
""",
|
||||
chat_client=_get_chat_client(),
|
||||
client=_get_chat_client(),
|
||||
)
|
||||
|
||||
developer = ChatAgent(
|
||||
developer = Agent(
|
||||
name="Developer",
|
||||
description="An urban software developer from the United States",
|
||||
instructions="""
|
||||
@@ -107,10 +107,10 @@ Share your perspective authentically. Feel free to:
|
||||
- Use concrete examples from your experience
|
||||
- Keep responses thoughtful but concise (2-4 sentences)
|
||||
""",
|
||||
chat_client=_get_chat_client(),
|
||||
client=_get_chat_client(),
|
||||
)
|
||||
|
||||
teacher = ChatAgent(
|
||||
teacher = Agent(
|
||||
name="Teacher",
|
||||
description="A retired history teacher from Eastern Europe",
|
||||
instructions="""
|
||||
@@ -124,10 +124,10 @@ Share your perspective authentically. Feel free to:
|
||||
- Use concrete examples from history or your teaching experience
|
||||
- Keep responses thoughtful but concise (2-4 sentences)
|
||||
""",
|
||||
chat_client=_get_chat_client(),
|
||||
client=_get_chat_client(),
|
||||
)
|
||||
|
||||
activist = ChatAgent(
|
||||
activist = Agent(
|
||||
name="Activist",
|
||||
description="A young activist from South America",
|
||||
instructions="""
|
||||
@@ -140,10 +140,10 @@ Share your perspective authentically. Feel free to:
|
||||
- Use concrete examples from your activism
|
||||
- Keep responses thoughtful but concise (2-4 sentences)
|
||||
""",
|
||||
chat_client=_get_chat_client(),
|
||||
client=_get_chat_client(),
|
||||
)
|
||||
|
||||
spiritual_leader = ChatAgent(
|
||||
spiritual_leader = Agent(
|
||||
name="SpiritualLeader",
|
||||
description="A spiritual leader from the Middle East",
|
||||
instructions="""
|
||||
@@ -156,10 +156,10 @@ Share your perspective authentically. Feel free to:
|
||||
- Use examples from spiritual teachings or community work
|
||||
- Keep responses thoughtful but concise (2-4 sentences)
|
||||
""",
|
||||
chat_client=_get_chat_client(),
|
||||
client=_get_chat_client(),
|
||||
)
|
||||
|
||||
artist = ChatAgent(
|
||||
artist = Agent(
|
||||
name="Artist",
|
||||
description="An artist from Africa",
|
||||
instructions="""
|
||||
@@ -172,10 +172,10 @@ Share your perspective authentically. Feel free to:
|
||||
- Use examples from your art or cultural traditions
|
||||
- Keep responses thoughtful but concise (2-4 sentences)
|
||||
""",
|
||||
chat_client=_get_chat_client(),
|
||||
client=_get_chat_client(),
|
||||
)
|
||||
|
||||
immigrant = ChatAgent(
|
||||
immigrant = Agent(
|
||||
name="Immigrant",
|
||||
description="An immigrant entrepreneur from Asia living in Canada",
|
||||
instructions="""
|
||||
@@ -188,10 +188,10 @@ Share your perspective authentically. Feel free to:
|
||||
- Use examples from your immigrant and entrepreneurial journey
|
||||
- Keep responses thoughtful but concise (2-4 sentences)
|
||||
""",
|
||||
chat_client=_get_chat_client(),
|
||||
client=_get_chat_client(),
|
||||
)
|
||||
|
||||
doctor = ChatAgent(
|
||||
doctor = Agent(
|
||||
name="Doctor",
|
||||
description="A doctor from Scandinavia",
|
||||
instructions="""
|
||||
@@ -204,7 +204,7 @@ Share your perspective authentically. Feel free to:
|
||||
- Use examples from healthcare and societal systems
|
||||
- Keep responses thoughtful but concise (2-4 sentences)
|
||||
""",
|
||||
chat_client=_get_chat_client(),
|
||||
client=_get_chat_client(),
|
||||
)
|
||||
|
||||
# termination_condition: stop after 10 assistant messages
|
||||
@@ -255,7 +255,7 @@ Share your perspective authentically. Feel free to:
|
||||
print(data.text, end="", flush=True)
|
||||
elif event.type == "output":
|
||||
# The output of the group chat workflow is a collection of chat messages from all participants
|
||||
outputs = cast(list[ChatMessage], event.data)
|
||||
outputs = cast(list[Message], event.data)
|
||||
print("\n" + "=" * 80)
|
||||
print("\nFinal Conversation Transcript:\n")
|
||||
for message in outputs:
|
||||
|
||||
@@ -4,9 +4,9 @@ import asyncio
|
||||
from typing import cast
|
||||
|
||||
from agent_framework import (
|
||||
Agent,
|
||||
AgentResponseUpdate,
|
||||
ChatAgent,
|
||||
ChatMessage,
|
||||
Message,
|
||||
)
|
||||
from agent_framework.azure import AzureOpenAIChatClient
|
||||
from agent_framework.orchestrations import GroupChatBuilder, GroupChatState
|
||||
@@ -33,20 +33,20 @@ def round_robin_selector(state: GroupChatState) -> str:
|
||||
|
||||
async def main() -> None:
|
||||
# Create a chat client using Azure OpenAI and Azure CLI credentials for all agents
|
||||
chat_client = AzureOpenAIChatClient(credential=AzureCliCredential())
|
||||
client = AzureOpenAIChatClient(credential=AzureCliCredential())
|
||||
|
||||
# Participant agents
|
||||
expert = ChatAgent(
|
||||
expert = Agent(
|
||||
name="PythonExpert",
|
||||
instructions=(
|
||||
"You are an expert in Python in a workgroup. "
|
||||
"Your job is to answer Python related questions and refine your answer "
|
||||
"based on feedback from all the other participants."
|
||||
),
|
||||
chat_client=chat_client,
|
||||
client=client,
|
||||
)
|
||||
|
||||
verifier = ChatAgent(
|
||||
verifier = Agent(
|
||||
name="AnswerVerifier",
|
||||
instructions=(
|
||||
"You are a programming expert in a workgroup. "
|
||||
@@ -54,10 +54,10 @@ async def main() -> None:
|
||||
"out statements that are technically true but practically dangerous."
|
||||
"If there is nothing woth pointing out, respond with 'The answer looks good to me.'"
|
||||
),
|
||||
chat_client=chat_client,
|
||||
client=client,
|
||||
)
|
||||
|
||||
clarifier = ChatAgent(
|
||||
clarifier = Agent(
|
||||
name="AnswerClarifier",
|
||||
instructions=(
|
||||
"You are an accessibility expert in a workgroup. "
|
||||
@@ -65,10 +65,10 @@ async def main() -> None:
|
||||
"out jargons or complex terms that may be difficult for a beginner to understand."
|
||||
"If there is nothing worth pointing out, respond with 'The answer looks clear to me.'"
|
||||
),
|
||||
chat_client=chat_client,
|
||||
client=client,
|
||||
)
|
||||
|
||||
skeptic = ChatAgent(
|
||||
skeptic = Agent(
|
||||
name="Skeptic",
|
||||
instructions=(
|
||||
"You are a devil's advocate in a workgroup. "
|
||||
@@ -76,7 +76,7 @@ async def main() -> None:
|
||||
"out caveats, exceptions, and alternative perspectives."
|
||||
"If there is nothing worth pointing out, respond with 'I have no further questions.'"
|
||||
),
|
||||
chat_client=chat_client,
|
||||
client=client,
|
||||
)
|
||||
|
||||
# Build the group chat workflow
|
||||
@@ -124,7 +124,7 @@ async def main() -> None:
|
||||
print(data.text, end="", flush=True)
|
||||
elif event.type == "output":
|
||||
# The output of the group chat workflow is a collection of chat messages from all participants
|
||||
outputs = cast(list[ChatMessage], event.data)
|
||||
outputs = cast(list[Message], event.data)
|
||||
print("\n" + "=" * 80)
|
||||
print("\nFinal Conversation Transcript:\n")
|
||||
for message in outputs:
|
||||
|
||||
@@ -5,9 +5,9 @@ import logging
|
||||
from typing import cast
|
||||
|
||||
from agent_framework import (
|
||||
Agent,
|
||||
AgentResponseUpdate,
|
||||
ChatAgent,
|
||||
ChatMessage,
|
||||
Message,
|
||||
resolve_agent_id,
|
||||
)
|
||||
from agent_framework.azure import AzureOpenAIChatClient
|
||||
@@ -37,10 +37,10 @@ Key Concepts:
|
||||
|
||||
|
||||
def create_agents(
|
||||
chat_client: AzureOpenAIChatClient,
|
||||
) -> tuple[ChatAgent, ChatAgent, ChatAgent]:
|
||||
client: AzureOpenAIChatClient,
|
||||
) -> tuple[Agent, Agent, Agent]:
|
||||
"""Create coordinator and specialists for autonomous iteration."""
|
||||
coordinator = chat_client.as_agent(
|
||||
coordinator = client.as_agent(
|
||||
instructions=(
|
||||
"You are a coordinator. You break down a user query into a research task and a summary task. "
|
||||
"Assign the two tasks to the appropriate specialists, one after the other."
|
||||
@@ -48,7 +48,7 @@ def create_agents(
|
||||
name="coordinator",
|
||||
)
|
||||
|
||||
research_agent = chat_client.as_agent(
|
||||
research_agent = client.as_agent(
|
||||
instructions=(
|
||||
"You are a research specialist that explores topics thoroughly using web search. "
|
||||
"When given a research task, break it down into multiple aspects and explore each one. "
|
||||
@@ -60,7 +60,7 @@ def create_agents(
|
||||
name="research_agent",
|
||||
)
|
||||
|
||||
summary_agent = chat_client.as_agent(
|
||||
summary_agent = client.as_agent(
|
||||
instructions=(
|
||||
"You summarize research findings. Provide a concise, well-organized summary. When done, return "
|
||||
"control to the coordinator."
|
||||
@@ -73,8 +73,8 @@ def create_agents(
|
||||
|
||||
async def main() -> None:
|
||||
"""Run an autonomous handoff workflow with specialist iteration enabled."""
|
||||
chat_client = AzureOpenAIChatClient(credential=AzureCliCredential())
|
||||
coordinator, research_agent, summary_agent = create_agents(chat_client)
|
||||
client = AzureOpenAIChatClient(credential=AzureCliCredential())
|
||||
coordinator, research_agent, summary_agent = create_agents(client)
|
||||
|
||||
# Build the workflow with autonomous mode
|
||||
# In autonomous mode, agents continue iterating until they invoke a handoff tool
|
||||
@@ -129,7 +129,7 @@ async def main() -> None:
|
||||
print(data.text, end="", flush=True)
|
||||
elif event.type == "output":
|
||||
# The output of the handoff workflow is a collection of chat messages from all participants
|
||||
outputs = cast(list[ChatMessage], event.data)
|
||||
outputs = cast(list[Message], event.data)
|
||||
print("\n" + "=" * 80)
|
||||
print("\nFinal Conversation Transcript:\n")
|
||||
for message in outputs:
|
||||
|
||||
@@ -4,9 +4,9 @@ import asyncio
|
||||
from typing import Annotated, cast
|
||||
|
||||
from agent_framework import (
|
||||
Agent,
|
||||
AgentResponse,
|
||||
ChatAgent,
|
||||
ChatMessage,
|
||||
Message,
|
||||
WorkflowEvent,
|
||||
WorkflowRunState,
|
||||
tool,
|
||||
@@ -54,17 +54,17 @@ def process_return(order_number: Annotated[str, "Order number to process return
|
||||
return f"Return initiated successfully for order {order_number}. You will receive return instructions via email."
|
||||
|
||||
|
||||
def create_agents(chat_client: AzureOpenAIChatClient) -> tuple[ChatAgent, ChatAgent, ChatAgent, ChatAgent]:
|
||||
def create_agents(client: AzureOpenAIChatClient) -> tuple[Agent, Agent, Agent, Agent]:
|
||||
"""Create and configure the triage and specialist agents.
|
||||
|
||||
Args:
|
||||
chat_client: The AzureOpenAIChatClient to use for creating agents.
|
||||
client: The AzureOpenAIChatClient to use for creating agents.
|
||||
|
||||
Returns:
|
||||
Tuple of (triage_agent, refund_agent, order_agent, return_agent)
|
||||
"""
|
||||
# Triage agent: Acts as the frontline dispatcher
|
||||
triage_agent = chat_client.as_agent(
|
||||
triage_agent = client.as_agent(
|
||||
instructions=(
|
||||
"You are frontline support triage. Route customer issues to the appropriate specialist agents "
|
||||
"based on the problem described."
|
||||
@@ -73,7 +73,7 @@ def create_agents(chat_client: AzureOpenAIChatClient) -> tuple[ChatAgent, ChatAg
|
||||
)
|
||||
|
||||
# Refund specialist: Handles refund requests
|
||||
refund_agent = chat_client.as_agent(
|
||||
refund_agent = client.as_agent(
|
||||
instructions="You process refund requests.",
|
||||
name="refund_agent",
|
||||
# In a real application, an agent can have multiple tools; here we keep it simple
|
||||
@@ -81,7 +81,7 @@ def create_agents(chat_client: AzureOpenAIChatClient) -> tuple[ChatAgent, ChatAg
|
||||
)
|
||||
|
||||
# Order/shipping specialist: Resolves delivery issues
|
||||
order_agent = chat_client.as_agent(
|
||||
order_agent = client.as_agent(
|
||||
instructions="You handle order and shipping inquiries.",
|
||||
name="order_agent",
|
||||
# In a real application, an agent can have multiple tools; here we keep it simple
|
||||
@@ -89,7 +89,7 @@ def create_agents(chat_client: AzureOpenAIChatClient) -> tuple[ChatAgent, ChatAg
|
||||
)
|
||||
|
||||
# Return specialist: Handles return requests
|
||||
return_agent = chat_client.as_agent(
|
||||
return_agent = client.as_agent(
|
||||
instructions="You manage product return requests.",
|
||||
name="return_agent",
|
||||
# In a real application, an agent can have multiple tools; here we keep it simple
|
||||
@@ -138,7 +138,7 @@ def _handle_events(events: list[WorkflowEvent]) -> list[WorkflowEvent[HandoffAge
|
||||
print(f"- {speaker}: {message.text}")
|
||||
elif event.type == "output":
|
||||
# The output of the handoff workflow is a collection of chat messages from all participants
|
||||
conversation = cast(list[ChatMessage], event.data)
|
||||
conversation = cast(list[Message], event.data)
|
||||
if isinstance(conversation, list):
|
||||
print("\n=== Final Conversation Snapshot ===")
|
||||
for message in conversation:
|
||||
@@ -189,10 +189,10 @@ async def main() -> None:
|
||||
replace the scripted_responses with actual user input collection.
|
||||
"""
|
||||
# Initialize the Azure OpenAI chat client
|
||||
chat_client = AzureOpenAIChatClient(credential=AzureCliCredential())
|
||||
client = AzureOpenAIChatClient(credential=AzureCliCredential())
|
||||
|
||||
# Create all agents: triage + specialists
|
||||
triage, refund, order, support = create_agents(chat_client)
|
||||
triage, refund, order, support = create_agents(client)
|
||||
|
||||
# Build the handoff workflow
|
||||
# - participants: All agents that can participate in the workflow
|
||||
|
||||
@@ -31,10 +31,10 @@ from contextlib import asynccontextmanager
|
||||
from typing import cast
|
||||
|
||||
from agent_framework import (
|
||||
Agent,
|
||||
AgentResponseUpdate,
|
||||
ChatAgent,
|
||||
ChatMessage,
|
||||
HostedCodeInterpreterTool,
|
||||
Message,
|
||||
WorkflowEvent,
|
||||
WorkflowRunState,
|
||||
)
|
||||
@@ -83,7 +83,7 @@ def _handle_events(events: list[WorkflowEvent]) -> tuple[list[WorkflowEvent[Hand
|
||||
file_ids.append(file_id)
|
||||
print(f"[Found file annotation: file_id={file_id}]")
|
||||
elif event.type == "output":
|
||||
conversation = cast(list[ChatMessage], event.data)
|
||||
conversation = cast(list[Message], event.data)
|
||||
if isinstance(conversation, list):
|
||||
print("\n=== Final Conversation Snapshot ===")
|
||||
for message in conversation:
|
||||
@@ -95,7 +95,7 @@ def _handle_events(events: list[WorkflowEvent]) -> tuple[list[WorkflowEvent[Hand
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def create_agents_v1(credential: AzureCliCredential) -> AsyncIterator[tuple[ChatAgent, ChatAgent]]:
|
||||
async def create_agents_v1(credential: AzureCliCredential) -> AsyncIterator[tuple[Agent, Agent]]:
|
||||
"""Create agents using V1 AzureAIAgentClient."""
|
||||
from agent_framework.azure import AzureAIAgentClient
|
||||
|
||||
@@ -122,7 +122,7 @@ async def create_agents_v1(credential: AzureCliCredential) -> AsyncIterator[tupl
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def create_agents_v2(credential: AzureCliCredential) -> AsyncIterator[tuple[ChatAgent, ChatAgent]]:
|
||||
async def create_agents_v2(credential: AzureCliCredential) -> AsyncIterator[tuple[Agent, Agent]]:
|
||||
"""Create agents using V2 AzureAIClient.
|
||||
|
||||
Each agent needs its own client instance because the V2 client binds
|
||||
|
||||
@@ -6,10 +6,10 @@ import logging
|
||||
from typing import cast
|
||||
|
||||
from agent_framework import (
|
||||
Agent,
|
||||
AgentResponseUpdate,
|
||||
ChatAgent,
|
||||
ChatMessage,
|
||||
HostedCodeInterpreterTool,
|
||||
Message,
|
||||
WorkflowEvent,
|
||||
)
|
||||
from agent_framework.openai import OpenAIChatClient, OpenAIResponsesClient
|
||||
@@ -24,9 +24,9 @@ Sample: Magentic Orchestration (multi-agent)
|
||||
What it does:
|
||||
- Orchestrates multiple agents using `MagenticBuilder` with streaming callbacks.
|
||||
|
||||
- ResearcherAgent (ChatAgent backed by an OpenAI chat client) for
|
||||
- ResearcherAgent (Agent backed by an OpenAI chat client) for
|
||||
finding information.
|
||||
- CoderAgent (ChatAgent backed by OpenAI Assistants with the hosted
|
||||
- CoderAgent (Agent backed by OpenAI Assistants with the hosted
|
||||
code interpreter tool) for analysis and computation.
|
||||
|
||||
The workflow is configured with:
|
||||
@@ -44,30 +44,30 @@ Prerequisites:
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
researcher_agent = ChatAgent(
|
||||
researcher_agent = Agent(
|
||||
name="ResearcherAgent",
|
||||
description="Specialist in research and information gathering",
|
||||
instructions=(
|
||||
"You are a Researcher. You find information without additional computation or quantitative analysis."
|
||||
),
|
||||
# This agent requires the gpt-4o-search-preview model to perform web searches.
|
||||
chat_client=OpenAIChatClient(model_id="gpt-4o-search-preview"),
|
||||
client=OpenAIChatClient(model_id="gpt-4o-search-preview"),
|
||||
)
|
||||
|
||||
coder_agent = ChatAgent(
|
||||
coder_agent = Agent(
|
||||
name="CoderAgent",
|
||||
description="A helpful assistant that writes and executes code to process and analyze data.",
|
||||
instructions="You solve questions using code. Please provide detailed analysis and computation process.",
|
||||
chat_client=OpenAIResponsesClient(),
|
||||
client=OpenAIResponsesClient(),
|
||||
tools=HostedCodeInterpreterTool(),
|
||||
)
|
||||
|
||||
# Create a manager agent for orchestration
|
||||
manager_agent = ChatAgent(
|
||||
manager_agent = Agent(
|
||||
name="MagenticManager",
|
||||
description="Orchestrator that coordinates the research and coding workflow",
|
||||
instructions="You coordinate a team to complete complex tasks efficiently.",
|
||||
chat_client=OpenAIChatClient(),
|
||||
client=OpenAIChatClient(),
|
||||
)
|
||||
|
||||
print("\nBuilding Magentic Workflow...")
|
||||
@@ -110,7 +110,7 @@ async def main() -> None:
|
||||
|
||||
elif event.type == "magentic_orchestrator":
|
||||
print(f"\n[Magentic Orchestrator Event] Type: {event.data.event_type.name}")
|
||||
if isinstance(event.data.content, ChatMessage):
|
||||
if isinstance(event.data.content, Message):
|
||||
print(f"Please review the plan:\n{event.data.content.text}")
|
||||
elif isinstance(event.data.content, MagenticProgressLedger):
|
||||
print(f"Please review progress ledger:\n{json.dumps(event.data.content.to_dict(), indent=2)}")
|
||||
@@ -130,7 +130,7 @@ async def main() -> None:
|
||||
|
||||
if output_event:
|
||||
# The output of the magentic workflow is a collection of chat messages from all participants
|
||||
outputs = cast(list[ChatMessage], output_event.data)
|
||||
outputs = cast(list[Message], output_event.data)
|
||||
print("\n" + "=" * 80)
|
||||
print("\nFinal Conversation Transcript:\n")
|
||||
for message in outputs:
|
||||
|
||||
@@ -6,9 +6,9 @@ from pathlib import Path
|
||||
from typing import cast
|
||||
|
||||
from agent_framework import (
|
||||
ChatAgent,
|
||||
ChatMessage,
|
||||
Agent,
|
||||
FileCheckpointStorage,
|
||||
Message,
|
||||
WorkflowCheckpoint,
|
||||
WorkflowEvent,
|
||||
WorkflowRunState,
|
||||
@@ -52,26 +52,26 @@ def build_workflow(checkpoint_storage: FileCheckpointStorage):
|
||||
|
||||
# Two vanilla ChatAgents act as participants in the orchestration. They do not need
|
||||
# extra state handling because their inputs/outputs are fully described by chat messages.
|
||||
researcher = ChatAgent(
|
||||
researcher = Agent(
|
||||
name="ResearcherAgent",
|
||||
description="Collects background facts and references for the project.",
|
||||
instructions=("You are the research lead. Gather crisp bullet points the team should know."),
|
||||
chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()),
|
||||
client=AzureOpenAIChatClient(credential=AzureCliCredential()),
|
||||
)
|
||||
|
||||
writer = ChatAgent(
|
||||
writer = Agent(
|
||||
name="WriterAgent",
|
||||
description="Synthesizes the final brief for stakeholders.",
|
||||
instructions=("You convert the research notes into a structured brief with milestones and risks."),
|
||||
chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()),
|
||||
client=AzureOpenAIChatClient(credential=AzureCliCredential()),
|
||||
)
|
||||
|
||||
# Create a manager agent for orchestration
|
||||
manager_agent = ChatAgent(
|
||||
manager_agent = Agent(
|
||||
name="MagenticManager",
|
||||
description="Orchestrator that coordinates the research and writing workflow",
|
||||
instructions="You coordinate a team to complete complex tasks efficiently.",
|
||||
chat_client=AzureOpenAIChatClient(credential=AzureCliCredential()),
|
||||
client=AzureOpenAIChatClient(credential=AzureCliCredential()),
|
||||
)
|
||||
|
||||
# The builder wires in the Magentic orchestrator, sets the plan review path, and
|
||||
@@ -167,7 +167,7 @@ async def main() -> None:
|
||||
if not result:
|
||||
print("No result data from workflow.")
|
||||
return
|
||||
output_messages = cast(list[ChatMessage], result)
|
||||
output_messages = cast(list[Message], result)
|
||||
print("\n=== Final Answer ===")
|
||||
# The output of the Magentic workflow is a list of ChatMessages with only one final message
|
||||
# generated by the orchestrator.
|
||||
@@ -234,7 +234,7 @@ async def main() -> None:
|
||||
print("No result data from post-plan resume.")
|
||||
return
|
||||
|
||||
output_messages = cast(list[ChatMessage], post_result)
|
||||
output_messages = cast(list[Message], post_result)
|
||||
print("\n=== Final Answer (post-plan resume) ===")
|
||||
# The output of the Magentic workflow is a list of ChatMessages with only one final message
|
||||
# generated by the orchestrator.
|
||||
|
||||
@@ -6,9 +6,9 @@ from collections.abc import AsyncIterable
|
||||
from typing import cast
|
||||
|
||||
from agent_framework import (
|
||||
Agent,
|
||||
AgentResponseUpdate,
|
||||
ChatAgent,
|
||||
ChatMessage,
|
||||
Message,
|
||||
WorkflowEvent,
|
||||
)
|
||||
from agent_framework.openai import OpenAIChatClient
|
||||
@@ -64,7 +64,7 @@ async def process_event_stream(stream: AsyncIterable[WorkflowEvent]) -> dict[str
|
||||
print("=" * 60)
|
||||
print("Final discussion summary:")
|
||||
# To make the type checker happy, we cast event.data to the expected type
|
||||
outputs = cast(list[ChatMessage], event.data)
|
||||
outputs = cast(list[Message], event.data)
|
||||
for msg in outputs:
|
||||
speaker = msg.author_name or msg.role
|
||||
print(f"[{speaker}]: {msg.text}")
|
||||
@@ -92,25 +92,25 @@ async def process_event_stream(stream: AsyncIterable[WorkflowEvent]) -> dict[str
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
researcher_agent = ChatAgent(
|
||||
researcher_agent = Agent(
|
||||
name="ResearcherAgent",
|
||||
description="Specialist in research and information gathering",
|
||||
instructions="You are a Researcher. You find information and gather facts.",
|
||||
chat_client=OpenAIChatClient(model_id="gpt-4o"),
|
||||
client=OpenAIChatClient(model_id="gpt-4o"),
|
||||
)
|
||||
|
||||
analyst_agent = ChatAgent(
|
||||
analyst_agent = Agent(
|
||||
name="AnalystAgent",
|
||||
description="Data analyst who processes and summarizes research findings",
|
||||
instructions="You are an Analyst. You analyze findings and create summaries.",
|
||||
chat_client=OpenAIChatClient(model_id="gpt-4o"),
|
||||
client=OpenAIChatClient(model_id="gpt-4o"),
|
||||
)
|
||||
|
||||
manager_agent = ChatAgent(
|
||||
manager_agent = Agent(
|
||||
name="MagenticManager",
|
||||
description="Orchestrator that coordinates the workflow",
|
||||
instructions="You coordinate a team to complete tasks efficiently.",
|
||||
chat_client=OpenAIChatClient(model_id="gpt-4o"),
|
||||
client=OpenAIChatClient(model_id="gpt-4o"),
|
||||
)
|
||||
|
||||
print("\nBuilding Magentic Workflow with Human Plan Review...")
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import asyncio
|
||||
from typing import cast
|
||||
|
||||
from agent_framework import ChatMessage
|
||||
from agent_framework import Message
|
||||
from agent_framework.azure import AzureOpenAIChatClient
|
||||
from agent_framework.orchestrations import SequentialBuilder
|
||||
from azure.identity import AzureCliCredential
|
||||
@@ -12,7 +12,7 @@ from azure.identity import AzureCliCredential
|
||||
Sample: Sequential workflow (agent-focused API) with shared conversation context
|
||||
|
||||
Build a high-level sequential workflow using SequentialBuilder and two domain agents.
|
||||
The shared conversation (list[ChatMessage]) flows through each participant. Each agent
|
||||
The shared conversation (list[Message]) flows through each participant. Each agent
|
||||
appends its assistant message to the context. The workflow outputs the final conversation
|
||||
list when complete.
|
||||
|
||||
@@ -30,14 +30,14 @@ Prerequisites:
|
||||
|
||||
async def main() -> None:
|
||||
# 1) Create agents
|
||||
chat_client = AzureOpenAIChatClient(credential=AzureCliCredential())
|
||||
client = AzureOpenAIChatClient(credential=AzureCliCredential())
|
||||
|
||||
writer = chat_client.as_agent(
|
||||
writer = client.as_agent(
|
||||
instructions=("You are a concise copywriter. Provide a single, punchy marketing sentence based on the prompt."),
|
||||
name="writer",
|
||||
)
|
||||
|
||||
reviewer = chat_client.as_agent(
|
||||
reviewer = client.as_agent(
|
||||
instructions=("You are a thoughtful reviewer. Give brief feedback on the previous assistant message."),
|
||||
name="reviewer",
|
||||
)
|
||||
@@ -46,10 +46,10 @@ async def main() -> None:
|
||||
workflow = SequentialBuilder(participants=[writer, reviewer]).build()
|
||||
|
||||
# 3) Run and collect outputs
|
||||
outputs: list[list[ChatMessage]] = []
|
||||
outputs: list[list[Message]] = []
|
||||
async for event in workflow.run("Write a tagline for a budget-friendly eBike.", stream=True):
|
||||
if event.type == "output":
|
||||
outputs.append(cast(list[ChatMessage], event.data))
|
||||
outputs.append(cast(list[Message], event.data))
|
||||
|
||||
if outputs:
|
||||
print("===== Final Conversation =====")
|
||||
|
||||
@@ -5,8 +5,8 @@ from typing import Any
|
||||
|
||||
from agent_framework import (
|
||||
AgentExecutorResponse,
|
||||
ChatMessage,
|
||||
Executor,
|
||||
Message,
|
||||
WorkflowContext,
|
||||
handler,
|
||||
)
|
||||
@@ -18,13 +18,13 @@ from azure.identity import AzureCliCredential
|
||||
Sample: Sequential workflow mixing agents and a custom summarizer executor
|
||||
|
||||
This demonstrates how SequentialBuilder chains participants with a shared
|
||||
conversation context (list[ChatMessage]). An agent produces content; a custom
|
||||
conversation context (list[Message]). An agent produces content; a custom
|
||||
executor appends a compact summary to the conversation. The workflow completes
|
||||
after all participants have executed in sequence, and the final output contains
|
||||
the complete conversation.
|
||||
|
||||
Custom executor contract:
|
||||
- Provide at least one @handler accepting AgentExecutorResponse and a WorkflowContext[list[ChatMessage]]
|
||||
- Provide at least one @handler accepting AgentExecutorResponse and a WorkflowContext[list[Message]]
|
||||
- Emit the updated conversation via ctx.send_message([...])
|
||||
|
||||
Prerequisites:
|
||||
@@ -36,30 +36,30 @@ class Summarizer(Executor):
|
||||
"""Simple summarizer: consumes full conversation and appends an assistant summary."""
|
||||
|
||||
@handler
|
||||
async def summarize(self, agent_response: AgentExecutorResponse, ctx: WorkflowContext[list[ChatMessage]]) -> None:
|
||||
async def summarize(self, agent_response: AgentExecutorResponse, ctx: WorkflowContext[list[Message]]) -> None:
|
||||
"""Append a summary message to a copy of the full conversation.
|
||||
|
||||
Note: A custom executor must be able to handle the message type from the prior participant, and produce
|
||||
the message type expected by the next participant. In this case, the prior participant is an agent thus
|
||||
the input is AgentExecutorResponse (an agent will be wrapped in an AgentExecutor, which produces
|
||||
`AgentExecutorResponse`). If the next participant is also an agent or this is the final participant,
|
||||
the output must be `list[ChatMessage]`.
|
||||
the output must be `list[Message]`.
|
||||
"""
|
||||
if not agent_response.full_conversation:
|
||||
await ctx.send_message([ChatMessage("assistant", ["No conversation to summarize."])])
|
||||
await ctx.send_message([Message("assistant", ["No conversation to summarize."])])
|
||||
return
|
||||
|
||||
users = sum(1 for m in agent_response.full_conversation if m.role == "user")
|
||||
assistants = sum(1 for m in agent_response.full_conversation if m.role == "assistant")
|
||||
summary = ChatMessage("assistant", [f"Summary -> users:{users} assistants:{assistants}"])
|
||||
summary = Message("assistant", [f"Summary -> users:{users} assistants:{assistants}"])
|
||||
final_conversation = list(agent_response.full_conversation) + [summary]
|
||||
await ctx.send_message(final_conversation)
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
# 1) Create a content agent
|
||||
chat_client = AzureOpenAIChatClient(credential=AzureCliCredential())
|
||||
content = chat_client.as_agent(
|
||||
client = AzureOpenAIChatClient(credential=AzureCliCredential())
|
||||
content = client.as_agent(
|
||||
instructions="Produce a concise paragraph answering the user's request.",
|
||||
name="content",
|
||||
)
|
||||
@@ -74,7 +74,7 @@ async def main() -> None:
|
||||
|
||||
if outputs:
|
||||
print("===== Final Conversation =====")
|
||||
messages: list[ChatMessage] | Any = outputs[0]
|
||||
messages: list[Message] | Any = outputs[0]
|
||||
for i, msg in enumerate(messages, start=1):
|
||||
name = msg.author_name or ("assistant" if msg.role == "assistant" else "user")
|
||||
print(f"{'-' * 60}\n{i:02d} [{name}]\n{msg.text}")
|
||||
|
||||
Reference in New Issue
Block a user