Customizing the system prompt
The base system prompt teaches the model to behave as a map assistant: who it is, what it can do, how to decline out-of-scope asks, how to format answers and treat incomplete data, how to call tools, and how session state works. Rather than replace it, prepend a preamble, append instructions, or override individual sections while the others keep their defaults.
How a prompt is assembled
The base prompt has seven named sections, exported as SYSTEM_PROMPT_SECTIONS. createMapAgent assembles, top to
bottom: your optional systemPromptPrefix, the base sections with any overrides, then your optional
systemPromptSuffix under an ADDITIONAL INSTRUCTIONS: heading.
Prefix, suffix and section overrides compose. A full-string systemPrompt replaces the whole grey box and ignores the
prefix and suffix.
The sections
| Section | Heading in prompt | Default governs |
|---|---|---|
identity |
(none — opening line) | Who the assistant is and its tone of voice. The natural hook to rebrand or set a persona. |
capabilities |
(none) | A compact summary of what the toolkit can do (search, routing, ranges, traffic, analysis, BYOD, map control). Anchors what rejectionRules steers users back toward. |
rejectionRules |
SCOPE & REJECTIONS: |
How to handle in-scope / mixed / out-of-scope / illegal requests, and that every rejection explains why and offers an in-scope next step. |
responseFormatting |
RESPONSE FORMATTING: |
Markdown usage, bolding key info, bullet lists, conciseness. |
dataConfidence |
DATA CONFIDENCE: |
Surfacing incomplete, partial, stale, or conflicting data honestly instead of presenting it as complete. |
toolExecution |
TOOL EXECUTION: |
Calling independent tools in parallel and rendering with a tool's own show / showOnMap in the same step; grounding every fact and map change in a tool call, answering without one only from what the conversation and state already hold; asking for missing input only by calling clarifyIntent. |
sessionState |
SESSION STATE: |
The append-only entry model (places-3, routes-1), checking recallState before referencing an id, and never inventing ids. |
Per-tool mechanics — coordinate order, "near me" vs "in this area" — live in the tool descriptions and the
classifier prompt, so they stay correct as tools change.
What the prompt doesn't reach
The system prompt goes to the main model only, never to the intent classifier,
which picks each turn's tools from their classificationPrompt. So "always also call toggleTilesTrafficFlow" has no
effect on a turn where the classifier didn't pick that tool:
- To change when a tool is offered, extend its
classificationPrompt(see Per-tool classification prompts). - To offer it on every turn, set
alwaysActive: trueon its entry. - Then use the prompt to tell the model when to call it.
Four ways to shape it
Prefer the least invasive option: nudge with the prefix or suffix, retune with section overrides, and replace the prompt only when you must remove base behaviour.
Prepend or append
systemPromptPrefix and systemPromptSuffix keep the whole base prompt live, so your agent picks up base refinements
on upgrade.
const agent = createMapAgent(map, {
model: openai('gpt-4o'),
systemPromptPrefix: 'You work for Acme Logistics.',
systemPromptSuffix: 'Always respond in Spanish. Never show more than 5 places at once.',
});
Override individual sections
Pass a SystemPromptSectionOverrides object as systemPrompt. Each key replaces that section's body — the heading
is added for you — and omitted sections keep their defaults. An empty string drops the section, heading included:
const agent = createMapAgent(map, {
model: openai('gpt-4o'),
systemPrompt: {
identity: 'You are a delivery fleet dispatcher built on the TomTom map.',
responseFormatting: 'Reply in Dutch, metric units, one short paragraph.',
dataConfidence: '', // dropped
// capabilities, rejectionRules, toolExecution, sessionState stay default
},
});
Extend a default section
To add to a section rather than replace it, read its default from SYSTEM_PROMPT_SECTIONS and
derive a new value — handy for appending one rule, or for handing the default to a coding agent to
rewrite under some criteria:
import { createMapAgent, SYSTEM_PROMPT_SECTIONS } from '@tomtom-org/maps-sdk-plugin-agent-toolkit';
const agent = createMapAgent(map, {
model: openai('gpt-4o'),
systemPrompt: {
rejectionRules: `${SYSTEM_PROMPT_SECTIONS.rejectionRules}\n- Decline weather questions.`,
},
});
composeSystemPrompt(overrides) returns the assembled string for logging, diffing or a token-budget check. Passed back
as systemPrompt it is a full string, so the prefix and suffix are then ignored.
The site selection agent example replaces identity and
capabilities and extends rejectionRules for a narrowly scoped agent.
Full replacement
You own the entire prompt and re-check the base on each upgrade. Extend BASE_SYSTEM_PROMPT rather than start from
scratch — its sections carry the guidance that keeps the agent reliable.
import { createMapAgent, BASE_SYSTEM_PROMPT } from '@tomtom-org/maps-sdk-plugin-agent-toolkit';
const agent = createMapAgent(map, {
model: openai('gpt-4o'),
systemPrompt: BASE_SYSTEM_PROMPT + `
ADDITIONAL INSTRUCTIONS:
- This is a logistics application. Prioritize route efficiency over scenery.
- Always show estimated arrival times in responses.
- When a vehicle ID is mentioned, call getFleetVehicle before anything else.
`,
});
Keeping a persona prompt lean
Section overrides keep the base scaffolding around your persona text, so they can assemble into a longer prompt than a
hand-written one. If size matters, don't restate what the base sections cover, and override only where the persona
differs — a one-line rejectionRules instead of the multi-bullet default, or '' to drop a section.
composeSystemPrompt(overrides).length gives the assembled length.
Related
- Securing your agent — why the prompt is public, and why it is not the place for credentials, internal detail or a spending rule.
- How it works — where the assembled prompt is used in a turn.
- Customizing tools — shaping the registry the prompt describes.