From d5b2fdfc7667f0ff68191f9540a5a1a39dcfb92b Mon Sep 17 00:00:00 2001 From: eavanvalkenburg Date: Wed, 7 Oct 2026 08:55:47 +0200 Subject: [PATCH 01/12] Python: Simplify getting-started samples --- .../samples/01-get-started/01_hello_agent.py | 40 +++---------------- python/samples/01-get-started/02_add_tools.py | 8 ---- .../samples/01-get-started/03_multi_turn.py | 4 -- python/samples/01-get-started/04_memory.py | 8 ---- .../05_functional_workflow_with_agents.py | 6 --- .../06_functional_workflow_basics.py | 6 --- .../01-get-started/07_first_graph_workflow.py | 6 --- 7 files changed, 6 insertions(+), 72 deletions(-) diff --git a/python/samples/01-get-started/01_hello_agent.py b/python/samples/01-get-started/01_hello_agent.py index 8f85af11f52..be7897e5c26 100644 --- a/python/samples/01-get-started/01_hello_agent.py +++ b/python/samples/01-get-started/01_hello_agent.py @@ -6,45 +6,17 @@ from agent_framework.foundry import FoundryChatClient from azure.identity import AzureCliCredential -""" -Hello Agent — Simplest possible agent - -This sample creates a minimal agent using FoundryChatClient via an -Microsoft Foundry project endpoint, and runs it in both non-streaming and streaming modes. - -There are XML tags in all of the get started samples, those are used to display the same code in the docs repo. -""" - async def main() -> None: - # - client = FoundryChatClient( - project_endpoint="https://your-account.services.ai.azure.com/api/projects/your-project", - model="gpt-4o", - credential=AzureCliCredential(), - ) - agent = Agent( - client=client, - name="HelloAgent", + client=FoundryChatClient( + project_endpoint="https://your-account.services.ai.azure.com/api/projects/your-project", + model="gpt-6-luna", + credential=AzureCliCredential(), + ), instructions="You are a friendly assistant. Keep your answers brief.", ) - # - - # - # Non-streaming: get the complete response at once - result = await agent.run("What is the capital of France?") - print(f"Agent: {result}") - # - - # - # Streaming: receive tokens as they are generated - print("Agent (streaming): ", end="", flush=True) - async for chunk in agent.run("Tell me a one-sentence fun fact.", stream=True): - if chunk.text: - print(chunk.text, end="", flush=True) - print() - # + print(await agent.run("What is the largest city of France?")) if __name__ == "__main__": diff --git a/python/samples/01-get-started/02_add_tools.py b/python/samples/01-get-started/02_add_tools.py index 37eca46ed59..102d8de0621 100644 --- a/python/samples/01-get-started/02_add_tools.py +++ b/python/samples/01-get-started/02_add_tools.py @@ -17,7 +17,6 @@ """ -# # NOTE: approval_mode="never_require" is for sample brevity. # Use "always_require" in production for user confirmation before tool execution. # For tools processing untrusted external data or performing privileged operations, @@ -34,9 +33,6 @@ def get_weather( return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C." -# - - async def main() -> None: client = FoundryChatClient( project_endpoint="https://your-account.services.ai.azure.com/api/projects/your-project", @@ -44,19 +40,15 @@ async def main() -> None: credential=AzureCliCredential(), ) - # agent = Agent( client=client, name="WeatherAgent", instructions="You are a helpful weather agent. Use the get_weather tool to answer questions.", tools=[get_weather], ) - # - # result = await agent.run("What's the weather like in Seattle?") print(f"Agent: {result}") - # if __name__ == "__main__": diff --git a/python/samples/01-get-started/03_multi_turn.py b/python/samples/01-get-started/03_multi_turn.py index f1ab3123d0a..1a2627c952e 100644 --- a/python/samples/01-get-started/03_multi_turn.py +++ b/python/samples/01-get-started/03_multi_turn.py @@ -15,7 +15,6 @@ async def main() -> None: - # client = FoundryChatClient( project_endpoint="https://your-account.services.ai.azure.com/api/projects/your-project", model="gpt-4o", @@ -27,9 +26,7 @@ async def main() -> None: name="ConversationAgent", instructions="You are a friendly assistant. Keep your answers brief.", ) - # - # # Create a session to maintain conversation history session = agent.create_session() @@ -40,7 +37,6 @@ async def main() -> None: # Second turn — the agent should remember the user's name and hobby result = await agent.run("What do you remember about me?", session=session) print(f"Agent: {result}") - # if __name__ == "__main__": diff --git a/python/samples/01-get-started/04_memory.py b/python/samples/01-get-started/04_memory.py index 92f38d5e5e5..a7ac7ed42a0 100644 --- a/python/samples/01-get-started/04_memory.py +++ b/python/samples/01-get-started/04_memory.py @@ -16,7 +16,6 @@ """ -# class UserMemoryProvider(ContextProvider): """A context provider that remembers user info in session state.""" @@ -61,11 +60,7 @@ async def after_run( state["user_name"] = text.lower().split("my name is")[-1].strip().split()[0].capitalize() -# - - async def main() -> None: - # client = FoundryChatClient( project_endpoint="https://your-account.services.ai.azure.com/api/projects/your-project", model="gpt-4o", @@ -78,9 +73,7 @@ async def main() -> None: instructions="You are a friendly assistant.", context_providers=[UserMemoryProvider()], ) - # - # session = agent.create_session() # The provider doesn't know the user yet — it will ask for a name @@ -98,7 +91,6 @@ async def main() -> None: # Inspect session state to see what the provider stored provider_state = session.state.get("user_memory", {}) print(f"[Session State] Stored user name: {provider_state.get('user_name')}") - # if __name__ == "__main__": diff --git a/python/samples/01-get-started/05_functional_workflow_with_agents.py b/python/samples/01-get-started/05_functional_workflow_with_agents.py index e77c1bc7431..e955d9e9f96 100644 --- a/python/samples/01-get-started/05_functional_workflow_with_agents.py +++ b/python/samples/01-get-started/05_functional_workflow_with_agents.py @@ -17,7 +17,6 @@ # Load environment variables from .env file (e.g., FOUNDRY_PROJECT_ENDPOINT, FOUNDRY_MODEL) load_dotenv() -# client = FoundryChatClient(credential=AzureCliCredential()) writer = Agent( @@ -31,10 +30,8 @@ instructions="Review the given poem in one sentence. Is it good?", client=client, ) -# -# @workflow async def poem_workflow(topic: str) -> str: """Write a poem, then review it.""" @@ -43,9 +40,6 @@ async def poem_workflow(topic: str) -> str: return f"Poem:\n{poem}\n\nReview: {review}" -# - - async def main() -> None: workflow_instance = poem_workflow.build() result = await workflow_instance.run("a cat learning to code") diff --git a/python/samples/01-get-started/06_functional_workflow_basics.py b/python/samples/01-get-started/06_functional_workflow_basics.py index ec6fd79f538..0374a4370cb 100644 --- a/python/samples/01-get-started/06_functional_workflow_basics.py +++ b/python/samples/01-get-started/06_functional_workflow_basics.py @@ -30,7 +30,6 @@ async def reverse_text(text: str) -> str: return text[::-1] -# @workflow async def text_workflow(text: str) -> str: """Uppercase the text, then reverse it.""" @@ -38,16 +37,11 @@ async def text_workflow(text: str) -> str: return await reverse_text(upper) -# - - async def main() -> None: - # workflow_instance = text_workflow.build() result = await workflow_instance.run("hello world") print(f"Output: {result.get_outputs()}") print(f"Final state: {result.get_final_state()}") - # """ Expected output: diff --git a/python/samples/01-get-started/07_first_graph_workflow.py b/python/samples/01-get-started/07_first_graph_workflow.py index 2a84fabfab2..4c8b29e3a78 100644 --- a/python/samples/01-get-started/07_first_graph_workflow.py +++ b/python/samples/01-get-started/07_first_graph_workflow.py @@ -25,7 +25,6 @@ """ -# # Step 1: A class-based executor that converts text to uppercase class UpperCase(Executor): def __init__(self, id: str): @@ -50,17 +49,12 @@ def create_workflow(): return WorkflowBuilder(start_executor=upper).add_edge(upper, reverse_text).build() -# - - async def main() -> None: - # workflow = create_workflow() events = await workflow.run("hello world") print(f"Output: {events.get_outputs()}") print(f"Final state: {events.get_final_state()}") - # """ Expected output: From 6d6e3dd6bd56fe7ffeac9a3075943d17c04edb20 Mon Sep 17 00:00:00 2001 From: eavanvalkenburg Date: Wed, 7 Oct 2026 08:59:08 +0200 Subject: [PATCH 02/12] Python: Minimize remaining get-started samples --- python/samples/01-get-started/02_add_tools.py | 44 +++-------- .../samples/01-get-started/03_multi_turn.py | 31 ++------ python/samples/01-get-started/04_memory.py | 74 ++++++------------- .../05_functional_workflow_with_agents.py | 25 +++---- .../06_functional_workflow_basics.py | 32 +------- .../01-get-started/07_first_graph_workflow.py | 55 ++------------ 6 files changed, 61 insertions(+), 200 deletions(-) diff --git a/python/samples/01-get-started/02_add_tools.py b/python/samples/01-get-started/02_add_tools.py index 102d8de0621..b4233153e54 100644 --- a/python/samples/01-get-started/02_add_tools.py +++ b/python/samples/01-get-started/02_add_tools.py @@ -1,54 +1,32 @@ # Copyright (c) Microsoft. All rights reserved. import asyncio -from random import randint -from typing import Annotated from agent_framework import Agent, tool from agent_framework.foundry import FoundryChatClient from azure.identity import AzureCliCredential -from pydantic import Field -""" -Add Tools — Give your agent a function tool +"""Give an agent a function tool.""" -This sample shows how to define a function tool with the @tool decorator -and wire it into an agent so the model can call it. -""" - -# NOTE: approval_mode="never_require" is for sample brevity. -# Use "always_require" in production for user confirmation before tool execution. -# For tools processing untrusted external data or performing privileged operations, -# review the Agent Safety guidance at -# https://learn.microsoft.com/en-us/agent-framework/concepts/agents/safety -# and see samples/02-agents/security/email_security_example.py for SecureAgentConfig -# and quarantined_llm defense patterns. +# This read-only sample tool can run without approval. @tool(approval_mode="never_require") -def get_weather( - location: Annotated[str, Field(description="The location to get the weather for.")], -) -> str: +def get_weather(location: str) -> str: """Get the weather for a given location.""" - conditions = ["sunny", "cloudy", "rainy", "stormy"] - return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C." + return f"The weather in {location} is sunny." async def main() -> None: - client = FoundryChatClient( - project_endpoint="https://your-account.services.ai.azure.com/api/projects/your-project", - model="gpt-4o", - credential=AzureCliCredential(), - ) - agent = Agent( - client=client, - name="WeatherAgent", - instructions="You are a helpful weather agent. Use the get_weather tool to answer questions.", + client=FoundryChatClient( + project_endpoint="https://your-account.services.ai.azure.com/api/projects/your-project", + model="gpt-4o", + credential=AzureCliCredential(), + ), + instructions="Use the weather tool to answer questions.", tools=[get_weather], ) - - result = await agent.run("What's the weather like in Seattle?") - print(f"Agent: {result}") + print(await agent.run("What's the weather like in Seattle?")) if __name__ == "__main__": diff --git a/python/samples/01-get-started/03_multi_turn.py b/python/samples/01-get-started/03_multi_turn.py index 1a2627c952e..beea611397d 100644 --- a/python/samples/01-get-started/03_multi_turn.py +++ b/python/samples/01-get-started/03_multi_turn.py @@ -6,37 +6,22 @@ from agent_framework.foundry import FoundryChatClient from azure.identity import AzureCliCredential -""" -Multi-Turn Conversations — Use AgentSession to maintain context - -This sample shows how to keep conversation history across multiple calls -by reusing the same session object. -""" +"""Reuse one agent session across multiple turns.""" async def main() -> None: - client = FoundryChatClient( - project_endpoint="https://your-account.services.ai.azure.com/api/projects/your-project", - model="gpt-4o", - credential=AzureCliCredential(), - ) - agent = Agent( - client=client, - name="ConversationAgent", + client=FoundryChatClient( + project_endpoint="https://your-account.services.ai.azure.com/api/projects/your-project", + model="gpt-4o", + credential=AzureCliCredential(), + ), instructions="You are a friendly assistant. Keep your answers brief.", ) - - # Create a session to maintain conversation history session = agent.create_session() - # First turn - result = await agent.run("My name is Alice and I love hiking.", session=session) - print(f"Agent: {result}\n") - - # Second turn — the agent should remember the user's name and hobby - result = await agent.run("What do you remember about me?", session=session) - print(f"Agent: {result}") + print(await agent.run("My name is Alice and I love hiking.", session=session)) + print(await agent.run("What do you remember about me?", session=session)) if __name__ == "__main__": diff --git a/python/samples/01-get-started/04_memory.py b/python/samples/01-get-started/04_memory.py index a7ac7ed42a0..69b88587ed3 100644 --- a/python/samples/01-get-started/04_memory.py +++ b/python/samples/01-get-started/04_memory.py @@ -7,22 +7,12 @@ from agent_framework.foundry import FoundryChatClient from azure.identity import AzureCliCredential -""" -Agent Memory with Context Providers and Session State - -Context providers inject dynamic context into each agent call. This sample -shows a provider that stores the user's name in session state and personalizes -responses — the name persists across turns via the session. -""" +"""Persist a user's name with a context provider and session state.""" class UserMemoryProvider(ContextProvider): - """A context provider that remembers user info in session state.""" - - DEFAULT_SOURCE_ID = "user_memory" - - def __init__(self): - super().__init__(self.DEFAULT_SOURCE_ID) + def __init__(self) -> None: + super().__init__("user_memory") async def before_run( self, @@ -32,18 +22,11 @@ async def before_run( context: SessionContext, state: dict[str, Any], ) -> None: - """Inject personalization instructions based on stored user info.""" user_name = state.get("user_name") - if user_name: - context.extend_instructions( - self.source_id, - f"The user's name is {user_name}. Always address them by name.", - ) - else: - context.extend_instructions( - self.source_id, - "You don't know the user's name yet. Ask for it politely.", - ) + instructions = ( + f"The user's name is {user_name}. Address them by name." if user_name else "Ask for the user's name." + ) + context.extend_instructions(self.source_id, instructions) async def after_run( self, @@ -53,44 +36,29 @@ async def after_run( context: SessionContext, state: dict[str, Any], ) -> None: - """Extract and store user info in session state after each call.""" - for msg in context.input_messages: - text = msg.text if hasattr(msg, "text") else "" - if isinstance(text, str) and "my name is" in text.lower(): - state["user_name"] = text.lower().split("my name is")[-1].strip().split()[0].capitalize() + for message in context.input_messages: + text = message.text + if not isinstance(text, str): + continue + _, marker, name = text.lower().partition("my name is") + if marker and name.strip(): + state["user_name"] = name.split()[0].capitalize() async def main() -> None: - client = FoundryChatClient( - project_endpoint="https://your-account.services.ai.azure.com/api/projects/your-project", - model="gpt-4o", - credential=AzureCliCredential(), - ) - agent = Agent( - client=client, - name="MemoryAgent", + client=FoundryChatClient( + project_endpoint="https://your-account.services.ai.azure.com/api/projects/your-project", + model="gpt-4o", + credential=AzureCliCredential(), + ), instructions="You are a friendly assistant.", context_providers=[UserMemoryProvider()], ) - session = agent.create_session() - # The provider doesn't know the user yet — it will ask for a name - result = await agent.run("Hello! What's the square root of 9?", session=session) - print(f"Agent: {result}\n") - - # Now provide the name — the provider stores it in session state - result = await agent.run("My name is Alice", session=session) - print(f"Agent: {result}\n") - - # Subsequent calls are personalized — name persists via session state - result = await agent.run("What is 2 + 2?", session=session) - print(f"Agent: {result}\n") - - # Inspect session state to see what the provider stored - provider_state = session.state.get("user_memory", {}) - print(f"[Session State] Stored user name: {provider_state.get('user_name')}") + print(await agent.run("My name is Alice", session=session)) + print(await agent.run("What is 2 + 2?", session=session)) if __name__ == "__main__": diff --git a/python/samples/01-get-started/05_functional_workflow_with_agents.py b/python/samples/01-get-started/05_functional_workflow_with_agents.py index e955d9e9f96..5545207063b 100644 --- a/python/samples/01-get-started/05_functional_workflow_with_agents.py +++ b/python/samples/01-get-started/05_functional_workflow_with_agents.py @@ -1,40 +1,33 @@ # Copyright (c) Microsoft. All rights reserved. -""" -Functional Workflow with Agents — Call agents inside @workflow - -This sample shows how to call agents inside a functional workflow. -Agent calls are just regular async function calls — no special wrappers needed. -""" - import asyncio from agent_framework import Agent, workflow from agent_framework.foundry import FoundryChatClient from azure.identity import AzureCliCredential -from dotenv import load_dotenv -# Load environment variables from .env file (e.g., FOUNDRY_PROJECT_ENDPOINT, FOUNDRY_MODEL) -load_dotenv() +"""Call two agents from a functional workflow.""" -client = FoundryChatClient(credential=AzureCliCredential()) + +client = FoundryChatClient( + project_endpoint="https://your-account.services.ai.azure.com/api/projects/your-project", + model="gpt-4o", + credential=AzureCliCredential(), +) writer = Agent( - name="WriterAgent", - instructions="Write a short poem (4 lines max) about the given topic.", client=client, + instructions="Write a four-line poem about the given topic.", ) reviewer = Agent( - name="ReviewerAgent", - instructions="Review the given poem in one sentence. Is it good?", client=client, + instructions="Review the poem in one sentence.", ) @workflow async def poem_workflow(topic: str) -> str: - """Write a poem, then review it.""" poem = (await writer.run(f"Write a poem about: {topic}")).text review = (await reviewer.run(f"Review this poem: {poem}")).text return f"Poem:\n{poem}\n\nReview: {review}" diff --git a/python/samples/01-get-started/06_functional_workflow_basics.py b/python/samples/01-get-started/06_functional_workflow_basics.py index 0374a4370cb..de1a6fcf8a6 100644 --- a/python/samples/01-get-started/06_functional_workflow_basics.py +++ b/python/samples/01-get-started/06_functional_workflow_basics.py @@ -1,53 +1,29 @@ # Copyright (c) Microsoft. All rights reserved. -""" -Functional Workflow Basics — Orchestrate async functions with @workflow - -The functional API lets you write workflows as plain Python async functions. -No graph concepts, no edges, no executor classes — just call functions -and use native control flow (if/else, loops, asyncio.gather). - -This sample builds a minimal pipeline with two steps: -1. Convert text to uppercase -2. Reverse the text - -No external services are required. -""" - import asyncio from agent_framework import workflow +"""Run two async functions as a functional workflow.""" + -# Plain async functions — no decorators needed async def to_upper_case(text: str) -> str: - """Convert input to uppercase.""" return text.upper() async def reverse_text(text: str) -> str: - """Reverse the string.""" return text[::-1] @workflow async def text_workflow(text: str) -> str: - """Uppercase the text, then reverse it.""" upper = await to_upper_case(text) return await reverse_text(upper) async def main() -> None: - workflow_instance = text_workflow.build() - result = await workflow_instance.run("hello world") - print(f"Output: {result.get_outputs()}") - print(f"Final state: {result.get_final_state()}") - - """ - Expected output: - Output: ['DLROW OLLEH'] - Final state: WorkflowRunState.IDLE - """ + result = await text_workflow.build().run("hello world") + print(result.get_outputs()[0]) if __name__ == "__main__": diff --git a/python/samples/01-get-started/07_first_graph_workflow.py b/python/samples/01-get-started/07_first_graph_workflow.py index 4c8b29e3a78..b61e7486f1b 100644 --- a/python/samples/01-get-started/07_first_graph_workflow.py +++ b/python/samples/01-get-started/07_first_graph_workflow.py @@ -2,65 +2,26 @@ import asyncio -from agent_framework import ( - Executor, - WorkflowBuilder, - WorkflowContext, - executor, - handler, -) +from agent_framework import WorkflowBuilder, WorkflowContext, executor from typing_extensions import Never -""" -First Graph Workflow — Chain executors with edges +"""Connect two function executors with a workflow edge.""" -The graph API gives you full control over execution topology: edges, -fan-out/fan-in, switch/case, and superstep-based checkpointing. -This sample builds a minimal graph workflow with two steps: -1. Convert text to uppercase (class-based executor) -2. Reverse the text (function-based executor) +@executor(id="upper_case") +async def upper_case(text: str, ctx: WorkflowContext[str]) -> None: + await ctx.send_message(text.upper()) -No external services are required. -""" - -# Step 1: A class-based executor that converts text to uppercase -class UpperCase(Executor): - def __init__(self, id: str): - super().__init__(id=id) - - @handler - async def to_upper_case(self, text: str, ctx: WorkflowContext[str]) -> None: - """Convert input to uppercase and forward to the next node.""" - await ctx.send_message(text.upper()) - - -# Step 2: A function-based executor that reverses the string and yields output @executor(id="reverse_text") async def reverse_text(text: str, ctx: WorkflowContext[Never, str]) -> None: - """Reverse the string and yield the final workflow output.""" await ctx.yield_output(text[::-1]) -def create_workflow(): - """Build the workflow: UpperCase → reverse_text.""" - upper = UpperCase(id="upper_case") - return WorkflowBuilder(start_executor=upper).add_edge(upper, reverse_text).build() - - async def main() -> None: - workflow = create_workflow() - - events = await workflow.run("hello world") - print(f"Output: {events.get_outputs()}") - print(f"Final state: {events.get_final_state()}") - - """ - Expected output: - Output: ['DLROW OLLEH'] - Final state: WorkflowRunState.IDLE - """ + workflow = WorkflowBuilder(start_executor=upper_case).add_edge(upper_case, reverse_text).build() + result = await workflow.run("hello world") + print(result.get_outputs()[0]) if __name__ == "__main__": From 79cfaea5a5d4046e796b7508843c9464ea937600 Mon Sep 17 00:00:00 2001 From: eavanvalkenburg Date: Wed, 7 Oct 2026 09:00:38 +0200 Subject: [PATCH 03/12] Python: Restore get-started sample context --- python/samples/01-get-started/01_hello_agent.py | 5 +++++ python/samples/01-get-started/02_add_tools.py | 7 +++++-- python/samples/01-get-started/03_multi_turn.py | 7 +++++-- python/samples/01-get-started/04_memory.py | 7 +++++-- .../01-get-started/05_functional_workflow_with_agents.py | 8 +++++--- .../01-get-started/06_functional_workflow_basics.py | 7 +++++-- python/samples/01-get-started/07_first_graph_workflow.py | 7 +++++-- 7 files changed, 35 insertions(+), 13 deletions(-) diff --git a/python/samples/01-get-started/01_hello_agent.py b/python/samples/01-get-started/01_hello_agent.py index be7897e5c26..ee5957f7a69 100644 --- a/python/samples/01-get-started/01_hello_agent.py +++ b/python/samples/01-get-started/01_hello_agent.py @@ -1,5 +1,10 @@ # Copyright (c) Microsoft. All rights reserved. +"""Create and run a minimal Agent Framework agent. + +The agent uses a Microsoft Foundry chat client and prints one response. +""" + import asyncio from agent_framework import Agent diff --git a/python/samples/01-get-started/02_add_tools.py b/python/samples/01-get-started/02_add_tools.py index b4233153e54..d46c47aca70 100644 --- a/python/samples/01-get-started/02_add_tools.py +++ b/python/samples/01-get-started/02_add_tools.py @@ -1,13 +1,16 @@ # Copyright (c) Microsoft. All rights reserved. +"""Give an agent a function tool. + +The weather function is registered with @tool and passed to the agent. +""" + import asyncio from agent_framework import Agent, tool from agent_framework.foundry import FoundryChatClient from azure.identity import AzureCliCredential -"""Give an agent a function tool.""" - # This read-only sample tool can run without approval. @tool(approval_mode="never_require") diff --git a/python/samples/01-get-started/03_multi_turn.py b/python/samples/01-get-started/03_multi_turn.py index beea611397d..b31651db102 100644 --- a/python/samples/01-get-started/03_multi_turn.py +++ b/python/samples/01-get-started/03_multi_turn.py @@ -1,13 +1,16 @@ # Copyright (c) Microsoft. All rights reserved. +"""Reuse one agent session across multiple turns. + +The shared session preserves conversation history between calls. +""" + import asyncio from agent_framework import Agent from agent_framework.foundry import FoundryChatClient from azure.identity import AzureCliCredential -"""Reuse one agent session across multiple turns.""" - async def main() -> None: agent = Agent( diff --git a/python/samples/01-get-started/04_memory.py b/python/samples/01-get-started/04_memory.py index 69b88587ed3..35ac848c685 100644 --- a/python/samples/01-get-started/04_memory.py +++ b/python/samples/01-get-started/04_memory.py @@ -1,5 +1,10 @@ # Copyright (c) Microsoft. All rights reserved. +"""Persist a user's name with a context provider. + +The provider stores the name in session state and injects it into later calls. +""" + import asyncio from typing import Any @@ -7,8 +12,6 @@ from agent_framework.foundry import FoundryChatClient from azure.identity import AzureCliCredential -"""Persist a user's name with a context provider and session state.""" - class UserMemoryProvider(ContextProvider): def __init__(self) -> None: diff --git a/python/samples/01-get-started/05_functional_workflow_with_agents.py b/python/samples/01-get-started/05_functional_workflow_with_agents.py index 5545207063b..75e88669177 100644 --- a/python/samples/01-get-started/05_functional_workflow_with_agents.py +++ b/python/samples/01-get-started/05_functional_workflow_with_agents.py @@ -1,14 +1,16 @@ # Copyright (c) Microsoft. All rights reserved. +"""Call two agents from a functional workflow. + +The workflow asks one agent to write a poem and another to review it. +""" + import asyncio from agent_framework import Agent, workflow from agent_framework.foundry import FoundryChatClient from azure.identity import AzureCliCredential -"""Call two agents from a functional workflow.""" - - client = FoundryChatClient( project_endpoint="https://your-account.services.ai.azure.com/api/projects/your-project", model="gpt-4o", diff --git a/python/samples/01-get-started/06_functional_workflow_basics.py b/python/samples/01-get-started/06_functional_workflow_basics.py index de1a6fcf8a6..492d02fd7ac 100644 --- a/python/samples/01-get-started/06_functional_workflow_basics.py +++ b/python/samples/01-get-started/06_functional_workflow_basics.py @@ -1,11 +1,14 @@ # Copyright (c) Microsoft. All rights reserved. +"""Run two async functions as a functional workflow. + +The workflow uppercases text, then reverses it using normal Python control flow. +""" + import asyncio from agent_framework import workflow -"""Run two async functions as a functional workflow.""" - async def to_upper_case(text: str) -> str: return text.upper() diff --git a/python/samples/01-get-started/07_first_graph_workflow.py b/python/samples/01-get-started/07_first_graph_workflow.py index b61e7486f1b..c181cbfa2ca 100644 --- a/python/samples/01-get-started/07_first_graph_workflow.py +++ b/python/samples/01-get-started/07_first_graph_workflow.py @@ -1,12 +1,15 @@ # Copyright (c) Microsoft. All rights reserved. +"""Connect two function executors with a workflow edge. + +The first executor transforms the input and sends it to the terminal executor. +""" + import asyncio from agent_framework import WorkflowBuilder, WorkflowContext, executor from typing_extensions import Never -"""Connect two function executors with a workflow edge.""" - @executor(id="upper_case") async def upper_case(text: str, ctx: WorkflowContext[str]) -> None: From e54aabb1ec500271cbfd0813e48227fe50d418e1 Mon Sep 17 00:00:00 2001 From: eavanvalkenburg Date: Wed, 7 Oct 2026 09:07:17 +0200 Subject: [PATCH 04/12] Python: Align get-started sample README --- python/samples/01-get-started/README.md | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/python/samples/01-get-started/README.md b/python/samples/01-get-started/README.md index 74aad7cccaa..dc8816e87af 100644 --- a/python/samples/01-get-started/README.md +++ b/python/samples/01-get-started/README.md @@ -6,27 +6,24 @@ concepts of **Agent Framework** one step at a time. ## Prerequisites ```bash -pip install agent-framework-foundry +pip install agent-framework-foundry azure-identity +az login ``` -Set the required environment variables: - -```bash -export FOUNDRY_PROJECT_ENDPOINT="https://your-project-endpoint" -export FOUNDRY_MODEL="gpt-4o" -``` +Each sample hardcodes example values for `project_endpoint` and `model`. Replace them with your Microsoft Foundry +project endpoint and model deployment name before running the sample. ## Samples | # | File | What you'll learn | |---|------|-------------------| -| 1 | [01_hello_agent.py](01_hello_agent.py) | Create your first agent and run it (streaming and non-streaming). | +| 1 | [01_hello_agent.py](01_hello_agent.py) | Create your first agent and run one request. | | 2 | [02_add_tools.py](02_add_tools.py) | Define a function tool with `@tool` and attach it to an agent. | -| 3 | [03_multi_turn.py](03_multi_turn.py) | Keep conversation history across turns with `AgentSession`. | -| 4 | [04_memory.py](04_memory.py) | Add dynamic context with a custom `ContextProvider`. | +| 3 | [03_multi_turn.py](03_multi_turn.py) | Reuse an agent session to keep conversation history across turns. | +| 4 | [04_memory.py](04_memory.py) | Store and inject session state with a custom `ContextProvider`. | | 5 | [05_functional_workflow_with_agents.py](05_functional_workflow_with_agents.py) | Call agents inside a functional workflow. | | 6 | [06_functional_workflow_basics.py](06_functional_workflow_basics.py) | Write a workflow as a plain async function. | -| 7 | [07_first_graph_workflow.py](07_first_graph_workflow.py) | Chain executors into a graph workflow with edges. | +| 7 | [07_first_graph_workflow.py](07_first_graph_workflow.py) | Connect function executors with a graph edge. | To host agents and workflows with Durable Task or Azure Functions, continue with the [Durable Agent Framework extension samples](https://github.com/microsoft/agent-framework-durable-extension/tree/main/python/samples). From 779f07f4fe96ebea27360a0dee1c856bcec664bf Mon Sep 17 00:00:00 2001 From: eavanvalkenburg Date: Wed, 7 Oct 2026 09:09:29 +0200 Subject: [PATCH 05/12] Python: Document environment-based sample configuration --- python/samples/01-get-started/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/python/samples/01-get-started/README.md b/python/samples/01-get-started/README.md index dc8816e87af..42e7ae182a6 100644 --- a/python/samples/01-get-started/README.md +++ b/python/samples/01-get-started/README.md @@ -13,6 +13,10 @@ az login Each sample hardcodes example values for `project_endpoint` and `model`. Replace them with your Microsoft Foundry project endpoint and model deployment name before running the sample. +Alternatively, set `FOUNDRY_PROJECT_ENDPOINT` and `FOUNDRY_MODEL`, then remove the `project_endpoint` and `model` +arguments from the `FoundryChatClient` constructor. To read those values from a `.env` file, call `load_dotenv()` +before creating the client; Agent Framework doesn't load `.env` files automatically. + ## Samples | # | File | What you'll learn | From d1fab142d89fc4c79fea5ed87a227c4ca6b0aff7 Mon Sep 17 00:00:00 2001 From: eavanvalkenburg Date: Wed, 7 Oct 2026 09:11:07 +0200 Subject: [PATCH 06/12] Python: Require project-scoped Foundry endpoint examples --- python/packages/azure-cosmos-memory/AGENTS.md | 3 +++ python/packages/core/AGENTS.md | 3 +++ python/samples/AGENTS.md | 4 ++++ 3 files changed, 10 insertions(+) diff --git a/python/packages/azure-cosmos-memory/AGENTS.md b/python/packages/azure-cosmos-memory/AGENTS.md index 19a45e41663..dcaf780afc4 100644 --- a/python/packages/azure-cosmos-memory/AGENTS.md +++ b/python/packages/azure-cosmos-memory/AGENTS.md @@ -32,6 +32,9 @@ from agent_framework_azure_cosmos_memory import CosmosMemoryContextProvider - Requires the `azure-cosmos-agent-memory` toolkit and an AI Foundry endpoint (used for both embeddings and fact extraction). +- Documentation and examples that show a literal Foundry project endpoint must use the full project-scoped form + `https://your-account.services.ai.azure.com/api/projects/your-project`, including the `/api/projects/your-project` + path. - Set a stable `user_id` in `state["user_id"]` or `session.state["user_id"]` for long-term, cross-session memory. Without it, memory scopes to the ephemeral session id and the provider logs a one-time warning. diff --git a/python/packages/core/AGENTS.md b/python/packages/core/AGENTS.md index 13361bbeaa9..7678a7a57d9 100644 --- a/python/packages/core/AGENTS.md +++ b/python/packages/core/AGENTS.md @@ -352,6 +352,9 @@ The vector store API is experimental under the shared `VECTOR_STORES` feature ID - **`FoundryChatClient`** - Chat client for Microsoft Foundry project endpoints - **`FoundryAgentSessionStore`** - Experimental Foundry-hosting session store, lazily re-exported from `agent-framework-foundry-hosting`; currently backed by `azure.ai.agentserver.core.storage.FoundryStateStore` +- Documentation and examples that show a literal Foundry project endpoint must use the full project-scoped form + `https://your-account.services.ai.azure.com/api/projects/your-project`, including the `/api/projects/your-project` + path. ## Key Patterns diff --git a/python/samples/AGENTS.md b/python/samples/AGENTS.md index 916e550e07b..1a49f42e739 100644 --- a/python/samples/AGENTS.md +++ b/python/samples/AGENTS.md @@ -77,6 +77,10 @@ Durable Task and Azure Functions samples are maintained in the [Durable Agent Fr All canonical samples (01-get-started) use **Microsoft Foundry project-backed chat** via `FoundryChatClient` with a Microsoft Foundry project endpoint: +Whenever a sample, README, or comment shows a literal Foundry project endpoint, use the full project-scoped form +`https://your-account.services.ai.azure.com/api/projects/your-project`. Do not shorten the example to the +account-level endpoint. + ```python import os from agent_framework import Agent From 47b28c742d213d49900aa0bb670953b22e41891b Mon Sep 17 00:00:00 2001 From: eavanvalkenburg Date: Wed, 7 Oct 2026 09:15:24 +0200 Subject: [PATCH 07/12] Python: Align get-started steps with docs --- ...=> 05a_functional_workflow_with_agents.py} | 2 +- ...s.py => 05b_functional_workflow_basics.py} | 2 +- ...orkflow.py => 05c_first_graph_workflow.py} | 2 +- .../01-get-started/06_agent_harness.py | 33 +++++++++++++++ python/samples/01-get-started/07_hosting.py | 42 +++++++++++++++++++ python/samples/01-get-started/README.md | 30 +++++++++---- python/samples/README.md | 20 +++++---- 7 files changed, 112 insertions(+), 19 deletions(-) rename python/samples/01-get-started/{05_functional_workflow_with_agents.py => 05a_functional_workflow_with_agents.py} (94%) rename python/samples/01-get-started/{06_functional_workflow_basics.py => 05b_functional_workflow_basics.py} (91%) rename python/samples/01-get-started/{07_first_graph_workflow.py => 05c_first_graph_workflow.py} (93%) create mode 100644 python/samples/01-get-started/06_agent_harness.py create mode 100644 python/samples/01-get-started/07_hosting.py diff --git a/python/samples/01-get-started/05_functional_workflow_with_agents.py b/python/samples/01-get-started/05a_functional_workflow_with_agents.py similarity index 94% rename from python/samples/01-get-started/05_functional_workflow_with_agents.py rename to python/samples/01-get-started/05a_functional_workflow_with_agents.py index 75e88669177..795a8fbf85b 100644 --- a/python/samples/01-get-started/05_functional_workflow_with_agents.py +++ b/python/samples/01-get-started/05a_functional_workflow_with_agents.py @@ -2,7 +2,7 @@ """Call two agents from a functional workflow. -The workflow asks one agent to write a poem and another to review it. +One agent writes a poem, and the other reviews it. """ import asyncio diff --git a/python/samples/01-get-started/06_functional_workflow_basics.py b/python/samples/01-get-started/05b_functional_workflow_basics.py similarity index 91% rename from python/samples/01-get-started/06_functional_workflow_basics.py rename to python/samples/01-get-started/05b_functional_workflow_basics.py index 492d02fd7ac..fc46f6be423 100644 --- a/python/samples/01-get-started/06_functional_workflow_basics.py +++ b/python/samples/01-get-started/05b_functional_workflow_basics.py @@ -1,6 +1,6 @@ # Copyright (c) Microsoft. All rights reserved. -"""Run two async functions as a functional workflow. +"""Compose two async functions into a functional workflow. The workflow uppercases text, then reverses it using normal Python control flow. """ diff --git a/python/samples/01-get-started/07_first_graph_workflow.py b/python/samples/01-get-started/05c_first_graph_workflow.py similarity index 93% rename from python/samples/01-get-started/07_first_graph_workflow.py rename to python/samples/01-get-started/05c_first_graph_workflow.py index c181cbfa2ca..1d635fa4cbf 100644 --- a/python/samples/01-get-started/07_first_graph_workflow.py +++ b/python/samples/01-get-started/05c_first_graph_workflow.py @@ -1,6 +1,6 @@ # Copyright (c) Microsoft. All rights reserved. -"""Connect two function executors with a workflow edge. +"""Build a graph workflow from two function executors. The first executor transforms the input and sends it to the terminal executor. """ diff --git a/python/samples/01-get-started/06_agent_harness.py b/python/samples/01-get-started/06_agent_harness.py new file mode 100644 index 00000000000..09fa98a6847 --- /dev/null +++ b/python/samples/01-get-started/06_agent_harness.py @@ -0,0 +1,33 @@ +# Copyright (c) Microsoft. All rights reserved. + +"""Create a harness agent for multi-step tasks. + +The harness adds planning, todo tracking, and compaction around a chat client. +""" + +import asyncio + +from agent_framework import create_harness_agent +from agent_framework.foundry import FoundryChatClient +from azure.identity import AzureCliCredential + + +async def main() -> None: + agent = create_harness_agent( + client=FoundryChatClient( + project_endpoint="https://your-account.services.ai.azure.com/api/projects/your-project", + model="gpt-4o", + credential=AzureCliCredential(), + ), + agent_instructions="Help users plan and complete multi-step tasks.", + disable_file_memory=True, + disable_web_search=True, + ) + session = agent.create_session() + + print(await agent.run("Plan a weekend trip to Seattle.", session=session)) + print(await agent.run("Turn that plan into a checklist.", session=session)) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/python/samples/01-get-started/07_hosting.py b/python/samples/01-get-started/07_hosting.py new file mode 100644 index 00000000000..774858e6840 --- /dev/null +++ b/python/samples/01-get-started/07_hosting.py @@ -0,0 +1,42 @@ +# /// script +# requires-python = ">=3.10" +# dependencies = [ +# "agent-framework-foundry", +# "agent-framework-foundry-hosting", +# "azure-identity", +# "python-dotenv", +# ] +# /// +# Run with: uv run python/samples/01-get-started/07_hosting.py + +# Copyright (c) Microsoft. All rights reserved. + +"""Host an agent with the Foundry Responses protocol. + +The same Responses host can run locally or as a Microsoft Foundry Hosted Agent. +""" + +import os + +from agent_framework import Agent +from agent_framework.foundry import FoundryChatClient, ResponsesHostServer +from azure.identity import DefaultAzureCredential +from dotenv import load_dotenv + +load_dotenv() + + +def main() -> None: + agent = Agent( + client=FoundryChatClient( + project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"], + model=os.environ.get("FOUNDRY_MODEL") or os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"], + credential=DefaultAzureCredential(), + ), + instructions="You are a friendly assistant. Keep your answers brief.", + ) + ResponsesHostServer(agent=agent, history_source="agent_server").run() + + +if __name__ == "__main__": + main() diff --git a/python/samples/01-get-started/README.md b/python/samples/01-get-started/README.md index 42e7ae182a6..9f9134bec10 100644 --- a/python/samples/01-get-started/README.md +++ b/python/samples/01-get-started/README.md @@ -10,13 +10,17 @@ pip install agent-framework-foundry azure-identity az login ``` -Each sample hardcodes example values for `project_endpoint` and `model`. Replace them with your Microsoft Foundry -project endpoint and model deployment name before running the sample. +Samples 1 through 6 hardcode example values for `project_endpoint` and `model`. Replace them with your Microsoft +Foundry project endpoint and model deployment name before running the sample. Alternatively, set `FOUNDRY_PROJECT_ENDPOINT` and `FOUNDRY_MODEL`, then remove the `project_endpoint` and `model` arguments from the `FoundryChatClient` constructor. To read those values from a `.env` file, call `load_dotenv()` before creating the client; Agent Framework doesn't load `.env` files automatically. +The hosting sample reads `FOUNDRY_PROJECT_ENDPOINT` and `FOUNDRY_MODEL` from the environment because hosted +deployments provide configuration that way. It also accepts the deployment-provided +`AZURE_AI_MODEL_DEPLOYMENT_NAME`. + ## Samples | # | File | What you'll learn | @@ -25,11 +29,14 @@ before creating the client; Agent Framework doesn't load `.env` files automatica | 2 | [02_add_tools.py](02_add_tools.py) | Define a function tool with `@tool` and attach it to an agent. | | 3 | [03_multi_turn.py](03_multi_turn.py) | Reuse an agent session to keep conversation history across turns. | | 4 | [04_memory.py](04_memory.py) | Store and inject session state with a custom `ContextProvider`. | -| 5 | [05_functional_workflow_with_agents.py](05_functional_workflow_with_agents.py) | Call agents inside a functional workflow. | -| 6 | [06_functional_workflow_basics.py](06_functional_workflow_basics.py) | Write a workflow as a plain async function. | -| 7 | [07_first_graph_workflow.py](07_first_graph_workflow.py) | Connect function executors with a graph edge. | +| 5a | [05a_functional_workflow_with_agents.py](05a_functional_workflow_with_agents.py) | Call agents inside a functional workflow. | +| 5b | [05b_functional_workflow_basics.py](05b_functional_workflow_basics.py) | Write a workflow as a plain async function. | +| 5c | [05c_first_graph_workflow.py](05c_first_graph_workflow.py) | Connect function executors with a graph edge. | +| 6 | [06_agent_harness.py](06_agent_harness.py) | Add planning, todo tracking, and compaction with an agent harness. | +| 7 | [07_hosting.py](07_hosting.py) | Serve an agent with `ResponsesHostServer`. | -To host agents and workflows with Durable Task or Azure Functions, continue with the [Durable Agent Framework extension samples](https://github.com/microsoft/agent-framework-durable-extension/tree/main/python/samples). +For more hosting patterns, continue with [`04-hosting/`](../04-hosting/). Durable Task and Azure Functions samples +are in the [Durable Agent Framework extension](https://github.com/microsoft/agent-framework-durable-extension/tree/main/python/samples). ## Security in Production @@ -42,10 +49,17 @@ For the official security guidance, see [Agent Safety](https://learn.microsoft.c 3. [`github_mcp_example.py`](../02-agents/security/github_mcp_example.py): Demonstrates `SecureMCPToolProxy` wrapping remote MCP endpoints with local policy enforcement. 4. [FIDES Developer Guide](../02-agents/security/FIDES_DEVELOPER_GUIDE.md): Architecture reference and security middleware documentation. -Run any sample with: +Run samples 1 through 6 with: ```bash python 01_hello_agent.py ``` -These samples use Azure Foundry models with the Responses API. To switch providers, just replace the client, see [all providers](../02-agents/providers/README.md) +Run the PEP 723 hosting sample from the repository root with: + +```bash +uv run python/samples/01-get-started/07_hosting.py +``` + +These samples use Microsoft Foundry models with the Responses API. To switch providers, replace the client. See +[all providers](../02-agents/providers/README.md). diff --git a/python/samples/README.md b/python/samples/README.md index a53b7eed88f..b03d09b576d 100644 --- a/python/samples/README.md +++ b/python/samples/README.md @@ -6,7 +6,7 @@ This directory contains samples demonstrating the capabilities of Microsoft Agen | Folder | Description | |--------|-------------| -| [`01-get-started/`](./01-get-started/) | Progressive tutorial: hello agent → graph workflows | +| [`01-get-started/`](./01-get-started/) | Progressive tutorial: hello agent → workflows → harness → hosting | | [`02-agents/`](./02-agents/) | Deep-dive by concept: tools, middleware, providers, orchestrations, security | | [`03-workflows/`](./03-workflows/) | Workflow patterns: sequential, concurrent, state, declarative, explicit output designation | | [`04-hosting/`](./04-hosting/) | Deployment: A2A, self-hosted protocol helpers, and Foundry hosted agents | @@ -16,13 +16,17 @@ This directory contains samples demonstrating the capabilities of Microsoft Agen Start with `01-get-started/` and work through the numbered files: -1. **[01_hello_agent.py](./01-get-started/01_hello_agent.py)** — Create and run your first agent -2. **[02_add_tools.py](./01-get-started/02_add_tools.py)** — Add function tools with `@tool` -3. **[03_multi_turn.py](./01-get-started/03_multi_turn.py)** — Multi-turn conversations with `AgentSession` -4. **[04_memory.py](./01-get-started/04_memory.py)** — Agent memory with `ContextProvider` -5. **[05_functional_workflow_with_agents.py](./01-get-started/05_functional_workflow_with_agents.py)** — Call agents inside a functional workflow -6. **[06_functional_workflow_basics.py](./01-get-started/06_functional_workflow_basics.py)** — Write a workflow as a plain async function -7. **[07_first_graph_workflow.py](./01-get-started/07_first_graph_workflow.py)** — Build a workflow with executors and edges +| Step | Sample | What it demonstrates | +| --- | --- | --- | +| 1 | [01_hello_agent.py](./01-get-started/01_hello_agent.py) | Create and run your first agent | +| 2 | [02_add_tools.py](./01-get-started/02_add_tools.py) | Add function tools with `@tool` | +| 3 | [03_multi_turn.py](./01-get-started/03_multi_turn.py) | Multi-turn conversations with `AgentSession` | +| 4 | [04_memory.py](./01-get-started/04_memory.py) | Agent memory with `ContextProvider` | +| 5a | [05a_functional_workflow_with_agents.py](./01-get-started/05a_functional_workflow_with_agents.py) | Call agents inside a functional workflow | +| 5b | [05b_functional_workflow_basics.py](./01-get-started/05b_functional_workflow_basics.py) | Write a workflow as a plain async function | +| 5c | [05c_first_graph_workflow.py](./01-get-started/05c_first_graph_workflow.py) | Build a workflow with function executors and edges | +| 6 | [06_agent_harness.py](./01-get-started/06_agent_harness.py) | Add planning, todo tracking, and compaction | +| 7 | [07_hosting.py](./01-get-started/07_hosting.py) | Serve an agent with `ResponsesHostServer` | Durable Task and Azure Functions samples have moved to the [Durable Agent Framework extension](https://github.com/microsoft/agent-framework-durable-extension/tree/main/python/samples). From 9231d558c251f22fd35f2e08d78c51cbfbc42955 Mon Sep 17 00:00:00 2001 From: eavanvalkenburg Date: Wed, 7 Oct 2026 09:18:04 +0200 Subject: [PATCH 08/12] Python: Put workflow basics first --- ...l_workflow_basics.py => 05a_functional_workflow_basics.py} | 0 ..._with_agents.py => 05b_functional_workflow_with_agents.py} | 0 python/samples/01-get-started/README.md | 4 ++-- python/samples/README.md | 4 ++-- 4 files changed, 4 insertions(+), 4 deletions(-) rename python/samples/01-get-started/{05b_functional_workflow_basics.py => 05a_functional_workflow_basics.py} (100%) rename python/samples/01-get-started/{05a_functional_workflow_with_agents.py => 05b_functional_workflow_with_agents.py} (100%) diff --git a/python/samples/01-get-started/05b_functional_workflow_basics.py b/python/samples/01-get-started/05a_functional_workflow_basics.py similarity index 100% rename from python/samples/01-get-started/05b_functional_workflow_basics.py rename to python/samples/01-get-started/05a_functional_workflow_basics.py diff --git a/python/samples/01-get-started/05a_functional_workflow_with_agents.py b/python/samples/01-get-started/05b_functional_workflow_with_agents.py similarity index 100% rename from python/samples/01-get-started/05a_functional_workflow_with_agents.py rename to python/samples/01-get-started/05b_functional_workflow_with_agents.py diff --git a/python/samples/01-get-started/README.md b/python/samples/01-get-started/README.md index 9f9134bec10..b8fbae310ae 100644 --- a/python/samples/01-get-started/README.md +++ b/python/samples/01-get-started/README.md @@ -29,8 +29,8 @@ deployments provide configuration that way. It also accepts the deployment-provi | 2 | [02_add_tools.py](02_add_tools.py) | Define a function tool with `@tool` and attach it to an agent. | | 3 | [03_multi_turn.py](03_multi_turn.py) | Reuse an agent session to keep conversation history across turns. | | 4 | [04_memory.py](04_memory.py) | Store and inject session state with a custom `ContextProvider`. | -| 5a | [05a_functional_workflow_with_agents.py](05a_functional_workflow_with_agents.py) | Call agents inside a functional workflow. | -| 5b | [05b_functional_workflow_basics.py](05b_functional_workflow_basics.py) | Write a workflow as a plain async function. | +| 5a | [05a_functional_workflow_basics.py](05a_functional_workflow_basics.py) | Write a workflow as a plain async function. | +| 5b | [05b_functional_workflow_with_agents.py](05b_functional_workflow_with_agents.py) | Call agents inside a functional workflow. | | 5c | [05c_first_graph_workflow.py](05c_first_graph_workflow.py) | Connect function executors with a graph edge. | | 6 | [06_agent_harness.py](06_agent_harness.py) | Add planning, todo tracking, and compaction with an agent harness. | | 7 | [07_hosting.py](07_hosting.py) | Serve an agent with `ResponsesHostServer`. | diff --git a/python/samples/README.md b/python/samples/README.md index b03d09b576d..34befd72f66 100644 --- a/python/samples/README.md +++ b/python/samples/README.md @@ -22,8 +22,8 @@ Start with `01-get-started/` and work through the numbered files: | 2 | [02_add_tools.py](./01-get-started/02_add_tools.py) | Add function tools with `@tool` | | 3 | [03_multi_turn.py](./01-get-started/03_multi_turn.py) | Multi-turn conversations with `AgentSession` | | 4 | [04_memory.py](./01-get-started/04_memory.py) | Agent memory with `ContextProvider` | -| 5a | [05a_functional_workflow_with_agents.py](./01-get-started/05a_functional_workflow_with_agents.py) | Call agents inside a functional workflow | -| 5b | [05b_functional_workflow_basics.py](./01-get-started/05b_functional_workflow_basics.py) | Write a workflow as a plain async function | +| 5a | [05a_functional_workflow_basics.py](./01-get-started/05a_functional_workflow_basics.py) | Write a workflow as a plain async function | +| 5b | [05b_functional_workflow_with_agents.py](./01-get-started/05b_functional_workflow_with_agents.py) | Call agents inside a functional workflow | | 5c | [05c_first_graph_workflow.py](./01-get-started/05c_first_graph_workflow.py) | Build a workflow with function executors and edges | | 6 | [06_agent_harness.py](./01-get-started/06_agent_harness.py) | Add planning, todo tracking, and compaction | | 7 | [07_hosting.py](./01-get-started/07_hosting.py) | Serve an agent with `ResponsesHostServer` | From 017b23dbf515095a21f8cd7e1ecb788aa8712fd6 Mon Sep 17 00:00:00 2001 From: eavanvalkenburg Date: Wed, 7 Oct 2026 09:19:26 +0200 Subject: [PATCH 09/12] Python: Keep hosted sample on Foundry settings --- python/samples/01-get-started/07_hosting.py | 3 ++- python/samples/01-get-started/README.md | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/python/samples/01-get-started/07_hosting.py b/python/samples/01-get-started/07_hosting.py index 774858e6840..feb193e953f 100644 --- a/python/samples/01-get-started/07_hosting.py +++ b/python/samples/01-get-started/07_hosting.py @@ -30,10 +30,11 @@ def main() -> None: agent = Agent( client=FoundryChatClient( project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"], - model=os.environ.get("FOUNDRY_MODEL") or os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"], + model=os.environ["FOUNDRY_MODEL"], credential=DefaultAzureCredential(), ), instructions="You are a friendly assistant. Keep your answers brief.", + default_options={"store": False}, ) ResponsesHostServer(agent=agent, history_source="agent_server").run() diff --git a/python/samples/01-get-started/README.md b/python/samples/01-get-started/README.md index b8fbae310ae..9108d16c33c 100644 --- a/python/samples/01-get-started/README.md +++ b/python/samples/01-get-started/README.md @@ -18,8 +18,7 @@ arguments from the `FoundryChatClient` constructor. To read those values from a before creating the client; Agent Framework doesn't load `.env` files automatically. The hosting sample reads `FOUNDRY_PROJECT_ENDPOINT` and `FOUNDRY_MODEL` from the environment because hosted -deployments provide configuration that way. It also accepts the deployment-provided -`AZURE_AI_MODEL_DEPLOYMENT_NAME`. +deployments provide configuration that way. ## Samples From 89322d7ce642901ccbce65319a33d5d0e1954363 Mon Sep 17 00:00:00 2001 From: eavanvalkenburg Date: Wed, 7 Oct 2026 09:20:46 +0200 Subject: [PATCH 10/12] Python: Use hardcoded Foundry settings in hosting sample --- python/samples/01-get-started/07_hosting.py | 10 ++-------- python/samples/01-get-started/README.md | 5 +---- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/python/samples/01-get-started/07_hosting.py b/python/samples/01-get-started/07_hosting.py index feb193e953f..d9a0c8727c0 100644 --- a/python/samples/01-get-started/07_hosting.py +++ b/python/samples/01-get-started/07_hosting.py @@ -4,7 +4,6 @@ # "agent-framework-foundry", # "agent-framework-foundry-hosting", # "azure-identity", -# "python-dotenv", # ] # /// # Run with: uv run python/samples/01-get-started/07_hosting.py @@ -16,21 +15,16 @@ The same Responses host can run locally or as a Microsoft Foundry Hosted Agent. """ -import os - from agent_framework import Agent from agent_framework.foundry import FoundryChatClient, ResponsesHostServer from azure.identity import DefaultAzureCredential -from dotenv import load_dotenv - -load_dotenv() def main() -> None: agent = Agent( client=FoundryChatClient( - project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"], - model=os.environ["FOUNDRY_MODEL"], + project_endpoint="https://your-account.services.ai.azure.com/api/projects/your-project", + model="gpt-4o", credential=DefaultAzureCredential(), ), instructions="You are a friendly assistant. Keep your answers brief.", diff --git a/python/samples/01-get-started/README.md b/python/samples/01-get-started/README.md index 9108d16c33c..709c9ed82ff 100644 --- a/python/samples/01-get-started/README.md +++ b/python/samples/01-get-started/README.md @@ -10,16 +10,13 @@ pip install agent-framework-foundry azure-identity az login ``` -Samples 1 through 6 hardcode example values for `project_endpoint` and `model`. Replace them with your Microsoft +All samples hardcode example values for `project_endpoint` and `model`. Replace them with your Microsoft Foundry project endpoint and model deployment name before running the sample. Alternatively, set `FOUNDRY_PROJECT_ENDPOINT` and `FOUNDRY_MODEL`, then remove the `project_endpoint` and `model` arguments from the `FoundryChatClient` constructor. To read those values from a `.env` file, call `load_dotenv()` before creating the client; Agent Framework doesn't load `.env` files automatically. -The hosting sample reads `FOUNDRY_PROJECT_ENDPOINT` and `FOUNDRY_MODEL` from the environment because hosted -deployments provide configuration that way. - ## Samples | # | File | What you'll learn | From f6aeb15386d5c563027c77a18d17a12cbe0131d2 Mon Sep 17 00:00:00 2001 From: eavanvalkenburg Date: Wed, 7 Oct 2026 09:22:37 +0200 Subject: [PATCH 11/12] Python: Use gpt-6-luna across get-started samples --- python/samples/01-get-started/02_add_tools.py | 2 +- python/samples/01-get-started/03_multi_turn.py | 2 +- python/samples/01-get-started/04_memory.py | 2 +- .../01-get-started/05b_functional_workflow_with_agents.py | 2 +- python/samples/01-get-started/06_agent_harness.py | 2 +- python/samples/01-get-started/07_hosting.py | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/python/samples/01-get-started/02_add_tools.py b/python/samples/01-get-started/02_add_tools.py index d46c47aca70..3f05ef66f4b 100644 --- a/python/samples/01-get-started/02_add_tools.py +++ b/python/samples/01-get-started/02_add_tools.py @@ -23,7 +23,7 @@ async def main() -> None: agent = Agent( client=FoundryChatClient( project_endpoint="https://your-account.services.ai.azure.com/api/projects/your-project", - model="gpt-4o", + model="gpt-6-luna", credential=AzureCliCredential(), ), instructions="Use the weather tool to answer questions.", diff --git a/python/samples/01-get-started/03_multi_turn.py b/python/samples/01-get-started/03_multi_turn.py index b31651db102..0efaa6ed3d2 100644 --- a/python/samples/01-get-started/03_multi_turn.py +++ b/python/samples/01-get-started/03_multi_turn.py @@ -16,7 +16,7 @@ async def main() -> None: agent = Agent( client=FoundryChatClient( project_endpoint="https://your-account.services.ai.azure.com/api/projects/your-project", - model="gpt-4o", + model="gpt-6-luna", credential=AzureCliCredential(), ), instructions="You are a friendly assistant. Keep your answers brief.", diff --git a/python/samples/01-get-started/04_memory.py b/python/samples/01-get-started/04_memory.py index 35ac848c685..8530fe6bbd1 100644 --- a/python/samples/01-get-started/04_memory.py +++ b/python/samples/01-get-started/04_memory.py @@ -52,7 +52,7 @@ async def main() -> None: agent = Agent( client=FoundryChatClient( project_endpoint="https://your-account.services.ai.azure.com/api/projects/your-project", - model="gpt-4o", + model="gpt-6-luna", credential=AzureCliCredential(), ), instructions="You are a friendly assistant.", diff --git a/python/samples/01-get-started/05b_functional_workflow_with_agents.py b/python/samples/01-get-started/05b_functional_workflow_with_agents.py index 795a8fbf85b..e17a74fd9a0 100644 --- a/python/samples/01-get-started/05b_functional_workflow_with_agents.py +++ b/python/samples/01-get-started/05b_functional_workflow_with_agents.py @@ -13,7 +13,7 @@ client = FoundryChatClient( project_endpoint="https://your-account.services.ai.azure.com/api/projects/your-project", - model="gpt-4o", + model="gpt-6-luna", credential=AzureCliCredential(), ) diff --git a/python/samples/01-get-started/06_agent_harness.py b/python/samples/01-get-started/06_agent_harness.py index 09fa98a6847..5cf2a590502 100644 --- a/python/samples/01-get-started/06_agent_harness.py +++ b/python/samples/01-get-started/06_agent_harness.py @@ -16,7 +16,7 @@ async def main() -> None: agent = create_harness_agent( client=FoundryChatClient( project_endpoint="https://your-account.services.ai.azure.com/api/projects/your-project", - model="gpt-4o", + model="gpt-6-luna", credential=AzureCliCredential(), ), agent_instructions="Help users plan and complete multi-step tasks.", diff --git a/python/samples/01-get-started/07_hosting.py b/python/samples/01-get-started/07_hosting.py index d9a0c8727c0..291416ba46c 100644 --- a/python/samples/01-get-started/07_hosting.py +++ b/python/samples/01-get-started/07_hosting.py @@ -24,7 +24,7 @@ def main() -> None: agent = Agent( client=FoundryChatClient( project_endpoint="https://your-account.services.ai.azure.com/api/projects/your-project", - model="gpt-4o", + model="gpt-6-luna", credential=DefaultAzureCredential(), ), instructions="You are a friendly assistant. Keep your answers brief.", From 54002dc2988a07b6ed8abda0337e86ef04cb8485 Mon Sep 17 00:00:00 2001 From: eavanvalkenburg Date: Wed, 7 Oct 2026 09:41:58 +0200 Subject: [PATCH 12/12] Python: Address get-started review feedback --- python/samples/01-get-started/01_hello_agent.py | 2 ++ python/samples/01-get-started/02_add_tools.py | 1 + python/samples/01-get-started/04_memory.py | 1 + python/samples/01-get-started/06_agent_harness.py | 3 ++- 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/python/samples/01-get-started/01_hello_agent.py b/python/samples/01-get-started/01_hello_agent.py index ee5957f7a69..7ee0d162b07 100644 --- a/python/samples/01-get-started/01_hello_agent.py +++ b/python/samples/01-get-started/01_hello_agent.py @@ -3,6 +3,8 @@ """Create and run a minimal Agent Framework agent. The agent uses a Microsoft Foundry chat client and prints one response. +For streaming, see `foundry_chat_client_basic.py` in: +https://github.com/microsoft/agent-framework/tree/main/python/samples/02-agents/providers/foundry """ import asyncio diff --git a/python/samples/01-get-started/02_add_tools.py b/python/samples/01-get-started/02_add_tools.py index 3f05ef66f4b..3a5b8fc33dc 100644 --- a/python/samples/01-get-started/02_add_tools.py +++ b/python/samples/01-get-started/02_add_tools.py @@ -3,6 +3,7 @@ """Give an agent a function tool. The weather function is registered with @tool and passed to the agent. +Safety guidance: https://learn.microsoft.com/agent-framework/concepts/agents/safety """ import asyncio diff --git a/python/samples/01-get-started/04_memory.py b/python/samples/01-get-started/04_memory.py index 8530fe6bbd1..3da53dd9a57 100644 --- a/python/samples/01-get-started/04_memory.py +++ b/python/samples/01-get-started/04_memory.py @@ -60,6 +60,7 @@ async def main() -> None: ) session = agent.create_session() + print(await agent.run("Hello! What's the square root of 9?", session=session)) print(await agent.run("My name is Alice", session=session)) print(await agent.run("What is 2 + 2?", session=session)) diff --git a/python/samples/01-get-started/06_agent_harness.py b/python/samples/01-get-started/06_agent_harness.py index 5cf2a590502..eb1ad53d43a 100644 --- a/python/samples/01-get-started/06_agent_harness.py +++ b/python/samples/01-get-started/06_agent_harness.py @@ -2,7 +2,8 @@ """Create a harness agent for multi-step tasks. -The harness adds planning, todo tracking, and compaction around a chat client. +A harness adds planning, todos, and compaction to a regular agent. +More samples: https://github.com/microsoft/agent-framework/tree/main/python/samples/02-agents/harness """ import asyncio