System Messages and Tools
- How to add system messages that instruct your LLM agent
- How to configure function-calling tools the agent can use
- How to set up pre-actions that run before the AI processes a message
When you create an LLM-based agent, three of the five configuration tabs control what the agent knows, what it can do, and what happens before it responds. This page covers the Messages, Actions, and Tools tabs.
Messages tab — System messages
The Messages tab contains the System messages section. System messages are the instructions you give the language model before any customer conversation begins. They define the agent's personality, knowledge, boundaries, and behavior.
How system messages work
System messages are evaluated and sent to the LLM with every request, before the conversation history. The model reads these instructions and follows them when generating responses. You can add multiple system messages, and they are processed in order. Because they are re-evaluated on every message, dynamic values in them (see below) always reflect the current conversation turn.
Adding system messages
- Open your LLM agent and go to the Messages tab.
- You will see a list area for system messages.
- Click the "Add item" button to add a new system message.
- Type your instruction in the text field that appears.
- Repeat to add as many system messages as needed.
Tips for writing effective system messages
- Identity: Tell the agent who it is. Example: "You are the virtual agent for Bella Salon, a hair and beauty studio in downtown Lisbon."
- Knowledge: Provide key facts. Example: "Our services include haircuts (starting at 25 euros), coloring, and styling. We are open Tuesday through Saturday, 9am to 7pm."
- Tone: Set the communication style. Example: "Be friendly, professional, and concise. Use the customer's first name when possible."
- Boundaries: Define what the agent should not do. Example: "Never discuss competitor pricing. Do not make medical or legal claims. If unsure, offer to connect the customer with a human attendant."
- Escalation: Explain when to hand off. Example: "If the customer asks to speak with a person, or if you cannot resolve their question in 3 exchanges, transfer the conversation to a human agent."
Using dynamic variables in system messages
System messages are CEL expressions, so you can insert dynamic information at runtime. This lets you personalize agent behavior based on the contact, the incoming message, or the conversation.
Write a native CEL expression using the available variables. For a fixed prompt, use the literal-text control to create a quoted and escaped CEL string. See the CEL expression editor for both authoring options.
Available variables:
| Variable | Description |
|---|---|
contact | Contact information (name, tags, custom attributes) |
contactMessage | The incoming message (type, text content, media, timestamp) |
conversation | The chat session (channel, status, last message) |
company | Your company record |
Examples:
- Personalizing greetings:
'The contact name is ' + contact.name - Including contact tags:
'Contact tags: ' + contact.tags.join(', ') - Conditional behavior based on message type:
contactMessage.type == 'text' ? contactMessage.body.text : '(media message)'
Use the Explorer panel to browse all available variables and their properties. Click a variable to see its sub-fields and insert them into your expression.
Tools tab — Function calling
The Tools tab lets you give your agent the ability to call external functions during a conversation. This is known as "function calling" or "tool use": the agent can look up data, perform calculations, check availability, or create bookings.
The Tools tab settings only appear when the agent can actually use tools. If you turn on Enable structured outputs (on the Advanced tab), or if the selected model does not support function calling, the Tool choice, Tools, and MCP Servers fields are hidden and the tab appears empty.
Tool choice
At the top of the Tools tab, you will find the Tool choice dropdown with four options:
- auto (default): The model decides on its own when to call a tool based on the conversation context.
- required: The model must call a tool before responding.
- none: The model never calls tools.
- function: The model is forced to call one specific tool. Selecting this reveals a Function field where you pick the tool by name.
Adding tools
- Go to the Tools tab on your LLM agent.
- In the Tools list, click the "Add item" button.
- Configure the tool by providing its name, description, and parameter schema. The description is critical because the LLM reads it to decide when and how to use the tool. Three optional switches sit beside them, all off by default:
- Hidden: the tool is left out of the tool list sent to the model, so the agent never calls it directly. It stays callable by name from another tool's actions.
- Parallel-safe: the tool may run at the same time as other read-only tool calls in the same turn. Leave it off for tools that change data or depend on call order — and note that a tool with actions is always run sequentially regardless of this flag.
- Ends the turn (
endsTurn): once this tool has run, if the contact has received a reply during the run, the run stops there and the model gets no further turn. A failed action does not prevent that — what decides is whether a reply reached the contact. See Ending the turn after a tool.
- In the tool's Actions section, add steps when the tool needs to query data, call an API or change a record. A tool can also have zero actions and return a pure CEL result through
function.output.main, including a function from a pinned tenant library. Tool parameters are available directly by name in that expression. - In the Output section, configure what the tool returns to the model. main is the only field here: a CEL expression whose value is handed back to the model after the actions run. Leave it empty and the tool returns nothing useful to the agent, so set it to whatever the model needs to see.
- Repeat for each tool you want the agent to access.
{
"type": "function",
"function": {
"name": "normalize_name",
"description": "Return a normalized name without calling an action",
"parameters": [{"name": "value", "type": "string", "isRequired": true}],
"actions": [],
"output": {"main": {"expr": "text.nameKey(value)", "onFailure": "throw"}}
}
}
By default the runner feeds every tool result back to the model as part of the normal agent loop, so a post-tool model turn happens on its own — you decide, through main, what the model gets to see. The one way to skip that turn is Ends the turn, below.
Ending the turn after a tool
Some tools are the reply: an options menu sent with Compose conversation response (type: "options"), a generated document, a booking confirmation. If the model takes another turn after such a tool, whatever it writes arrives after the menu — on WhatsApp, as text below the buttons. Turn on Ends the turn (endsTurn) on the tool to prevent that.
When the model calls a tool with Ends the turn on, three things keep that call from ending the turn, and the model then takes its next turn as usual:
- the arguments were not valid JSON, so the tool did not run;
- main failed to evaluate — the model receives the error as the tool's result;
- the call repeated one with the same arguments that had already returned the same result twice in this run, so the runner answered with that result instead of running the tool again.
A failing action is not on that list. The runtime records the failure on that step — status failed, with the error in the agent log — and carries on with the next action and main, so the call still counts as run; an action whose condition cannot be evaluated is skipped and does not stop it either. What decides is whether a reply actually reached the contact — a menu whose send failed still ends the run if an earlier message of this run did reach the contact — so the runner checks whether the contact has received anything during this run, from this tool's actions or earlier:
- Yes: the run ends right after that batch of tool calls, the same way as when a tool sent the reply and the model's last turn was empty (
tool_reply). The model gets no further turn, and the agent log records "Run ended by an endsTurn tool after its actions". - No — the send failed, say, or the only message went to someone else: the setting is ignored for this call, a warning is logged ("endsTurn tool finished without a user-visible reply; the run continues"), and the run carries on normally, so the model can still answer.
A few more details:
- It works on the tools defined on this tab. Tools from MCP servers and the built-in tools cannot end the turn.
- If the model called other tools in the same turn, they still run and are saved to the history, but the model does not get to read their results before the run ends.
- A
wait_for_userpause requested in the same turn takes precedence: the run ends waiting for the contact instead. - The model does see the menu on its next run: option menus the agent sent are replayed into its history as the contact saw them (title, description and numbered labels), so a tap that arrives as an option's value can be read against the menu that offered it.
Which sends count as the agent's reply
Ends the turn, and the rule that a run whose last model turn is empty still counts as answered when a tool already sent the reply (tool_reply), both depend on whether the contact has been answered. Only messages delivered to this conversation count:
- Messages that Compose conversation response delivers here count, one per message, and so do those Send message to contact delivers to the run's own contact.
- A message sent to a different contact — a tool that notifies a staff member, for example — does not count. If that is all the run sent and the model's last turn is empty, the run ends as an error (
no_output) instead of being recorded as a reply, because the contact received nothing. Let the model write the answer, or send it to this conversation from the tool. - An action that does not report which messages it sent — running a manual workflow, or calling a resource function — counts as one reply, because the runtime cannot tell where its message went.
- Internal status messages never count.
Example tools
| Tool | Purpose |
|---|---|
| Check calendar availability | Lets the agent look up open appointment slots |
| Create appointment | Lets the agent book an appointment for a customer |
| Look up contact record | Retrieves contact details from your database |
| Send notification | Sends an alert to a team member |
MCP servers
Below the Tools list, the MCP Servers list lets you connect external MCP (Model Context Protocol) servers whose tools become available to the agent at runtime, alongside the tools you define locally. For each server you provide:
- Server URL: The server's public HTTP(S) address.
- Server label and Description (optional): Help you and the model identify the server.
- Require approval: Controls approval for supported native OpenAI Responses MCP execution. The locally bridged MCP path does not enforce this setting; it is not a universal approval gate for every model/server execution path.
- Allowed tools (optional): Restrict which of the server's tools the agent may use.
- Headers (optional): HTTP headers sent to the server, typically for authentication.
The server's tools are discovered automatically and offered to the model together with your local tools.
Start with one or two essential tools and test thoroughly before adding more. Each tool increases the agent's capability but also adds complexity. The model needs clear tool descriptions to use them correctly.
For tools that may be chained in one user request, avoid adding a status-only Compose conversation response as the first tool action. That action runs every time the tool is called, so a search -> fetch -> summarize flow can send several "I'm working..." messages before the useful answer. Prefer returning tool data and letting the final agent response summarize it once. If a long-running tool needs a progress note, send at most one short note for the whole request.
Actions tab — Pre-actions
The Actions tab lets you configure Pre-actions -- steps that run automatically before the LLM processes each incoming message. Pre-actions can transform data, call APIs, set variables, or perform other logic to prepare context for the AI.
If you need a field-by-field guide to the available action types, see the Actions Reference.
Configuring pre-actions
- Go to the Actions tab on your LLM agent.
- You will see the Pre-actions section.
- Click "Add step" to add a new pre-action step.
- Configure the step with the desired logic.
- Add additional steps as needed. Steps run in sequence.
Debugging pre-actions
The Actions tab includes a Start Debug button (its tooltip reads "Debug all"). Clicking it runs all pre-action steps in debug mode so you can see what each step does, what data it produces, and whether any errors occur.
Use pre-actions to enrich the conversation context before the AI responds. For example, a pre-action could fetch the customer's recent order history so the agent can reference it in its reply without needing a tool call during the conversation.