Durable Agents

A Durable Agent is an LLM agent whose loop (model calls, tool use, handoffs) runs inside a workflow, so its state survives crashes and restarts. The Mistral plugin (mistralai-workflows[mistralai]) provides the primitives — Agent, Runner, RemoteSession, and LocalSession (experimental) — for building them.

What you get on top of a regular agent loop:

  • Durability: agent state is preserved across worker crashes and restarts
  • Tool integration: workflow activities can be passed directly as agent tools
  • Multi-agent handoffs: agents can delegate tasks to specialized agents
  • MCP support: connect to external tools via the Model Context Protocol (stdio / SSE)
Installation

Installation

To use Durable Agents, install the Mistral plugin:

uv add 'mistralai-workflows[mistralai]'
Core Components

Core Components

Agent

Agent

The Agent class defines an LLM agent with its model, instructions, tools and handoffs:

import mistralai.workflows as workflows
import mistralai.workflows.plugins.mistralai as workflows_mistralai

agent = workflows_mistralai.Agent(
    model="mistral-medium-latest",
    name="my-agent",
    description="Agent that performs specific tasks",
    instructions="Use tools to complete the user's request.",
    tools=[my_activity],  # Workflows activities as tools
    handoffs=[other_agent],  # Agents to delegate to
)
Runner

Runner

The Runner executes an agent with user inputs and manages the conversation loop. It calls the model, processes tool calls, and repeats until the agent produces a final response or reaches max_turns. If max_turns is reached, the runner returns whatever outputs have been collected so far.

import mistralai.workflows as workflows
import mistralai.workflows.plugins.mistralai as workflows_mistralai

outputs = await workflows_mistralai.Runner.run(
    agent=agent,
    inputs="What is the interest rate for 2024?",
    session=session,
    max_turns=10,  # Maximum model call iterations before stopping
)

The returned outputs is a list of output items produced by the agent during the run (text responses, tool results, handoff results).

Sessions

Sessions

Sessions manage agent state and API communication. Two session types are available:

SessionUse CaseBackend
RemoteSessionProduction (recommended)Mistral Agents SDK
LocalSessionExperimental / On-premisesDirect completion API

RemoteSession lazily creates an agent server-side (or reuses an existing one if agent.id is set) and delegates completions to it. LocalSession runs the agent loop in your worker process using the chat completions API directly.

Basic example

Basic example

Here's a simple agent workflow that uses an activity as a tool:

import mistralai.workflows as workflows
import mistralai.workflows.plugins.mistralai as workflows_mistralai
from mistralai.client.models import TextChunk


@workflows.activity()
async def get_interest_rate(year: int) -> dict:
    """Get the interest rate for a given year.

    Args:
        year: The year to get the interest rate for
    """
    # Your implementation here
    return {"interest_rate": 1.62}


@workflows.workflow.define(name="finance_agent_workflow")
class FinanceAgentWorkflow:
    @workflows.workflow.entrypoint
    async def entrypoint(self, question: str) -> dict:
        session = workflows_mistralai.RemoteSession()

        agent = workflows_mistralai.Agent(
            model="mistral-medium-latest",
            name="finance-agent",
            description="Agent for financial queries",
            instructions="Use tools to answer financial questions.",
            tools=[get_interest_rate],
        )

        outputs = await workflows_mistralai.Runner.run(
            agent=agent,
            inputs=question,
            session=session,
        )

        answer = "\n".join([
            output.text for output in outputs
            if isinstance(output, TextChunk)
        ])

        return {"answer": answer}
Multi-Agent Handoffs

Multi-Agent Handoffs

Agents can delegate tasks to specialized agents using handoffs. The coordinator agent's instructions and the specialized agents' description fields guide handoff decisions; the model decides when to hand off based on the user query and the available specialists' descriptions. The system automatically manages the handoff conversation.

import mistralai.workflows as workflows
import mistralai.workflows.plugins.mistralai as workflows_mistralai

# Create a specialized agent for interest rate queries
interest_rate_agent = workflows_mistralai.Agent(
    model="mistral-medium-latest",
    name="ecb-interest-rate-agent",
    description="Agent for European Central Bank interest rate research",
    instructions="Use tools to get the interest rate for a given year.",
    tools=[get_interest_rate],
)

# Main agent that can hand off to the specialist
finance_agent = workflows_mistralai.Agent(
    model="mistral-medium-latest",
    name="finance-agent",
    description="Agent for financial queries",
    handoffs=[interest_rate_agent],  # Can delegate to interest_rate_agent
)

outputs = await workflows_mistralai.Runner.run(
    agent=finance_agent,
    inputs="What was the ECB interest rate in 2023?",
    session=workflows_mistralai.RemoteSession(),
)

When the finance agent receives a query about ECB interest rates, it can automatically hand off to the specialized interest_rate_agent.

MCP Integration

MCP Integration

Connect to external tool servers using the Model Context Protocol. Two transport types are supported:

Stdio MCP Server

Stdio MCP Server

For local command-line MCP servers:

import mistralai.workflows.plugins.mistralai as workflows_mistralai
from mistralai.workflows.plugins.mistralai import MCPStdioConfig

mcp_config = MCPStdioConfig(
    command="npx",
    args=["-y", "@modelcontextprotocol/server-everything"],
    name="server-everything",
)

agent = workflows_mistralai.Agent(
    model="mistral-medium-latest",
    name="mcp-agent",
    description="Agent with access to MCP tools",
    mcp_clients=[mcp_config],
)
SSE MCP Server

SSE MCP Server

For remote MCP servers over Server-Sent Events:

from mistralai.workflows.plugins.mistralai import MCPSSEConfig

mcp_config = MCPSSEConfig(
    url="https://your-mcp-server.com/sse",
    timeout=60,
    name="remote-tools",
    headers={"Authorization": "Bearer your-token"},  # Optional
)

agent = workflows_mistralai.Agent(
    model="mistral-medium-latest",
    name="sse-mcp-agent",
    description="Agent with access to remote MCP tools",
    mcp_clients=[mcp_config],
)
Built-in Tools

Built-in Tools

Use Mistral's built-in tools alongside activities:

import mistralai.workflows as workflows
import mistralai.workflows.plugins.mistralai as workflows_mistralai
from mistralai.client.models import WebSearchTool

agent = workflows_mistralai.Agent(
    model="mistral-medium-latest",
    name="web-search-agent",
    description="Agent with web search capability",
    instructions="Use web search to answer user questions",
    tools=[WebSearchTool()],
)

Available built-in tools:

ToolDescription
WebSearchTool()Search the web and return results to the model
CodeInterpreterTool()Execute Python code in a sandboxed environment
ImageGenerationTool()Generate images from text descriptions
DocumentLibraryTool()Analyze and extract information from uploaded documents

These tools are executed server-side by the Mistral platform; they do not run in your worker process. Pass them in the tools list alongside any activity-based tools.

warning

Built-in tools require RemoteSession: LocalSession silently drops built-in tools (logs a warning and proceeds without them). Use RemoteSession for any agent that relies on WebSearchTool, CodeInterpreterTool, ImageGenerationTool, or DocumentLibraryTool.

Session Types

Session Types

RemoteSession (Recommended)

RemoteSession (Recommended)

Uses the Mistral Agents SDK for production workloads:

import mistralai.workflows as workflows
import mistralai.workflows.plugins.mistralai as workflows_mistralai

session = workflows_mistralai.RemoteSession()

outputs = await workflows_mistralai.Runner.run(
    agent=agent,
    inputs="Your question here",
    session=session,
)

Features:

  • Full Agents SDK integration
  • Automatic agent creation and updates
  • Managed conversation state
  • Production-ready
Agent Lifecycle

Agent Lifecycle

When a RemoteSession initializes a conversation, it iterates over every agent in the handoff graph and either creates or updates it via the Agents API:

  • Agent(id=None) — a new remote agent is created via beta.agents.create(). The returned ID is stored back on the Agent object, so subsequent runs with the same instance will reuse it.
  • Agent(id="existing-id") — the existing remote agent is updated via beta.agents.update(). No new agent is created.

To reuse a pre-existing agent across workflow executions, set agent.id before calling Runner.run():

agent = workflows_mistralai.Agent(
    id="ag_abc123",  # Reuse an existing remote agent
    model="mistral-medium-latest",
    name="finance-agent",
    instructions="Use tools to answer financial questions.",
    tools=[get_interest_rate],
)
warning

Agents created by RemoteSession are not cleaned up automatically after a run completes. If you create agents dynamically (without setting id), a new remote agent will be created on every workflow execution.

LocalSession (Experimental)

LocalSession (Experimental)

Runs agents locally using the completion endpoint:

import mistralai.workflows as workflows
import mistralai.workflows.plugins.mistralai as workflows_mistralai

session = workflows_mistralai.LocalSession()

outputs = await workflows_mistralai.Runner.run(
    agent=agent,
    inputs="Your question here",
    session=session,
)

Use cases:

  • On-premises or private cloud deployments where the Agents API is not available
  • Development and testing
  • Full context control
warning

LocalSession is experimental and may be removed in future versions. Use RemoteSession for production workloads.

Complete Workflow Example

Complete Workflow Example

A full example combining activities, handoffs and workflow orchestration:

import asyncio
import mistralai.workflows as workflows
import mistralai.workflows.plugins.mistralai as workflows_mistralai
from mistralai.client.models import TextChunk


@workflows.activity()
async def calculate_risk_score(deal_type: str, amount: float) -> dict:
    """Calculate financial risk score for a deal.

    Args:
        deal_type: The type of deal being analyzed
        amount: The monetary amount of the deal
    """
    risk_score = min(100.0, amount / 10000.0)
    risk_factors = []
    if amount > 100000:
        risk_factors.append("High value transaction")
    return {"risk_score": risk_score, "risk_factors": risk_factors}


@workflows.workflow.define(name="deal_analysis_workflow")
class DealAnalysisWorkflow:
    @workflows.workflow.entrypoint
    async def entrypoint(self, deal_request: str) -> dict:
        """Analyze a deal request.

        Args:
            deal_request: The deal request to analyze
        """
        session = workflows_mistralai.RemoteSession()

        # Risk assessment agent
        risk_agent = workflows_mistralai.Agent(
            model="mistral-medium-latest",
            name="risk-agent",
            description="Analyzes financial risk of deals",
            instructions="Use the risk calculation tool to assess deal risk.",
            tools=[calculate_risk_score],
        )

        # Main coordinator agent
        coordinator = workflows_mistralai.Agent(
            model="mistral-medium-latest",
            name="deal-coordinator",
            description="Coordinates deal analysis",
            instructions="Analyze the deal request and hand off to specialists.",
            handoffs=[risk_agent],
        )

        outputs = await workflows_mistralai.Runner.run(
            agent=coordinator,
            inputs=deal_request,
            session=session,
        )

        analysis = "\n".join([
            output.text for output in outputs
            if isinstance(output, TextChunk)
        ])

        return {"analysis": analysis}


if __name__ == "__main__":
    asyncio.run(workflows.run_worker([DealAnalysisWorkflow]))
Observability

Observability

Agent activity emits the same workflow and activity events as any other workflow. Subscribe to them live via Streaming and Consuming Streaming Events, or replay the full event history after the fact.

Best practices

Best practices

  1. Use RemoteSession for production so the agent runs against the Agents API rather than the raw completion endpoint.
  2. Set agent.id on agents you want to reuse across runs, otherwise RemoteSession creates a new remote agent on every execution and never cleans them up.
  3. Wrap tool side-effects in activities so they get retry isolation and appear in the execution history.