Answer-first: Dapr Workflows can coordinate long-running Go agent tasks that need durable state, retries, and explicit compensation. Keep LLM and tool calls in idempotent activities—not the orchestrator—so replay rebuilds decision state without repeating completed side effects.
As multi-agent architectures mature beyond single-turn API wrappers, enterprise workloads are shifting toward long-running autonomous workflows. Building on the Agentic System Architecture series and recent analyses of modular monoliths for AI agents and zero-trust AI swarms, platform teams now face the execution-durability problem: a task may combine multi-step reasoning, external tools, and human approvals over several minutes.
This radar focuses on the production boundary between an agent request and a durable business workflow. For a full Go implementation of compensation activities, see the Dapr Workflow Saga tutorial. For a workflow-engine comparison and determinism constraints, see the Temporal orchestrated Saga guide.
1. The Dangling Agent Execution Problem
A synchronous HTTP or gRPC request is a poor ownership model for a multi-minute task. A client can disconnect, an ingress timeout can expire, or a pod can be evicted while external tools are still running. The platform must distinguish a lost client connection from the lifecycle of the underlying business command.
Typical failure modes include:
- Unpredictable latency: browsing, retrieval, code generation, validation, and approval steps can take seconds or minutes rather than the milliseconds expected by a normal API request.
- Duplicate side effects: a caller retries after a timeout while the original tool call is still in flight, potentially creating two tickets, two cloud resources, or two payment attempts.
- Lost in-memory state: a pod restart discards a goroutine’s local context unless progress and outputs have been persisted outside the process.
[!IMPORTANT] A durable workflow does not make an agent safe by itself. Authorization, input validation, retry budgets, tool allowlists, and audit logging remain separate responsibilities.
2. What Dapr Workflows Actually Makes Durable
Dapr Workflows uses replay-based durable orchestration. The application hosts a workflow worker, while Dapr runtime services and a configured Dapr state-store component persist orchestration history and coordinate execution. A PostgreSQL or Redis state store is configured through Dapr; application code does not use GORM as the workflow persistence mechanism.
On recovery, the runtime replays orchestration history to rebuild the orchestrator’s decision state. A completed activity returns its recorded result instead of being invoked again. An activity that had not completed can be retried, so external LLM calls and tools still require application-level idempotency and persisted result handling.
This is also why orchestrator code must remain deterministic. Put network I/O, model invocation, database writes, and non-deterministic calls inside activities. Keep the orchestrator focused on ordered decisions, waits, retries, and compensation.
Dapr Actors solve a different problem: single-key coordination for an entity or an agent’s state. Use an Actor when one identity needs serialized state changes; use a Workflow when a process spans ordered steps, timers, retries, approvals, and compensations. They can be combined, but one is not the implementation detail of the other.
3. Idempotent Tool Dispatch and Explicit Compensation
Agentic tool calls are distributed side effects, not pure functions. An agent that provisions infrastructure, changes a CRM record, or initiates a payment must make the same business command safe under retries.
Derive an application-owned idempotency key from the workflow instance ID, business command ID, and activity name. Persist that key with the downstream effect, and return the prior result when the same command is delivered again. Do not assume a framework-provided InstanceID + StepID token automatically protects every dependency.
Compensation is similarly explicit business logic. If provisioning succeeds but deployment fails, invoke a de-provisioning activity in reverse order; if compensation fails, surface an operationally actionable incident rather than silently retrying forever.
if err := ctx.CallActivity(DeployService, workflow.ActivityInput(input)).Await(&result); err != nil {
var compensationResult DeprovisionResult
compensationErr := ctx.CallActivity(
DeprovisionDatabase,
workflow.ActivityInput(provisioned),
).Await(&compensationResult)
if compensationErr != nil {
return nil, fmt.Errorf("deploy failed: %v; compensation failed: %w", err, compensationErr)
}
return nil, err
}
The snippet illustrates the ownership boundary: the orchestrator decides which compensation is required, while each activity owns the idempotent API call and its durable business record.
4. A Go Service Boundary for Long-Running Agent Work
For a Go service, expose an API that accepts a validated command and returns a workflow identifier rather than holding an HTTP connection open. The client can then poll a status endpoint or consume server-sent events while the workflow progresses.
A clean separation looks like this:
| Layer | Responsibility |
|---|---|
api | Authenticate the caller, validate the command, start the workflow, and return 202 Accepted plus a workflow ID. |
biz | Define the workflow decision rules, activity interfaces, retry policy, and compensation order. |
data | Implement idempotent activity adapters for LLM providers, databases, and external tools. |
| Dapr runtime | Coordinate workflow execution and durable history through the configured components. |
For teams using Kratos and Wire, keep the Dapr client and activity adapters in the data/infrastructure boundary; inject business interfaces into the transport layer. This avoids coupling HTTP handlers to sidecar-specific details and keeps activity behavior testable.
5. Decision Guide: Workflow, Actor, or Dedicated Engine?
| Requirement | Best starting point | Why |
|---|---|---|
| A chat response that finishes in seconds | HTTP/gRPC request | No durable process is needed. |
| A multi-step agent task with waits and retries | Dapr Workflow | The process needs persisted orchestration history. |
| State coordination for one agent, cart, or entity key | Dapr Actor | One identity needs serialized state mutation. |
| Complex cross-domain workflows with a dedicated platform team | Evaluate Temporal or another workflow engine | Operational and ecosystem requirements may justify a dedicated engine. |
Choose the smallest durable mechanism that fits the business failure mode. Workflow durability cannot replace a permission model, and an Actor cannot automatically compensate a multi-service process.
FAQ
Does Dapr Workflow prevent duplicate tool execution automatically?
Completed activity results can be replayed from workflow history, but an activity that did not complete may be retried. Use application-owned idempotency keys, persist downstream outcomes, and make every external side effect safe for duplicate delivery.
Should every AI request become a workflow?
No. Use a normal request for fast, read-only interactions. Start a workflow when the task has durable business state, long waits, retries, human approval, or compensating actions that must survive a client disconnect or pod restart.