Skip to main content
A well-written prompt is the difference between an agent that sounds robotic and one that handles real conversations naturally. This guide covers the key principles and patterns for writing prompts on Osvi.

Anatomy of a Good System Prompt

Every agent prompt should cover four things:
  1. Identity — who the agent is and who it works for
  2. Goal — what the agent is trying to accomplish in this conversation
  3. Rules — constraints on what the agent can and cannot do
  4. Tone — how the agent should sound

Structure the Prompt with Tags

Once a prompt grows past a few paragraphs, wrap each part in an XML-style tag. Tags give the model hard boundaries between who you are, what to do, and what never to do, so an instruction in one section stops bleeding into another. They also make prompts reviewable — a teammate can read one block without holding the whole prompt in their head, and you can rewrite the flow without touching the guardrails.
The tags worth having, in the order they usually read best: Rules that keep tagged prompts working:
  • One topic per tag. If a block covers two things, split it. Instructions buried in the wrong section get missed.
  • Name tags for what’s inside, in lowercase with underscores. The model reads the name as a heading.
  • Don’t nest more than one level deep. Flat blocks beat a tree.
  • Keep the whole rule in one place. A rule half in <call_flow> and half in <guardrails> is a rule that gets half-followed.
  • Put the unbreakable things in <guardrails> and phrase them as absolutes.
Tags are for the model, not the caller. Never let the agent read a tag name, a variable name, or a bracketed placeholder aloud — say so explicitly in your guardrails.

Writing for Voice vs Chat

Voice and chat conversations have different rhythms. Keep these differences in mind:

Single-Prompt Agents

For straightforward workflows, a single prompt is usually enough. Structure it as:
Example — appointment reminder:

Multi-Prompt Agents

Multi-prompt agents let you define distinct states, each with its own prompt and transition conditions. Use this when your flow has meaningful branches. When to use multi-prompt:
  • The conversation has 3+ distinct phases (greeting → qualification → closing)
  • Different parts of the conversation require very different tones or instructions
  • You want explicit control over when the agent moves between topics
State design tips:
  • Name states clearly: greeting, qualification, objection_handling, closing
  • Keep each state prompt focused — it should only describe what happens in that state
  • Define clear transition triggers: “Move to closing when the user agrees to a demo”
Example state structure for a sales agent:

Using Jinja for Dynamic System Prompts

Osvi system prompts are rendered as Jinja2 templates before the call begins, using the runtime context you pass in — the additional_data object and the top-level person_name on POST /call, or the columns of a campaign CSV. So you can use the full Jinja syntax, not just simple variable substitution, to build prompts that adapt to your data.

Variable Substitution

The most common use. Any key from additional_data (or the top-level person_name) is available as a Jinja variable:

Conditionals

Use {% if %} to include or exclude sections of the prompt based on the data passed in:

Loops

Use {% for %} to enumerate lists — useful when the agent needs to cover multiple items:

Default Values

Use the default filter to guard against missing fields so the prompt doesn’t break if a value isn’t provided:

Filters

Jinja filters let you transform values inline:

Passing Data from the API

All Jinja variables are populated from the additional_data object and the top-level person_name field in your POST /call request:
Keep your Jinja logic simple. Complex template logic is hard to debug and maintain. If you find yourself writing deeply nested conditionals, consider splitting the workflow into multiple agent states instead.

Guardrails

Guardrails are the rules the agent must never break, whatever the caller says. Keep them in one <guardrails> block, phrased as absolutes — “never”, “only”, “at most once” — and keep them short enough to be read in one go. Cover these:
Write the refusal line, not just the rule. “Never accept an OTP” leaves the agent to improvise; giving it the sentence to say keeps the wording consistent on every call.

Frequently Asked Questions (FAQ)

Callers ask the same handful of questions on every campaign: why are you calling, how did you get my number, can I speak to a human, is this legitimate. Answer them once in the prompt and the agent stops improvising — improvised answers are where agents make promises you’ll have to honour. Put them in a <faqs> block as question-and-answer pairs, and say plainly that the agent answers only when asked, gives one answer, then returns to where it was.
Writing good FAQ answers:
  • Short enough to say out loud. One or two sentences.
  • Answer, then return. Tell the agent to resume the flow rather than waiting for a new question.
  • Say what you can’t do. “I can’t change the amount, but I can record what you’ve told me.”
  • Never let the answer promise something outside the agent’s authority — no waivers, no refunds, no deadlines.

Show the Agent, Don’t Tell It

A rule tells the agent what you want; an example shows it. Pair a caller line with the right response and the wrong one, and label the wrong one — the contrast teaches faster than a paragraph of instruction.
Pick examples for the moments agents actually get wrong: a caller disputing a fact, answering two questions at once, going quiet, offering a credential, or deflecting to someone else.

Common Mistakes to Avoid

Bad: “Help the user with their query.”Good: “Help the caller track their order status. Ask for their order number and registered email address to look up the order.”Vague prompts lead to inconsistent behaviour. Be specific about the goal and the steps.
Long lists of rules are hard for the model to follow consistently. Group related rules together, and prioritise the most important ones at the top.
For outbound calls, always define what the agent should do if someone other than the intended contact answers. Example: “If the person who answers is not {{person_name}}, ask them to pass on the message and end the call politely.”
Agents need to know how and when to end a conversation. Always include a closing instruction: “Once you have confirmed the appointment, thank the caller and end the call.”
A rule placed in the middle of a flow step applies to that step; the agent won’t generalise it. Absolute rules belong together in <guardrails>, and stock answers in <faqs> — leave the flow to what happens in order.

Prompt Testing Checklist

Before deploying an agent, test these scenarios:
  • Happy path — the conversation goes exactly as planned
  • Wrong person answers the call
  • User goes off-topic or asks something unrelated
  • User refuses or says they’re not interested
  • User asks to speak to a human
  • User gives ambiguous or incomplete answers
  • User interrupts the agent mid-sentence
  • User asks whether they’re talking to a bot
  • User offers an OTP, PIN, or card number
  • User objects to the call being recorded
  • User becomes abusive — check the agent warns once and then ends the call
  • User asks something from your <faqs> block, then returns to the flow