Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
20 changes: 20 additions & 0 deletions samples-v2/openai_agents/docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Durable OpenAI Agents

Build production-ready AI agents with automatic state persistence and failure recovery.

## Overview

The Durable OpenAI Agents integration combines the familiar OpenAI Agents SDK with Azure Durable Functions to create reliable, stateful AI agents that can survive any failure and continue exactly where they stopped.

## Key Benefits

- **Enhanced Agent Resilience**: Built-in retry mechanisms for LLM calls and tool executions
- **Multi-Agent Orchestration Reliability**: Individual agent failures don't crash entire workflows
- **Built-in Observability**: Monitor agent progress through the Durable Task Scheduler dashboard
- **Familiar Developer Experience**: Keep using the OpenAI Agents SDK with minimal code changes
- **Distributed Compute and Scalability**: Agent workflows automatically scale across multiple compute instances

## Documentation

- [Getting Started](getting-started.md) - Setup and your first durable agent
- [Reference](reference.md) - Complete reference documentation
167 changes: 167 additions & 0 deletions samples-v2/openai_agents/docs/getting-started.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
# Getting Started with Durable OpenAI Agents

Getting started guide for implementing stateful AI agents using Azure Durable Functions orchestration with automatic checkpointing and replay semantics.

## Prerequisites

- Python 3.10+ runtime environment
- Azure Functions Core Tools v4.x (`npm install -g azure-functions-core-tools@4 --unsafe-perm true`)
- Azure OpenAI service endpoint with model deployment
- Docker (Optional for the Durable Task Scheduler Emulator)

## Environment Setup

### Create Python Virtual Environment

Create and activate a virtual environment to isolate dependencies:

```bash
# Create virtual environment
python -m venv venv

# Activate virtual environment
# On macOS/Linux:
source venv/bin/activate
# On Windows:
# venv\Scripts\activate
```

### Install Dependencies

Install runtime dependencies in the activated virtual environment:

```bash
pip install azure-functions-durable azure-functions openai openai-agents azure-identity
```

### Configuring Durable Task Scheduler Backend

**Durable Task Scheduler is the preferred backend** for this integration as it provides enhanced performance, better observability, and simplified local development. While not a hard requirement, it's strongly recommended for production workloads.

There are two ways to configure the backend locally:

#### Using the Emulator (Recommended)

The emulator simulates a scheduler and taskhub in a Docker container, making it ideal for development and learning.

1. **Pull the Docker Image for the Emulator:**
```bash
docker pull mcr.microsoft.com/dts/dts-emulator:latest
```

2. **Run the Emulator:**
```bash
docker run --name dtsemulator -d -p 8080:8080 -p 8082:8082 mcr.microsoft.com/dts/dts-emulator:latest
```

3. **Wait for container readiness** (approximately 10-15 seconds)

4. **Verify emulator status:**
```bash
curl http://localhost:8080/health
```

**Note**: The sample code automatically uses the default emulator settings (`endpoint: http://localhost:8080`, `taskhub: default`). No additional environment variables are required.

#### Alternative: Azure Storage Backend

If you prefer using Azure Storage as the backend (legacy approach):

```bash
# Uses local storage emulator - requires Azurite
npm install -g azurite
azurite --silent --location /tmp/azurite --debug /tmp/azurite/debug.log
```

Update `local.settings.json`:
```json
{
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true"
}
}
```

## Configuration

1. **Install project dependencies:**

```bash
pip install -r requirements.txt
```

2. **Configure service settings:**

Update `local.settings.json` with your service configuration:

```json
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "python",
"AZURE_OPENAI_ENDPOINT": "https://<resource-name>.openai.azure.com/",
"AZURE_OPENAI_DEPLOYMENT": "<deployment-name>",
"AZURE_OPENAI_API_VERSION": "2024-10-01-preview",
"DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "http://localhost:8080;Authentication=None;",
"TASKHUB": "default"
}
}
```

## Hello World Example

Execute the included hello world sample.

```python
# basic/hello_world.py - Standard OpenAI Agent
from agents import Agent, Runner

def main():
agent = Agent(
name="Assistant",
instructions="You only respond in haikus.",
)
result = Runner.run_sync(agent, "Tell me about recursion in programming.")
return result.final_output
```

**Durable Transformation**: The `@durable_openai_agent_orchestrator` decorator in `function_app.py` wraps this agent execution within a Durable Functions orchestrator, providing agent state persisted at each LLM and tool interaction.

## Execution and Monitoring

1. **Start the Azure Functions host:**

```bash
func start --port 7071
```

2. **Initiate orchestration instance:**

```bash
curl -X POST http://localhost:7071/api/orchestrators/hello_world \
-H "Content-Type: application/json"
```

Response contains orchestration instance metadata:

```json
{
"id": "f4b2c8d1e9a7...",
"statusQueryGetUri": "http://localhost:7071/runtime/webhooks/durabletask/instances/f4b2c8d1e9a7...",
"sendEventPostUri": "http://localhost:7071/runtime/webhooks/durabletask/instances/f4b2c8d1e9a7.../raiseEvent/{eventName}",
"terminatePostUri": "http://localhost:7071/runtime/webhooks/durabletask/instances/f4b2c8d1e9a7.../terminate",
"purgeHistoryDeleteUri": "http://localhost:7071/runtime/webhooks/durabletask/instances/f4b2c8d1e9a7..."
}
```

3. **Monitor execution via Durable Task Scheduler dashboard:**

Navigate to `http://localhost:8082` for real-time orchestration monitoring:
- Instance execution timeline with LLM call latencies
- State transition logs and checkpoint data
- Retry attempt tracking and failure analysis

## Next Steps

- Reference [Reference Documentation](api-reference.md) for complete technical details.
151 changes: 151 additions & 0 deletions samples-v2/openai_agents/docs/reference.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
# API Reference

Complete reference for Durable OpenAI Agents integration.

## Durable Orchestration

### @durable_openai_agent_orchestrator

Primary decorator enabling durable execution for agent invocations.

```python
from azure.durable_functions.openai_agents import durable_openai_agent_orchestrator

@app.orchestration_trigger(context_name="context")
@durable_openai_agent_orchestrator
def my_agent_orchestrator(context):
# Agent implementation
pass
```

**Features**:
- Automatic state persistence for agent conversations
- Built-in retry mechanisms for LLM calls
- Tool call durability and replay protection
- Integration with Durable Functions monitoring using the Durable Task Scheduler

**Constraints**:
- Functions must be deterministic (identical outputs for identical inputs)
- No non-deterministic operations: `datetime.now()`, `random`, `uuid.uuid4()`
- See [Durable Functions Code Constraints](https://learn.microsoft.com/en-us/azure/azure-functions/durable/durable-functions-code-constraints?tabs=csharp)

### @app.orchestration_trigger

Azure Functions orchestration trigger decorator. Required with `@durable_openai_agent_orchestrator`.

```python
@app.orchestration_trigger(context_name="context")
@durable_openai_agent_orchestrator
def my_orchestrator(context):
# ...
```

## Agent Execution

### Runner.run_sync()

Runner for agents in durable orchestration context.

```python
from agents import Agent, Runner

def my_orchestrator(context):
agent = Agent(name="Assistant", instructions="Be helpful")
result = Runner.run_sync(agent, "Hello world")
return result.final_output
```

**Parameters**:
- `agent` (Agent): Agent instance to run
- `messages` (str | list): Input message(s)

**Returns**: Agent result object with `final_output` property

## Tools

### Open AI Function Tools

Simple, deterministic tools that execute within the orchestration context.

```python
from agents import function_tool

@function_tool
def calculate(expression: str) -> str:
"""Calculate mathematical expressions."""
return str(eval(expression))
```

**Requirements**:
- Must be deterministic (same input → same output)
- Should be fast-executing
- No external API calls (use activity tools instead)
- Input/output must be JSON serializable

**Best For**: Calculations, data transformations, validation logic, quick lookups

### Durable Functions Activity Tools

Durable Function Activities that execute as durable tool invocations for complex operations.

```python
# 1. Define activity function
@app.activity_trigger(input_name="input_param")
async def my_activity(input_param):
# External API calls, database operations, etc.
return result

# 2. Use in orchestrator
@app.orchestration_trigger(context_name="context")
@durable_openai_agent_orchestrator
def my_orchestrator(context):
agent = Agent(
tools=[context.create_activity_tool(my_activity)]
)
# ...
```

**Components**:
- `@app.activity_trigger(input_name="param")`: Decorator for activity functions
- `context.create_activity_tool(activity_function)`: Creates tool from activity function

**Best For**: External API calls, database operations, file I/O, expensive computations, non-deterministic operations

### Tool Selection Guide

**Use Function Tools when**:
- Operations are deterministic and fast
- No external dependencies required
- Simple calculations or data transformations

**Use Activity Tools when**:
- Making external API calls or database queries
- Operations may fail and need retry logic
- Long-running or expensive computations
- Non-deterministic operations

### Current Limitations

**MCP (Model Context Protocol)**: MCP tool support is not currently available. Use function tools or activity tools instead.

## Constraints

Orchestration functions must be deterministic and replay-safe:

- **Deterministic**: Same input always produces same output
- **Idempotent**: Safe to execute multiple times
- **Side-effect free**: No external calls in orchestration logic

```python
# ✅ Good: Deterministic
def good_orchestrator(context):
input_data = context.get_input()
agent = high_priority_agent if input_data.get("priority") == "high" else standard_agent
return Runner.run_sync(agent, input_data["content"])

# ❌ Bad: Non-deterministic
def bad_orchestrator(context):
import random
agent = agent_a if random.choice([True, False]) else agent_b # Non-deterministic!
return Runner.run_sync(agent, context.get_input())
```