The 5-Step Process
A clear protocol between you and the model
Mistral function calling follows a well-defined five-step process. What makes this protocol readable is that each step has a single owner: either you (the developer), the model, or the user. No step is shared, and none is ambiguous. Take time to memorize this division of labor before writing a single line of code: most function-calling bugs happen when a developer expects the model to handle a step that is actually their responsibility.
Define the functions (Developer)
You describe each available function with a JSON schema: name, description, parameters, types, and required fields. The model sees only this specification, not your code.
User asks a question (User)
The user asks their question in natural language. You send this request to the Mistral model along with the list of available tools.
Generate arguments (Model)
The model analyzes the request, selects the appropriate function, and generates the arguments in JSON format. It does not execute the function—it simply tells you what to call and with which parameters.
Execute the function (Developer)
Your code extracts the function name and arguments from the model's response, runs the corresponding function, and retrieves the result.
Generate the final response (Model)
You send the function result back to the model. It integrates the result into the conversation context and formulates a natural response for the user.
The flow in detail
These five steps map directly to code. Let’s walk through them using a single, intentionally minimal example: a user wants to know the status of payment T1001. We’ll use this same example as our running thread through the end of the “Python Implementation” block, so get familiar with its identifiers now.
Step 1: You define the tools
tools = [
{
"type": "function",
"function": {
"name": "get_payment_status",
"description": "Retrieves the status of a payment given its transaction ID",
"parameters": {
"type": "object",
"properties": {
"transaction_id": {
"type": "string",
"description": "The unique transaction identifier (e.g., T1001)"
}
},
"required": ["transaction_id"]
}
}
}
]
Nothing here is executable code: it’s a description. The model learns that a function named get_payment_status exists, what it does, and that it requires a transaction_id of type string. It never learns how the function is implemented.
Step 2: The user asks their question
messages = [
{"role": "user", "content": "What is the status of payment T1001?"}
]
Step 3: The model generates the arguments
from mistralai.client import Mistral
client = Mistral(api_key="your-api-key")
response = client.chat.complete(
model="mistral-large-latest",
messages=messages,
tools=tools
)
# The model returns a tool_call:
# function.name = "get_payment_status"
# function.arguments = '{"transaction_id": "T1001"}'
The model extracted “T1001” from the sentence and placed it in the correct field. Note that arguments arrives as a string, not a dictionary: you’ll handle that detail in the next step.
Step 4: You execute the function
import json
tool_call = response.choices[0].message.tool_calls[0]
args = json.loads(tool_call.function.arguments)
result = get_payment_status(**args)
# → {"status": "paid", "date": "2026-03-15"}
Step 5: The model formulates the response
messages.append(response.choices[0].message)
messages.append({
"role": "tool",
"name": "get_payment_status",
"content": json.dumps(result),
"tool_call_id": tool_call.id
})
final = client.chat.complete(
model="mistral-large-latest",
messages=messages,
tools=tools
)
print(final.choices[0].message.content)
# → "Payment T1001 was made on March 15, 2026."
Two messages are added to the history, never just one: the assistant message carrying the call request, followed by the message carrying the result. The tool_call_id links the two. Omit either one and the API will reject the request, or the model will respond off-topic.
Flow patterns
Depending on the complexity of the request, this flow can take different forms. The simplest case is when no function is involved: the user asks a general question—“What can you do?”—and the model responds directly without ever touching the tools. Next comes the simple call we just walked through: one function, one result, one response.
The next two forms are more interesting. In a sequential chain, the model calls a first function, reads the result, and uses it to decide on a second call—typically retrieving a customer ID before querying their invoices. In a parallel call, it requests execution of multiple functions at once because none depends on the other. We’ll devote entire lessons to these two patterns; for now, remember that both rely on exactly the five steps above, simply repeated.
Key takeaways
- Function calling follows a 5-step process with clear responsibilities
- The model can never execute a function directly—it only generates arguments
- Your code is the gatekeeper: it decides whether or not to execute the function
- The function result is sent back to the model so it can formulate a natural response
- The flow can be simple (one call) or complex (sequential, parallel)