This quickstart introduces the core concepts of Dapr Agents and walks you through progressively more advanced examples. You'll learn how to run durable agents backed by workflows, and how to orchestrate multiple agents in deterministic workflows.
You will learn how to:
- Use native LLM client
- Run an agent triggered by the Dapr Workflow API — standalone
trigger_agentorcall_agentinside an orchestrator - Run an agent as a durable workflow with HTTP
- Trigger durable agents using pub/sub messages
- Use deterministic workflows that call LLMs
- Orchestrate multiple agents inside a workflow
- Enable distributed tracing for agents with Zipkin
- Hot-reload agent configuration at runtime
These examples form the foundation of the Dapr Agents programming model and illustrate how LLM reasoning, tool execution, durable workflows, and agent coordination fit together.
- Python >= 3.11 (https://www.python.org/downloads/)
- Docker (https://docs.docker.com/get-docker/)
- Dapr CLI (https://docs.dapr.io/getting-started/install-dapr-cli/)
- uv package manager (https://docs.astral.sh/uv/getting-started/installation/)
- Ollama (https://ollama.com/) or an OpenAI API key (https://platform.openai.com/api-keys) or another LLM provider
Install dependencies
uv venv
# Activate the virtual environment
# On Windows:
.venv\Scripts\activate
# On macOS/Linux:
source .venv/bin/activate
uv sync --activeEnsure Dapr is running locally
dapr initBy default, the quickstart uses Ollama so you can run everything locally without an API key.
-
Install and start Ollama:
# macOS brew install ollama # Linux curl -fsSL https://ollama.com/install.sh | sh
-
Pull a model with tool-calling support:
ollama serve # Start the server (skip if already running) ollama pull qwen3:0.6b -
Set environment variables:
export OLLAMA_ENDPOINT=http://localhost:11434/v1 export OLLAMA_MODEL=qwen3:0.6b
The
resources/llm-provider.yamlcomponent resolvesOLLAMA_ENDPOINTandOLLAMA_MODELfrom your environment automatically.
Tip: For more reliable tool calling, use a larger model such as
qwen2.5:3borllama3.1:8b.
To use OpenAI instead, replace resources/llm-provider.yaml with:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: llm-provider
spec:
type: conversation.openai
version: v1
metadata:
- name: key
envRef: OPENAI_API_KEY
- name: model
value: "gpt-4o-mini"Note: In production, you should store sensitive information like API keys in secrets by using a dedicated secret store.
Dapr supports Anthropic, Mistral, and other LLM providers through the Conversation API. Replace the component type and metadata while keeping the component name as llm-provider. See the Dapr Conversation component reference for the full list of supported providers and their configuration.
This example shows the simplest way to call an LLM using the Dapr Chat Client, which sends prompts through the Dapr Conversation API. It’s a minimal starting point before introducing agents in later examples.
uv run dapr run --app-id llm-client --resources-path resources -- python 01_llm_client.pyRunning the script sends the prompt to the LLM provider and prints the model’s reply. By default, the Conversation API component uses Ollama, but you can switch to OpenAI or other providers by updating the component YAML (see LLM Configuration).
- The DaprChatClient sends the prompt to the Dapr sidecar using the Conversation API under the hood.
- The Dapr sidecar uses the configured conversation component to forward the prompt to the LLM provider (Ollama by default) and returns the generated response to your application.
Dapr Agents also include native LLM clients for other modalities (e.g., audio), which you can explore when your application requires more than simple chat.
This example introduces runner.workflow(), which starts the agent’s workflow runtime without wiring pub/sub or HTTP routes.
Use this pattern when your agent is triggered by external Dapr workflows or the Dapr Workflow API — not by pub/sub messages or HTTP requests.
The agent keeps its runtime alive and waits for incoming workflow calls.
Terminal 1 — start the agent:
uv run dapr run --app-id weather-agent --resources-path resources -- python 02_durable_agent_workflow.pyThen, in a second terminal, choose one of the two trigger options below depending on your use case.
Use this when you want to fire-and-wait from a plain Python script. trigger_agent handles the WorkflowRuntime lifecycle, registration, scheduling, and waiting internally.
Terminal 2:
uv run dapr run --app-id workflow-trigger --dapr-http-port 3501 -- python 02_durable_agent_trigger.pytrigger_agent registers a short-lived wrapper workflow, schedules it against the WeatherAgent’s Dapr app (app_id="weather-agent"), blocks until it completes, and returns the serialized output — all in one call.
Use this when you are building an orchestrator workflow that calls one or more agents as child workflow steps.
call_agent returns a yieldable Task; the surrounding @wfr.workflow provides the durability and lifecycle management.
Terminal 2:
uv run dapr run --app-id agent-orchestrator --dapr-http-port 3501 -- python 02_durable_agent_trigger_within_workflow.pyInside the orchestrator, call_agent(ctx, "WeatherAgent", input={...}, app_id="weather-agent") resolves the workflow name and delegates to ctx.call_child_workflow, routing execution to the WeatherAgent’s workflow runtime in its own Dapr app.
The agent starts its workflow runtime and waits for external triggers. When triggered by either option, it executes the agent’s workflow durably — every step is persisted so execution can survive interruptions.
runner.workflow(agent)initializes the Dapr Workflow runtime and registers the agent’s workflows and activities.- No pub/sub subscriptions and no HTTP routes are created — the agent is only reachable via the Dapr Workflow API or from other Dapr workflows.
- Option A (
trigger_agent): spins up a temporary localWorkflowRuntime, registers a wrapper workflow, schedules it, waits for completion, then shuts the runtime down. - Option B (
call_agent): yields a child-workflow Task from within a parent@wfr.workflow; the parent workflow’s runtime provides the durability. wait_for_shutdown()in the agent keeps its process running until a shutdown signal is received.
- Use Option B to chain multiple agents in sequence or in parallel inside a single orchestrator workflow (see Workflow with Agent Activities).
- Add pub/sub triggers using
runner.subscribe()(see Durable Agent Subscribe) or HTTP routes usingrunner.serve()(see Durable Agent Serve).
This example introduces the DurableAgent, a workflow-native agent backed by the Dapr Workflow engine. Every step of the agent’s execution is persisted to durable storage, allowing long-running interactions to survive interruptions. The agent exposes an HTTP endpoint to start a new workflow and provides a way to query progress or retrieve the final result at any time.
uv run dapr run --app-id durable-agent --resources-path resources -- python 03_durable_agent_http.pyOn a different terminal, trigger the agent:
curl -i -X POST http://localhost:8001/agent/run \
-H "Content-Type: application/json" \
-d '{"task": "What is the weather in London?"}'You will receive a WORKFLOW_ID in response. Query the result:
curl -i -X GET http://localhost:8001/agent/instances/WORKFLOW_IDReplace WORKFLOW_ID with the ID returned from the POST request.
The agent exposes a REST endpoint, accepts a prompt, and returns a workflow ID that represents a durable execution. You can query this workflow at any time—even after stopping and restarting the agent—and it will resume exactly where it left off. The agent performs an LLM call and a tool call as part of completing the workflow and produces a final result.
- The agent schedules the prompt as a workflow execution and persists every step to durable storage.
- The agent creates a workflow activity to perform the LLM interaction and determine whether a tool call is needed.
- The agent creates another workflow activity to perform the tool call.
- The agent creates another workflow activity to return the tool call result to the LLM and complete the reasoning step.
- The agent finishes the execution, persisting every interaction and the final result. The workflow engine ensures reliable progression so no LLM or tool call is repeated unless required.
Testing durability:
This example includes a different tool, SlowWeatherTool, which intentionally waits five seconds before returning a result. This delay allows you to interrupt the agent mid-execution and verify that the workflow engine resumes from the same point after the agent restarts.
To test this:
- Trigger the agent with a prompt using the POST command shown above.
- During the 5-second delay inside SlowWeatherTool, stop the agent by pressing Ctrl+C.
- Restart the agent using the same
dapr runcommand. - Query the workflow using the same
WORKFLOW_ID; you will see that it continues from the step it was on—without starting over, without repeating the LLM call, and without requiring a new prompt. - Once the workflow finishes, the GET request will show the completed result.
In summary, the workflow engine preserves execution state across restarts, enabling reliable continuation of long-running agent interactions.
- Add custom workflow activities for business logic or integrations.
- Combine multiple agents inside the same durable workflow.
- To see multi-step workflows with LLM interactions, refer to the LLM-based workflows example.
This example takes the same durable agent behavior from the previous example, but instead of exposing an HTTP endpoint, it uses pub/sub. With this setup, the durable agent runs in the background as an ambient agent and listens for incoming events on a message topic. When a message arrives, it automatically starts a workflow execution.
The agent code remains unchanged; only the AgentRunner configuration switches from REST to pub/sub.
uv run dapr run --app-id durable-agent-subscriber --resources-path resources --dapr-http-port 3500 -- python 04_durable_agent_pubsub.pyOn a different terminal, publish a message to the subscribed topic:
dapr publish --publish-app-id durable-agent-subscriber --pubsub agent-pubsub --topic weather.requests --data '{"task": "What is the weather in London?"}'The agent listens to the weather.requests topic and, when a message is published, begins a durable workflow execution using the same logic as in the previous example. You can restart the agent at any time during execution, and the workflow will continue from the exact step where it was interrupted.
- The agent runs as a durable agent subscribed to a pub/sub topic and listens for incoming events.
- A message is published to the topic using the dapr publish command.
- The agent runner receives the event and forwards it to the durable agent.
- The message triggers a workflow execution, which performs the LLM and tool-call activities with durable state persisted at every step.
Try publishing multiple messages to the topic and observe the agent process each message as an independent durable workflow execution.
This example does not use an agent. Instead, it demonstrates how to create a Dapr workflow that performs LLM calls in a deterministic, durable sequence.
uv run dapr run --app-id workflow-llms --resources-path resources -- python 05_workflow_llm.pyThe workflow generates a short outline for the given topic using an LLM, then uses that outline to produce a short blog post. Both steps run as durable activities, so the workflow can restart without repeating completed LLM calls.
- The workflow first performs an LLM-backed activity that generates an outline from the topic. This activity uses a direct LLM call, optionally with schema validation, for predictable and validated output.
- The resulting outline is passed to a second LLM-backed activity, which uses the LLM to generate the final blog post. This output is returned as the result of the workflow.
- Modify the workflow to include additional activities that do not interact with LLMs, such as inserting validation steps, transformations, or business logic between LLM activities.
- Use structured output to enforce schema-based responses from the LLM for predictable and validated workflow inputs. To see structured output and validation, refer to the LLM Call example.
This example shows how a workflow can invoke entire agents as child workflows, allowing you to orchestrate multi-step agent reasoning in a durable and deterministic way. Unlike previous examples where activities called LLMs directly, this workflow delegates each step to an agent with tools and memory, while the workflow engine provides durability and reliable progression.
# Patch the multi-app YAML to use the resolved resources path, then run
sed "s|resourcesPath: ./resources|resourcesPath: $DAPR_RESOURCES|g" 06_workflow_agents.yaml > /tmp/06_resolved.yaml
uv run dapr run -f /tmp/06_resolved.yamlWhen the workflow runs, it first delegates the request to a triage agent, which gathers customer information using tools and produces a summary. It then passes that summary to an expert agent, which generates a final recommendation. Both steps run under a durable workflow, so if the process is interrupted, it resumes from the last completed activity even though the agents themselves are not durable.
- The workflow invokes each agent by calling agent-backed activities as child workflows using
ctx.call_child_workflow, which handles calling the agent and returning structured output. - The triage activity runs first, producing a summary based on customer data and the issue description.
- The output of the triage agent is passed into the expert agent activity to generate the final recommendation.
- Although agents can use tools and maintain their own memory, the workflow execution is what provides durability: if interrupted, it restarts from the last completed step.
Add additional workflow activities—some invoking agents, others performing business logic or LLM steps to create richer multi-stage workflows.
This example shows how to enable end-to-end tracing for a durable agent using OpenTelemetry and Zipkin. While the Dapr sidecar automatically emits workflow-related spans for each durable execution, this example extends the tracing model by adding application-level tracing inside the agent itself, allowing you to observe agent-specific steps such as LLM calls, tool calls, and memory operations alongside the workflow spans.
Dapr CLI installs and runs Zipkin by default. You can check whether it is running by visiting:
If Zipkin is not running, start it manually:
# Start Zipkin locally
docker run -d -p 9411:9411 openzipkin/zipkinNow run the durable agent with tracing enabled and prompting included:
uv run dapr run --app-id durable-agent-trace --resources-path resources -- python 07_durable_agent_tracing.py
When the script runs, the durable agent executes its workflow in-process and emits tracing spans for every LLM call, tool call, and workflow step. These spans appear in Zipkin alongside the spans generated by the Dapr sidecar, giving you a unified, end-to-end view of the agent’s reasoning and workflow execution.
- The Dapr sidecar automatically intercepts the durable workflow execution and emits workflow-level spans for each step, retry, and state transition, giving you visibility into the orchestration layer.
- The application enables Dapr Agents instrumentation, which intercepts agent-level operations—including LLM invocations, tool calls, memory reads/writes, and decision steps—and records them as additional spans. Once the agent runs, you can open the Zipkin UI at the URL above and inspect the complete trace to see exactly how the agent behaves and how these spans are connected.
Open the Zipkin UI at the URL above and explore the full trace to see how the workflow spans and agent spans connect end-to-end.
This example shows how to subscribe a durable agent to a Dapr Configuration Store so that its persona (role, goal, instructions) and other settings can be updated at runtime without restarting the process. When a value changes in the backing store (e.g. Redis), the agent picks up the update automatically.
First, ensure the runtime-config component is available in your resources path. You can use the one provided in resources/configstore.yaml. For supported configuration store backends, see the Dapr docs.
dapr run --app-id hot-reload-agent --resources-path resources -- python 08_durable_agent_hot_reload.pyIn a separate terminal, update a configuration value directly in Redis:
redis-cli SET agent_role "New Hot-Reloaded Role"The agent starts with its initial role (Original Role) and subscribes to the Dapr configuration store for the keys agent_role, agent_goal, and agent_instructions. When you update a value in Redis, the agent's profile updates in-place and the change is visible in the periodic log output—without restarting.
- The agent is initialized with a
RuntimeSubscriptionConfigthat specifies the configuration store name and the keys to watch. - When the runner calls
subscribe()(orserve()), the agent loads existing values and subscribes to the Dapr Configuration API usingsubscribe_configuration, which streams updates from the backing store, then starts the workflow runtime. - When a configuration key changes, the
_config_handlerinAgentBasereceives the update and applies it to the agent's profile, LLM settings, or component references. - If a registry store is configured, the agent re-registers its updated metadata automatically.
- Update multiple keys at once by setting a JSON object as the configuration value.
- Add additional keys for LLM settings (
llm_model,llm_provider) to swap models at runtime. - For the full list of supported configuration keys, see the hot-reload example README.
The MCPServer auto-discovery walkthrough — a DurableAgent that discovers MCP
tools through the Dapr sidecar, plus the multi-server variant — has moved to
examples/06-agent-mcp-dapr-workflow.
If you want to coordinate multiple agents that run in separate applications or communicate through Pub/Sub, check out the multi-agent workflows example.
- Ollama not responding: Ensure
ollama serveis running and the model is pulled (ollama pull qwen3:0.6b) - Environment variables: Verify
OLLAMA_ENDPOINTandOLLAMA_MODELare exported (orOPENAI_API_KEYif using OpenAI) - API Key Issues: If using OpenAI and you see an authentication error, verify your key is set in the
llm-provider.yamlcomponent - Python Version: If you encounter compatibility issues, make sure you're using Python 3.10+
- Environment Activation: Ensure your virtual environment is activated before running examples
- Import Errors: If you see module not found errors, verify that
uv sync --activecompleted successfully
If you want to see more Dapr Agents examples, check out the examples folder.