mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python: restructure: Python samples into progressive 01-05 layout (#3862)
* restructure: Python samples into progressive 01-05 layout - 01-get-started/: 6 numbered steps (hello agent → hosting) - 02-agents/: all agent concept samples (tools, middleware, providers, etc.) - 03-workflows/: ALL existing workflow samples preserved as-is - 04-hosting/: azure-functions, durabletask, a2a - 05-end-to-end/: demos, evaluation, hosted agents - Old files moved to _to_delete/ for review - Added AGENTS.md with structure documentation - autogen-migration/ and semantic-kernel-migration/ preserved at root * fix: switch to AzureOpenAI Foundry, fix CI failures - Switch all 01-get-started samples to AzureOpenAIResponsesClient with Azure AI Foundry project endpoint (AZURE_AI_PROJECT_ENDPOINT + AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME + AzureCliCredential) - Add _to_delete/ and 05-end-to-end/ to pyrightconfig.samples.json excludes - Fix test paths in packages/ that referenced old getting_started/ dirs: durabletask conftest + streaming test, azurefunctions conftest, devui conftest + capture_messages + openai_sdk_integration - Fix workflow_as_agent_human_in_the_loop.py import (sibling import) - Update hosting READMEs and tool comment paths - Replace root README.md with new structure overview - Update AGENTS.md to document Azure OpenAI Foundry as default provider * cleanup: remove _to_delete folder, copy resource files to active dirs All files in _to_delete/ were either: - Exact duplicates of files in the new structure (240 files) - Same file with only comment path updates (100 files) - One import-fix diff (workflow_as_agent_human_in_the_loop.py) - One superseded minimal_sample.py Resource files (sample.pdf, countries.json, employees.pdf, weather.json) copied to 02-agents/sample_assets/ and 02-agents/resources/ since active samples reference them. * fix: address PR review comments, centralize resources, remove root duplicates - Fix type annotation in 04_memory.py (string union -> proper types) - Fix old sample paths in observability files - Fix grammar/spelling in observability samples - Move sample_assets/ and resources/ to shared/ folder - Remove 8 duplicate observability files from 02-agents root - Update resource path references in multimodal_input and provider samples * fix: update broken links from old getting_started paths to new structure - Update relative paths in READMEs: getting_started/ → 01-get-started/, 02-agents/, 03-workflows/, 04-hosting/, 05-end-to-end/ - Fix absolute GitHub URLs in package READMEs - Fix broken link in ollama package README * fix: convert absolute GitHub URLs to relative paths for link checker Absolute URLs to python/samples/ on main branch 404 until PR merges. Converted to relative paths that linkspector can verify locally. * fix: update link for handoff sample moved to orchestrations/ * fix: update chatkit-integration README path from demos/ to 05-end-to-end/ * fix: update broken links in orchestrations README to match flat directory structure
This commit is contained in:
committed by
GitHub
Unverified
parent
69dcfe31ee
commit
a2856d3b92
@@ -0,0 +1,166 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
from dataclasses import dataclass
|
||||
|
||||
from agent_framework import (
|
||||
AgentExecutor,
|
||||
AgentExecutorRequest,
|
||||
AgentExecutorResponse,
|
||||
Executor,
|
||||
Message,
|
||||
WorkflowBuilder,
|
||||
WorkflowContext,
|
||||
WorkflowViz,
|
||||
handler,
|
||||
)
|
||||
from agent_framework.azure import AzureOpenAIResponsesClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from typing_extensions import Never
|
||||
|
||||
"""
|
||||
Sample: Concurrent (Fan-out/Fan-in) with Agents + Visualization
|
||||
|
||||
What it does:
|
||||
- Fan-out: dispatch the same prompt to multiple domain agents (research, marketing, legal).
|
||||
- Fan-in: aggregate their responses into one consolidated output.
|
||||
- Visualization: generate Mermaid and GraphViz representations via `WorkflowViz` and optionally export SVG.
|
||||
|
||||
Prerequisites:
|
||||
- AZURE_AI_PROJECT_ENDPOINT must be your Azure AI Foundry Agent Service (V2) project endpoint.
|
||||
- Azure AI/ Azure OpenAI for `AzureOpenAIResponsesClient` agents.
|
||||
- Authentication via `azure-identity` — uses `AzureCliCredential()` (run `az login`).
|
||||
- For visualization export: `pip install graphviz>=0.20.0` and install GraphViz binaries.
|
||||
"""
|
||||
|
||||
|
||||
class DispatchToExperts(Executor):
|
||||
"""Dispatches the incoming prompt to all expert agent executors (fan-out)."""
|
||||
|
||||
@handler
|
||||
async def dispatch(self, prompt: str, ctx: WorkflowContext[AgentExecutorRequest]) -> None:
|
||||
# Wrap the incoming prompt as a user message for each expert and request a response.
|
||||
initial_message = Message("user", text=prompt)
|
||||
await ctx.send_message(AgentExecutorRequest(messages=[initial_message], should_respond=True))
|
||||
|
||||
|
||||
@dataclass
|
||||
class AggregatedInsights:
|
||||
"""Structured output from the aggregator."""
|
||||
|
||||
research: str
|
||||
marketing: str
|
||||
legal: str
|
||||
|
||||
|
||||
class AggregateInsights(Executor):
|
||||
"""Aggregates expert agent responses into a single consolidated result (fan-in)."""
|
||||
|
||||
@handler
|
||||
async def aggregate(self, results: list[AgentExecutorResponse], ctx: WorkflowContext[Never, str]) -> None:
|
||||
# Map responses to text by executor id for a simple, predictable demo.
|
||||
by_id: dict[str, str] = {}
|
||||
for r in results:
|
||||
# AgentExecutorResponse.agent_response.text contains concatenated assistant text
|
||||
by_id[r.executor_id] = r.agent_response.text
|
||||
|
||||
research_text = by_id.get("researcher", "")
|
||||
marketing_text = by_id.get("marketer", "")
|
||||
legal_text = by_id.get("legal", "")
|
||||
|
||||
aggregated = AggregatedInsights(
|
||||
research=research_text,
|
||||
marketing=marketing_text,
|
||||
legal=legal_text,
|
||||
)
|
||||
|
||||
# Provide a readable, consolidated string as the final workflow result.
|
||||
consolidated = (
|
||||
"Consolidated Insights\n"
|
||||
"====================\n\n"
|
||||
f"Research Findings:\n{aggregated.research}\n\n"
|
||||
f"Marketing Angle:\n{aggregated.marketing}\n\n"
|
||||
f"Legal/Compliance Notes:\n{aggregated.legal}\n"
|
||||
)
|
||||
|
||||
await ctx.yield_output(consolidated)
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
"""Build and run the concurrent workflow with visualization."""
|
||||
|
||||
# Create agent instances
|
||||
researcher = AgentExecutor(
|
||||
AzureOpenAIResponsesClient(
|
||||
project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"],
|
||||
deployment_name=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"],
|
||||
credential=AzureCliCredential(),
|
||||
).as_agent(
|
||||
instructions=(
|
||||
"You're an expert market and product researcher. Given a prompt, provide concise, factual insights,"
|
||||
" opportunities, and risks."
|
||||
),
|
||||
name="researcher",
|
||||
)
|
||||
)
|
||||
|
||||
marketer = AgentExecutor(
|
||||
AzureOpenAIResponsesClient(
|
||||
project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"],
|
||||
deployment_name=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"],
|
||||
credential=AzureCliCredential(),
|
||||
).as_agent(
|
||||
instructions=(
|
||||
"You're a creative marketing strategist. Craft compelling value propositions and target messaging"
|
||||
" aligned to the prompt."
|
||||
),
|
||||
name="marketer",
|
||||
)
|
||||
)
|
||||
|
||||
legal = AgentExecutor(
|
||||
AzureOpenAIResponsesClient(
|
||||
project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"],
|
||||
deployment_name=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"],
|
||||
credential=AzureCliCredential(),
|
||||
).as_agent(
|
||||
instructions=(
|
||||
"You're a cautious legal/compliance reviewer. Highlight constraints, disclaimers, and policy concerns"
|
||||
" based on the prompt."
|
||||
),
|
||||
name="legal",
|
||||
)
|
||||
)
|
||||
|
||||
# Create executor instances
|
||||
dispatcher = DispatchToExperts(id="dispatcher")
|
||||
aggregator = AggregateInsights(id="aggregator")
|
||||
|
||||
# Build a simple fan-out/fan-in workflow
|
||||
workflow = (
|
||||
WorkflowBuilder(start_executor=dispatcher)
|
||||
.add_fan_out_edges(dispatcher, [researcher, marketer, legal])
|
||||
.add_fan_in_edges([researcher, marketer, legal], aggregator)
|
||||
.build()
|
||||
)
|
||||
|
||||
# Generate workflow visualization
|
||||
print("Generating workflow visualization...")
|
||||
viz = WorkflowViz(workflow)
|
||||
# Print out the mermaid string.
|
||||
print("Mermaid string: \n=======")
|
||||
print(viz.to_mermaid())
|
||||
print("=======")
|
||||
# Print out the DiGraph string with internal executors.
|
||||
print("DiGraph string: \n=======")
|
||||
print(viz.to_digraph(include_internal_executors=True))
|
||||
print("=======")
|
||||
|
||||
# Export the DiGraph visualization as SVG.
|
||||
svg_file = viz.export(format="svg")
|
||||
print(f"SVG file saved to: {svg_file}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user