diff --git a/.github/workflows/update-agent-framework.yml b/.github/workflows/update-agent-framework.yml new file mode 100644 index 0000000..bcc6a37 --- /dev/null +++ b/.github/workflows/update-agent-framework.yml @@ -0,0 +1,48 @@ +name: Update agent-framework submodule + +on: + schedule: + - cron: '0 9 * * 1' # Every Monday at 9:00 AM UTC + workflow_dispatch: # Allow manual trigger + +permissions: + contents: write + pull-requests: write + +jobs: + update-submodule: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: true + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Update submodule to latest + run: | + cd external/agent-framework + git fetch origin main + git checkout origin/main + cd ../.. + + - name: Check for changes + id: check + run: | + if git diff --quiet external/agent-framework; then + echo "changed=false" >> "$GITHUB_OUTPUT" + else + echo "changed=true" >> "$GITHUB_OUTPUT" + fi + + - name: Create pull request + if: steps.check.outputs.changed == 'true' + uses: peter-evans/create-pull-request@v7 + with: + commit-message: "Update agent-framework submodule to latest" + title: "Update agent-framework submodule" + body: | + Automated weekly update of the `external/agent-framework` submodule to the latest commit on `main`. + + This keeps the symlinked samples under `samples/durable-extension-for-agent-framework/` in sync with the upstream [microsoft/agent-framework](https://github.com/microsoft/agent-framework) repo. + branch: automation/update-agent-framework + delete-branch: true diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..017401f --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "external/agent-framework"] + path = external/agent-framework + url = https://github.com/microsoft/agent-framework.git diff --git a/README.md b/README.md index dd55f6a..27a5097 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,15 @@ Durable execution is an industry-wide approach to making ordinary code fault-tol ## ⚡ Get Started in 5 Minutes +### Step 0: Clone the repo + +```bash +git clone --recurse-submodules https://github.com/Azure-Samples/Durable-Task-Scheduler.git +cd Durable-Task-Scheduler +``` + +> **Note:** The `--recurse-submodules` flag is needed to pull sample code from linked repositories. If you already cloned without it, run: `git submodule update --init --recursive` + ### Step 1: Start the emulator ```bash @@ -105,6 +114,8 @@ Explore production-ready examples across languages and frameworks. 🔄 **[Saga Pattern](./samples/durable-functions/dotnet/Saga)** - Distributed transactions with compensating actions for failure recovery (Durable Functions, .NET) +🧩 **[Durable Extension for Microsoft Agent Framework](./samples/durable-extension-for-agent-framework/)** - Make any [Microsoft Agent Framework](https://github.com/microsoft/agent-framework) agent durable with persistent sessions, multi-agent orchestrations, and graph-based workflows (.NET, Python) + --- ## Observability diff --git a/external/agent-framework b/external/agent-framework new file mode 160000 index 0000000..3e54a68 --- /dev/null +++ b/external/agent-framework @@ -0,0 +1 @@ +Subproject commit 3e54a689fc96d681a072fe7e7cfc445909dac74b diff --git a/samples/README.md b/samples/README.md index da16876..34a70a4 100644 --- a/samples/README.md +++ b/samples/README.md @@ -157,6 +157,47 @@ A quick-reference matrix showing which patterns are available in each language a --- +## Durable Extension for Microsoft Agent Framework + +The [Durable Task extension for Microsoft Agent Framework](https://learn.microsoft.com/azure/durable-task/sdks/durable-agents-microsoft-agent-framework) lets you make any [Microsoft Agent Framework](https://github.com/microsoft/agent-framework) agent durable with persistent sessions, built-in API endpoints, and distributed scaling — without changes to your agent logic. It also supports graph-based workflows via `WorkflowBuilder`. + +> **Setup:** These samples live in the `microsoft/agent-framework` repo and are included here via a Git submodule. Run `git submodule update --init external/agent-framework` after cloning. + +📂 **[Full details and sample index →](./durable-extension-for-agent-framework/)** + +### Durable Agents (.NET) + +| Sample | Hosting | Description | +|--------|---------|-------------| +| [Single Agent](./durable-extension-for-agent-framework/dotnet/hosting/azure-functions/01-SingleAgent) | Azure Functions | Basic durable agent with persistent sessions | +| [Orchestration Chaining](./durable-extension-for-agent-framework/dotnet/hosting/azure-functions/02-OrchestrationChaining) | Azure Functions | Multi-agent sequential orchestration | +| [Orchestration Concurrency](./durable-extension-for-agent-framework/dotnet/hosting/azure-functions/03-OrchestrationConcurrency) | Azure Functions | Parallel agent execution (fan-out/fan-in) | +| [Orchestration Conditionals](./durable-extension-for-agent-framework/dotnet/hosting/azure-functions/04-OrchestrationConditionals) | Azure Functions | Conditional routing between agents | +| [Human-in-the-Loop](./durable-extension-for-agent-framework/dotnet/hosting/azure-functions/05-HumanInTheLoop) | Azure Functions | Agent pauses for human approval | +| [Reliable Streaming](./durable-extension-for-agent-framework/dotnet/hosting/azure-functions/06-ReliableStreaming) | Azure Functions | Real-time token streaming with durability | +| [Single Agent](./durable-extension-for-agent-framework/dotnet/hosting/console-apps/01-SingleAgent) | Console App | Same pattern without Azure Functions | + +### Durable MAF Workflows (.NET) + +| Sample | Hosting | Description | +|--------|---------|-------------| +| [Sequential](./durable-extension-for-agent-framework/dotnet/durable-maf-workflows/azure-functions/01-Sequential) | Azure Functions | Order cancellation pipeline: look up → cancel → notify | +| [Concurrent](./durable-extension-for-agent-framework/dotnet/durable-maf-workflows/azure-functions/02-Concurrent) | Azure Functions | Fan-out to multiple expert agents, fan-in to aggregate | +| [Human-in-the-Loop](./durable-extension-for-agent-framework/dotnet/durable-maf-workflows/azure-functions/03-HumanInTheLoop) | Azure Functions | Expense reimbursement with multi-stage approvals | +| [Sequential](./durable-extension-for-agent-framework/dotnet/durable-maf-workflows/console-apps/01-Sequential) | Console App | Sequential executor pipeline | +| [Conditional Edges](./durable-extension-for-agent-framework/dotnet/durable-maf-workflows/console-apps/03-ConditionalEdges) | Console App | Runtime routing based on conditions | + +### Durable Agents (Python) + +| Sample | Hosting | Description | +|--------|---------|-------------| +| [Single Agent](./durable-extension-for-agent-framework/python/hosting/azure-functions/01-single-agent) | Azure Functions | Basic durable agent with persistent sessions | +| [Multi-Agent Orchestration](./durable-extension-for-agent-framework/python/hosting/azure-functions/02-multi-agent-orchestration) | Azure Functions | Multiple agents in a durable orchestration | +| [Tool Calling](./durable-extension-for-agent-framework/python/hosting/azure-functions/03-tool-calling) | Azure Functions | Agent with function tools | +| [Single Agent](./durable-extension-for-agent-framework/python/hosting/durable-task/01-single-agent) | Durable Task SDK | Agent hosted with DT SDK directly | + +--- + ## Scenarios | Sample | Description | diff --git a/samples/durable-extension-for-agent-framework/README.md b/samples/durable-extension-for-agent-framework/README.md new file mode 100644 index 0000000..cfe87f1 --- /dev/null +++ b/samples/durable-extension-for-agent-framework/README.md @@ -0,0 +1,201 @@ +# Durable Extension for Microsoft Agent Framework — Samples + +The [Durable Task extension for Microsoft Agent Framework](https://learn.microsoft.com/azure/durable-task/sdks/durable-agents-microsoft-agent-framework) brings [durable execution](https://learn.microsoft.com/azure/durable-task/sdks/durable-task-for-ai-agents) directly into the [Microsoft Agent Framework](https://github.com/microsoft/agent-framework). You can register agents with the extension to make them automatically durable — with persistent sessions, built-in API endpoints, and distributed scaling — **without changes to your agent logic**. + +The extension internally implements [entity-based agent loops](https://learn.microsoft.com/azure/durable-task/sdks/durable-agents-patterns#entity-based-agent-loops), where each agent session is a durable entity that automatically manages conversation state and checkpointing. + +## What You Get + +| Capability | Description | +|------------|-------------| +| **Persistent sessions** | Conversation history survives restarts, crashes, and scaling events — no external database needed | +| **Automatic checkpointing** | Every agent interaction and tool call is checkpointed; completed work is never re-executed on recovery | +| **Built-in HTTP endpoints** | Send messages, check status, and manage sessions via auto-generated REST APIs | +| **Multi-agent orchestration** | Coordinate multiple specialized agents as steps in a durable orchestration with automatic recovery | +| **Graph-based workflows** | Define multi-step pipelines of executors and agents using `WorkflowBuilder` with automatic checkpointing | +| **Session TTL** | Automatic cleanup of idle sessions to manage storage and costs | +| **Two hosting options** | Azure Functions (serverless) or bring-your-own compute (console apps, containers, etc.) | + +## Hosting Approaches + +### Azure Functions + +One line to make your agent durable with serverless hosting: + +```csharp +using IHost app = FunctionsApplication + .CreateBuilder(args) + .ConfigureFunctionsWebApplication() + .ConfigureDurableAgents(options => options.AddAIAgent(agent)) + .Build(); +app.Run(); +``` + +### Bring Your Own Compute (Console Apps, Containers, etc.) + +Host the agent with the Durable Task SDK directly — no Azure Functions dependency: + +```csharp +IHost host = Host.CreateDefaultBuilder(args) + .ConfigureServices(services => + { + services.ConfigureDurableAgents( + options => options.AddAIAgent(agent), + workerBuilder: builder => builder.UseDurableTaskScheduler(connectionString), + clientBuilder: builder => builder.UseDurableTaskScheduler(connectionString)); + }) + .Build(); +await host.StartAsync(); +``` + +## Patterns Demonstrated + +### Durable Agents (Hosting) + +These samples show how to host agents with the Durable Task extension. Each agent session is a durable entity that persists conversation history, supports tool calling, and recovers automatically from failures. + +| Pattern | Description | +|---------|-------------| +| **Single agent** | One LLM agent with persistent sessions and built-in HTTP endpoints | +| **Multi-agent orchestration** | Multiple specialized agents coordinated as checkpointed steps in a durable orchestration | +| **Tool calling** | Agents with function tools — tool calls are checkpointed and not re-executed on recovery | +| **MCP server** | Agent hosted as a [Model Context Protocol](https://modelcontextprotocol.io/) server | +| **Reliable streaming** | Real-time token streaming with durable delivery guarantees | +| **Human-in-the-loop** | Agents that pause for human approval before continuing | + +### Durable MAF Workflows + +These samples show how to use the [Microsoft Agent Framework `WorkflowBuilder`](https://learn.microsoft.com/agent-framework/workflows) with the Durable Task extension. The extension automatically checkpoints each step in the graph and recovers from failures without changes to the workflow definition. + +| Pattern | Description | +|---------|-------------| +| **Sequential** | Chain executors into a multi-step pipeline (e.g., look up → cancel → notify) | +| **Fan-out/fan-in** | Run multiple agents or executors in parallel, then aggregate results | +| **Conditional routing** | Route execution to different branches based on runtime results (e.g., spam detection) | +| **Human-in-the-loop** | Pause workflow execution at designated points to wait for external approval | +| **Sub-workflows** | Compose complex workflows from reusable sub-workflows | +| **Shared state** | Pass state between workflow steps using context | +| **Events** | React to external events during workflow execution | + +## Sample Structure + +``` +python/ + hosting/ + azure-functions/ → Azure Functions agent hosting samples + durable-task/ → Durable Task SDK agent hosting samples (no Azure Functions) +dotnet/ + hosting/ + azure-functions/ → Azure Functions agent hosting samples + console-apps/ → Console app agent hosting samples + durable-maf-workflows/ + azure-functions/ → Graph-based workflow samples (Azure Functions) + console-apps/ → Graph-based workflow samples (Console Apps) +``` + +> **Note:** These directories are symlinks into the [`microsoft/agent-framework`](https://github.com/microsoft/agent-framework) repo, which is included as a Git submodule at `external/agent-framework`. This avoids duplicating samples across repos. + +## Getting Started + +### Prerequisites + +1. [Docker](https://www.docker.com/products/docker-desktop/) — for the Durable Task Scheduler emulator +2. [.NET 10 SDK](https://dotnet.microsoft.com/download/dotnet/10.0) (for .NET samples) or [Python 3.10+](https://www.python.org/) (for Python samples) +3. (Optional) [Azure OpenAI](https://learn.microsoft.com/azure/ai-services/openai/) endpoint for real LLM integration + +### First-time setup + +Clone this repo with submodules to pull the sample code: + +```bash +git clone --recurse-submodules https://github.com/Azure-Samples/Durable-Task-Scheduler.git +``` + +If you've already cloned without submodules, initialize them separately: + +```bash +git submodule update --init external/agent-framework +``` + +Then start the Durable Task Scheduler emulator: + +```bash +docker run -d -p 8080:8080 -p 8082:8082 mcr.microsoft.com/dts/dts-emulator:latest +``` + +The dashboard is available at [http://localhost:8082](http://localhost:8082). + +### Updating to latest + +To pull the latest samples from the agent-framework repo: + +```bash +cd external/agent-framework +git pull origin main +cd ../.. +git add external/agent-framework +git commit -m "Update agent-framework submodule" +``` + +A [GitHub Actions workflow](../../.github/workflows/update-agent-framework.yml) also runs weekly to auto-update the submodule via PR. + +## Sample Index + +### .NET — Durable Agents (Hosting) + +| Sample | Hosting | Description | +|--------|---------|-------------| +| [Single Agent](dotnet/hosting/azure-functions/01-SingleAgent) | Azure Functions | Basic durable agent with persistent sessions | +| [Orchestration Chaining](dotnet/hosting/azure-functions/02-OrchestrationChaining) | Azure Functions | Multi-agent sequential orchestration | +| [Orchestration Concurrency](dotnet/hosting/azure-functions/03-OrchestrationConcurrency) | Azure Functions | Parallel agent execution with fan-out/fan-in | +| [Orchestration Conditionals](dotnet/hosting/azure-functions/04-OrchestrationConditionals) | Azure Functions | Conditional routing between agents | +| [Human-in-the-Loop](dotnet/hosting/azure-functions/05-HumanInTheLoop) | Azure Functions | Agent pauses for human approval | +| [ReliableStreaming](dotnet/hosting/azure-functions/06-ReliableStreaming) | Azure Functions | Real-time token streaming with durability | +| [MCP Server](dotnet/hosting/azure-functions/07-McpServer) | Azure Functions | Agent as a Model Context Protocol server | +| [Custom State](dotnet/hosting/azure-functions/08-CustomState) | Azure Functions | Agent with custom persistent state | +| [Single Agent](dotnet/hosting/console-apps/01-SingleAgent) | Console App | Basic durable agent without Azure Functions | +| [Orchestration Chaining](dotnet/hosting/console-apps/02-OrchestrationChaining) | Console App | Multi-agent orchestration in a console app | +| [Orchestration Concurrency](dotnet/hosting/console-apps/03-OrchestrationConcurrency) | Console App | Parallel agents in a console app | +| [Orchestration Conditionals](dotnet/hosting/console-apps/04-OrchestrationConditionals) | Console App | Conditional agent routing in a console app | +| [Human-in-the-Loop](dotnet/hosting/console-apps/05-HumanInTheLoop) | Console App | Human approval in a console app | +| [Custom State](dotnet/hosting/console-apps/06-CustomState) | Console App | Custom agent state in a console app | +| [Durable Agent Client](dotnet/hosting/console-apps/07-DurableAgentClient) | Console App | Client for interacting with durable agents | + +### .NET — Durable MAF Workflows + +| Sample | Hosting | Description | +|--------|---------|-------------| +| [Sequential](dotnet/durable-maf-workflows/azure-functions/01-Sequential) | Azure Functions | Order cancellation pipeline: look up → cancel → notify | +| [Concurrent](dotnet/durable-maf-workflows/azure-functions/02-Concurrent) | Azure Functions | Fan-out to physicist & chemist agents, fan-in to aggregate | +| [Human-in-the-Loop](dotnet/durable-maf-workflows/azure-functions/03-HumanInTheLoop) | Azure Functions | Expense reimbursement with manager + parallel finance approvals | +| [MCP Tool](dotnet/durable-maf-workflows/azure-functions/04-McpTool) | Azure Functions | Workflow exposed as an MCP tool | +| [Combined](dotnet/durable-maf-workflows/azure-functions/05-Combined) | Azure Functions | Workflows + agents in the same app | +| [Sequential](dotnet/durable-maf-workflows/console-apps/01-Sequential) | Console App | Sequential executor pipeline | +| [Concurrent](dotnet/durable-maf-workflows/console-apps/02-Concurrent) | Console App | Fan-out/fan-in with parallel execution | +| [Conditional Edges](dotnet/durable-maf-workflows/console-apps/03-ConditionalEdges) | Console App | Runtime routing based on conditions | +| [Events](dotnet/durable-maf-workflows/console-apps/04-Events) | Console App | Reacting to external events in workflows | +| [Shared State](dotnet/durable-maf-workflows/console-apps/05-SharedState) | Console App | Passing state between workflow steps | +| [Sub-Workflows](dotnet/durable-maf-workflows/console-apps/06-SubWorkflows) | Console App | Composing reusable sub-workflows | +| [Human-in-the-Loop](dotnet/durable-maf-workflows/console-apps/07-HumanInTheLoop) | Console App | Workflow pauses for external approval | +| [Streaming](dotnet/durable-maf-workflows/console-apps/08-Streaming) | Console App | Streaming workflow events | + +### Python — Durable Agents (Hosting) + +| Sample | Hosting | Description | +|--------|---------|-------------| +| [Single Agent](python/hosting/azure-functions/01-single-agent) | Azure Functions | Basic durable agent with persistent sessions | +| [Multi-Agent Orchestration](python/hosting/azure-functions/02-multi-agent-orchestration) | Azure Functions | Multiple agents coordinated in an orchestration | +| [Tool Calling](python/hosting/azure-functions/03-tool-calling) | Azure Functions | Agent with function tools | +| [Human-in-the-Loop](python/hosting/azure-functions/04-human-in-the-loop) | Azure Functions | Agent pauses for human approval | +| [Single Agent](python/hosting/durable-task/01-single-agent) | Durable Task SDK | Agent hosted with the DT SDK directly | +| [Multi-Agent Orchestration](python/hosting/durable-task/02-multi-agent-orchestration) | Durable Task SDK | Multi-agent orchestration without Azure Functions | +| [Tool Calling](python/hosting/durable-task/03-tool-calling) | Durable Task SDK | Tool-calling agent with the DT SDK | +| [Human-in-the-Loop](python/hosting/durable-task/04-human-in-the-loop) | Durable Task SDK | Human approval with the DT SDK | + +## Learn More + +- [Durable Task extension for Microsoft Agent Framework](https://learn.microsoft.com/azure/durable-task/sdks/durable-agents-microsoft-agent-framework) — Full documentation +- [Durable Task for AI Agents](https://learn.microsoft.com/azure/durable-task/sdks/durable-task-for-ai-agents) — Why durable execution matters for AI agents +- [Agentic Application Patterns](https://learn.microsoft.com/azure/durable-task/sdks/durable-agents-patterns) — All supported patterns +- [Microsoft Agent Framework](https://github.com/microsoft/agent-framework) — The agent framework itself +- [Durable Task Scheduler Dashboard](https://learn.microsoft.com/azure/durable-task/scheduler/durable-task-scheduler-dashboard) — Monitor agents, orchestrations, and workflows diff --git a/samples/durable-extension-for-agent-framework/dotnet/durable-maf-workflows/azure-functions b/samples/durable-extension-for-agent-framework/dotnet/durable-maf-workflows/azure-functions new file mode 120000 index 0000000..eb6ddd4 --- /dev/null +++ b/samples/durable-extension-for-agent-framework/dotnet/durable-maf-workflows/azure-functions @@ -0,0 +1 @@ +../../../../external/agent-framework/dotnet/samples/04-hosting/DurableWorkflows/AzureFunctions \ No newline at end of file diff --git a/samples/durable-extension-for-agent-framework/dotnet/durable-maf-workflows/console-apps b/samples/durable-extension-for-agent-framework/dotnet/durable-maf-workflows/console-apps new file mode 120000 index 0000000..84531fb --- /dev/null +++ b/samples/durable-extension-for-agent-framework/dotnet/durable-maf-workflows/console-apps @@ -0,0 +1 @@ +../../../../external/agent-framework/dotnet/samples/04-hosting/DurableWorkflows/ConsoleApps \ No newline at end of file diff --git a/samples/durable-extension-for-agent-framework/dotnet/hosting/azure-functions b/samples/durable-extension-for-agent-framework/dotnet/hosting/azure-functions new file mode 120000 index 0000000..4201e15 --- /dev/null +++ b/samples/durable-extension-for-agent-framework/dotnet/hosting/azure-functions @@ -0,0 +1 @@ +../../../../external/agent-framework/dotnet/samples/04-hosting/DurableAgents/AzureFunctions \ No newline at end of file diff --git a/samples/durable-extension-for-agent-framework/dotnet/hosting/console-apps b/samples/durable-extension-for-agent-framework/dotnet/hosting/console-apps new file mode 120000 index 0000000..27f502a --- /dev/null +++ b/samples/durable-extension-for-agent-framework/dotnet/hosting/console-apps @@ -0,0 +1 @@ +../../../../external/agent-framework/dotnet/samples/04-hosting/DurableAgents/ConsoleApps \ No newline at end of file diff --git a/samples/durable-extension-for-agent-framework/python/hosting/azure-functions b/samples/durable-extension-for-agent-framework/python/hosting/azure-functions new file mode 120000 index 0000000..c542c6b --- /dev/null +++ b/samples/durable-extension-for-agent-framework/python/hosting/azure-functions @@ -0,0 +1 @@ +../../../../external/agent-framework/python/samples/04-hosting/azure_functions \ No newline at end of file diff --git a/samples/durable-extension-for-agent-framework/python/hosting/durable-task b/samples/durable-extension-for-agent-framework/python/hosting/durable-task new file mode 120000 index 0000000..1424217 --- /dev/null +++ b/samples/durable-extension-for-agent-framework/python/hosting/durable-task @@ -0,0 +1 @@ +../../../../external/agent-framework/python/samples/04-hosting/durabletask \ No newline at end of file