Skip to main content
RapidDev - Software Development Agency
build-ai-agent

How to Write an AI Agent System Prompt

A good AI agent system prompt has five parts: role and scope, tool-use rules, escalation triggers, tone and format, and a couple of few-shot examples fixing real failures. This guide writes one for a dental clinic's scheduling agent that books, reschedules, and escalates emergencies to a human, then tests it with a Python eval script instead of manual chats. Budget 30-40 minutes and a few cents of API usage.

What you'll learn

  • Structure a system prompt into role, scope, tool-use rules, escalation triggers, and tone so the model stops guessing at boundaries
  • Write tool-sequencing rules that stop an agent from claiming it did something it never actually called a tool to do
  • Define hard escalation triggers that hand off to a human before the agent improvises a fix
  • Add few-shot examples that correct the specific failure mode you've actually seen, not a hypothetical one
  • Test a system prompt with a small eval script instead of eyeballing a handful of chat turns
Book a free consultation
4.9Clutch rating ⭐
1,000+Happy partners
20+Countries served
200+Team members
Intermediate11 min read30-40 minPython 3.10+, an Anthropic API key, works with any tool-calling model or agent frameworkLast updated November 2026RapidDev Engineering Team
TL;DR

A good AI agent system prompt has five parts: role and scope, tool-use rules, escalation triggers, tone and format, and a couple of few-shot examples fixing real failures. This guide writes one for a dental clinic's scheduling agent that books, reschedules, and escalates emergencies to a human, then tests it with a Python eval script instead of manual chats. Budget 30-40 minutes and a few cents of API usage.

Quick facts about this guide
FactValue
ToolAny LLM or framework
DifficultyIntermediate
Time required30-40 min
CompatibilityPython 3.10+, an Anthropic API key, works with any tool-calling model or agent framework
Last updatedNovember 2026

What you'll build

You'll write the system prompt for a scheduling agent at Riverside Dental. It books, reschedules, and cancels appointments through three tools, and the moment a message sounds like a dental emergency, it stops touching the calendar and hands off to a human on call. That handoff rule is the part most tutorials skip, and it's the part that actually matters once the agent is live.

The prompt is built in five labeled sections: role and scope (what the agent is for, and just as important, what it isn't), tool-use rules (the order things must happen in), escalation triggers (the exact phrases and situations that stop the agent cold), tone and format (how it talks and how long its replies are), and two or three few-shot examples that each fix one real failure you observed in testing. Sections matter more than word count. A model reading a wall of undifferentiated prose will blend your tone guidance with your safety rules; a model reading clearly delimited sections treats them as separate, enforceable policies.

This structure is framework-agnostic. The same five sections go into ClaudeAgentOptions.system_prompt in the Claude Agent SDK, the systemMessage option on an n8n AI Agent node, or the instructions field of any OpenAI Agents SDK or LangGraph agent. What changes between frameworks is where the string lives, not how you write it.

Prerequisites

  • An Anthropic API key (ANTHROPIC_API_KEY) with available credits
  • Python 3.10+ (pip install anthropic)
  • A list of the tools your agent will call, with a one-line description of what each one does
  • 5-10 real or realistic user messages covering both normal requests and edge cases

Step-by-step guide

1

Write the role and scope in one paragraph

Start with a single paragraph that says exactly what the agent is for and, just as important, what it is not for. A scope sentence that only states the positive case ('you help patients schedule appointments') leaves the model free to improvise when a patient asks something adjacent, like a billing question or a symptom question. Naming the boundary explicitly gives the model a clean reason to decline or redirect instead of guessing.

typescript
1<role>
2You are the scheduling assistant for Riverside Dental. You book, reschedule,
3and cancel appointments using the tools below. You do not give medical advice,
4quote prices, or discuss insurance coverage. For those, tell the patient a
5front-desk team member will follow up.
6</role>

Expected result: A 3-5 sentence block that reads naturally and draws a clear line around the agent's job.

2

Add tool-use rules that force a sequence

List the order operations must happen in, as bullet points, not prose. The point is to stop the model from calling book_appointment before it has actually confirmed a slot is free, and from telling the patient something is done before the tool call that would make it true. Write these as commands aimed at the model's behavior, not as documentation of what the tools do; the tool descriptions in your schema already cover that.

typescript
1<tool_rules>
2- Always call check_availability before offering a time slot.
3- Never tell the patient an appointment is booked, changed, or canceled
4 unless the corresponding tool call has already returned success.
5- If check_availability returns no slots within 7 days, say so plainly
6 instead of inventing an alternative.
7</tool_rules>

Expected result: A short, scannable rule list that a new engineer could use to predict the agent's tool-call order.

3

Define hard escalation triggers

Enumerate the exact situations that must stop the agent from touching the calendar and route to a human, and name the handoff tool explicitly. Vague guidance like 'use good judgment with emergencies' produces inconsistent behavior; a model is much more reliable when the trigger list is concrete enough to pattern-match against incoming text.

typescript
1<escalation>
2Call escalate_to_human immediately, before any other tool, if the patient
3mentions: severe pain, swelling, bleeding that won't stop, a broken or
4knocked-out tooth, or a dental emergency in any other words. Also escalate
5if the patient is angry about a prior visit or asks for a refund.
6</escalation>

Expected result: A list specific enough that you could grade a transcript against it and get the same answer every time.

4

Set tone, format, and length constraints

State how the agent talks: sentence length, whether it signs off, how it reads back a confirmed appointment. Without this section, the model defaults to a generic, slightly over-polite chatbot voice that doesn't match a dental office's phone or chat channel, and it tends to pad every reply with pleasantries that cost tokens and patience.

typescript
1<tone>
2Keep replies under 2 sentences unless reading back a confirmed appointment.
3When confirming, state the date, time, and provider name plainly: 'You're
4booked with Dr. Patel on Thursday at 2:00 PM.' No sign-offs, no emoji.
5</tone>

Expected result: Replies in testing come back short and consistent instead of varying wildly in length and formality.

5

Add two or three few-shot examples for your worst failure mode

Pull these from real testing, not imagination. If the agent once told a patient 'you're all set' without having called reschedule_appointment, that's the example to write, because it's the exact pattern the model will otherwise repeat. Two or three examples is usually enough; past that, you're mostly adding tokens without changing behavior.

typescript
1<examples>
2User: can you just move my Tuesday appointment to Wednesday?
3Wrong: Done, see you Wednesday!
4Right: [call check_availability] [call reschedule_appointment] Then: You're
5moved to Wednesday at 10:00 AM with Dr. Patel.
6</examples>

Expected result: A short dialogue pair the model can pattern-match against the exact mistake it was making.

6

Assemble the full system prompt with labeled sections

Concatenate the five blocks in order: role and scope first, tool rules and escalation triggers next since those carry the most consequence, tone after, examples last. Section tags like <role> and <escalation> aren't decorative; they give the model clear boundaries to anchor each policy to, which matters more as the prompt grows past a page.

typescript
1SYSTEM_PROMPT = (
2 ROLE_SECTION + "\n\n" + TOOL_RULES_SECTION + "\n\n"
3 + ESCALATION_SECTION + "\n\n" + TONE_SECTION + "\n\n" + EXAMPLES_SECTION
4)

Expected result: One assembled string, a few hundred to a thousand tokens, ready to pass as the system prompt.

7

Test it with an eval script, not a few manual chats

Write a loop that sends a handful of representative messages, including at least one that should trigger escalation and one that should trigger a normal tool call, through the real API with your tools attached. Check which tool, if any, the model called, and assert it matches what you expect. A handful of scripted cases catches regressions a manual chat session won't, especially after you edit the prompt later.

typescript
1import anthropic
2client = anthropic.Anthropic()
3
4def run_case(user_msg):
5 resp = client.messages.create(
6 model="claude-sonnet-5-5", max_tokens=512,
7 system=SYSTEM_PROMPT, tools=TOOLS,
8 messages=[{"role": "user", "content": user_msg}],
9 )
10 calls = [b.name for b in resp.content if b.type == "tool_use"]
11 return calls

Expected result: A printed PASS/FAIL per test case, including one confirming the emergency message called escalate_to_human and nothing else.

Complete working example

system_prompt_eval.py
1"""Build and eval-test the Riverside Dental scheduling agent's system prompt.
2Run: python system_prompt_eval.py (pip install anthropic; set ANTHROPIC_API_KEY)
3"""
4import anthropic
5
6ROLE = (
7 "<role>\nYou are the scheduling assistant for Riverside Dental. You book, "
8 "reschedule, and cancel appointments using the tools below. You do not "
9 "give medical advice, quote prices, or discuss insurance coverage. For "
10 "those, tell the patient a front-desk team member will follow up.\n</role>"
11)
12
13TOOL_RULES = (
14 "<tool_rules>\n- Always call check_availability before offering a time slot.\n"
15 "- Never tell the patient an appointment is booked, changed, or canceled "
16 "unless the corresponding tool call has already returned success.\n"
17 "- If check_availability returns no slots within 7 days, say so plainly "
18 "instead of inventing an alternative.\n</tool_rules>"
19)
20
21ESCALATION = (
22 "<escalation>\nCall escalate_to_human immediately, before any other tool, "
23 "if the patient mentions: severe pain, swelling, bleeding that won't stop, "
24 "a broken or knocked-out tooth, or a dental emergency in any other words. "
25 "Also escalate if the patient is angry about a prior visit or asks for a "
26 "refund.\n</escalation>"
27)
28
29TONE = (
30 "<tone>\nKeep replies under 2 sentences unless reading back a confirmed "
31 "appointment. When confirming, state the date, time, and provider name "
32 "plainly: 'You're booked with Dr. Patel on Thursday at 2:00 PM.' No "
33 "sign-offs, no emoji.\n</tone>"
34)
35
36EXAMPLES = (
37 "<examples>\nUser: can you just move my Tuesday appointment to Wednesday?\n"
38 "Wrong: Done, see you Wednesday!\nRight: [call check_availability] "
39 "[call reschedule_appointment] Then: You're moved to Wednesday at 10:00 AM "
40 "with Dr. Patel.\n</examples>"
41)
42
43SYSTEM_PROMPT = "\n\n".join([ROLE, TOOL_RULES, ESCALATION, TONE, EXAMPLES])
44
45TOOLS = [
46 {"name": "check_availability", "description": "Check open appointment slots.",
47 "input_schema": {"type": "object", "properties": {"date": {"type": "string"}}, "required": ["date"]}},
48 {"name": "book_appointment", "description": "Book a confirmed slot.",
49 "input_schema": {"type": "object", "properties": {"slot_id": {"type": "string"}}, "required": ["slot_id"]}},
50 {"name": "reschedule_appointment", "description": "Move an existing appointment to a new slot.",
51 "input_schema": {"type": "object", "properties": {"appointment_id": {"type": "string"}, "new_slot_id": {"type": "string"}}, "required": ["appointment_id", "new_slot_id"]}},
52 {"name": "escalate_to_human", "description": "Hand off to the front-desk team immediately.",
53 "input_schema": {"type": "object", "properties": {"reason": {"type": "string"}}, "required": ["reason"]}},
54]
55
56TEST_CASES = [
57 ("Can I move my Tuesday 10am to Wednesday?", "reschedule_appointment"),
58 ("My tooth is broken and bleeding, what do I do?", "escalate_to_human"),
59 ("Do you have anything open next Monday?", "check_availability"),
60]
61
62
63def run_case(client, user_msg):
64 resp = client.messages.create(
65 model="claude-sonnet-5-5",
66 max_tokens=512,
67 system=SYSTEM_PROMPT,
68 tools=TOOLS,
69 messages=[{"role": "user", "content": user_msg}],
70 )
71 return [b.name for b in resp.content if b.type == "tool_use"]
72
73
74def main():
75 client = anthropic.Anthropic()
76 for user_msg, expected_tool in TEST_CASES:
77 calls = run_case(client, user_msg)
78 ok = expected_tool in calls
79 print(f"[{'PASS' if ok else 'FAIL'}] '{user_msg}' -> called {calls}, expected {expected_tool}")
80
81
82if __name__ == "__main__":
83 main()
84

Common mistakes

Why it's a problem: Writing the prompt as a wall of undifferentiated prose

How to avoid: Split it into labeled sections (role, tool rules, escalation, tone, examples). A model reading unstructured prose blends tone guidance with safety rules; sections keep them separate and enforceable.

Why it's a problem: Describing how the tools work inside the system prompt

How to avoid: Tool mechanics belong in the tool schema's own description field. Use the system prompt only for sequencing and policy: when to call a tool, not what it does.

Why it's a problem: Leaving escalation guidance vague, like 'use good judgment with emergencies'

How to avoid: Enumerate the exact trigger phrases and situations. A concrete list produces consistent behavior; a vague instruction produces a coin flip.

Why it's a problem: Testing only polite, happy-path phrasing before launch

How to avoid: Include ambiguous and adversarial messages in your eval cases, like a patient burying an emergency in a casual sentence, since that's what actually shows up in production.

Why it's a problem: Editing the prompt in production without re-running the eval script

How to avoid: Every edit can regress a behavior you fixed earlier. Re-run the full test case list on every change, not just once before the first launch.

Best practices

  • Keep the role and scope sentence short enough to quote back verbatim when you're debugging drift.
  • Put escalation triggers right after the role section, not buried at the end where a long prompt can bury them.
  • State consequences explicitly: 'never say you rescheduled it without calling reschedule_appointment,' not just 'use the tools correctly.'
  • Use two or three few-shot examples max, each one fixing a failure you actually observed, not a hypothetical edge case.
  • Re-run your eval script after every prompt edit, including ones that feel purely cosmetic.
  • Version the system prompt in your repo like code, with a short comment explaining why each section exists.
  • Keep tone rules in their own section so you can loosen the voice later without touching escalation or tool-sequencing logic.

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I have this AI agent system prompt for a dental scheduling agent (below), with tools check_availability, book_appointment, reschedule_appointment, cancel_appointment, and escalate_to_human. Add a section for multi-location clinics where the agent must confirm which location before calling any tool. [paste complete_code]

Build AI Agents Prompt

In Claude Code, open system_prompt_eval.py and ask: 'Add three adversarial test cases where the user tries to get the agent to promise something no tool supports, like a refund, and make the eval script fail loudly if the agent does it anyway.'

Frequently asked questions

What's the difference between a system prompt and a tool description?

The tool description (in your tool's JSON schema) tells the model what a tool does and what arguments it takes. The system prompt tells the model when and in what order to use its tools, what to never claim without calling one, and how to handle situations no single tool covers, like an emergency.

How long should an AI agent system prompt look like?

Long enough to cover role, tool rules, escalation, tone, and a couple of examples, usually a few hundred to a thousand tokens. Past that, most additions stop changing behavior and just add latency and cost on every single turn.

Should few-shot examples live in the system prompt or a separate file?

Keep them in the system prompt string itself so they ship with every request, but store the source examples in a separate test or eval file. That way the examples you write are traceable back to the real transcript that produced them.

Does a longer system prompt cost more per request?

Yes, it's billed as input tokens on every single turn, not just once. Prompt caching cuts that cost sharply once the prompt stabilizes, since repeated prefixes hit the cache instead of being billed at full input price.

How do I know if my system prompt is actually working?

Run it against a fixed set of test messages, including edge cases, and check which tool (if any) got called for each one. A prompt that 'reads well' in a quick chat can still fail silently on the exact message that matters most.

Can the same system prompt work across Claude, GPT, and Gemini?

The five-section structure transfers cleanly, but tool-calling reliability and how strictly a model follows sequencing rules differ by model and by how the tools are defined, so re-run your eval cases on each model before you consider it ported.

Can someone write and test this system prompt for me?

Yes. RapidDev writes and evals production system prompts, escalation logic included, as part of building the full agent. A free 30-minute consultation is the fastest way to scope what your version needs.

RapidDev

Talk to an Expert

Our team has built 1,000+ apps. Get personalized help with your project.

Book a free consultation
Matt Graham

Written by

Matt Graham · CEO & Founder, RapidDev

1,000+ client projects delivered. Columbia University & Harvard Business School alumnus, U.S. Navy veteran. About the author →

Learning is great. Shipping is faster with help.

Our engineers have built 1,000+ apps on your platform and the tools around it. If your project needs to be live sooner than your learning curve allows — book a free consultation.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.