This is the documentation of version 1.0.0-rc.0. Go to the latest version, 1.0.0-rc.5.

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 requests, how to format answers and treat incomplete data, how to call tools, and how session state works. Instead of replacing 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. In order, createMapAgent assembles your optional systemPromptPrefix, the base sections with any overrides, and 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

Prefix, suffix and section overrides work together. 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. Override it 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, and 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 only to the main model, never to the intent classifier, which picks each turn's tools from their classificationPrompt. So "always also call getTrafficIncidents" has no effect on a turn where the classifier didn't pick that tool:

Four ways to shape it

Choose 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, so your agent gets base refinements when you 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, and the SDK adds the heading. Sections you leave out keep their defaults. An empty string drops the section, including its heading:

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 instead of replacing it, read its default from SYSTEM_PROMPT_SECTIONS and derive a new value. Use this to append one rule, or to hand 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. If you pass it back as systemPrompt, it is a full string, so the prefix and suffix are 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 instead of starting 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 the result can be longer than a hand-written prompt. If size matters, don't restate what the base sections cover. Override only where the persona differs, for example a one-line rejectionRules instead of the multi-bullet default, or '' to drop a section. composeSystemPrompt(overrides).length gives the assembled length.

Related