Skip to main content
Durable agent execution ensures that your agents can survive process crashes, restarts, or failures without losing progress. Using Restate, each step of the agent’s execution (LLM calls, tool executions) is checkpointed, allowing the agent to resume from exactly where it left off after a failure.

Why Durable Execution?

Traditional agents run in-memory. If your process crashes during:
  • A long-running LLM call
  • An external API request in a tool
  • A multi-step conversation with many tool calls
…all progress is lost, and the entire execution must restart from scratch. With durable execution:
  • Checkpoint Every Step: Each LLM call and tool execution is automatically saved
  • Automatic Recovery: After a crash, the agent resumes from the last checkpoint
  • Exactly-Once Guarantees: Tool executions are never duplicated, even after retries
  • Long-Running Workflows: Agents can run for hours or days without risk

Prerequisites

  1. Restate Server: You need a running Restate server. Follow the Restate installation guide to set one up.

Creating a Durable Agent

To create a durable agent, use NewRestateAgent() instead of NewAgent():
package main

import (
    "log"
    "net/http"
    "os"

    "github.com/curaious/uno/pkg/gateway"
    "github.com/curaious/uno/pkg/llm"
    "github.com/curaious/uno/pkg/sdk"
)

func main() {
    // Initialize SDK with Restate configuration
    client, err := sdk.New(&sdk.ClientOptions{
        LLMConfigs: sdk.NewInMemoryConfigStore([]*gateway.ProviderConfig{
            {
                ProviderName: llm.ProviderNameOpenAI,
                ApiKeys: []*gateway.APIKeyConfig{
                    {
                        Name:   "Key 1",
                        APIKey: os.Getenv("OPENAI_API_KEY"),
                    },
                },
            },
        }),
        RestateConfig: sdk.RestateConfig{
            Endpoint: "http://localhost:8081", // Restate server endpoint
        },
    })
    if err != nil {
        log.Fatal(err)
    }

    model := client.NewLLM(sdk.LLMOptions{
        Provider: llm.ProviderNameOpenAI,
        Model:    "gpt-4o-mini",
    })

    // Create a durable agent with NewRestateAgent
    agentName := "SampleAgent"
    agent := client.NewRestateAgent(&sdk.AgentOptions{
        Name:        agentName,
        Instruction: client.Prompt("You are helpful assistant."),
        LLM:         model,
        History:     client.NewConversationManager(),
    })
}

Deployment Options

There are two ways to deploy durable agents:

Option 1: Single Process (Development/Testing)

Run both the Restate service handler and the application code in the same process. This is simpler but less resilient since both components share the same process lifecycle.
func main() {
    // ... client and agent setup from above ...

    // Start Restate service in a goroutine
    client.StartRestateService("0.0.0.0", "9081")
    
    // Start HTTP server for invoking the workflow
    http.ListenAndServe(":8070", client)
}
With this setup:
  • The Restate service runs on http://localhost:9080
  • Your application server runs on http://localhost:8070
  • Both are in the same process
Note: If this process crashes, both components restart together. True durability requires separation (Option 2).

Option 2: Separate Processes (Production)

For production deployments, run the Restate service and application in separate processes. This provides true fault isolation—if your application crashes, the Restate service continues running and can recover the workflow.

Application Process

// application/main.go
package main

import (
    "context"
    "log"
    "net/http"
    "os"

    "github.com/curaious/uno/pkg/agent-framework/agents"
    "github.com/curaious/uno/pkg/agent-framework/prompts"
    "github.com/curaious/uno/pkg/gateway"
    "github.com/curaious/uno/pkg/llm"
    "github.com/curaious/uno/pkg/llm/responses"
    "github.com/curaious/uno/pkg/sdk"
)

func main() {
    client, err := sdk.New(&sdk.ClientOptions{
        LLMConfigs: sdk.NewInMemoryConfigStore([]*gateway.ProviderConfig{
            {
                ProviderName: llm.ProviderNameOpenAI,
                ApiKeys: []*gateway.APIKeyConfig{
                    {
                        Name:   "Key 1",
                        APIKey: os.Getenv("OPENAI_API_KEY"),
                    },
                },
            },
        }),
        RestateConfig: sdk.RestateConfig{
            Endpoint: "http://localhost:8081", // Restate server
        },
    })
    if err != nil {
        log.Fatal(err)
    }

    model := client.NewLLM(sdk.LLMOptions{
        Provider: llm.ProviderNameOpenAI,
        Model:    "gpt-4o-mini",
    })

    // Register the agent
    agentName := "my-agent"
    _ = client.NewRestateAgent(&sdk.AgentOptions{
        Name:        agentName,
        Instruction: client.Prompt("You are a helpful assistant."),
        LLM:         model,
        History:     client.NewConversationManager(),
    })

    // Start application server
    log.Println("Application server starting on :8070")
    http.ListenAndServe(":8070", client)
}

Restate Service Process

// restate-service/main.go
package main

import (
    "log"
    "os"

    "github.com/curaious/uno/pkg/agent-framework/prompts"
    "github.com/curaious/uno/pkg/gateway"
    "github.com/curaious/uno/pkg/llm"
    "github.com/curaious/uno/pkg/sdk"
)

func main() {
    client, err := sdk.New(&sdk.ClientOptions{
        LLMConfigs: sdk.NewInMemoryConfigStore([]*gateway.ProviderConfig{
            {
                ProviderName: llm.ProviderNameOpenAI,
                ApiKeys: []*gateway.APIKeyConfig{
                    {
                        Name:   "Key 1",
                        APIKey: os.Getenv("OPENAI_API_KEY"),
                    },
                },
            },
        }),
        RestateConfig: sdk.RestateConfig{
            Endpoint: "http://localhost:8081",
        },
    })
    if err != nil {
        log.Fatal(err)
    }

    model := client.NewLLM(sdk.LLMOptions{
        Provider: llm.ProviderNameOpenAI,
        Model:    "gpt-4o-mini",
    })

    // Register the SAME agent with the SAME configuration
    agentName := "my-agent"
    _ = client.NewRestateAgent(&sdk.AgentOptions{
        Name:        agentName,
        Instruction: client.Prompt("You are a helpful assistant."),
        LLM:         model,
        History:     client.NewConversationManager(),
    })

    // Start only the Restate service
    log.Println("Restate service starting on :9081")
    client.StartRestateService("0.0.0.0", "9081")
}
Important: Both processes must register the agent with the exact same name and configuration. This ensures the Restate service can execute the agent with the correct setup.

Running the Services

# Terminal 1: Start the Restate service
cd restate-service
go run main.go

# Terminal 2: Start the application
cd application
go run main.go

Registering the Deployment with Restate Server

After starting your Restate service (on port 9081), you must register it with the Restate server so it can discover and invoke your agent workflows.

Using the Restate CLI

# Install Restate CLI (if not already installed)
npm install -g @restatedev/restate

# Register the deployment
restate deployments register http://localhost:9081

Using the Admin API

curl localhost:9070/deployments --json '{"uri": "http://localhost:9081"}'

Using the Restate UI

Navigate to your Restate server’s UI (typically http://localhost:9070) and register the deployment through the web interface. Note: If running Restate in Docker, replace localhost with host.docker.internal:
restate deployments register http://host.docker.internal:9081

Automatic Registration

For production environments, consider automating deployment registration:
  • Kubernetes: Use the Restate Kubernetes Operator for automatic registration and lifecycle management
  • CI/CD Pipeline: Add deployment registration as a step in your CI/CD pipeline
  • FaaS Platforms: AWS Lambda, Vercel, and other FaaS platforms automatically handle versioning through version-specific ARNs/URLs
See the Restate deployment documentation for more details.

Example: Complete Durable Agent

See the complete working example in the repository: examples/13_durable_agent/main.go

Learn More