Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"dockerComposeFile": "docker-compose.yml",
"service": "app",
"workspaceFolder": "/workspace",
"forwardPorts": [5432, 6379, 18888],
"forwardPorts": [5432, 6379, 8082, 18888],
Comment thread
madebygps marked this conversation as resolved.
"features": {
"ghcr.io/azure/azure-dev/azd:latest": {},
"ghcr.io/devcontainers/features/azure-cli:latest": {},
Expand Down
8 changes: 8 additions & 0 deletions .devcontainer/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ services:
- OTEL_EXPORTER_OTLP_PROTOCOL=grpc
- REDIS_URL=redis://redis:6379
- POSTGRES_URL=postgresql://admin:LocalPasswordOnly@db:5432/postgres
- DTS_ENDPOINT=http://dts-emulator:8080

db:
image: pgvector/pgvector:pg17
Expand All @@ -37,5 +38,12 @@ services:
environment:
- DASHBOARD__FRONTEND__AUTHMODE=Unsecured

dts-emulator:
image: mcr.microsoft.com/dts/dts-emulator:latest
restart: unless-stopped
ports:
- "8080:8080"
- "8082:8082"

volumes:
postgres-data:
4 changes: 4 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,7 @@ AZURE_SEARCH_ENDPOINT=https://YOUR-SEARCH-SERVICE.search.windows.net
AZURE_SEARCH_KNOWLEDGE_BASE_NAME=YOUR-KB-NAME
# Optional: Set to log evaluation results to Microsoft Foundry for rich visualization
AZURE_AI_PROJECT=https://YOUR-ACCOUNT.services.ai.azure.com/api/projects/YOUR-PROJECT
# Configure for Durable Task Scheduler (used by agent_durabletask.py):
# In the dev container the emulator is at http://dts-emulator:8080; use http://localhost:8080 on the host.
DTS_ENDPOINT=http://localhost:8080
DTS_TASKHUB=default
12 changes: 12 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,18 @@ Start the MCP server first: `uv run python examples/mcp_server.py`
| `agent_mcp_local.py` | Local MCP server (stdio) |
| `agent_mcp_remote.py` | Remote MCP server (SSE) |

### Requires Durable Task Scheduler (dev container or Azure)

The DTS emulator runs automatically in the dev container. Outside the dev container, start it manually: `docker run -d --name dts-emulator -p 8080:8080 -p 8082:8082 mcr.microsoft.com/dts/dts-emulator:latest`

Alternatively, deploy an Azure-hosted scheduler: `azd env set DEPLOY_DTS true && azd provision`

Emulator dashboard: `http://localhost:8082` | Azure dashboard: `https://dashboard.durabletask.io/`

| Examples | Notes |
|----------|-------|
| `agent_durabletask.py` | Durable Task persistence — IT support conversation survives worker restart |

### Requires OTel / Aspire

| Examples | Notes |
Expand Down
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,14 @@ The dev container includes a Redis server, which is used by the `agent_history_r
docker run -d -p 5432:5432 -e POSTGRES_USER=admin -e POSTGRES_PASSWORD=LocalPasswordOnly pgvector/pgvector:pg17
```

6. *Optional:* To run the `agent_durabletask.py` example locally, you need the DTS emulator:

```shell
docker run -d --name dts-emulator -p 8080:8080 -p 8082:8082 mcr.microsoft.com/dts/dts-emulator:latest
```

The DTS dashboard is available at <http://localhost:8082>. To use an Azure-hosted scheduler instead, see [Deploying the Durable Task Scheduler](#deploying-the-durable-task-scheduler).

## Configuring model providers

These examples can be run with Microsoft Foundry or OpenAI.com, depending on the environment variables you set. All the scripts reference the environment variables from a `.env` file, and an example `.env.sample` file is provided. Host-specific instructions are below.
Expand Down Expand Up @@ -211,6 +219,7 @@ You can run the examples in this repository by executing the scripts in the `exa
| [agent_evaluation.py](examples/agent_evaluation.py) | Evaluate a travel planner agent using [Azure AI Evaluation](https://learn.microsoft.com/azure/ai-foundry/concepts/evaluation-evaluators/agent-evaluators) agent evaluators (IntentResolution, ToolCallAccuracy, TaskAdherence, ResponseCompleteness). Optionally set `AZURE_AI_PROJECT` in `.env` to log results to [Microsoft Foundry](https://learn.microsoft.com/azure/ai-foundry/how-to/develop/agent-evaluate-sdk). |
| [agent_evaluation_batch.py](examples/agent_evaluation_batch.py) | Batch evaluation of agent responses using Azure AI Evaluation's `evaluate()` function. |
| [agent_redteam.py](examples/agent_redteam.py) | Red-team a financial advisor agent using [Azure AI Evaluation](https://learn.microsoft.com/azure/ai-foundry/how-to/develop/red-teaming-agent) to test resilience against adversarial attacks across risk categories (Violence, HateUnfairness, Sexual, SelfHarm). Requires `AZURE_AI_PROJECT` in `.env`. |
| [agent_durabletask.py](examples/agent_durabletask.py) | Durable Task persistence — an IT support agent's conversation state survives a worker restart via [Durable Task Scheduler](https://learn.microsoft.com/azure/durable-task-scheduler/). |

## Using the Aspire Dashboard for telemetry

Expand Down Expand Up @@ -307,6 +316,32 @@ After running the example, navigate to your Application Insights resource in the

Telemetry data may take 2–5 minutes to appear in the portal.

## Deploying the Durable Task Scheduler

The `agent_durabletask.py` example works out of the box with the local DTS emulator (included in the dev container). To use an Azure-hosted [Durable Task Scheduler](https://learn.microsoft.com/azure/durable-task-scheduler/) instead:

1. Enable the optional DTS deployment:

```shell
azd env set DEPLOY_DTS true
```

2. Provision (or re-provision) the resources:

```shell
azd provision
```

This creates a DTS scheduler, task hub, and RBAC role assignment. The `DTS_ENDPOINT` and `DTS_TASKHUB` variables are written to your `.env` automatically.

3. Run the example:

```shell
uv run python examples/agent_durabletask.py
```

The Azure DTS dashboard is available at <https://dashboard.durabletask.io/>.

## Resources

* [(February 2026) Python + Agents: Learn how to build agents and workflows in Python](https://aka.ms/pythonagents/rewatch)
Expand Down
151 changes: 151 additions & 0 deletions examples/agent_durabletask.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
"""Durable Task persistence demo — IT support scenario.

Demonstrates: DurableTaskSchedulerWorker, DurableAIAgentClient,
DurableAgentSession.to_dict/from_dict, and automatic state persistence.

An IT support agent begins troubleshooting a Wi-Fi issue, the worker is
stopped (simulating the user leaving to restart their laptop), and a new
worker picks up the same conversation seamlessly — proving DTS preserves
state with no custom checkpoint code.

Note: You may see noisy warnings ("Invalid or missing created_at value",
"StatusCode.CANCELLED") — these are harmless and tracked upstream:
https://github.com/microsoft/agent-framework/issues/5347

Run:
uv run python examples/agent_durabletask.py
"""

import asyncio
import os

from agent_framework import Agent
from agent_framework.openai import OpenAIChatClient
from agent_framework_durabletask import DurableAIAgentClient, DurableAIAgentWorker, DurableAgentSession
from azure.identity import DefaultAzureCredential as SyncDefaultAzureCredential
from azure.identity.aio import DefaultAzureCredential, get_bearer_token_provider
from dotenv import load_dotenv
from durabletask.azuremanaged.client import DurableTaskSchedulerClient
from durabletask.azuremanaged.worker import DurableTaskSchedulerWorker
from rich import print

# Configure OpenAI client based on environment
load_dotenv(override=True)
API_HOST = os.getenv("API_HOST", "azure")

async_credential = None
if API_HOST == "azure":
async_credential = DefaultAzureCredential()
token_provider = get_bearer_token_provider(async_credential, "https://cognitiveservices.azure.com/.default")
client = OpenAIChatClient(
base_url=f"{os.environ['AZURE_OPENAI_ENDPOINT']}/openai/v1/",
api_key=token_provider,
model=os.environ["AZURE_OPENAI_CHAT_DEPLOYMENT"],
)
else:
client = OpenAIChatClient(
api_key=os.environ["OPENAI_API_KEY"], model=os.environ.get("OPENAI_MODEL", "gpt-5.4")
)

# DTS configuration
DTS_ENDPOINT = os.getenv("DTS_ENDPOINT") or "http://dts-emulator:8080"
DTS_TASKHUB = os.getenv("DTS_TASKHUB") or "default"

PLAN_PROMPT = (
"My laptop won't connect to Wi-Fi. "
"I've already tried toggling the Wi-Fi switch and forgetting the network."
)
FOLLOWUP_PROMPT = (
"OK I restarted, and Wi-Fi is working now but it's very slow. "
"Pages take 10+ seconds to load. What should I try next?"
)

agent = Agent(
name="ITSupport",
instructions=(
"You are a friendly IT support agent. "
"Reply in 1-2 short sentences — never bullet lists or numbered steps. "
"Suggest ONE thing to try at a time. If the fix requires leaving "
"(e.g. restart), tell the user to come back after."
),
client=client,
)


def create_runtime() -> tuple[DurableTaskSchedulerWorker, DurableAIAgentClient]:
"""Create a DTS worker/client pair for the demo."""
is_emulator = DTS_ENDPOINT.startswith("http://")
credential = None if is_emulator else SyncDefaultAzureCredential()

dts_worker = DurableTaskSchedulerWorker(
host_address=DTS_ENDPOINT,
secure_channel=not is_emulator,
taskhub=DTS_TASKHUB,
token_credential=credential,
)
agent_worker = DurableAIAgentWorker(dts_worker)
agent_worker.add_agent(agent)

dts_client = DurableTaskSchedulerClient(
host_address=DTS_ENDPOINT,
secure_channel=not is_emulator,
taskhub=DTS_TASKHUB,
token_credential=credential,
)

return dts_worker, DurableAIAgentClient(dts_client)


def run_demo() -> bool:
"""Run a support-ticket demo that proves state survives a worker restart."""
print("[bold]Durable Task persistence demo — IT Support[/bold]\n")

worker, agent_client = create_runtime()
with worker:
print("[cyan]1.[/cyan] Starting the first worker and creating a durable session...")
worker.start()

support = agent_client.get_agent(agent.name)
session = support.create_session()
print(f" [dim]Session: {session.session_id}[/dim]")
print(f" [dim]Durable session: {session.durable_session_id}[/dim]")

print("\n[cyan]2.[/cyan] User reports a Wi-Fi issue...")
plan_response = support.run(PLAN_PROMPT, session=session)
print(f" [green]Agent:[/green] {plan_response.text}")

saved_session = session.to_dict()

print("\n[cyan]3.[/cyan] [bold red]Worker stopped.[/bold red] (User goes away to restart their laptop...)")
print(" Restoring the same session after worker restart...")
restored_session = DurableAgentSession.from_dict(saved_session)

worker, agent_client = create_runtime()
with worker:
worker.start()
support = agent_client.get_agent(agent.name)

print("\n[cyan]4.[/cyan] User comes back after restarting — agent must remember the context...")
followup_response = support.run(FOLLOWUP_PROMPT, session=restored_session)
print(f" [green]Agent:[/green] {followup_response.text}")

keywords = ["wi-fi", "wifi", "network", "dns", "connection", "slow"]
passed = any(kw in followup_response.text.lower() for kw in keywords)
if passed:
print("\n[bold green]PASS[/bold green] DTS preserved the support conversation across the worker restart.")
else:
print("\n[bold red]FAIL[/bold red] The agent did not recall the support context after the worker restart.")

return passed


async def main() -> None:
"""Run the DTS persistence demo."""
run_demo()

if async_credential:
await async_credential.close()


if __name__ == "__main__":
asyncio.run(main())
35 changes: 35 additions & 0 deletions examples/spanish/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,14 @@ El dev container incluye un servidor Redis, que se usa en el ejemplo `agent_hist
docker run -d -p 5432:5432 -e POSTGRES_USER=admin -e POSTGRES_PASSWORD=LocalPasswordOnly pgvector/pgvector:pg17
```

6. *Opcional:* Para ejecutar el ejemplo `agent_durabletask.py` localmente, necesitas el emulador DTS:

```shell
docker run -d --name dts-emulator -p 8080:8080 -p 8082:8082 mcr.microsoft.com/dts/dts-emulator:latest
```

El dashboard de DTS está disponible en <http://localhost:8082>. Para usar un scheduler en Azure, consulta [Desplegar el Durable Task Scheduler](#desplegar-el-durable-task-scheduler).

## Configurar proveedores de modelos

Estos ejemplos se pueden ejecutar con Microsoft Foundry u OpenAI.com, dependiendo de las variables de entorno que configures. Todos los scripts hacen referencia a las variables de entorno de un archivo `.env`, y se proporciona un archivo de ejemplo `.env.sample`. Las instrucciones específicas de cada proveedor se encuentran a continuación.
Expand Down Expand Up @@ -206,6 +214,7 @@ Puedes ejecutar los ejemplos en este repositorio ejecutando los scripts en el di
| [agent_evaluation.py](agent_evaluation.py) | Evalúa un agente planificador de viajes usando evaluadores de [Azure AI Evaluation](https://learn.microsoft.com/azure/ai-foundry/concepts/evaluation-evaluators/agent-evaluators) (IntentResolution, ToolCallAccuracy, TaskAdherence, ResponseCompleteness). Opcionalmente configura `AZURE_AI_PROJECT` en `.env` para registrar resultados en [Microsoft Foundry](https://learn.microsoft.com/azure/ai-foundry/how-to/develop/agent-evaluate-sdk). |
| [agent_evaluation_batch.py](agent_evaluation_batch.py) | Evaluación por lotes de respuestas de agentes con la función `evaluate()` de Azure AI Evaluation. |
| [agent_redteam.py](agent_redteam.py) | Prueba de red team a un agente asesor financiero usando [Azure AI Evaluation](https://learn.microsoft.com/azure/ai-foundry/how-to/develop/red-teaming-agent) para evaluar su resiliencia ante ataques adversariales en categorías de riesgo (Violence, HateUnfairness, Sexual, SelfHarm). Requiere `AZURE_AI_PROJECT` en `.env`. |
| [agent_durabletask.py](agent_durabletask.py) | Persistencia con Durable Task — la conversación de un agente de soporte técnico sobrevive un reinicio del worker vía [Durable Task Scheduler](https://learn.microsoft.com/azure/durable-task-scheduler/). |

## Usar el Aspire Dashboard para telemetría

Expand Down Expand Up @@ -302,6 +311,32 @@ Después de ejecutar el ejemplo, navega a tu recurso de Application Insights en

Los datos de telemetría pueden tardar entre 2 y 5 minutos en aparecer en el portal.

## Desplegar el Durable Task Scheduler

El ejemplo `agent_durabletask.py` funciona directamente con el emulador DTS local (incluido en el dev container). Para usar un [Durable Task Scheduler](https://learn.microsoft.com/azure/durable-task-scheduler/) en Azure:

1. Habilita el despliegue opcional de DTS:

```shell
azd env set DEPLOY_DTS true
```

2. Aprovisiona (o re-aprovisiona) los recursos:

```shell
azd provision
```

Esto crea un scheduler DTS, task hub y asignación de rol RBAC. Las variables `DTS_ENDPOINT` y `DTS_TASKHUB` se escriben automáticamente en tu `.env`.

3. Ejecuta el ejemplo:

```shell
uv run python examples/spanish/agent_durabletask.py
```

El dashboard de Azure DTS está disponible en <https://dashboard.durabletask.io/>.

## Recursos

* [Documentación de Agent Framework](https://learn.microsoft.com/agent-framework/)
Loading