Customizing the system prompt

The base system prompt teaches the LLM how to behave as a map assistant: who it is, what it can do, how to decline out-of-scope asks, how to format answers, how to read incomplete data, how to call tools, and how session state works. You rarely need to replace it wholesale — instead you can prepend a preamble, append instructions, or override individual sections while every other section keeps its default.

How a prompt is assembled

The base prompt is split into seven named sections (exported as SYSTEM_PROMPT_SECTIONS). createMapAgent assembles the final prompt top-to-bottom: your optional systemPromptPrefix, then the (possibly overridden) base sections, then your optional systemPromptSuffix under an ADDITIONAL INSTRUCTIONS: heading.

MapAgentOptions (you supply)Assembled system promptsystemPromptPrefixsystemPrompt:SystemPromptSectionOverridessystemPromptSuffixsystemPromptPrefix — optional preamble, no headingBASE_SYSTEM_PROMPT — composeSystemPrompt(): each section overridableADDITIONAL INSTRUCTIONS: ← systemPromptSuffix (optional)identity (no heading)capabilities (no heading)rejectionRules → SCOPE & REJECTIONS:responseFormatting → RESPONSE FORMATTING:dataConfidence → DATA CONFIDENCE:toolExecution → TOOL EXECUTION:sessionState → SESSION STATE: prependsreplaces any section body(heading kept)appends

A full-string systemPrompt replaces the entire grey box (and ignores the prefix and suffix); the prefix, suffix, and section overrides otherwise all compose together.

The sections

SectionHeading in promptDefault 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.
rejectionRulesSCOPE & 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.
responseFormattingRESPONSE FORMATTING:Markdown usage, bolding key info, bullet lists, conciseness.
dataConfidenceDATA CONFIDENCE:Surfacing incomplete, partial, stale, or conflicting data honestly instead of presenting it as complete.
toolExecutionTOOL EXECUTION:Calling context-independent tools in parallel and using a tool’s own show / showOnMap in the same step.
sessionStateSESSION 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" routing — deliberately live in the tool descriptions and the classifier prompt, not the base prompt, so they stay correct as tools change.

Four ways to shape it

Prefer the least invasive option that does the job: nudge with the prefix/suffix, retune with section overrides, and reach for a full replacement only when you must remove base behaviour.

Prepend or append

systemPromptPrefix and systemPromptSuffix keep the whole base prompt live — future SDK releases can refine the base sections and your agent picks them up 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 just that section’s body — the heading is added for you — and omitted sections keep their defaults:

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.',
// capabilities, rejectionRules, dataConfidence, 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) performs the same composition explicitly if you need the assembled string elsewhere — logging, diffing, or a token-budget check.

Full replacement

The escape hatch: you own the entire prompt and re-check the base on each SDK upgrade. Import BASE_SYSTEM_PROMPT as a baseline to extend rather than starting from scratch — it carries the identity, capability summary, scope/rejection, formatting, data-confidence, tool-execution, and session-state guidance that matter for reliability.

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.
`,
});

A full-string systemPrompt ignores systemPromptPrefix and systemPromptSuffix — the caller owns the whole prompt.

Keeping a persona prompt lean

Section overrides add the base scope/formatting scaffolding around your persona text, so an override object can assemble into a larger prompt than an equivalent full-string version. If prompt size matters, lean on the base sections (don’t restate what they already cover) and override only where the persona genuinely differs — overriding rejectionRules with a one-liner, for instance, instead of inheriting the multi-bullet default. composeSystemPrompt(overrides).length gives you the assembled character count to compare against.