Conversations Without an Agent
On this page
Direct Model Mode
You don’t always need to create an Agent object to use the Conversations API. Mistral lets you start a conversation directly with a model, giving you persistence without the Agent configuration layer. This capability follows directly from the architecture we covered earlier: the conversation is independent of the agent, so it can exist perfectly well on its own.
Several scenarios make this preferable to using a registered agent. You might want persistence without defining a reusable agent, for example because every thread in your application is unique. You might be quickly testing behavior with built-in tools, and creating then deleting an agent for every trial slows you down. Your use case might be one-off: nothing justifies saving a configuration you’ll only use once. Or your instructions might be dynamic, calculated at runtime based on the user’s profile or the current document—in which case a static agent would be the wrong tool.
Starting a Conversation with a Model
Instead of passing an agent_id, you pass a model directly:
from mistralai.client import Mistral
import os
client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])
response = client.beta.conversations.start(
model="mistral-medium-latest",
inputs=[
{"role": "user", "content": "Who is Albert Einstein?"}
]
)
print(response.outputs[-1].content)
Everything hinges on this parameter choice. With agent_id, the call inherits the agent’s registered configuration: its instructions, tools, and guardrails. With model, you’re in direct mode and configure everything in the request itself—nothing is remembered beyond the call.
Adding Tools in Direct Mode
Built-in tools aren’t reserved for agents. You declare them in the request, exactly as you would during agent creation.
response = client.beta.conversations.start(
model="mistral-large-latest",
inputs=[
{"role": "user", "content": "What are the latest Ligue 1 results?"}
],
tools=[{"type": "web_search"}]
)
This is functionally equivalent to an agent with web_search, but without configuration persistence. The difference shows up on the tenth call: with an agent, you would have changed the temperature once and for all; here, you must repeat it in every request, and nothing guarantees that a forgotten call somewhere in your code uses the same settings.
Passing Dynamic Instructions
In direct mode, the system prompt takes the form of a system message placed at the head of the inputs.
response = client.beta.conversations.start(
model="mistral-large-latest",
inputs=[
{"role": "system", "content": "You are an expert in French cuisine. Answer in 3 sentences maximum."},
{"role": "user", "content": "How do I make a perfect béarnaise sauce?"}
]
)
This gives you the same flexibility as agent instructions, but ephemeral—and crucially, this string can be built at runtime, which a registered agent instruction does not allow.
Continuing a Conversation in Direct Mode
The persistence mechanism works exactly as it does with an agent: the first call returns a conversation_id, subsequent calls attach to it, and each append returns a new identifier in turn.
# First exchange
response1 = client.beta.conversations.start(
model="mistral-large-latest",
inputs=[{"role": "user", "content": "Tell me about the French Revolution."}]
)
# Second exchange — context is preserved
response2 = client.beta.conversations.append(
conversation_id=response1.conversation_id,
inputs="Who were the main figures?"
)
# The model knows we're talking about the French Revolution
The second question doesn’t name its subject and doesn’t need to: persistence doesn’t depend on the existence of an agent, only on the conversation.
Agent vs. Direct Mode: How to Decide
Create an agent as soon as the same configuration serves multiple conversations, because you then gain a single point of modification: correcting an instruction updates all future threads. This is also the only path if your instructions are stable from session to session, if you plan multi-agent handoffs—which the lesson dedicated to the topic will detail—or if your organization requires centralized, auditable configuration where you can answer the question, “With what instructions was this assistant executed?”
Stay in direct mode for the symmetric cases: a prototype or one-off usage, instructions that change with every conversation, no need for handoffs, or a desire to reduce API calls by eliminating agent creation and deletion. In development, this last reason is often enough on its own.
Example: Question Within a Forced Context
The function below illustrates direct mode perfectly. The context is an argument, so it differs with every call, and the system prompt is built around it to prevent the model from drawing on outside knowledge. No registered agent could carry this constraint, since it changes with every invocation.
def ask_question(context: str, question: str):
"""Ask a question within a given context, without creating an agent."""
response = client.beta.conversations.start(
model="mistral-large-latest",
inputs=[
{"role": "system", "content": f"Context: {context}. Answer using only this context."},
{"role": "user", "content": question}
]
)
return response.outputs[-1].content
# Usage
answer = ask_question(
context="Corsen AI is a French startup specializing in sovereign AI, founded in 2025.",
question="When was Corsen AI founded?"
)
Reuse this pattern for your own needs by replacing the hardcoded context with the actually relevant text—the product sheet viewed, the selected document excerpt—and you get a framed Q&A building block without any configuration to administer.
Key Takeaways
- Conversations can be started with
model=instead ofagent_id= - Direct mode offers persistence without the Agent configuration layer
- You can pass tools and instructions directly in the request
- Direct mode is ideal for prototyping and one-off usage
- For handoffs and reusable configurations, prefer creating an agent