Run an agent in your process
A local session runs inside your Python program: the SDK runs the agent loop, your tools and the session's record in your process. A local session lives as long as your program: run it, continue it, and cancel it once you no longer need it. To keep it after the program exits, save it to a file (experimental).
To start, you need the SDK installed and MISTRAL_API_KEY set.
Run a session
Build the agent and run it. model is the only required argument of agents.Agent(...): see
Configuring the agent for every argument and its default. agent.run()
answers one message; a session keeps the conversation across messages:
import asyncio
from mistralai import agents
agent = agents.Agent(
model="mistral-large-4",
name="trip-planner",
instructions="Answer in one short sentence.",
)
async def main() -> None:
state = await agent.run("Name one museum in Lisbon.")
print(state.text)
session = await agent.start_session()
await session.run("My name is Ada.")
state = await session.run("What is my name?")
if isinstance(state, agents.Completed):
print(state.text)
await session.cancel() # ends the session
if __name__ == "__main__":
asyncio.run(main())There are two ways to run it:
- As a script:
uv run python main.pyrunsmain(), which sends the agent its messages from code. - Interactively:
uv run agents chat, run from the location ofmain.py, loads the same agent and lets you type each message in your terminal. It importsmain.py, so theif __name__ == "__main__":line keepsmain()from running on import.
agent.run() answers approvals only through an
approval handler, passed as on_approval=.
Without one, a tool call that needs an approval makes the run return agents.Blocked. The turn then
waits for an answer on a session handle, up to the
approval timeout, 1 hour by default. After
that, the model gets a timeout result that tells it not to retry the call right away, and the turn
goes on.
Continue a session
To continue the conversation, use a session. The session that agent.run() starts closes once its turn
is done, and you can then only read it. To follow up on an agent.run(), run it inside an
async with agents.local_runtime() block, and open its session by id there:
# Inside main() of the script above.
async with agents.local_runtime():
state = await agent.run("Name three hotels in Lisbon, on one line.")
print(state.text)
session = agent.session(state.session_id)
state = await session.run("Which of them is usually the cheapest?")
if isinstance(state, agents.Completed):
print(state.text)
await session.cancel()Save a session
By default, local sessions stay in memory, and end with your process. To keep them on disk, use the
experimental
JsonFileSessionJournal.
Ending turns and sessions
A local session ends when you cancel it, when its agent.run() turn is done, when a local_runtime()
block exits, or when its event loop stops:
| Event | Effect |
|---|---|
You stop waiting: timeout=, asyncio.wait_for, a cancelled task | The turn keeps running until you stop it with turn.interrupt(), or end the whole session with session.cancel(). |
agent.run() returns a finished turn | Its own turn has ended, and any background process it started has stopped. Its subagents keep running. Once they are done, its session closes, and you can still read it. Inside an async with agents.local_runtime() block, or with idempotency_key=, the session stays open until you cancel it. |
await session.cancel() returns | The session and its subagents have ended, and its sandbox is free. |
The async with agents.local_runtime() block exits | Every local session of the event loop ends. You can still read it until the event loop stops. A session saved with the experimental journal is only unloaded, and reopens. |
| The event loop stops, or the process exits | Every local session stops, and is gone: no worker continues it. |
Here the run returns after one second, with the turn still running, and session.cancel() stops it:
# Inside main() of the script above.
state = await agent.run("Write a 2,000-word story about a lighthouse.", timeout=1)
print(type(state).__name__)
await agent.session(state.session_id).cancel() # stops the turn and ends the sessionA command on a session that has ended raises agents.SessionTerminatedError:
Cancel each session once you no longer need it. A session still open when the event loop stops makes
the SDK log the warning agents_sdk_local_runtime_closed_open_sessions, with the ids of the sessions.
A session that ended stays in memory until the event loop stops, so you can still read its turns. A server that runs many sessions on one event loop grows with each one.
Stop every local session
Each event loop has one local runtime: the part of the SDK that runs the local sessions of that
loop, and their subagents. agent.run(), agent.start_session()
and agent.session(id) all use it. Two asyncio.run calls in one script have two runtimes, and do
not share sessions.
agents.local_runtime() returns the event loop's local runtime. Getting it starts nothing. Use it as
an async with block to stop all the local work of the event loop when the block ends:
# Inside main() of the script above.
async with agents.local_runtime():
session = await agent.start_session()
await session.start_turn("Plan a weekend in Lisbon.")
# Every local session of this event loop has stopped: running turns, subagents and sandboxes.The block's exit also stops the sessions started before the block, and the turns and subagents that a run you stopped waiting for left running. In nested blocks, only the outermost block's exit stops the sessions.
When the block exits, every local session it ran ends, and the exit frees it. After that, a read or
a command on one, such as turn.get() or session.send_message(...), raises
agents.SessionTerminatedError, and session.cancel() returns. To continue, start a new session.
A session saved with the experimental journal is only unloaded, and reopens.
Other settings
Raise the program time budget
The model can call your tools from a program: TypeScript
code the model writes and runs with the run_typescript tool. Each program has a time budget of 60
seconds. The budget applies to the whole program, not to each tool call. A running tool call always finishes, but once the budget is used
up, the program stops before its next step and the model gets a ProgramBudgetExceeded error. Time
spent waiting for an approval or a hook does not count.
If a program that calls your tools can take longer than 60 seconds, raise the budget for every local session of the event loop:
# Wrap the body of main() in the block, before any other local session.
async with agents.local_runtime(program_timeout_seconds=300):
await agent.run("Build the report")The budget is fixed when the runtime starts, so open the block before the event loop's first local session and run your sessions inside it.
Common issues
SessionTerminatedError: The local Session has ended. The session was cancelled, or anasync with agents.local_runtime()block it ran in has exited. Start a new session.SdkError: This event loop's local runtime already runs with .... A local session started the runtime before the block. Open the block first, at the top ofmain().- The warning
agents_sdk_local_runtime_closed_open_sessions. Sessions were still open when the event loop stopped. Callawait session.cancel()on each one once you no longer need it.
Next steps
- Session lifecycle and listing: list the local sessions of the event loop.
- Self-hosted:
agents serve: deploy the same agent on your own infrastructure, so its sessions survive your process.