Python: Restructure Python samples: progressive learning path (01→05)#3764
Python: Restructure Python samples: progressive learning path (01→05)#3764eavanvalkenburg wants to merge 7 commits intomicrosoft:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Restructures the Python samples into a 5-part progressive learning path and adds/updates sample content + maintainers’ guidance to support docs integration and long-term upkeep.
Changes:
- Introduces new
01-get-started→05-end-to-endsample layout with section READMEs. - Adds concept-focused samples for agents, tools, middleware, conversations, and workflows.
- Moves legacy samples into
python/samples/_to_delete/for review (not deleted) and addspython/samples/AGENTS.mdconventions/mapping.
Reviewed changes
Copilot reviewed 76 out of 607 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| python/samples/_to_delete/getting_started/agents/a2a/README.md | Added legacy A2A sample README retained under _to_delete/. |
| python/samples/_to_delete/demos/workflow_evaluation/run_evaluation.py | Added legacy workflow evaluation runner retained under _to_delete/. |
| python/samples/_to_delete/demos/workflow_evaluation/create_workflow.py | Added legacy multi-agent workflow creation + tracking code retained under _to_delete/. |
| python/samples/_to_delete/demos/workflow_evaluation/README.md | Added legacy workflow evaluation documentation retained under _to_delete/. |
| python/samples/_to_delete/demos/workflow_evaluation/.env.example | Added example env file for legacy workflow evaluation demo. |
| python/samples/_to_delete/demos/m365-agent/m365_agent_demo/app.py | Added legacy M365 agent demo app retained under _to_delete/. |
| python/samples/_to_delete/demos/m365-agent/README.md | Added legacy M365 demo documentation retained under _to_delete/. |
| python/samples/_to_delete/demos/m365-agent/.env.example | Added example env file for legacy M365 demo. |
| python/samples/_to_delete/demos/hosted_agents/agents_in_workflow/requirements.txt | Added legacy hosted agent requirements retained under _to_delete/. |
| python/samples/_to_delete/demos/hosted_agents/agents_in_workflow/main.py | Added legacy hosted agent workflow demo retained under _to_delete/. |
| python/samples/_to_delete/demos/hosted_agents/agents_in_workflow/agent.yaml | Added legacy hosted agent manifest retained under _to_delete/. |
| python/samples/_to_delete/demos/hosted_agents/agents_in_workflow/Dockerfile | Added legacy hosted agent Dockerfile retained under _to_delete/. |
| python/samples/_to_delete/demos/hosted_agents/agent_with_text_search_rag/requirements.txt | Added legacy hosted agent requirements retained under _to_delete/. |
| python/samples/_to_delete/demos/hosted_agents/agent_with_text_search_rag/main.py | Added legacy text-search RAG hosted agent demo retained under _to_delete/. |
| python/samples/_to_delete/demos/hosted_agents/agent_with_text_search_rag/agent.yaml | Added legacy hosted agent manifest retained under _to_delete/. |
| python/samples/_to_delete/demos/hosted_agents/agent_with_text_search_rag/Dockerfile | Added legacy hosted agent Dockerfile retained under _to_delete/. |
| python/samples/_to_delete/demos/hosted_agents/agent_with_hosted_mcp/requirements.txt | Added legacy hosted agent requirements retained under _to_delete/. |
| python/samples/_to_delete/demos/hosted_agents/agent_with_hosted_mcp/main.py | Added legacy hosted MCP agent demo retained under _to_delete/. |
| python/samples/_to_delete/demos/hosted_agents/agent_with_hosted_mcp/agent.yaml | Added legacy hosted agent manifest retained under _to_delete/. |
| python/samples/_to_delete/demos/hosted_agents/agent_with_hosted_mcp/Dockerfile | Added legacy hosted agent Dockerfile retained under _to_delete/. |
| python/samples/AGENTS.md | New maintainer guide documenting sample structure, conventions, and docs mapping. |
| python/samples/05-end-to-end/README.md | New section README for end-to-end samples. |
| python/samples/04-hosting/a2a/agent_with_a2a.py | New A2A protocol hosting sample with snippet tags. |
| python/samples/04-hosting/README.md | New section README for hosting/protocol samples. |
| python/samples/03-workflows/visualization/visualization.py | New workflow visualization pattern sample. |
| python/samples/03-workflows/state-management/state_management.py | New workflow state management pattern sample. |
| python/samples/03-workflows/sequential/sequential.py | New sequential workflow pattern sample. |
| python/samples/03-workflows/human-in-the-loop/human_in_the_loop.py | New human-in-the-loop workflow pattern sample. |
| python/samples/03-workflows/declarative/declarative.py | New declarative workflow (YAML) sample. |
| python/samples/03-workflows/concurrent/concurrent.py | New concurrent (fan-out/fan-in) workflow pattern sample. |
| python/samples/03-workflows/checkpoints/checkpoints.py | New checkpoint/resume workflow sample. |
| python/samples/03-workflows/branching/branching.py | New branching/conditional routing workflow sample. |
| python/samples/03-workflows/agents-in-workflows/agents_in_workflows.py | New sample using agents directly as workflow steps. |
| python/samples/03-workflows/README.md | New section README indexing workflow patterns. |
| python/samples/02-agents/typed_options.py | New typed options deep-dive sample. |
| python/samples/02-agents/tools/web_search.py | New web search tool sample. |
| python/samples/02-agents/tools/tool_approval.py | New tool approval (HITL) sample. |
| python/samples/02-agents/tools/local_mcp_tools.py | New local MCP tool sample (streamable HTTP transport). |
| python/samples/02-agents/tools/hosted_mcp_tools.py | New hosted MCP tool sample (provider-managed). |
| python/samples/02-agents/tools/function_tools.py | New advanced function tools sample (kwargs injection). |
| python/samples/02-agents/tools/file_search.py | New hosted file search tool sample (vector stores). |
| python/samples/02-agents/tools/code_interpreter.py | New hosted code interpreter tool sample. |
| python/samples/02-agents/structured_output.py | New structured output sample using Pydantic. |
| python/samples/02-agents/response_stream.py | New deep-dive sample explaining ResponseStream. |
| python/samples/02-agents/rag.py | New RAG sample using a ContextProvider. |
| python/samples/02-agents/providers/openai_provider.py | New OpenAI provider “hello world” sample. |
| python/samples/02-agents/providers/ollama_provider.py | New Ollama provider “hello world” sample. |
| python/samples/02-agents/providers/github_copilot.py | New GitHub Copilot provider “hello world” sample. |
| python/samples/02-agents/providers/custom_provider.py | New custom provider (BaseAgent) “hello world” sample. |
| python/samples/02-agents/providers/copilot_studio.py | New Copilot Studio provider “hello world” sample. |
| python/samples/02-agents/providers/azure_openai.py | New Azure OpenAI provider “hello world” sample. |
| python/samples/02-agents/providers/azure_ai_foundry.py | New Azure AI Foundry provider “hello world” sample. |
| python/samples/02-agents/providers/anthropic_provider.py | New Anthropic provider “hello world” sample. |
| python/samples/02-agents/observability.py | New OpenTelemetry observability/tracing sample. |
| python/samples/02-agents/middleware/thread_tracking.py | New middleware sample demonstrating thread behavior visibility. |
| python/samples/02-agents/middleware/termination.py | New middleware termination sample. |
| python/samples/02-agents/middleware/shared_state.py | New middleware sample demonstrating shared state patterns. |
| python/samples/02-agents/middleware/runtime_context.py | New middleware sample demonstrating runtime context delegation patterns. |
| python/samples/02-agents/middleware/override_result.py | New middleware sample overriding results (streaming + non-streaming). |
| python/samples/02-agents/middleware/exception_handling.py | New middleware sample for centralized exception handling. |
| python/samples/02-agents/middleware/defining_middleware.py | New middleware sample showing class/function/decorator forms. |
| python/samples/02-agents/middleware/chat_middleware.py | New chat middleware sample (observe/modify/override). |
| python/samples/02-agents/middleware/agent_vs_run_middleware.py | New sample contrasting agent-level vs run-level middleware. |
| python/samples/02-agents/declarative_agents.py | New sample for declarative agent creation from YAML. |
| python/samples/02-agents/conversations/suspend_resume.py | New sample for suspend/resume via thread serialize/deserialize. |
| python/samples/02-agents/conversations/redis_storage.py | New sample for Redis-backed conversation persistence. |
| python/samples/02-agents/conversations/persistent_conversation.py | New sample implementing a custom message store. |
| python/samples/02-agents/README.md | New section README indexing advanced single-agent samples. |
| python/samples/01-get-started/README.md | New section README for progressive “get started” steps. |
| python/samples/01-get-started/06_host_your_agent.py | New step 6 sample: connect/invoke via A2A protocol. |
| python/samples/01-get-started/05_first_workflow.py | New step 5 sample: build a simple sequential workflow. |
| python/samples/01-get-started/04_memory.py | New step 4 sample: context provider “memory” injection. |
| python/samples/01-get-started/03_multi_turn.py | New step 3 sample: multi-turn using AgentThread. |
| python/samples/01-get-started/02_add_tools.py | New step 2 sample: define and use tools. |
| python/samples/01-get-started/01_hello_agent.py | New step 1 sample: minimal agent run + streaming. |
| async for event in events: | ||
| if event.type == "output": | ||
| workflow_output = event.data | ||
| # Handle Unicode characters that may not be displayable in Windows console | ||
| try: | ||
| print(f"\nWorkflow Output: {event.data}\n") | ||
| except UnicodeEncodeError: | ||
| output_str = str(event.data).encode("ascii", "replace").decode("ascii") | ||
| print(f"\nWorkflow Output: {output_str}\n") | ||
|
|
||
| elif event.type == "output" and isinstance(event.data, AgentResponseUpdate): | ||
| _track_agent_ids(event, event.executor_id, response_ids, conversation_ids) |
There was a problem hiding this comment.
The elif event.type == \"output\" and isinstance(event.data, AgentResponseUpdate) branch is unreachable because the preceding if event.type == \"output\" matches first. As written, _track_agent_ids(...) will never run, so response/conversation IDs won’t be tracked. Reorder the checks (handle AgentResponseUpdate first) or use the correct event type for updates vs final outputs so both tracking and workflow output capture work.
|
|
||
| def condition(message: Any) -> bool: | ||
| if not isinstance(message, DetectionResult): | ||
| return True |
There was a problem hiding this comment.
For non-DetectionResult messages, this condition currently returns True, which can cause unintended edge traversal (including multiple conditional edges evaluating to true for unexpected message types). Return False when the message type doesn’t match so routing only occurs when DetectionResult is present.
| return True | |
| return False |
| "Request Timeout: The data service is taking longer than expected to respond.", | ||
| "Respond with message - 'Sorry for the inconvenience, please try again later.'", |
There was a problem hiding this comment.
In function middleware, context.result should be set to the tool’s expected return type (here the tool returns str). Assigning a tuple of strings risks serialization/type errors downstream. Set context.result to a single string message (or whatever return type the tool contract requires).
| "Request Timeout: The data service is taking longer than expected to respond.", | |
| "Respond with message - 'Sorry for the inconvenience, please try again later.'", | |
| "Request Timeout: The data service is taking longer than expected to respond. " | |
| "Sorry for the inconvenience, please try again later." |
| agent = client.as_agent( | ||
| name="PersonalAssistant", | ||
| instructions="You are a helpful personal assistant. Be concise.", | ||
| context_providers=preferences, |
There was a problem hiding this comment.
This uses context_providers=preferences, but other samples consistently use context_provider=... for a single provider (e.g., rag.py). If the API expects context_provider (singular), this will raise a TypeError at runtime. Use the correct keyword for a single provider (or pass a list if the API truly supports multiple providers).
| context_providers=preferences, | |
| context_provider=preferences, |
| last_guess = original_request.prompt.split(": ")[1].split(".")[0] | ||
| feedback_text = ( | ||
| f"Feedback: {reply}. Your last guess was {last_guess}. " | ||
| f"Use this feedback to adjust and make your next guess (1-10)." | ||
| ) | ||
| user_msg = ChatMessage("user", text=feedback_text) | ||
| await ctx.send_message(AgentExecutorRequest(messages=[user_msg], should_respond=True)) |
There was a problem hiding this comment.
Extracting last_guess by parsing the human prompt text is brittle (any prompt wording change breaks the split logic). Consider carrying last_guess as a dedicated field on HumanFeedbackRequest (e.g., last_guess: int) so the response handler can reuse it without string parsing.
| Step 1: Your First Agent | ||
|
|
||
| The simplest possible agent — send a message, get a response. | ||
| Uses Azure OpenAI Responses as the default provider. |
There was a problem hiding this comment.
This docstring says the sample uses 'Azure OpenAI Responses' by default, but the code constructs OpenAIResponsesClient(...), which is the OpenAI provider client. Update the wording to match the actual default provider used in this sample (and optionally clarify how to switch to Azure OpenAI).
| Uses Azure OpenAI Responses as the default provider. | |
| Uses OpenAI Responses as the default provider (see below for Azure OpenAI options). |
| agent = ChatAgent( | ||
| chat_client=OpenAIResponsesClient(), | ||
| instructions="You are a helpful assistant that can search the web for current information.", |
There was a problem hiding this comment.
python/samples/AGENTS.md states that canonical samples should use explicit env var wiring (pass api_key= / model_id=) rather than implicit detection. Here OpenAIResponsesClient() is constructed with no explicit parameters. To keep samples consistent with the stated convention, pass api_key=os.environ.get(\"OPENAI_API_KEY\") and model_id=os.environ.get(\"OPENAI_RESPONSES_MODEL_ID\", ...) (and import os).
| without exposing them to the model. | ||
|
|
||
| For more on tools: | ||
| - Basic tools: ../01-get-started/02_add_tools.py |
There was a problem hiding this comment.
This relative path is incorrect from python/samples/02-agents/tools/ (it would resolve under 02-agents/01-get-started/...). Update it to the correct relative path (e.g. ../../01-get-started/02_add_tools.py) to avoid broken references.
| - Basic tools: ../01-get-started/02_add_tools.py | |
| - Basic tools: ../../01-get-started/02_add_tools.py |
|
|
||
| Environment variables: | ||
| - OPENAI_API_KEY: Your OpenAI API key | ||
| - OPENAI_RESPONSES_MODEL_ID: Model to use (default: gpt-4.1-mini) |
There was a problem hiding this comment.
This claims the default model is gpt-4.1-mini, but python/samples/AGENTS.md shows a default of gpt-4o. Align the documented default model across samples/docs (or explicitly state what OpenAIResponsesClient defaults to) to reduce confusion.
| - OPENAI_RESPONSES_MODEL_ID: Model to use (default: gpt-4.1-mini) | |
| - OPENAI_RESPONSES_MODEL_ID: Model to use (default: gpt-4o) |
…o _to_delete New structure: - 01-get-started/ (6 progressive samples) - 02-agents/ (tools, middleware, conversations, providers) - 03-workflows/ (9 workflow patterns) - 04-hosting/ (a2a, azure-functions, durable-tasks) - 05-end-to-end/ (full apps, evaluation) - _to_delete/ (old samples for reviewer reference) Old files preserved in _to_delete/ for review before deletion. autogen-migration/ and semantic-kernel-migration/ kept as-is.
- 01_hello_agent.py: explicit api_key and model_id from os.environ - 02_add_tools.py: same pattern - 03_multi_turn.py: same pattern - Added env var documentation in docstrings
- Rename next → call_next in all 9 middleware samples (upstream breaking change) - Add function_tool_explicit_schema.py to 02-agents/tools/ (new upstream sample) - Add agents_with_declaration_only_tools.py to 03-workflows/human-in-the-loop/ (new upstream sample)
7e02f41 to
525f2fe
Compare
…tools.py - Added Pattern 2: explicit schema via Pydantic model and JSON dict - Added Pattern 3: declaration-only tool (func=None) with FunctionTool - Removed standalone function_tool_explicit_schema.py (merged in) - Declaration-only full workflow demo stays in 03-workflows/human-in-the-loop/
TaoChenOSU
left a comment
There was a problem hiding this comment.
A bunch of workflow samples are moved to _to_delete. Why is that?
|
We're going to work on workflows samples this week. I'd prefer if the current samples are left alone so we can figure out how we'd like to proceed. |
|
Closing to redo with updated structure — workflows will be handled separately. |
Summary
Restructures the Python samples into a clear progressive learning path with 5 sections:
Key design decisions
.pydemonstrates a single topic# <name>/# </name>tags for docs:::codereferencesapi_key=os.environ.get("OPENAI_API_KEY")instead of implicit detectionOpenAIResponsesClientWhat changed
_to_delete/for reviewer inspection (not deleted)autogen-migration/andsemantic-kernel-migration/preserved at rootSAMPLE_GUIDELINES.mdTODOs addressed by new structureRelated PRs
restructure/dotnet-samples)semantic-kernel-prrepo)