Python: Foundry hosted agent V2 (#5379)

* Python: Wrapper + Samples 1st (#5177)

* Experiment

* Update dependency and add non streaming

* Add more samples

* Rename samples

* Add invocations

* Comments 1

* Comments 2

* Comments 3

* Improve README

* Add local shell sample

* WIP: Add eval and memory samples

* Update user agent prefix

* Update user agent prefix doc

* Update dependency (#5215)

* Add tests and more content types (#5235)

* Add tests

* fix tests and sample

* Fix formatting

* Remove function approval contents

* Python: Refine samples and upgrade packages (#5261)

* Refine samples and upgrade pacakges

* Upgrade to a new package that fixes a bug

* Update model env var

* Move samples (#5281)

* Python: Upgrade agentserver packages (#5284)

* Upgrade agentserver packages

* Fix new types

* Python: Add special handling for workflows (#5298)

* Add special handling for workflows

* Address comments

* Improve samples (#5372)

* Python: Add more types (#5378)

* Add more type supports

* Upgrade packages

* Remove TODOs in README

* Fix README

* Comments and mypy

* User agent scoped

* Fix README

* Fix pre commit

* Fix pre commit 2

* Fix pre commit 3

* Fix pre commit 4

* Fix pre commit 5

* Fix pre commit 6

* Add azure-monitor-opentelemetry to dev deps

Fixes Samples & Markdown CI failure. The PR's new transitive dep on
azure-monitor-opentelemetry-exporter (via azure-ai-agentserver-core) makes
pyright resolve the azure.monitor.opentelemetry namespace, flipping the
check_md_code_blocks diagnostic for `configure_azure_monitor` from
reportMissingImports (filtered) to reportAttributeAccessIssue (not filtered).
Installing the umbrella azure-monitor-opentelemetry package in dev makes
pyright resolve the symbol correctly, matching the install guidance the
observability README already gives users.

---------

Co-authored-by: Evan Mattson <evan.mattson@microsoft.com>
This commit is contained in:
Tao Chen
2026-04-20 22:21:27 -07:00
committed by GitHub
Unverified
parent 07f4c8a8d6
commit ce8b6305d8
87 changed files with 3597 additions and 1197 deletions
@@ -347,28 +347,29 @@ setup_observability(
```
**After (Current):**
```python
# For Microsoft Foundry projects
from agent_framework.foundry import FoundryChatClient
from azure.identity import AzureCliCredential
client = FoundryChatClient(
project_endpoint="https://your-project.services.ai.azure.com",
model="gpt-4o",
credential=AzureCliCredential(),
)
await client.configure_azure_monitor(enable_live_metrics=True)
# For non-Azure AI projects
from azure.monitor.opentelemetry import configure_azure_monitor
from agent_framework.observability import create_resource, enable_instrumentation
from azure.identity import AzureCliCredential
from azure.monitor.opentelemetry import configure_azure_monitor
configure_azure_monitor(
connection_string="InstrumentationKey=...",
resource=create_resource(),
enable_live_metrics=True,
)
enable_instrumentation()
async def main():
# For Microsoft Foundry projects
client = FoundryChatClient(
project_endpoint="https://your-project.services.ai.azure.com",
model="gpt-4o",
credential=AzureCliCredential(),
)
await client.configure_azure_monitor(enable_live_metrics=True)
# For non-Azure AI projects
configure_azure_monitor(
connection_string="InstrumentationKey=...",
resource=create_resource(),
enable_live_metrics=True,
)
enable_instrumentation()
```
### Console Output
@@ -0,0 +1,56 @@
# Foundry Hosted Agents Samples
This directory contains samples that demonstrate how to use the Agent Framework to host agents on Foundry with different capabilities and configurations. Each sample includes a README with instructions on how to set up, run, and interact with the agent.
Read more about Foundry Hosted Agents [here](https://learn.microsoft.com/en-us/azure/foundry/agents/concepts/hosted-agents).
## Environment setup
1. Navigate to the sample directory you want to run. For example:
```bash
python -m venv .venv
# Windows
.venv\Scripts\Activate
# macOS/Linux
source .venv/bin/activate
```
2. Install dependencies:
```bash
pip install -r requirements.txt
```
3. Create a `.env` file with your Foundry configuration following the `env.example` file in the sample.
4. Make sure you are logged in with the Azure CLI:
```bash
az login
```
## Deploying to a Docker container
Navigate to the sample directory and build the Docker image:
```bash
docker build -t hosted-agent-sample .
```
Run the container, passing in the required environment variables:
```bash
docker run -p 8088:8088 \
-e FOUNDRY_PROJECT_ENDPOINT=<your-endpoint> \
-e MODEL_DEPLOYMENT_NAME=<your-model> \
hosted-agent-sample
```
The server will be available at `http://localhost:8088`. You can send requests using the same `curl` command shown above.
## Deploying to Foundry
Follow this [guide](https://learn.microsoft.com/en-us/azure/foundry/agents/how-to/deploy-hosted-agent?tabs=bash#configure-your-agent) to deploy your agent to Foundry.
@@ -0,0 +1,6 @@
.venv
__pycache__
*.pyc
*.pyo
*.pyd
.Python
@@ -0,0 +1,2 @@
FOUNDRY_PROJECT_ENDPOINT="..."
MODEL_DEPLOYMENT_NAME="..."
@@ -0,0 +1,44 @@
# Basic example of hosting an agent with the `invocations` API
## Running the server locally
### Environment setup
Follow the instructions in the [Environment setup](../../README.md#environment-setup) section of the README in the parent directory to set up your environment and install dependencies.
Run the following command to start the server:
```bash
python main.py
```
### Interacting with the agent
Send a POST request to the server with a JSON body containing a "message" field to interact with the agent. For example:
```bash
curl -X POST http://localhost:8088/invocations -i -H "Content-Type: application/json" -d '{"message": "Hi"}'
```
The server will respond with a JSON object containing the response text. The `-i` flag in the `curl` command includes the HTTP response headers in the output, which includes the session ID that can be used for multi-turn conversations. Here is an example of the response:
```bash
HTTP/1.1 200
content-length: 34
content-type: application/json
x-agent-invocation-id: ec04d020-a0e7-441e-ae83-db75635a9f83
x-agent-session-id: 9370b9d4-cd13-4436-a57f-03b843ac0e17
x-platform-server: azure-ai-agentserver-core/2.0.0a20260410006 (python/3.12)
date: Fri, 17 Apr 2026 23:46:44 GMT
server: hypercorn-h11
{"response":"Hi! How can I help?"}
```
### Multi-turn conversation
To have a multi-turn conversation with the agent, take the session ID from the response headers of the previous request and include it in URL parameters for the next request. For example:
```bash
curl -X POST http://localhost:8088/invocations?agent_session_id=9370b9d4-cd13-4436-a57f-03b843ac0e17 -i -H "Content-Type: application/json" -d '{"message": "How are you?"}'
```
@@ -0,0 +1,23 @@
name: agent-framework-agent-basic-invocations
description: >
A basic Agent Framework agent hosted by Foundry.
metadata:
tags:
- Agent Framework
- AI Agent Hosting
- Azure AI AgentServer
- Invocations Protocol
- Streaming
template:
name: agent-framework-agent-basic-invocations
kind: hosted
protocols:
- protocol: invocations
version: 1.0.0
environment_variables:
- name: MODEL_DEPLOYMENT_NAME
value: "{{MODEL_DEPLOYMENT_NAME}}"
resources:
- kind: model
id: gpt-4.1-mini
name: MODEL_DEPLOYMENT_NAME
@@ -0,0 +1,9 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/microsoft/AgentSchema/refs/heads/main/schemas/v1.0/ContainerAgent.yaml
kind: hosted
name: agent-framework-agent-basic-invocations
protocols:
- protocol: invocations
version: 1.0.0
resources:
cpu: '0.25'
memory: '0.5Gi'
@@ -0,0 +1,36 @@
# Copyright (c) Microsoft. All rights reserved.
import os
from agent_framework import Agent
from agent_framework.foundry import FoundryChatClient
from agent_framework_foundry_hosting import InvocationsHostServer
from azure.identity import AzureCliCredential
from dotenv import load_dotenv
# Load environment variables from .env file
load_dotenv()
def main():
client = FoundryChatClient(
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
model=os.environ["MODEL_DEPLOYMENT_NAME"],
credential=AzureCliCredential(),
)
agent = Agent(
client=client,
instructions="You are a friendly assistant. Keep your answers brief.",
# History will be managed by the hosting infrastructure, thus there
# is no need to store history by the service. Learn more at:
# https://developers.openai.com/api/reference/resources/responses/methods/create
default_options={"store": False},
)
server = InvocationsHostServer(agent)
server.run()
if __name__ == "__main__":
main()
@@ -0,0 +1,2 @@
agent-framework
agent-framework-foundry-hosting
@@ -0,0 +1,6 @@
.venv
__pycache__
*.pyc
*.pyo
*.pyd
.Python
@@ -0,0 +1,2 @@
FOUNDRY_PROJECT_ENDPOINT="..."
MODEL_DEPLOYMENT_NAME="..."
@@ -0,0 +1,46 @@
# Basic example of hosting an agent with the `invocations` API
This is the same as the [01_basic](../01_basic/README.md) example, but demonstrates the "break glass" scenario where you can create your own `invoke_handler` to handle specific types of invocations. This is useful when you want to override the default behavior for certain requests or add custom processing logic.
## Running the server locally
### Environment setup
Follow the instructions in the [Environment setup](../../README.md#environment-setup) section of the README in the parent directory to set up your environment and install dependencies.
Run the following command to start the server:
```bash
python main.py
```
### Interacting with the agent
Send a POST request to the server with a JSON body containing a "message" field to interact with the agent. For example:
```bash
curl -X POST http://localhost:8088/invocations -i -H "Content-Type: application/json" -d '{"message": "Hi"}'
```
The server will respond with a JSON object containing the response text. The `-i` flag in the `curl` command includes the HTTP response headers in the output, which includes the session ID that can be used for multi-turn conversations. Here is an example of the response:
```bash
HTTP/1.1 200
content-length: 34
content-type: application/json
x-agent-invocation-id: ec04d020-a0e7-441e-ae83-db75635a9f83
x-agent-session-id: 9370b9d4-cd13-4436-a57f-03b843ac0e17
x-platform-server: azure-ai-agentserver-core/2.0.0a20260410006 (python/3.12)
date: Fri, 17 Apr 2026 23:46:44 GMT
server: hypercorn-h11
{"response":"Hi! How can I help?"}
```
### Multi-turn conversation
To have a multi-turn conversation with the agent, take the session ID from the response headers of the previous request and include it in URL parameters for the next request. For example:
```bash
curl -X POST http://localhost:8088/invocations?agent_session_id=9370b9d4-cd13-4436-a57f-03b843ac0e17 -i -H "Content-Type: application/json" -d '{"message": "How are you?"}'
```
@@ -0,0 +1,23 @@
name: agent-framework-agent-basic-invocations
description: >
A basic Agent Framework agent hosted by Foundry.
metadata:
tags:
- Agent Framework
- AI Agent Hosting
- Azure AI AgentServer
- Invocations Protocol
- Streaming
template:
name: agent-framework-agent-basic-invocations
kind: hosted
protocols:
- protocol: invocations
version: 1.0.0
environment_variables:
- name: MODEL_DEPLOYMENT_NAME
value: "{{MODEL_DEPLOYMENT_NAME}}"
resources:
- kind: model
id: gpt-4.1-mini
name: MODEL_DEPLOYMENT_NAME
@@ -0,0 +1,9 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/microsoft/AgentSchema/refs/heads/main/schemas/v1.0/ContainerAgent.yaml
kind: hosted
name: agent-framework-agent-basic-invocations
protocols:
- protocol: invocations
version: 1.0.0
resources:
cpu: '0.25'
memory: '0.5Gi'
@@ -0,0 +1,74 @@
# Copyright (c) Microsoft. All rights reserved.
import os
from collections.abc import AsyncGenerator
from agent_framework import Agent, AgentSession
from agent_framework.foundry import FoundryChatClient
from azure.ai.agentserver.invocations import InvocationAgentServerHost
from azure.identity import DefaultAzureCredential
from dotenv import load_dotenv
from starlette.requests import Request
from starlette.responses import JSONResponse, Response, StreamingResponse
# Load environment variables from .env file
load_dotenv()
# In-memory session store — keyed by session ID.
# WARNING: This is lost on restart. Use durable storage in production.
_sessions: dict[str, AgentSession] = {}
# Create the agent
client = FoundryChatClient(
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
model=os.environ["MODEL_DEPLOYMENT_NAME"],
credential=DefaultAzureCredential(),
)
agent = Agent(
client=client,
instructions="You are a friendly assistant. Keep your answers brief.",
# History will be managed by the hosting infrastructure, thus there
# is no need to store history by the service. Learn more at:
# https://developers.openai.com/api/reference/resources/responses/methods/create
default_options={"store": False},
)
app = InvocationAgentServerHost()
@app.invoke_handler
async def handle_invoke(request: Request):
"""Handle streaming multi-turn chat with Azure OpenAI via SSE."""
data = await request.json()
session_id = request.state.session_id
stream = data.get("stream", False)
user_message = data.get("message", None)
if user_message is None:
error = "Missing 'message' in request"
if stream:
return StreamingResponse(content=error, status_code=400)
return Response(content=error, status_code=400)
session = _sessions.setdefault(session_id, AgentSession(session_id=session_id))
if stream:
async def stream_response() -> AsyncGenerator[str]:
async for update in agent.run(user_message, session=session, stream=True):
yield update.text
return StreamingResponse(
stream_response(),
media_type="text/event-stream",
headers={"Cache-Control": "no-cache", "Connection": "keep-alive"},
)
response = await agent.run([user_message], session=session, stream=stream)
return JSONResponse({"response": response.text})
if __name__ == "__main__":
app.run()
@@ -0,0 +1,2 @@
agent-framework
azure-ai-agentserver-invocations
@@ -0,0 +1,8 @@
# Hosting agents with Foundry Hosting and the `invocations` API
This folder contains a list of samples that show how to host agents using the `invocations` API and deploy them to Foundry Hosting.
| Sample | Description |
| --- | --- |
| [01_basic](./01_basic) | A basic example of hosting an agent with the `invocations` API and carrying on a multi-turn conversation. |
| [02_break_glass](./02_break_glass) | An example of hosting an agent with the `invocations` API and a "break glass" scenario where you can create your own `invoke_handler` to handle specific types of invocations. |
@@ -0,0 +1,6 @@
.venv
__pycache__
*.pyc
*.pyo
*.pyd
.Python
@@ -0,0 +1,2 @@
FOUNDRY_PROJECT_ENDPOINT="..."
MODEL_DEPLOYMENT_NAME="..."
@@ -0,0 +1,31 @@
# Basic example of hosting an agent with the `responses` API
This agent only contains an instruction (personal). It's the most basic agent with an LLM and no tools.
## Running the server locally
### Environment setup
Follow the instructions in the [Environment setup](../../README.md#environment-setup) section of the README in the parent directory to set up your environment and install dependencies.
Run the following command to start the server:
```bash
python main.py
```
## Interacting with the agent
Send a POST request to the server with a JSON body containing a "input" field to interact with the agent. For example:
```bash
curl -X POST http://localhost:8088/responses -H "Content-Type: application/json" -d '{"input": "Hi"}'
```
## Multi-turn conversation
To have a multi-turn conversation with the agent, include the previous response id in the request body. For example:
```bash
curl -X POST http://localhost:8088/responses -H "Content-Type: application/json" -d '{"input": "How are you?", "previous_response_id": "REPLACE_WITH_PREVIOUS_RESPONSE_ID"}'
```
@@ -0,0 +1,23 @@
name: agent-framework-agent-basic
description: >
A basic Agent Framework agent hosted by Foundry.
metadata:
tags:
- Agent Framework
- AI Agent Hosting
- Azure AI AgentServer
- Responses Protocol
- Streaming
template:
name: agent-framework-agent-basic
kind: hosted
protocols:
- protocol: responses
version: 1.0.0
environment_variables:
- name: MODEL_DEPLOYMENT_NAME
value: "{{MODEL_DEPLOYMENT_NAME}}"
resources:
- kind: model
id: gpt-4.1-mini
name: MODEL_DEPLOYMENT_NAME
@@ -0,0 +1,8 @@
kind: hosted
name: agent-framework-agent-basic
protocols:
- protocol: responses
version: 1.0.0
resources:
cpu: "0.25"
memory: 0.5Gi
@@ -0,0 +1,36 @@
# Copyright (c) Microsoft. All rights reserved.
import os
from agent_framework import Agent
from agent_framework.foundry import FoundryChatClient
from agent_framework_foundry_hosting import ResponsesHostServer
from azure.identity import AzureCliCredential
from dotenv import load_dotenv
# Load environment variables from .env file
load_dotenv()
def main():
client = FoundryChatClient(
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
model=os.environ["MODEL_DEPLOYMENT_NAME"],
credential=AzureCliCredential(),
)
agent = Agent(
client=client,
instructions="You are a friendly assistant. Keep your answers brief.",
# History will be managed by the hosting infrastructure, thus there
# is no need to store history by the service. Learn more at:
# https://developers.openai.com/api/reference/resources/responses/methods/create
default_options={"store": False},
)
server = ResponsesHostServer(agent)
server.run()
if __name__ == "__main__":
main()
@@ -0,0 +1,2 @@
agent-framework
agent-framework-foundry-hosting
@@ -0,0 +1,6 @@
.venv
__pycache__
*.pyc
*.pyo
*.pyd
.Python
@@ -0,0 +1,2 @@
FOUNDRY_PROJECT_ENDPOINT="..."
MODEL_DEPLOYMENT_NAME="..."
@@ -1,11 +1,11 @@
FROM python:3.14-slim
FROM python:3.12-slim
WORKDIR /app
COPY ./ .
COPY . user_agent/
WORKDIR /app/user_agent
RUN pip install --upgrade pip && \
if [ -f requirements.txt ]; then \
RUN if [ -f requirements.txt ]; then \
pip install -r requirements.txt; \
else \
echo "No requirements.txt found"; \
@@ -13,4 +13,4 @@ RUN pip install --upgrade pip && \
EXPOSE 8088
CMD ["python", "main.py"]
CMD ["python", "main.py"]
@@ -0,0 +1,27 @@
# Basic example of hosting an agent with the `responses` API and local tools
This agent is equipped with a function tool and a local shell tool.
> We recommend deploying this sample on a local container or to Foundry Hosting because the agent has access to a local shell tool, which can run arbitrary commands on the machine.
## Running the server locally
### Environment setup
Follow the instructions in the [Environment setup](../../README.md#environment-setup) section of the README in the parent directory to set up your environment and install dependencies.
Run the following command to start the server:
```bash
python main.py
```
## Interacting with the agent
Send a POST request to the server with a JSON body containing a "input" field to interact with the agent. For example:
```bash
curl -X POST http://localhost:8088/responses -H "Content-Type: application/json" -d '{"input": "What is the weather in Seattle?"}'
curl -X POST http://localhost:8088/responses -H "Content-Type: application/json" -d '{"input": "List the files in the current directory."}'
```
@@ -0,0 +1,23 @@
name: agent-framework-agent-with-local-tools
description: >
An Agent Framework agent with local tools hosted by Foundry.
metadata:
tags:
- Agent Framework
- AI Agent Hosting
- Azure AI AgentServer
- Responses Protocol
- Streaming
template:
name: agent-framework-agent-with-local-tools
kind: hosted
protocols:
- protocol: responses
version: 1.0.0
environment_variables:
- name: MODEL_DEPLOYMENT_NAME
value: "{{MODEL_DEPLOYMENT_NAME}}"
resources:
- kind: model
id: gpt-4.1-mini
name: MODEL_DEPLOYMENT_NAME
@@ -0,0 +1,8 @@
kind: hosted
name: agent-framework-agent-with-local-tools
protocols:
- protocol: responses
version: 1.0.0
resources:
cpu: "0.25"
memory: 0.5Gi
@@ -0,0 +1,74 @@
# Copyright (c) Microsoft. All rights reserved.
import os
import subprocess
from random import randint
from agent_framework import Agent, tool
from agent_framework.foundry import FoundryChatClient
from agent_framework_foundry_hosting import ResponsesHostServer
from azure.identity import AzureCliCredential
from dotenv import load_dotenv
from pydantic import Field
from typing import Annotated
# Load environment variables from .env file
load_dotenv()
@tool(approval_mode="never_require")
def get_weather(
location: Annotated[str, Field(description="The location to get the weather for.")],
) -> 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."
@tool(approval_mode="always_require")
def run_bash(command: str) -> str:
"""Execute a shell command locally and return stdout, stderr, and exit code."""
try:
result = subprocess.run(
command,
shell=True,
capture_output=True,
text=True,
timeout=30,
)
parts: list[str] = []
if result.stdout:
parts.append(result.stdout)
if result.stderr:
parts.append(f"stderr: {result.stderr}")
parts.append(f"exit_code: {result.returncode}")
return "\n".join(parts)
except subprocess.TimeoutExpired:
return "Command timed out after 30 seconds"
except Exception as e:
return f"Error executing command: {e}"
def main():
client = FoundryChatClient(
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
model=os.environ["MODEL_DEPLOYMENT_NAME"],
credential=AzureCliCredential(),
)
agent = Agent(
client=client,
instructions="You are a friendly assistant. Keep your answers brief.",
tools=[get_weather, run_bash],
# History will be managed by the hosting infrastructure, thus there
# is no need to store history by the service. Learn more at:
# https://developers.openai.com/api/reference/resources/responses/methods/create
default_options={"store": False},
)
server = ResponsesHostServer(agent)
server.run()
if __name__ == "__main__":
main()
@@ -0,0 +1,2 @@
agent-framework
agent-framework-foundry-hosting
@@ -0,0 +1,6 @@
.venv
__pycache__
*.pyc
*.pyo
*.pyd
.Python
@@ -0,0 +1,4 @@
FOUNDRY_PROJECT_ENDPOINT="..."
MODEL_DEPLOYMENT_NAME="..."
TOOLBOX_NAME="..."
GITHUB_PAT="..."
@@ -1,11 +1,11 @@
FROM python:3.14-slim
FROM python:3.12-slim
WORKDIR /app
COPY ./ .
COPY . user_agent/
WORKDIR /app/user_agent
RUN pip install --upgrade pip && \
if [ -f requirements.txt ]; then \
RUN if [ -f requirements.txt ]; then \
pip install -r requirements.txt; \
else \
echo "No requirements.txt found"; \
@@ -13,4 +13,4 @@ RUN pip install --upgrade pip && \
EXPOSE 8088
CMD ["python", "main.py"]
CMD ["python", "main.py"]
@@ -0,0 +1,25 @@
# Basic example of hosting an agent with the `responses` API and a remote MCP
This agent is equipped with a GitHub MCP server and a Foundry Toolbox, which are both remote MCPs.
> Note that there are other ways to interact with Foundry toolboxes. Using it as a MCP is just one of the options.
## Running the server locally
### Environment setup
Follow the instructions in the [Environment setup](../../README.md#environment-setup) section of the README in the parent directory to set up your environment and install dependencies.
Run the following command to start the server:
```bash
python main.py
```
## Interacting with the agent
Send a POST request to the server with a JSON body containing a "input" field to interact with the agent. For example:
```bash
curl -X POST http://localhost:8088/responses -H "Content-Type: application/json" -d '{"input": "List all the repositories I own on GitHub."}'
```
@@ -0,0 +1,27 @@
name: agent-framework-agent-with-remote-mcp-tools
description: >
An Agent Framework agent with remote MCP tools hosted by Foundry.
metadata:
tags:
- Agent Framework
- AI Agent Hosting
- Azure AI AgentServer
- Responses Protocol
- Streaming
template:
name: agent-framework-agent-with-remote-mcp-tools
kind: hosted
protocols:
- protocol: responses
version: 1.0.0
environment_variables:
- name: MODEL_DEPLOYMENT_NAME
value: "{{MODEL_DEPLOYMENT_NAME}}"
- name: GITHUB_PAT
value: ${GITHUB_PAT}
- name: TOOLBOX_NAME
value: ${TOOLBOX_NAME}
resources:
- kind: model
id: gpt-4.1-mini
name: MODEL_DEPLOYMENT_NAME
@@ -0,0 +1,8 @@
kind: hosted
name: agent-framework-agent-with-remote-mcp-tools
protocols:
- protocol: responses
version: 1.0.0
resources:
cpu: "0.25"
memory: 0.5Gi
@@ -0,0 +1,76 @@
# Copyright (c) Microsoft. All rights reserved.
import os
import httpx
from agent_framework import Agent, MCPStreamableHTTPTool
from agent_framework.foundry import FoundryChatClient
from agent_framework_foundry_hosting import ResponsesHostServer
from azure.identity import AzureCliCredential
from dotenv import load_dotenv
# Load environment variables from .env file
load_dotenv()
class ToolboxAuth(httpx.Auth):
"""httpx Auth that injects a fresh bearer token on every request."""
def auth_flow(self, request: httpx.Request):
credential = AzureCliCredential()
token = credential.get_token("https://ai.azure.com/.default").token
request.headers["Authorization"] = f"Bearer {token}"
yield request
def main():
client = FoundryChatClient(
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
model=os.environ["MODEL_DEPLOYMENT_NAME"],
credential=AzureCliCredential(),
)
# Foundry Toolbox as a MCP tool
project_endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"]
toolbox_name = os.environ["TOOLBOX_NAME"]
toolbox_endpoint = f"{project_endpoint.rstrip('/')}/toolboxes/{toolbox_name}/mcp?api-version=v1"
http_client = httpx.AsyncClient(auth=ToolboxAuth(), headers={"Foundry-Features": "Toolboxes=V1Preview"})
foundry_mcp_tool = MCPStreamableHTTPTool(
name="toolbox",
url=toolbox_endpoint,
http_client=http_client,
load_prompts=False,
)
# GitHub MCP server
github_pat = os.environ["GITHUB_PAT"]
if not github_pat:
raise ValueError(
"GITHUB_PAT environment variable must be set. Create a token at https://github.com/settings/tokens"
)
github_mcp_tool = client.get_mcp_tool(
name="GitHub",
url="https://api.githubcopilot.com/mcp/",
headers={
"Authorization": f"Bearer {github_pat}",
},
approval_mode="never_require",
)
agent = Agent(
client=client,
instructions="You are a friendly assistant. Keep your answers brief.",
tools=[foundry_mcp_tool, github_mcp_tool],
# History will be managed by the hosting infrastructure, thus there
# is no need to store history by the service. Learn more at:
# https://developers.openai.com/api/reference/resources/responses/methods/create
default_options={"store": False},
)
server = ResponsesHostServer(agent)
server.run()
if __name__ == "__main__":
main()
@@ -0,0 +1,2 @@
agent-framework
agent-framework-foundry-hosting
@@ -0,0 +1,6 @@
.venv
__pycache__
*.pyc
*.pyo
*.pyd
.Python
@@ -0,0 +1,2 @@
FOUNDRY_PROJECT_ENDPOINT="..."
MODEL_DEPLOYMENT_NAME="..."
@@ -0,0 +1,16 @@
FROM python:3.12-slim
WORKDIR /app
COPY . user_agent/
WORKDIR /app/user_agent
RUN if [ -f requirements.txt ]; then \
pip install -r requirements.txt; \
else \
echo "No requirements.txt found"; \
fi
EXPOSE 8088
CMD ["python", "main.py"]
@@ -0,0 +1,23 @@
# Basic example of hosting an agent with the `responses` API and a workflow
This sample demonstrates how to host a workflow using the `responses` API.
## Running the server locally
### Environment setup
Follow the instructions in the [Environment setup](../../README.md#environment-setup) section of the README in the parent directory to set up your environment and install dependencies.
Run the following command to start the server:
```bash
python main.py
```
## Interacting with the agent
Send a POST request to the server with a JSON body containing a "input" field to interact with the agent. For example:
```bash
curl -X POST http://localhost:8088/responses -H "Content-Type: application/json" -d '{"input": "Create a slogan for a new electric SUV that is affordable and fun to drive."}'
```
@@ -0,0 +1,23 @@
name: agent-framework-workflows
description: >
An Agent Framework workflow hosted by Foundry.
metadata:
tags:
- Agent Framework
- AI Agent Hosting
- Azure AI AgentServer
- Responses Protocol
- Streaming
template:
name: agent-framework-workflows
kind: hosted
protocols:
- protocol: responses
version: 1.0.0
environment_variables:
- name: MODEL_DEPLOYMENT_NAME
value: "{{MODEL_DEPLOYMENT_NAME}}"
resources:
- kind: model
id: gpt-4.1-mini
name: MODEL_DEPLOYMENT_NAME
@@ -0,0 +1,8 @@
kind: hosted
name: agent-framework-workflows
protocols:
- protocol: responses
version: 1.0.0
resources:
cpu: "0.25"
memory: 0.5Gi
@@ -0,0 +1,70 @@
# Copyright (c) Microsoft. All rights reserved.
import os
from agent_framework import Agent, AgentExecutor, WorkflowBuilder
from agent_framework.foundry import FoundryChatClient
from agent_framework_foundry_hosting import ResponsesHostServer
from azure.identity import AzureCliCredential
from dotenv import load_dotenv
# Load environment variables from .env file
load_dotenv()
def main():
client = FoundryChatClient(
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
model=os.environ["MODEL_DEPLOYMENT_NAME"],
credential=AzureCliCredential(),
)
writer_agent = Agent(
client=client,
instructions=("You are an excellent slogan writer. You create new slogans based on the given topic."),
name="writer",
)
legal_agent = Agent(
client=client,
instructions=(
"You are an excellent legal reviewer. "
"Make necessary corrections to the slogan so that it is legally compliant."
),
name="legal_reviewer",
)
format_agent = Agent(
client=client,
instructions=(
"You are an excellent content formatter. "
"You take the slogan and format it in a cool retro style when printing to a terminal."
),
name="formatter",
)
# Set the context mode to `last_agent` so that each agent only sees the output of the
# previous agent instead of the full conversation history
writer_executor = AgentExecutor(writer_agent, context_mode="last_agent")
legal_executor = AgentExecutor(legal_agent, context_mode="last_agent")
format_executor = AgentExecutor(format_agent, context_mode="last_agent")
workflow_agent = (
WorkflowBuilder(
start_executor=writer_executor,
# Limiting the output to only the final formatted result.
# If this is not set, all intermediate results will be included in the output.
output_executors=[format_executor],
)
.add_edge(writer_executor, legal_executor)
.add_edge(legal_executor, format_executor)
.build()
.as_agent()
)
server = ResponsesHostServer(workflow_agent)
server.run()
if __name__ == "__main__":
main()
@@ -0,0 +1,2 @@
agent-framework
agent-framework-foundry-hosting
@@ -0,0 +1,11 @@
# Hosting agents with Foundry Hosting and the `responses` API
This folder contains a list of samples that show how to host agents using the `responses` API and deploy them to Foundry Hosting.
| Sample | Description |
| --- | --- |
| [01_basic](./01_basic) | A basic example of hosting an agent with the `responses` API and carrying on a multi-turn conversation. |
| [02_local_tools](./02_local_tools) | An example of hosting an agent with the `responses` API and local tools including a function tool and a local shell tool. |
| [03_remote_mcp](./03_remote_mcp) | An example of hosting an agent with the `responses` API and remote MCPs, including a GitHub MCP server and a Foundry Toolbox. |
| [04_workflows](./04_workflows) | An example of hosting a workflow with the `responses` API. |
| [using_deployed_agent.py](./using_deployed_agent.py) | An example of how to use the deployed agent in Agent Framework. |
@@ -0,0 +1,50 @@
# Copyright (c) Microsoft. All rights reserved.
import asyncio
from agent_framework import Agent, AgentResponse, AgentResponseUpdate, ResponseStream
from agent_framework.openai import OpenAIChatClient
from typing_extensions import Any
"""
This script demonstrates how to talk to a deployed agent using the OpenAIChatClient.
Depending on where you have deployed your agent (local or Foundry Hosting), you may
need to change the base_url when initializing the OpenAIChatClient.
"""
async def print_streaming_response(streaming_response: ResponseStream[AgentResponseUpdate, AgentResponse[Any]]) -> None:
async for chunk in streaming_response:
if chunk.text:
print(chunk.text, end="", flush=True)
async def main() -> None:
agent = Agent(client=OpenAIChatClient(base_url="http://localhost:8088"))
session = agent.create_session()
# First turn
query = "Hi!"
print(f"User: {query}")
print("Agent: ", end="", flush=True)
streaming_response = agent.run(query, session=session, stream=True)
await print_streaming_response(streaming_response)
# Second turn
query = "Your name is Javis. What can you do?"
print(f"\nUser: {query}")
print("Agent: ", end="", flush=True)
streaming_response = agent.run(query, session=session, stream=True)
await print_streaming_response(streaming_response)
# Third turn
query = "What is your name?"
print(f"\nUser: {query}")
print("Agent: ", end="", flush=True)
streaming_response = agent.run(query, session=session, stream=True)
await print_streaming_response(streaming_response)
if __name__ == "__main__":
asyncio.run(main())
@@ -1,145 +0,0 @@
# Hosted Agent Samples
These samples demonstrate how to build and host AI agents in Python using the [Azure AI AgentServer SDK](https://pypi.org/project/azure-ai-agentserver-agentframework/) together with Microsoft Agent Framework. Each sample runs locally as a hosted agent and includes `Dockerfile` and `agent.yaml` assets for deployment to Microsoft Foundry.
## Samples
| Sample | Description |
| ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| [`agent_with_hosted_mcp`](./agent_with_hosted_mcp/) | Hosted MCP tool that connects to Microsoft Learn via `https://learn.microsoft.com/api/mcp` |
| [`agent_with_text_search_rag`](./agent_with_text_search_rag/) | Retrieval-augmented generation using a custom `ContextProvider` with Contoso Outdoors sample data |
| [`agents_in_workflow`](./agents_in_workflow/) | Concurrent workflow that combines researcher, marketer, and legal specialist agents |
| [`agent_with_local_tools`](./agent_with_local_tools/) | Local Python tool execution for Seattle hotel search |
| [`writer_reviewer_agents_in_workflow`](./writer_reviewer_agents_in_workflow/) | Writer/Reviewer workflow using `FoundryChatClient` |
## Common Prerequisites
Before running any sample, ensure you have:
1. Python 3.10 or later
2. [Azure CLI](https://learn.microsoft.com/cli/azure/install-azure-cli) installed
3. An Azure OpenAI resource or a Microsoft Foundry project with a chat model deployment
### Authenticate with Azure CLI
All samples rely on Azure credentials. For local development, the simplest approach is Azure CLI authentication:
```powershell
az login
az account show
```
## Running a Sample
Each sample folder contains its own `requirements.txt`. Run commands from the specific sample directory you want to try.
### Recommended: `uv`
The sample dependencies include preview packages, so allow prerelease installs:
```powershell
cd <sample-directory>
uv venv .venv
uv pip install --prerelease=allow -r requirements.txt
uv run main.py
```
### Alternative: `venv`
Windows PowerShell:
```powershell
cd <sample-directory>
python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -r requirements.txt
python main.py
```
macOS/Linux:
```bash
cd <sample-directory>
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python main.py
```
Each sample starts a hosted agent locally on `http://localhost:8088/`.
## Environment Variable Setup
You can either export variables in your shell or create a local `.env` file in the sample directory.
Example `.env` for Azure OpenAI samples:
```dotenv
AZURE_OPENAI_ENDPOINT=https://<your-openai-resource>.openai.azure.com/
AZURE_OPENAI_MODEL=gpt-4.1
```
Example `.env` for Foundry project samples:
```dotenv
FOUNDRY_PROJECT_ENDPOINT=https://<your-resource>.services.ai.azure.com/api/projects/<your-project>
FOUNDRY_MODEL=gpt-4.1
```
## Interacting with the Agent
After starting a sample, send requests to the Responses endpoint.
PowerShell:
```powershell
$body = @{
input = "Your question here"
stream = $false
} | ConvertTo-Json
Invoke-RestMethod -Uri "http://localhost:8088/responses" -Method Post -Body $body -ContentType "application/json"
```
curl:
```bash
curl -sS -H "Content-Type: application/json" -X POST http://localhost:8088/responses \
-d '{"input":"Your question here","stream":false}'
```
Example prompts by sample:
| Sample | Example input |
| ------------------------------------ | ---------------------------------------------------------------------------- |
| `agent_with_hosted_mcp` | `What does Microsoft Learn say about managed identities in Azure?` |
| `agent_with_text_search_rag` | `What is Contoso Outdoors' return policy for refunds?` |
| `agents_in_workflow` | `Create a launch strategy for a budget-friendly electric SUV.` |
| `agent_with_local_tools` | `Find me Seattle hotels from 2025-03-15 to 2025-03-18 under $200 per night.` |
| `writer_reviewer_agents_in_workflow` | `Write a slogan for a new affordable electric SUV.` |
## Deploying to Microsoft Foundry
Each sample includes a `Dockerfile` and `agent.yaml` for deployment. For deployment steps, follow the hosted agents guidance in Microsoft Foundry:
- [Hosted agents overview](https://learn.microsoft.com/en-us/azure/ai-foundry/agents/concepts/hosted-agents)
- [Create a hosted agent with CLI](https://learn.microsoft.com/en-us/azure/ai-foundry/agents/concepts/hosted-agents?tabs=cli#create-a-hosted-agent)
- [Create a hosted agent in Visual Studio Code](https://learn.microsoft.com/en-us/azure/foundry/agents/how-to/vs-code-agents-workflow-pro-code?tabs=windows-powershell&pivots=python)
## Troubleshooting
### Missing Azure credentials
If startup fails with authentication errors, run `az login` and verify the selected subscription with `az account show`.
### Preview package install issues
These samples depend on preview packages such as `azure-ai-agentserver-agentframework`. Use `uv pip install --prerelease=allow -r requirements.txt` or `pip install -r requirements.txt`.
### ARM64 container images fail after deployment
If you build images locally on ARM64 hardware such as Apple Silicon, build for `linux/amd64`:
```bash
docker build --platform=linux/amd64 -t image .
```
@@ -1,30 +0,0 @@
# Unique identifier/name for this agent
name: agent-with-hosted-mcp
# Brief description of what this agent does
description: >
An AI agent that uses Azure OpenAI with a Hosted Model Context Protocol (MCP) server.
The agent answers questions by searching Microsoft Learn documentation using MCP tools.
metadata:
# Categorization tags for organizing and discovering agents
authors:
- Microsoft Agent Framework Team
tags:
- Azure AI AgentServer
- Microsoft Agent Framework
- Model Context Protocol
- MCP
template:
name: agent-with-hosted-mcp
# The type of agent - "hosted" for HOBO, "container" for COBO
kind: hosted
protocols:
- protocol: responses
environment_variables:
- name: AZURE_OPENAI_ENDPOINT
value: ${AZURE_OPENAI_ENDPOINT}
- name: AZURE_OPENAI_MODEL
value: "{{chat}}"
resources:
- kind: model
id: gpt-4o-mini
name: chat
@@ -1,34 +0,0 @@
# Copyright (c) Microsoft. All rights reserved.
from agent_framework import Agent
from agent_framework.foundry import FoundryChatClient
from azure.ai.agentserver.agentframework import from_agent_framework # pyright: ignore[reportUnknownVariableType]
from azure.identity import AzureCliCredential
from dotenv import load_dotenv
# Load environment variables from .env file
load_dotenv()
def main():
client = FoundryChatClient(credential=AzureCliCredential())
# Create MCP tool configuration as dict
mcp_tool = client.get_mcp_tool(
name="Microsoft_Learn_MCP",
url="https://learn.microsoft.com/api/mcp",
)
# Create an Agent using the Azure OpenAI Chat Client with a MCP Tool that connects to Microsoft Learn MCP
agent = Agent(
client=client,
name="DocsAgent",
instructions="You are a helpful assistant that can help with microsoft documentation questions.",
tools=[mcp_tool],
)
# Run the agent as a hosted agent
from_agent_framework(agent).run()
if __name__ == "__main__":
main()
@@ -1,2 +0,0 @@
azure-ai-agentserver-agentframework==1.0.0b16
agent-framework
@@ -1,66 +0,0 @@
# Virtual environments
.venv/
venv/
env/
.python-version
# Environment files with secrets
.env
.env.*
*.local
# Python build artifacts
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# Testing
.tox/
.nox/
.coverage
.coverage.*
htmlcov/
.pytest_cache/
.mypy_cache/
# IDE and OS files
.DS_Store
.idea/
.vscode/
*.swp
*.swo
*~
# Foundry config
.foundry/
build-source-*/
# Git
.git/
.gitignore
# Docker
.dockerignore
# Documentation
docs/
*.md
!README.md
LICENSE
@@ -1,3 +0,0 @@
# IMPORTANT: Never commit .env to version control - add it to .gitignore
FOUNDRY_PROJECT_ENDPOINT=
FOUNDRY_MODEL=
@@ -1,162 +0,0 @@
**IMPORTANT!** All samples and other resources made available in this GitHub repository ("samples") are designed to assist in accelerating development of agents, solutions, and agent workflows for various scenarios. Review all provided resources and carefully test output behavior in the context of your use case. AI responses may be inaccurate and AI actions should be monitored with human oversight. Learn more in the transparency documents for [Agent Service](https://learn.microsoft.com/en-us/azure/ai-foundry/responsible-ai/agents/transparency-note) and [Agent Framework](https://github.com/microsoft/agent-framework/blob/main/TRANSPARENCY_FAQ.md).
Agents, solutions, or other output you create may be subject to legal and regulatory requirements, may require licenses, or may not be suitable for all industries, scenarios, or use cases. By using any sample, you are acknowledging that any output created using those samples are solely your responsibility, and that you will comply with all applicable laws, regulations, and relevant safety standards, terms of service, and codes of conduct.
Third-party samples contained in this folder are subject to their own designated terms, and they have not been tested or verified by Microsoft or its affiliates.
Microsoft has no responsibility to you or others with respect to any of these samples or any resulting output.
# What this sample demonstrates
This sample demonstrates a **key advantage of code-based hosted agents**:
- **Local Python tool execution** - Run custom Python functions as agent tools
Code-based agents can execute **any Python code** you write. This sample includes a Seattle Hotel Agent with a `get_available_hotels` tool that searches for available hotels based on check-in/check-out dates and budget preferences.
The agent is hosted using the [Azure AI AgentServer SDK](https://pypi.org/project/azure-ai-agentserver-agentframework/) and can be deployed to Microsoft Foundry using the Azure Developer CLI.
## How It Works
### Local Tools Integration
In [main.py](main.py), the agent uses a local Python function (`get_available_hotels`) that simulates a hotel availability API. This demonstrates how code-based agents can execute custom server-side logic that prompt agents cannot access.
The tool accepts:
- **check_in_date** - Check-in date in YYYY-MM-DD format
- **check_out_date** - Check-out date in YYYY-MM-DD format
- **max_price** - Maximum price per night in USD (optional, defaults to $500)
### Agent Hosting
The agent is hosted using the [Azure AI AgentServer SDK](https://pypi.org/project/azure-ai-agentserver-agentframework/),
which provisions a REST API endpoint compatible with the OpenAI Responses protocol.
### Agent Deployment
The hosted agent can be deployed to Microsoft Foundry using the Azure Developer CLI [ai agent](https://learn.microsoft.com/en-us/azure/ai-foundry/agents/concepts/hosted-agents?view=foundry&tabs=cli#create-a-hosted-agent) extension.
## Running the Agent Locally
### Prerequisites
Before running this sample, ensure you have:
1. **Microsoft Foundry Project**
- Project created in [Microsoft Foundry](https://learn.microsoft.com/en-us/azure/ai-foundry/what-is-foundry?view=foundry#microsoft-foundry-portals)
- Chat model deployed (e.g., `gpt-4o` or `gpt-4.1`)
- Note your project endpoint URL and model deployment name
2. **Azure CLI**
- Installed and authenticated
- Run `az login` and verify with `az account show`
3. **Python 3.10 or higher**
- Verify your version: `python --version`
### Environment Variables
Set the following environment variables (matching `agent.yaml`):
- `FOUNDRY_PROJECT_ENDPOINT` - Your Microsoft Foundry project endpoint URL (required)
- `FOUNDRY_MODEL` - The deployment name for your chat model (defaults to `gpt-4.1-mini`)
This sample loads environment variables from a local `.env` file if present.
Create a `.env` file in this directory with the following content:
```
FOUNDRY_PROJECT_ENDPOINT=https://<your-resource>.services.ai.azure.com/api/projects/<your-project>
FOUNDRY_MODEL=gpt-4.1-mini
```
Or set them via PowerShell:
```powershell
# Replace with your actual values
$env:FOUNDRY_PROJECT_ENDPOINT="https://<your-resource>.services.ai.azure.com/api/projects/<your-project>"
$env:FOUNDRY_MODEL="gpt-4.1-mini"
```
### Running the Sample
**Recommended (`uv`):**
We recommend using [uv](https://docs.astral.sh/uv/) to create and manage the virtual environment for this sample.
```bash
uv venv .venv
uv pip install --prerelease=allow -r requirements.txt
uv run main.py
```
The sample depends on preview packages, so `--prerelease=allow` is required when installing with `uv`.
**Alternative (`venv`):**
If you do not have `uv` installed, you can use Python's built-in `venv` module instead:
**Windows (PowerShell):**
```powershell
python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -r requirements.txt
python main.py
```
**macOS/Linux:**
```bash
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python main.py
```
This will start the hosted agent locally on `http://localhost:8088/`.
### Interacting with the Agent
**PowerShell (Windows):**
```powershell
$body = @{
input = "I need a hotel in Seattle from 2025-03-15 to 2025-03-18, budget under $200 per night"
stream = $false
} | ConvertTo-Json
Invoke-RestMethod -Uri http://localhost:8088/responses -Method Post -Body $body -ContentType "application/json"
```
**Bash/curl (Linux/macOS):**
```bash
curl -sS -H "Content-Type: application/json" -X POST http://localhost:8088/responses \
-d '{"input": "Find me hotels in Seattle for March 20-23, 2025 under $200 per night","stream":false}'
```
The agent will use the `get_available_hotels` tool to search for available hotels matching your criteria.
### Deploying the Agent to Microsoft Foundry
To deploy your agent to Microsoft Foundry, follow the comprehensive deployment guide at https://learn.microsoft.com/en-us/azure/ai-foundry/agents/concepts/hosted-agents?view=foundry&tabs=cli
## Troubleshooting
### Images built on Apple Silicon or other ARM64 machines do not work on our service
We **recommend using `azd` cloud build**, which always builds images with the correct architecture.
If you choose to **build locally**, and your machine is **not `linux/amd64`** (for example, an Apple Silicon Mac), the image will **not be compatible with our service**, causing runtime failures.
**Fix for local builds**
Use this command to build the image locally:
```shell
docker build --platform=linux/amd64 -t image .
```
This forces the image to be built for the required `amd64` architecture.
@@ -1,27 +0,0 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/microsoft/AgentSchema/refs/heads/main/schemas/v1.0/ContainerAgent.yaml
kind: hosted
name: agent-with-local-tools
# Brief description of what this agent does
description: >
A travel assistant agent that helps users find hotels in Seattle.
Demonstrates local Python tool execution - a key advantage of code-based
hosted agents over prompt agents.
metadata:
# Categorization tags for organizing and discovering agents
authors:
- Microsoft
tags:
- Azure AI AgentServer
- Microsoft Agent Framework
- Local Tools
- Travel Assistant
- Hotel Search
protocols:
- protocol: responses
version: v1
environment_variables:
- name: FOUNDRY_PROJECT_ENDPOINT
value: ${FOUNDRY_PROJECT_ENDPOINT}
- name: FOUNDRY_MODEL
value: ${FOUNDRY_MODEL}
@@ -1,144 +0,0 @@
# Copyright (c) Microsoft. All rights reserved.
"""
Seattle Hotel Agent - A simple agent with a tool to find hotels in Seattle.
Uses Microsoft Agent Framework with Azure AI Foundry.
Ready for deployment to Foundry Hosted Agent service.
"""
import asyncio
import os
from datetime import datetime
from typing import Annotated
from agent_framework import Agent
from agent_framework.foundry import FoundryChatClient
from azure.ai.agentserver.agentframework import from_agent_framework
from azure.identity.aio import AzureCliCredential, ManagedIdentityCredential
# Configure these for your Foundry project
# Read the explicit variables present in the .env file
FOUNDRY_PROJECT_ENDPOINT = os.getenv("FOUNDRY_PROJECT_ENDPOINT") # e.g., "https://<project>.services.ai.azure.com"
FOUNDRY_MODEL = os.getenv("FOUNDRY_MODEL", "gpt-4.1-mini") # Your model deployment name e.g., "gpt-4.1-mini"
# Simulated hotel data for Seattle
SEATTLE_HOTELS = [
{
"name": "Contoso Suites",
"price_per_night": 189,
"rating": 4.5,
"location": "Downtown",
},
{
"name": "Fabrikam Residences",
"price_per_night": 159,
"rating": 4.2,
"location": "Pike Place Market",
},
{
"name": "Alpine Ski House",
"price_per_night": 249,
"rating": 4.7,
"location": "Seattle Center",
},
{
"name": "Margie's Travel Lodge",
"price_per_night": 219,
"rating": 4.4,
"location": "Waterfront",
},
{
"name": "Northwind Inn",
"price_per_night": 139,
"rating": 4.0,
"location": "Capitol Hill",
},
{
"name": "Relecloud Hotel",
"price_per_night": 99,
"rating": 3.8,
"location": "University District",
},
]
def get_available_hotels(
check_in_date: Annotated[str, "Check-in date in YYYY-MM-DD format"],
check_out_date: Annotated[str, "Check-out date in YYYY-MM-DD format"],
max_price: Annotated[int, "Maximum price per night in USD (optional)"] = 500,
) -> str:
"""
Get available hotels in Seattle for the specified dates.
This simulates a call to a fake hotel availability API.
"""
try:
# Parse dates
check_in = datetime.strptime(check_in_date, "%Y-%m-%d")
check_out = datetime.strptime(check_out_date, "%Y-%m-%d")
# Validate dates
if check_out <= check_in:
return "Error: Check-out date must be after check-in date."
nights = (check_out - check_in).days
# Filter hotels by price
available_hotels = [hotel for hotel in SEATTLE_HOTELS if hotel["price_per_night"] <= max_price]
if not available_hotels:
return f"No hotels found in Seattle within your budget of ${max_price}/night."
# Build response
result = f"Available hotels in Seattle from {check_in_date} to {check_out_date} ({nights} nights):\n\n"
for hotel in available_hotels:
total_cost = hotel["price_per_night"] * nights
result += f"**{hotel['name']}**\n"
result += f" Location: {hotel['location']}\n"
result += f" Rating: {hotel['rating']}/5\n"
result += f" ${hotel['price_per_night']}/night (Total: ${total_cost})\n\n"
return result
except ValueError as e:
return f"Error parsing dates. Please use YYYY-MM-DD format. Details: {str(e)}"
def get_credential():
"""Will use Managed Identity when running in Azure, otherwise falls back to Azure CLI Credential."""
return ManagedIdentityCredential() if os.getenv("MSI_ENDPOINT") else AzureCliCredential()
async def main():
"""Main function to run the agent as a web server."""
async with get_credential() as credential:
client = FoundryChatClient(
project_endpoint=FOUNDRY_PROJECT_ENDPOINT,
model=FOUNDRY_MODEL,
credential=credential,
)
agent = Agent(
client=client,
name="SeattleHotelAgent",
instructions="""You are a helpful travel assistant specializing in finding hotels in Seattle, Washington.
When a user asks about hotels in Seattle:
1. Ask for their check-in and check-out dates if not provided
2. Ask about their budget preferences if not mentioned
3. Use the get_available_hotels tool to find available options
4. Present the results in a friendly, informative way
5. Offer to help with additional questions about the hotels or Seattle
Be conversational and helpful. If users ask about things outside of Seattle hotels,
politely let them know you specialize in Seattle hotel recommendations.""",
tools=[get_available_hotels],
)
print("Seattle Hotel Agent Server running on http://localhost:8088")
server = from_agent_framework(agent)
await server.run_async()
if __name__ == "__main__":
asyncio.run(main())
@@ -1,2 +0,0 @@
azure-ai-agentserver-agentframework==1.0.0b16
agent-framework-foundry
@@ -1,33 +0,0 @@
# Unique identifier/name for this agent
name: agent-with-text-search-rag
# Brief description of what this agent does
description: >
An AI agent that uses a ContextProvider for retrieval augmented generation (RAG) capabilities.
The agent runs searches against an external knowledge base before each model invocation and
injects the results into the model context. It can answer questions about Contoso Outdoors
policies and products, including return policies, refunds, shipping options, and product care
instructions such as tent maintenance.
metadata:
# Categorization tags for organizing and discovering agents
authors:
- Microsoft Agent Framework Team
tags:
- Azure AI AgentServer
- Microsoft Agent Framework
- Retrieval-Augmented Generation
- RAG
template:
name: agent-with-text-search-rag
# The type of agent - "hosted" for HOBO, "container" for COBO
kind: hosted
protocols:
- protocol: responses
environment_variables:
- name: AZURE_OPENAI_ENDPOINT
value: ${AZURE_OPENAI_ENDPOINT}
- name: AZURE_OPENAI_MODEL
value: "{{chat}}"
resources:
- kind: model
id: gpt-4o-mini
name: chat
@@ -1,123 +0,0 @@
# Copyright (c) Microsoft. All rights reserved.
import json
import sys
from dataclasses import dataclass
from typing import Any
from agent_framework import Agent, AgentSession, ContextProvider, Message, SessionContext
from agent_framework.foundry import FoundryChatClient
from azure.ai.agentserver.agentframework import from_agent_framework # pyright: ignore[reportUnknownVariableType]
from azure.identity import DefaultAzureCredential
from dotenv import load_dotenv
if sys.version_info >= (3, 12):
from typing import override
else:
from typing_extensions import override
# Load environment variables from .env file
load_dotenv()
@dataclass
class TextSearchResult:
source_name: str
source_link: str
text: str
class TextSearchContextProvider(ContextProvider):
"""A simple context provider that simulates text search results based on keywords in the user's message."""
def __init__(self):
super().__init__("text-search")
def _get_most_recent_message(self, messages: list[Message]) -> Message:
"""Helper method to extract the most recent message from the input."""
if messages:
return messages[-1]
raise ValueError("No messages provided")
@override
async def before_run(
self,
*,
agent: Any,
session: AgentSession | None,
context: SessionContext,
state: dict[str, Any],
) -> None:
messages = context.get_messages()
if not messages:
return
message = self._get_most_recent_message(messages)
query = message.text.lower()
results: list[TextSearchResult] = []
if "return" in query and "refund" in query:
results.append(
TextSearchResult(
source_name="Contoso Outdoors Return Policy",
source_link="https://contoso.com/policies/returns",
text=(
"Customers may return any item within 30 days of delivery. "
"Items should be unused and include original packaging. "
"Refunds are issued to the original payment method within 5 business days of inspection."
),
)
)
if "shipping" in query:
results.append(
TextSearchResult(
source_name="Contoso Outdoors Shipping Guide",
source_link="https://contoso.com/help/shipping",
text=(
"Standard shipping is free on orders over $50 and typically arrives in 3-5 business days "
"within the continental United States. Expedited options are available at checkout."
),
)
)
if "tent" in query or "fabric" in query:
results.append(
TextSearchResult(
source_name="TrailRunner Tent Care Instructions",
source_link="https://contoso.com/manuals/trailrunner-tent",
text=(
"Clean the tent fabric with lukewarm water and a non-detergent soap. "
"Allow it to air dry completely before storage and avoid prolonged UV "
"exposure to extend the lifespan of the waterproof coating."
),
)
)
if not results:
return
context.extend_messages(
self.source_id,
[Message(role="user", contents=["\n\n".join(json.dumps(result.__dict__, indent=2) for result in results)])],
)
def main():
# Create an Agent using the Azure OpenAI Chat Client
agent = Agent(
client=FoundryChatClient(credential=DefaultAzureCredential()),
name="SupportSpecialist",
instructions=(
"You are a helpful support specialist for Contoso Outdoors. "
"Answer questions using the provided context and cite the source document when available."
),
context_providers=[TextSearchContextProvider()],
)
# Run the agent as a hosted agent
from_agent_framework(agent).run()
if __name__ == "__main__":
main()
@@ -1,2 +0,0 @@
azure-ai-agentserver-agentframework==1.0.0b3
agent-framework
@@ -1,28 +0,0 @@
# Unique identifier/name for this agent
name: agents-in-workflow
# Brief description of what this agent does
description: >
A workflow agent that responds to product launch strategy inquiries by concurrently leveraging insights from three specialized agents.
metadata:
# Categorization tags for organizing and discovering agents
authors:
- Microsoft Agent Framework Team
tags:
- Azure AI AgentServer
- Microsoft Agent Framework
- Workflows
template:
name: agents-in-workflow
# The type of agent - "hosted" for HOBO, "container" for COBO
kind: hosted
protocols:
- protocol: responses
environment_variables:
- name: AZURE_OPENAI_ENDPOINT
value: ${AZURE_OPENAI_ENDPOINT}
- name: AZURE_OPENAI_MODEL
value: "{{chat}}"
resources:
- kind: model
id: gpt-4o-mini
name: chat
@@ -1,52 +0,0 @@
# Copyright (c) Microsoft. All rights reserved.
from agent_framework import Agent
from agent_framework.foundry import FoundryChatClient
from agent_framework_orchestrations import ConcurrentBuilder
from azure.ai.agentserver.agentframework import from_agent_framework
from azure.identity import DefaultAzureCredential # pyright: ignore[reportUnknownVariableType]
from dotenv import load_dotenv
# Load environment variables from .env file
load_dotenv()
def main():
# Create agents
researcher = Agent(
client=FoundryChatClient(credential=DefaultAzureCredential()),
instructions=(
"You're an expert market and product researcher. "
"Given a prompt, provide concise, factual insights, opportunities, and risks."
),
name="researcher",
)
marketer = Agent(
client=FoundryChatClient(credential=DefaultAzureCredential()),
instructions=(
"You're a creative marketing strategist. "
"Craft compelling value propositions and target messaging aligned to the prompt."
),
name="marketer",
)
legal = Agent(
client=FoundryChatClient(credential=DefaultAzureCredential()),
instructions=(
"You're a cautious legal/compliance reviewer. "
"Highlight constraints, disclaimers, and policy concerns based on the prompt."
),
name="legal",
)
# Build a concurrent workflow
workflow = ConcurrentBuilder(participants=[researcher, marketer, legal]).build()
# Convert the workflow to an agent
workflow_agent = Agent(client=workflow)
# Run the agent as a hosted agent
from_agent_framework(workflow_agent).run()
if __name__ == "__main__":
main()
@@ -1,2 +0,0 @@
azure-ai-agentserver-agentframework==1.0.0b3
agent-framework
@@ -1,51 +0,0 @@
# Build artifacts
bin/
obj/
# IDE and editor files
.vs/
.vscode/
*.user
*.suo
.foundry/
# Source control
.git/
# Documentation
README.md
# Ignore files
.gitignore
.dockerignore
# Logs
*.log
# Temporary files
*.tmp
*.temp
# OS files
.DS_Store
Thumbs.db
# Package manager directories
node_modules/
packages/
# Test results
TestResults/
*.trx
# Coverage reports
coverage/
*.coverage
*.coveragexml
# Local development config
appsettings.Development.json
.env
.venv/
__pycache__/
@@ -1,3 +0,0 @@
# IMPORTANT: Never commit .env to version control - add it to .gitignore
FOUNDRY_PROJECT_ENDPOINT=
FOUNDRY_MODEL=
@@ -1,157 +0,0 @@
**IMPORTANT!** All samples and other resources made available in this GitHub repository ("samples") are designed to assist in accelerating development of agents, solutions, and agent workflows for various scenarios. Review all provided resources and carefully test output behavior in the context of your use case. AI responses may be inaccurate and AI actions should be monitored with human oversight. Learn more in the transparency documents for [Agent Service](https://learn.microsoft.com/en-us/azure/ai-foundry/responsible-ai/agents/transparency-note) and [Agent Framework](https://github.com/microsoft/agent-framework/blob/main/TRANSPARENCY_FAQ.md).
Agents, solutions, or other output you create may be subject to legal and regulatory requirements, may require licenses, or may not be suitable for all industries, scenarios, or use cases. By using any sample, you are acknowledging that any output created using those samples are solely your responsibility, and that you will comply with all applicable laws, regulations, and relevant safety standards, terms of service, and codes of conduct.
Third-party samples contained in this folder are subject to their own designated terms, and they have not been tested or verified by Microsoft or its affiliates.
Microsoft has no responsibility to you or others with respect to any of these samples or any resulting output.
# What this sample demonstrates
This sample demonstrates a **key advantage of code-based hosted agents**:
- **Agents in Workflows** - Use AI agents as executors within a workflow pipeline
Code-based agents can execute **any Python code** you write. This sample includes a multi-agent workflow where Writer and Reviewer agents collaborate to draft content and provide review feedback.
The agent is hosted using the [Azure AI AgentServer SDK](https://pypi.org/project/azure-ai-agentserver-agentframework/) and can be deployed to Microsoft Foundry using the Azure Developer CLI.
## How It Works
### Agents in Workflows
This sample demonstrates the integration of AI agents within a workflow pipeline. The workflow operates as follows:
1. **Writer Agent** - Drafts content
2. **Reviewer Agent** - Reviews the draft and provides concise, actionable feedback
### Agent Hosting
The agent workflow is hosted using the [Azure AI AgentServer SDK](https://pypi.org/project/azure-ai-agentserver-agentframework/),
which provisions a REST API endpoint compatible with the OpenAI Responses protocol.
### Agent Deployment
The hosted agent workflow can be deployed to Microsoft Foundry using the Azure Developer CLI [ai agent](https://learn.microsoft.com/en-us/azure/ai-foundry/agents/concepts/hosted-agents?view=foundry&tabs=cli#create-a-hosted-agent) extension.
## Running the Agent Locally
### Prerequisites
Before running this sample, ensure you have:
1. **Microsoft Foundry Project**
- Project created in [Microsoft Foundry](https://learn.microsoft.com/en-us/azure/ai-foundry/what-is-foundry?view=foundry#microsoft-foundry-portals)
- Chat model deployed (e.g., `gpt-4o` or `gpt-4.1`)
- Note your project endpoint URL and model deployment name
2. **Azure CLI**
- Installed and authenticated
- Run `az login` and verify with `az account show`
3. **Python 3.10 or higher**
- Verify your version: `python --version`
### Environment Variables
Set the following environment variables (matching `agent.yaml`):
- `FOUNDRY_PROJECT_ENDPOINT` - Your Microsoft Foundry project endpoint URL (required)
- `FOUNDRY_MODEL` - The deployment name for your chat model (defaults to `gpt-4.1-mini`)
This sample loads environment variables from a local `.env` file if present.
Create a `.env` file in this directory with the following content:
```
FOUNDRY_PROJECT_ENDPOINT=https://<your-resource>.services.ai.azure.com/api/projects/<your-project>
FOUNDRY_MODEL=gpt-4.1-mini
```
Or set them via PowerShell:
```powershell
# Replace with your actual values
$env:FOUNDRY_PROJECT_ENDPOINT="https://<your-resource>.services.ai.azure.com/api/projects/<your-project>"
$env:FOUNDRY_MODEL="gpt-4.1-mini"
```
### Running the Sample
**Recommended (`uv`):**
We recommend using [uv](https://docs.astral.sh/uv/) to create and manage the virtual environment for this sample.
```bash
uv venv .venv
uv pip install --prerelease=allow -r requirements.txt
uv run main.py
```
The sample depends on preview packages, so `--prerelease=allow` is required when installing with `uv`.
**Alternative (`venv`):**
If you do not have `uv` installed, you can use Python's built-in `venv` module instead:
**Windows (PowerShell):**
```powershell
python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -r requirements.txt
python main.py
```
**macOS/Linux:**
```bash
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python main.py
```
This will start the hosted agent locally on `http://localhost:8088/`.
### Interacting with the Agent
**PowerShell (Windows):**
```powershell
$body = @{
input = "Create a slogan for a new electric SUV that is affordable and fun to drive."
stream = $false
} | ConvertTo-Json
Invoke-RestMethod -Uri http://localhost:8088/responses -Method Post -Body $body -ContentType "application/json"
```
**Bash/curl (Linux/macOS):**
```bash
curl -sS -H "Content-Type: application/json" -X POST http://localhost:8088/responses \
-d '{"input": "Create a slogan for a new electric SUV that is affordable and fun to drive.","stream":false}'
```
### Deploying the Agent to Microsoft Foundry
To deploy your agent to Microsoft Foundry, follow the comprehensive deployment guide at https://learn.microsoft.com/en-us/azure/ai-foundry/agents/concepts/hosted-agents?view=foundry&tabs=cli
## Troubleshooting
### Images built on Apple Silicon or other ARM64 machines do not work on our service
We **recommend using `azd` cloud build**, which always builds images with the correct architecture.
If you choose to **build locally**, and your machine is **not `linux/amd64`** (for example, an Apple Silicon Mac), the image will **not be compatible with our service**, causing runtime failures.
**Fix for local builds**
Use this command to build the image locally:
```shell
docker build --platform=linux/amd64 -t image .
```
This forces the image to be built for the required `amd64` architecture.
@@ -1,24 +0,0 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/microsoft/AgentSchema/refs/heads/main/schemas/v1.0/ContainerAgent.yaml
kind: hosted
name: writer-reviewer-agents-in-workflow
description: >
A multi-agent workflow featuring a Writer and Reviewer that collaborate
to create and refine content.
metadata:
authors:
- Microsoft
tags:
- Azure AI AgentServer
- Microsoft Agent Framework
- Multi-Agent Workflow
- Writer-Reviewer
- Content Creation
protocols:
- protocol: responses
version: v1
environment_variables:
- name: FOUNDRY_PROJECT_ENDPOINT
value: ${FOUNDRY_PROJECT_ENDPOINT}
- name: FOUNDRY_MODEL
value: ${FOUNDRY_MODEL}
@@ -1,71 +0,0 @@
# Copyright (c) Microsoft. All rights reserved.
import asyncio
import os
from contextlib import asynccontextmanager
from agent_framework import Agent, WorkflowBuilder
from agent_framework.foundry import FoundryChatClient
from azure.ai.agentserver.agentframework import from_agent_framework
from azure.identity.aio import AzureCliCredential, ManagedIdentityCredential
from dotenv import load_dotenv
load_dotenv(override=True)
# Configure these for your Foundry project
# Read the explicit variables present in the .env file
FOUNDRY_PROJECT_ENDPOINT = os.getenv(
"FOUNDRY_PROJECT_ENDPOINT"
) # e.g., "https://<project>.services.ai.azure.com/api/projects/<project-name>"
FOUNDRY_MODEL = os.getenv("FOUNDRY_MODEL", "gpt-4.1-mini") # Your model deployment name e.g., "gpt-4.1-mini"
def get_credential():
"""Will use Managed Identity when running in Azure, otherwise falls back to Azure CLI Credential."""
return ManagedIdentityCredential() if os.getenv("MSI_ENDPOINT") else AzureCliCredential()
@asynccontextmanager
async def create_agents():
async with get_credential() as credential:
client = FoundryChatClient(
project_endpoint=FOUNDRY_PROJECT_ENDPOINT,
model=FOUNDRY_MODEL,
credential=credential,
)
writer = Agent(
client=client,
name="Writer",
instructions="You are an excellent content writer. You create new content and edit contents based on the feedback.",
)
reviewer = Agent(
client=client,
name="Reviewer",
instructions="You are an excellent content reviewer. Provide actionable feedback to the writer about the provided content in the most concise manner possible.",
)
yield writer, reviewer
def create_workflow(writer, reviewer):
workflow = WorkflowBuilder(start_executor=writer).add_edge(writer, reviewer).build()
return Agent(
client=workflow,
)
async def main() -> None:
"""
The writer and reviewer multi-agent workflow.
Environment variables required:
- FOUNDRY_PROJECT_ENDPOINT: Your Microsoft Foundry project endpoint
- FOUNDRY_MODEL: Your Microsoft Foundry model deployment name
"""
async with create_agents() as (writer, reviewer):
agent = create_workflow(writer, reviewer)
await from_agent_framework(agent).run_async()
if __name__ == "__main__":
asyncio.run(main())
@@ -1,2 +0,0 @@
azure-ai-agentserver-agentframework==1.0.0b16
agent-framework-foundry