Skip to content

Commit ccfcffb

Browse files
authored
Initial Docs for the Durable OpenAI Agents Integration (#17)
1 parent 6edbf26 commit ccfcffb

File tree

4 files changed

+375
-176
lines changed

4 files changed

+375
-176
lines changed

docs/openai_agents/README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Durable OpenAI Agents
2+
3+
Build production-ready AI agents with automatic state persistence and failure recovery.
4+
5+
## Overview
6+
7+
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.
8+
9+
## Key Benefits
10+
11+
- **Enhanced Agent Resilience**: Built-in retry mechanisms for LLM calls and tool executions
12+
- **Multi-Agent Orchestration Reliability**: Individual agent failures don't crash entire workflows
13+
- **Built-in Observability**: Monitor agent progress through the Durable Task Scheduler dashboard
14+
- **Familiar Developer Experience**: Keep using the OpenAI Agents SDK with minimal code changes
15+
- **Distributed Compute and Scalability**: Agent workflows automatically scale across multiple compute instances
16+
17+
## Documentation
18+
19+
- [Getting Started](getting-started.md) - Setup and your first durable agent
20+
- [Reference](reference.md) - Complete reference documentation
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
# Getting Started with Durable OpenAI Agents
2+
3+
Getting started guide for implementing stateful AI agents using Azure Durable Functions orchestration with automatic checkpointing and replay semantics.
4+
5+
## Prerequisites
6+
7+
- Python 3.10+ runtime environment
8+
- Azure Functions Core Tools v4.x (`npm install -g azure-functions-core-tools@4 --unsafe-perm true`)
9+
- Azure OpenAI service endpoint with model deployment
10+
- Docker (Optional for the Durable Task Scheduler Emulator)
11+
12+
## Environment Setup
13+
14+
### Create an Azure Functions App
15+
16+
This framework is designed specifically for **Azure Functions applications**. You need to create a Python Functions app to use Durable OpenAI Agents.
17+
18+
**For new users**: If you're new to Azure Functions, follow these guides to get started:
19+
- [Create your first Python function in Azure](https://learn.microsoft.com/en-us/azure/azure-functions/create-first-function-vs-code-python)
20+
- [Azure Functions Python developer guide](https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-python)
21+
22+
**For experienced Functions users**: Create a new Python Functions app or use an existing one.
23+
24+
**Note**: The `samples-v2/openai_agents` directory contains a complete working example you can reference or use as a starting point.
25+
26+
### Set Up Local Development Environment
27+
28+
Create and activate a virtual environment to isolate dependencies:
29+
30+
```bash
31+
# Create virtual environment
32+
python -m venv venv
33+
34+
# Activate virtual environment
35+
# On macOS/Linux:
36+
source venv/bin/activate
37+
# On Windows:
38+
# venv\Scripts\activate
39+
```
40+
41+
### Install Dependencies
42+
43+
Add the OpenAI Agents dependencies to your `requirements.txt`:
44+
45+
```
46+
azure-functions-durable
47+
azure-functions
48+
openai
49+
openai-agents
50+
azure-identity
51+
```
52+
53+
Then install them:
54+
55+
```bash
56+
pip install -r requirements.txt
57+
```
58+
59+
### Configuring Durable Task Scheduler Backend
60+
61+
**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.
62+
63+
There are two ways to configure the backend locally:
64+
65+
#### Using the Emulator (Recommended)
66+
67+
The emulator simulates a scheduler and taskhub in a Docker container, making it ideal for development and learning.
68+
69+
1. **Pull the Docker Image for the Emulator:**
70+
```bash
71+
docker pull mcr.microsoft.com/dts/dts-emulator:latest
72+
```
73+
74+
2. **Run the Emulator:**
75+
```bash
76+
docker run --name dtsemulator -d -p 8080:8080 -p 8082:8082 mcr.microsoft.com/dts/dts-emulator:latest
77+
```
78+
79+
3. **Wait for container readiness** (approximately 10-15 seconds)
80+
81+
4. **Verify emulator status:**
82+
```bash
83+
curl http://localhost:8080/health
84+
```
85+
86+
**Note**: The sample code automatically uses the default emulator settings (`endpoint: http://localhost:8080`, `taskhub: default`). No additional environment variables are required.
87+
88+
#### Alternative: Azure Storage Backend
89+
90+
If you prefer using Azure Storage as the backend (legacy approach):
91+
92+
```bash
93+
# Uses local storage emulator - requires Azurite
94+
npm install -g azurite
95+
azurite --silent --location /tmp/azurite --debug /tmp/azurite/debug.log
96+
```
97+
98+
Update `local.settings.json`:
99+
```json
100+
{
101+
"Values": {
102+
"AzureWebJobsStorage": "UseDevelopmentStorage=true"
103+
}
104+
}
105+
```
106+
107+
## Configuration
108+
109+
1. **Install project dependencies:**
110+
111+
```bash
112+
pip install -r requirements.txt
113+
```
114+
115+
2. **Configure service settings:**
116+
117+
Update `local.settings.json` with your service configuration:
118+
119+
```json
120+
{
121+
"IsEncrypted": false,
122+
"Values": {
123+
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
124+
"FUNCTIONS_WORKER_RUNTIME": "python",
125+
"AZURE_OPENAI_ENDPOINT": "https://<resource-name>.openai.azure.com/",
126+
"AZURE_OPENAI_DEPLOYMENT": "<deployment-name>",
127+
"AZURE_OPENAI_API_VERSION": "2024-10-01-preview",
128+
"DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "http://localhost:8080;Authentication=None;",
129+
"TASKHUB": "default"
130+
}
131+
}
132+
```
133+
134+
## Hello World Example
135+
136+
Execute the included hello world sample.
137+
138+
```python
139+
# basic/hello_world.py - Standard OpenAI Agent
140+
from agents import Agent, Runner
141+
142+
def main():
143+
agent = Agent(
144+
name="Assistant",
145+
instructions="You only respond in haikus.",
146+
)
147+
result = Runner.run_sync(agent, "Tell me about recursion in programming.")
148+
return result.final_output
149+
```
150+
151+
**Durable Transformation**: The `@app.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.
152+
153+
## Execution and Monitoring
154+
155+
1. **Start the Azure Functions host:**
156+
157+
Navigate to the `samples-v2/openai_agents` directory and run:
158+
159+
```bash
160+
func start --port 7071
161+
```
162+
163+
2. **Initiate orchestration instance:**
164+
165+
```bash
166+
curl -X POST http://localhost:7071/api/orchestrators/hello_world \
167+
-H "Content-Type: application/json"
168+
```
169+
170+
Response contains orchestration instance metadata:
171+
172+
```json
173+
{
174+
"id": "f4b2c8d1e9a7...",
175+
"statusQueryGetUri": "http://localhost:7071/runtime/webhooks/durabletask/instances/f4b2c8d1e9a7...",
176+
"sendEventPostUri": "http://localhost:7071/runtime/webhooks/durabletask/instances/f4b2c8d1e9a7.../raiseEvent/{eventName}",
177+
"terminatePostUri": "http://localhost:7071/runtime/webhooks/durabletask/instances/f4b2c8d1e9a7.../terminate",
178+
"purgeHistoryDeleteUri": "http://localhost:7071/runtime/webhooks/durabletask/instances/f4b2c8d1e9a7..."
179+
}
180+
```
181+
182+
3. **Monitor execution via Durable Task Scheduler dashboard:**
183+
184+
Navigate to `http://localhost:8082` for real-time orchestration monitoring:
185+
- Instance execution timeline with LLM call latencies
186+
- State transition logs and checkpoint data
187+
- Retry attempt tracking and failure analysis
188+
189+
## Next Steps
190+
191+
- Reference [Reference Documentation](reference.md) for complete technical details.

docs/openai_agents/reference.md

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# Reference Documentation
2+
3+
Complete reference for Durable OpenAI Agents integration.
4+
5+
## Durable Orchestration
6+
7+
### @app.durable_openai_agent_orchestrator
8+
9+
Primary decorator enabling durable execution for agent invocations.
10+
11+
```python
12+
from azure.durable_functions.openai_agents import durable_openai_agent_orchestrator
13+
14+
@app.orchestration_trigger(context_name="context")
15+
@app.durable_openai_agent_orchestrator
16+
def my_agent_orchestrator(context):
17+
# Agent implementation
18+
pass
19+
```
20+
21+
**Features**:
22+
- Automatic state persistence for agent conversations
23+
- Built-in retry mechanisms for LLM calls
24+
- Tool call durability and replay protection
25+
- Integration with Durable Functions monitoring using the Durable Task Scheduler
26+
27+
**Constraints**:
28+
- Functions must be deterministic (identical outputs for identical inputs)
29+
- No non-deterministic operations: `datetime.now()`, `random`, `uuid.uuid4()`
30+
- See [Durable Functions Code Constraints](https://learn.microsoft.com/azure/azure-functions/durable/durable-functions-code-constraints?tabs=csharp)
31+
32+
### @app.orchestration_trigger
33+
34+
Azure Functions orchestration trigger decorator. Required with `@app.durable_openai_agent_orchestrator`.
35+
36+
```python
37+
@app.orchestration_trigger(context_name="context")
38+
@app.durable_openai_agent_orchestrator
39+
def my_orchestrator(context):
40+
# ...
41+
```
42+
43+
## Agent Execution
44+
45+
### Runner.run_sync()
46+
47+
Runner for agents in durable orchestration context.
48+
49+
```python
50+
from agents import Agent, Runner
51+
52+
def my_orchestrator(context):
53+
agent = Agent(name="Assistant", instructions="Be helpful")
54+
result = Runner.run_sync(agent, "Hello world")
55+
return result.final_output
56+
```
57+
58+
**Parameters**:
59+
- `agent` (Agent): Agent instance to run
60+
- `messages` (str | list): Input message(s)
61+
62+
**Returns**: Agent result object with `final_output` property
63+
64+
## Tools
65+
66+
### Durable Functions Activity Tools
67+
68+
Durable Function Activities that execute as durable tool invocations. **This is the recommended approach for most use cases** as it provides the strongest correctness guarantees. - **When in doubt - this is the safe choice**
69+
70+
```python
71+
# 1. Define activity function
72+
@app.activity_trigger(input_name="input_param")
73+
async def my_activity(input_param):
74+
# External API calls, database operations, etc.
75+
return result
76+
77+
# 2. Use in orchestrator
78+
@app.orchestration_trigger(context_name="context")
79+
@app.durable_openai_agent_orchestrator
80+
def my_orchestrator(context):
81+
agent = Agent(
82+
tools=[context.create_activity_tool(my_activity)]
83+
)
84+
# ...
85+
```
86+
87+
**Components**:
88+
- `@app.activity_trigger(input_name="param")`: Decorator for activity functions
89+
- `context.create_activity_tool(activity_function)`: Creates tool from activity function
90+
91+
**Best For**: External API calls, database operations, file I/O, expensive computations, non-deterministic operations
92+
93+
### Open AI Function Tools
94+
95+
Simple, deterministic tools that execute within the orchestration context. **Recommended only as a performance optimization when you're certain the tool meets all deterministic requirements.**
96+
97+
```python
98+
from agents import function_tool
99+
100+
@function_tool
101+
def calculate(expression: str) -> str:
102+
"""Calculate mathematical expressions."""
103+
return str(eval(expression))
104+
```
105+
106+
**Requirements**:
107+
- Must be deterministic (same input → same output)
108+
- Should be fast-executing
109+
- No external API calls (use activity tools instead)
110+
- Input/output must be JSON serializable
111+
112+
**Best For**: Calculations, data transformations, validation logic, quick lookups
113+
114+
### Current Limitations
115+
116+
**MCP (Model Context Protocol)**: MCP tool support is not currently available. Use function tools or activity tools instead.
117+
118+
## Constraints
119+
120+
Orchestration functions must be deterministic and replay-safe:
121+
122+
- **Deterministic**: Same input always produces same output
123+
- **Idempotent**: Safe to execute multiple times
124+
- **Side-effect free**: No external calls in orchestration logic
125+
126+
```python
127+
# ✅ Good: Deterministic
128+
def good_orchestrator(context):
129+
input_data = context.get_input()
130+
agent = high_priority_agent if input_data.get("priority") == "high" else standard_agent
131+
return Runner.run_sync(agent, input_data["content"])
132+
133+
# ❌ Bad: Non-deterministic
134+
def bad_orchestrator(context):
135+
import random
136+
agent = agent_a if random.choice([True, False]) else agent_b # Non-deterministic!
137+
return Runner.run_sync(agent, context.get_input())
138+
```

0 commit comments

Comments
 (0)