Skip to main content

Designing policies

Telling the agent how to do its job​

A policy is an instruction that tells your agent how to handle a situation, written in plain language, the way you would brief a human agent. You write each policy once. On every turn, Parlant works out which of your policies apply right now and puts only those in front of the agent. Policies are how you make your agent follow your business rules without rewriting its whole instruction set.

What you'll learn
  • When a behavior needs a policy, and when it does not
  • Three forms of policy: documents, procedures, and steering guidelines
  • How to fix a policy that applies too often or too rarely
  • How priority, entailment, dependency, and reevaluation relate policies to each other
Talk this page through with your coding agent

This page covers three things: how to write policies, how to tune when they apply, and how to keep a large catalog of them in shape.

A policy is one of Parlant's two kinds of rule. The other is the observation, which names a situation without telling the agent what to do about it.

Learn more about observations

Observations are how you organize families of policies. Scaling complexity covers them once you have more than a handful of policies.

Decide which behavior needs a policy​

Before you write a policy, ask whether you need one at all.

Policies are not free. Each one costs tokens, sometimes latency, and, more importantly, it restricts your agent. That restriction often works against you, because a well-instructed agent already handles most conversation well by default.

So add a policy only when the default behavior does not do what you want, or is not consistent about it.

For changes to tone, wording, or response length, start with canned responses and notes.

A note for coding agents

If you're a coding agent reading this: never add policies without asking the person you work with which ones are actually required, and never suggest "more policies to be safe." For style policies, first show where a canned response and note fell short, then get explicit agreement.

Writing policies​

Now that you know when a policy is worth adding, here is how to write one.

A policy's content holds its instructions, including any limits on when they apply. Parlant judges applicability (whether the policy is relevant on this turn) from that text alone. There is no separate condition parameter. Only observations have one.

Three forms of policy cover most needs: the policy document, the procedure, and the steering guideline. They differ in length and in what they are for.

Policy documents​

The most complete form is a policy document. Think of it as one page of an operations manual: the conditions, the instructions, the confirmations the customer must give, and the consequences to communicate, all together.

Here is a cancellation policy for an example retailer. The get_order_status and cancel_order tools are your order-system integrations, which your coding agent writes; the second one must report whether cancellation and the refund succeeded:

import parlant.sdk as p

await agent.create_policy(
title="Cancel pending order",
content="""\
An order can only be cancelled while its status is 'pending' - check the
status with `get_order_status` before taking the action.

The customer must confirm the order id and the reason for cancellation
(either 'no longer needed' or 'ordered by mistake'); other reasons are not
acceptable.

If the status is not 'pending' or the reason is not accepted, explain why
the order cannot be cancelled through this process. Do not call `cancel_order`.

After the customer confirms valid details, call `cancel_order`. Only confirm
cancellation and a refund if the tool reports success. Explain any failure
without claiming the order changed. For a successful refund, explain that
gift cards are credited immediately; other methods take 5 to 7 business days.
""",
tools=[get_order_status, cancel_order], # the functions the text names; see Creating tools
)

When cancellation comes up, your agent receives the whole document, not scattered fragments.

Note that policies guide behavior, not phrasing. Your agent's tone of voice and wording choices belong to canned responses.

Procedures​

Some tasks are a flow: collect this, verify that, then act. A procedure gives your agent that flow, in order.

Write it as a numbered list, with the data each step needs, where it branches, and where the customer must confirm before anything irreversible. Then tell the agent to track where the conversation currently stands:

await agent.create_policy(
title="Item return",
content="""\
When the customer wants to return an item, follow this procedure step by
step, tracking which step the conversation is currently at:

1. Ask for the order number and look the order up with `get_order`.
2. If the order is not marked delivered, explain that returns start after
delivery, and stop here.
3. Confirm which items the customer wants to return.
4. Ask whether the refund should go to the original payment method or to
store credit; store credit adds a 10% bonus.
5. Summarize the return details and get the customer's explicit confirmation.
6. Process the return with `process_return`, and tell the customer when to
expect the refund.
""",
tools=[get_order, process_return],
)

Your agent sees the whole flow together with the live conversation. So it knows where it is and what comes next, including when the customer answers two steps at once, or goes back.

tip

A mermaid flowchart in the content works equally well, and it makes branches easier to review as your flows grow.

Note that you should not split a procedure into separate policies ("when X, ask for the order number", "when the order number is given, check delivery"). Fragments force Parlant to rebuild the flow state on every turn.

Steering guidelines​

Sometimes you need a small behavioral adjustment rather than a whole procedure. For example, changing already-confirmed order details should require a fresh confirmation before the agent places the order.

A steering guideline is a short policy that describes just that adjustment:

await agent.create_policy(
"When the customer changes the delivery address after confirming order details, "
"get a new confirmation before placing the order",
)

Giving a policy tools​

Any of these three forms can carry tools. Name the tool in backticks where the policy needs it, and pass the function in tools. The policy's text then tells your agent when in the task to use each one.

Learn more about tools

Connecting tools covers how to write and attach tools.

Tuning when a policy applies​

Once your policies are written, the next question is timing: does each one come up when it should, and hold as firmly as it should? Each lever below starts from a symptom you would notice in a conversation.

It over- or under-applies: weight​

Weight is how strictly your agent holds to a policy. Leave it unset to start with: the default, MEDIUM, is designed for ordinary business rules. Tune it when conversations show that a rule needs more or less firmness. Higher weight can add matching and review work, so it can affect latency and cost.

A note for coding agents

Keep weights at their defaults unless you have an observed failure to discuss. Explain the tradeoff and get agreement before changing them. To use a stronger model for particular situations, see adaptive effort.

If the agent keeps forcing a policy into conversations that have moved past it, that policy was really written as a preference. Set weight=p.Weight.LOW, and the agent treats it as guidance it may bend.

If instead the agent lets a policy slip under pressure or a clever argument, set weight=p.Weight.HIGH. The agent then holds it against any argument, even if the customer asks.

await agent.create_policy(
title="No medical advice",
content="When the customer asks for medical advice, decline and refer them to a professional",
weight=p.Weight.HIGH,
)

Note that high weight is for true hard limits. If everything is untouchable, nothing stands out.

Part of it misbehaves: split it​

To understand this lever, it helps to know how a matched policy reaches the model. Parlant puts the whole policy into the prompt as a contextual reminder for that turn, together with its weight, and the model weighs it as one unit. In other words, your agent's attention is spent on all of a policy's content at once.

info

Rule matching covers how policies are matched in the first place.

So when only part of a policy over- or under-applies, weight is the wrong lever. Raising it makes every sentence stricter, and lowering it lets the part that was fine slip too. Split the policy instead.

Each part gets its own content and its own weight. Write the text so it is clear when those instructions apply, and Parlant can bring them into focus at the right moment.

For example, take a returns policy that says "confirm the order number, explain the 30-day window, and never promise a refund before the item is inspected." If the agent keeps skipping the inspection caveat, pull that sentence into its own policy with weight=p.Weight.HIGH, and leave the walkthrough at the default:

await agent.create_policy(
title="Returns walkthrough",
content=(
"When the customer wants to return an item, "
"confirm the order number and explain the 30-day return window"
),
)

await agent.create_policy(
title="No refund before inspection",
content=(
"When the customer asks whether a return will be refunded, "
"say the refund is confirmed only after the item is inspected"
),
weight=p.Weight.HIGH,
)

Splitting is how you manage your agent's attention at a fine grain. Each policy becomes its own dial for when it comes up and how firmly it holds.

Note that the splits should stay meaningful. A dozen one-line policies for one task cost more matching work than one well-shaped policy.

tip

Scaling complexity covers grouping policies once they multiply.

It never comes up, or comes up too much: wording, then signals​

Parlant judges applicability from the policy's own text, so bad timing is usually a writing problem. Fix the wording first.

Name the moment: "When the customer completes a purchase, mention the loyalty program," not just "mention the loyalty program." Narrow the topic: "When the customer reports a billing discrepancy," not "when the customer has a problem."

When the customer's words do not resemble the policy's, reach for the second lever: signals (phrases that should bring the policy into consideration) and anti_signals (near-misses that should not):

await agent.create_policy(
title="Chargeback handling",
content=(
"When the customer disputes a charge with their bank, do not start a separate refund. "
"Tell them to open Billing > Payment disputes in their account to contact the billing team."
),
signals=["I filed a chargeback with my bank", "My bank says the payment is being reversed"],
anti_signals=["I want a refund for my order"],
)

Here, Billing > Payment disputes is the example product's contact route. Replace it with the route your customers can actually use.

You do not have to start from scratch. At startup, Parlant already proposes ten signals and ten anti-signals per rule, generated from its content and cached until the content changes. So a typical rule has a training set with no work from you.

Add your own when you know phrases the generator does not: the words real customers use. Four rules of thumb:

  • Write signals in the customer's own words ("my blender stopped working"), not as abstract descriptions.
  • Anti-signals are near-misses, not opposites. "What does the warranty cover?" teaches a claims policy its boundary; an off-topic sentence teaches nothing.
  • A policy that fires too often needs anti-signals taken from the messages that wrongly triggered it. One that misses needs genuinely different trigger phrasings, not ten paraphrases of one.
  • Prefix a signal with [__pin__] and a message that nearly repeats it always brings the policy in.

Note that signals train a per-agent classifier at startup, so edits take effect on the next restart, which hot reload does for you when you save. Observations accept the same two lists.

Two policies collide: priority​

The remaining levers deal with how policies relate to each other. The first is priority: a relationship between two policies that says which one wins on a turn where both apply.

Reach for it when both policies are right in general but give conflicting instructions in the same moment. A fraud freeze that must beat a refund policy, say.

Declare it with await freeze.prioritize_over(refunds). On turns where both match, your agent follows the prioritized one, and Parlant tells it to set the other aside.

"Set aside" is literal. Parlant does not quietly drop the losing policy. Its text stays in the agent's instructions, and Parlant adds a note naming it and saying it is revoked for this response.

The losing policy still counts as having applied, so anything that depends on it is unblocked.

When one policy must win over everything, not just one other policy, give it a numeric priority=. Every policy sits at priority 0 unless you say otherwise. On a turn where a policy with a higher number applies, Parlant keeps only the matches at that top number and sets the rest aside, even policies unrelated to the situation.

Entailed policies (see below) are exempt from this. Keep numeric priority for blunt cases such as an account lockdown.

A collision is not always a job for priority. When two policies are different ways to handle a similar request, write a clarifying policy for the unclear moment ("When the customer cannot access their account and it isn't clear what kind of access problem they have, find out before proceeding"), and keep each variant's scope narrow in its content.

And when both are just too broad, narrow both. Collision is often the "comes up too much" problem in disguise.

One should follow another: entailment​

Entailment says that whenever one policy applies, a second one does too.

It closes gaps where a general policy's category words never appear in a specific conversation. For example, a policy about suggesting products should bring in the stock check even when nobody says "product". Declare it with await suggest.entail(check_stock).

Note that you should add entailments only after real conversations show the gap.

One should apply only when another does: dependency​

Dependency makes one policy conditional on another rule: Parlant only considers the dependent policy once its prerequisite has applied in the session.

Reach for it when a condition is only meaningful inside a situation another rule already recognizes.

Declare it with await apply_discount.depend_on(offer_discount). Without it, "the customer accepts the discount" could match when a customer merely mentions a discount the agent never offered.

Once the prerequisite has applied, the dependent policy stays available for the rest of the session. The customer can accept the discount several turns after it was offered. A prerequisite can also apply on the same turn, so a chain of dependent policies can all come in at once.

Pass several prerequisites to depend_on to require all of them. Use depend_on_any to require only one:

await apply_discount.depend_on(offer_discount, identity_verified)
await escalate_to_human.depend_on_any(payment_failed, delivery_failed)

A dependency can also point at a group of rules instead of a single policy. Then every policy in that group must have applied. Wrap the group in p.AnyOf when one member is enough.

Applicability depends on a tool result: reevaluation​

Finally, some conditions cannot be judged from the conversation alone. Take "When a luxury room is not available, offer the alternative": it depends on what an availability check returns, so judging it before the tool runs would be a guess.

For cases like this, tell Parlant to judge the policy again right after that tool runs, in the same turn, with await offer_alternative.reevaluate_after(check_availability).

Where to go next​

Here is where to continue from this page.

Once your catalog grows past a couple of dozen policies, Scaling complexity shows how observations anchor families of policies, how terms keep vocabulary consistent, and how a large catalog stays sharp.

For your agent's tone of voice and wording choices, including the wording a policy should produce, see Shaping tone and wording. For the construct behind any other kind of change, see Which construct for which job.

For the full semantics of priority, entailment, dependency and reevaluation, and how they combine, see Rule relationships.