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/01-get-started/01_hello_agent.py b/python/samples/01-get-started/01_hello_agent.py index 8f85af11f52..7ee0d162b07 100644 --- a/python/samples/01-get-started/01_hello_agent.py +++ b/python/samples/01-get-started/01_hello_agent.py @@ -1,50 +1,29 @@ # 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. +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 from agent_framework import Agent 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..3a5b8fc33dc 100644 --- a/python/samples/01-get-started/02_add_tools.py +++ b/python/samples/01-get-started/02_add_tools.py @@ -1,62 +1,36 @@ # Copyright (c) Microsoft. All rights reserved. +"""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 -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 - -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-6-luna", + 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 f1ab3123d0a..0efaa6ed3d2 100644 --- a/python/samples/01-get-started/03_multi_turn.py +++ b/python/samples/01-get-started/03_multi_turn.py @@ -1,46 +1,30 @@ # 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 -""" -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. -""" - 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-6-luna", + 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 92f38d5e5e5..3da53dd9a57 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,23 +12,10 @@ 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. -""" - - -# 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, @@ -33,18 +25,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, @@ -54,51 +39,30 @@ 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-6-luna", + 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("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)) if __name__ == "__main__": diff --git a/python/samples/01-get-started/05a_functional_workflow_basics.py b/python/samples/01-get-started/05a_functional_workflow_basics.py new file mode 100644 index 00000000000..fc46f6be423 --- /dev/null +++ b/python/samples/01-get-started/05a_functional_workflow_basics.py @@ -0,0 +1,33 @@ +# Copyright (c) Microsoft. All rights reserved. + +"""Compose two async functions into a functional workflow. + +The workflow uppercases text, then reverses it using normal Python control flow. +""" + +import asyncio + +from agent_framework import workflow + + +async def to_upper_case(text: str) -> str: + return text.upper() + + +async def reverse_text(text: str) -> str: + return text[::-1] + + +@workflow +async def text_workflow(text: str) -> str: + upper = await to_upper_case(text) + return await reverse_text(upper) + + +async def main() -> None: + result = await text_workflow.build().run("hello world") + print(result.get_outputs()[0]) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/python/samples/01-get-started/05_functional_workflow_with_agents.py b/python/samples/01-get-started/05b_functional_workflow_with_agents.py similarity index 50% rename from python/samples/01-get-started/05_functional_workflow_with_agents.py rename to python/samples/01-get-started/05b_functional_workflow_with_agents.py index e77c1bc7431..e17a74fd9a0 100644 --- a/python/samples/01-get-started/05_functional_workflow_with_agents.py +++ b/python/samples/01-get-started/05b_functional_workflow_with_agents.py @@ -1,10 +1,8 @@ # Copyright (c) Microsoft. All rights reserved. -""" -Functional Workflow with Agents — Call agents inside @workflow +"""Call two agents from a functional workflow. -This sample shows how to call agents inside a functional workflow. -Agent calls are just regular async function calls — no special wrappers needed. +One agent writes a poem, and the other reviews it. """ import asyncio @@ -12,40 +10,31 @@ 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() -# -client = FoundryChatClient(credential=AzureCliCredential()) +client = FoundryChatClient( + project_endpoint="https://your-account.services.ai.azure.com/api/projects/your-project", + model="gpt-6-luna", + 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}" -# - - 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/05c_first_graph_workflow.py b/python/samples/01-get-started/05c_first_graph_workflow.py new file mode 100644 index 00000000000..1d635fa4cbf --- /dev/null +++ b/python/samples/01-get-started/05c_first_graph_workflow.py @@ -0,0 +1,31 @@ +# Copyright (c) Microsoft. All rights reserved. + +"""Build a graph workflow from two function executors. + +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 + + +@executor(id="upper_case") +async def upper_case(text: str, ctx: WorkflowContext[str]) -> None: + await ctx.send_message(text.upper()) + + +@executor(id="reverse_text") +async def reverse_text(text: str, ctx: WorkflowContext[Never, str]) -> None: + await ctx.yield_output(text[::-1]) + + +async def main() -> None: + 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__": + asyncio.run(main()) 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..eb1ad53d43a --- /dev/null +++ b/python/samples/01-get-started/06_agent_harness.py @@ -0,0 +1,34 @@ +# Copyright (c) Microsoft. All rights reserved. + +"""Create a harness agent for multi-step tasks. + +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 + +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-6-luna", + 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/06_functional_workflow_basics.py b/python/samples/01-get-started/06_functional_workflow_basics.py deleted file mode 100644 index ec6fd79f538..00000000000 --- a/python/samples/01-get-started/06_functional_workflow_basics.py +++ /dev/null @@ -1,60 +0,0 @@ -# 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 - - -# 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 - """ - - -if __name__ == "__main__": - asyncio.run(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 deleted file mode 100644 index 2a84fabfab2..00000000000 --- a/python/samples/01-get-started/07_first_graph_workflow.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright (c) Microsoft. All rights reserved. - -import asyncio - -from agent_framework import ( - Executor, - WorkflowBuilder, - WorkflowContext, - executor, - handler, -) -from typing_extensions import Never - -""" -First Graph Workflow — Chain executors with edges - -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) - -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 - """ - - -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..291416ba46c --- /dev/null +++ b/python/samples/01-get-started/07_hosting.py @@ -0,0 +1,37 @@ +# /// script +# requires-python = ">=3.10" +# dependencies = [ +# "agent-framework-foundry", +# "agent-framework-foundry-hosting", +# "azure-identity", +# ] +# /// +# 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. +""" + +from agent_framework import Agent +from agent_framework.foundry import FoundryChatClient, ResponsesHostServer +from azure.identity import DefaultAzureCredential + + +def main() -> None: + agent = Agent( + client=FoundryChatClient( + project_endpoint="https://your-account.services.ai.azure.com/api/projects/your-project", + model="gpt-6-luna", + credential=DefaultAzureCredential(), + ), + instructions="You are a friendly assistant. Keep your answers brief.", + default_options={"store": False}, + ) + 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 74aad7cccaa..709c9ed82ff 100644 --- a/python/samples/01-get-started/README.md +++ b/python/samples/01-get-started/README.md @@ -6,29 +6,33 @@ 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: +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. -```bash -export FOUNDRY_PROJECT_ENDPOINT="https://your-project-endpoint" -export FOUNDRY_MODEL="gpt-4o" -``` +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 | |---|------|-------------------| -| 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`. | -| 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. | +| 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_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`. | -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 @@ -41,10 +45,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/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 diff --git a/python/samples/README.md b/python/samples/README.md index a53b7eed88f..34befd72f66 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_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` | 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).