AI Agent Best Practice
Building an AI Agent that works reliably in production is not just about writing a prompt. It requires thinking clearly about what job the agent is doing, what data it needs, what it should return, and how it fits into the larger decision flow around it. This page covers the principles that make the difference between an agent that works in a demo and one that works in production.
Start With the Job, Not the Prompt
Before you open the designer, define what the agent is actually supposed to do. The most common mistake is building an agent that tries to do too many things at once — extract data, evaluate it, score it, and write a summary all in one rule. That produces an agent that does everything adequately and nothing well.
We have identified four task patterns that cover the vast majority of real-world AI Agent use cases in DecisionRules:
Decision Maker
Evaluates input and produces a decision, score, or routing outcome
Deep Analyzer
Reads documents, records, and signals to extract structured intelligence
Report Writer
Condenses complex input into a concise, decision-ready narrative
Policy Validator
Checks data against rules and standards; Fixes and normalizes non-conforming records
Together these four patterns cover around 95% of tasks you would want to automate with an AI Agent in DecisionRules. Before you build, ask yourself which one your use case maps to. If it maps to more than one, split it into multiple agents. And if none of them fit — you are not limited to these patterns. Define the role and task freely from scratch in the prompt.
A well-scoped agent is easier to prompt, easier to annotate, easier to test, and easier to maintain when something changes.
Role & Task Templates
Decision Maker
## ROLE
You are a [domain] risk analyst with deep expertise in [field].
## TASK
Evaluate the input and produce a scored assessment with a recommended action.Deep Analyzer
Report Writer
Policy Validator
Writing Effective Prompts
Structure Is Not Optional
Models read your prompt from top to bottom, in order. What comes first shapes how everything after it is interpreted — the role you define at the top influences how the model reads the context, and the context influences how it approaches the task. This is why the order matters:
## ROLE— tells the model who it is and what expertise it brings. This frames all subsequent reasoning## CONTEXT— gives the model the data it needs to work with. Comes after ROLE so the model reads it through the right lens## TASK— tells the model what to do with that context. Comes last because it depends on both
Prompt Formatting
Use ## headers to create these anchors — they signal to the model where one concern ends and another begins. A flat wall of text with no structure forces the model to infer the structure itself, which it will do inconsistently.
For emphasis within a section — to highlight a constraint or a rule that must not be broken — use **bold text**. It signals to the model that those parts carry higher priority than the surrounding prose.
Label Your Variables
When the model receives {applicant.score} it gets the value 45. It does not know that 45 is a credit score, a debt ratio, or a temperature — it just gets 45. Always provide context around your variables so the model knows what it is looking at:
Prompt Comparison
Good Prompt
Bad Prompt
What is wrong:
No ROLE — the model has no clinical or policy expertise to draw on
Variables dumped without labels —
{patient.age}delivers34,{treatment.estimated_cost_usd}delivers24000. The model receives a sequence of raw numbers and strings with no indication of what any of them representAttachment not referenced — the guidelines file is attached to the rule but never referenced in the prompt, so it is never sent to the model
Output fields in the prompt —
approval_status,risk_score,justificationbelong in Annotations, not hereNo constraints — nothing tells the model what step therapy means, what the cost threshold is, or what counts as clinical necessity
Writing Effective Annotations
Annotations are not labels or comments — they are instructions. The model reads every description as a directive for what to produce. A vague description produces vague output. A precise description produces output your downstream rules can depend on.
The key shift is this: stop thinking about what a field is and start thinking about what the model needs to know to fill it correctly.
The name risk_tier tells the model nothing.
The description Risk tier derived from overall_risk_score. Allowed values: low · moderate · high · critical. Return null if overall_risk_score is null. tells it everything.
How to Write Each Type
Enumerations — list every allowed value explicitly. If the field is used in a downstream Switch or Decision Table, the values must match exactly:
Allowed values: approve · conditional_approve · refer_to_underwriter · declineBooleans — state both conditions. Never leave the false case implicit:
True if debt-to-income ratio exceeds 0.40. False if within acceptable threshold.Scores — define the range, what drives each end, and how to compute it:
Range: 0 (no risk) to 100 (extreme risk).Weighted: clinical necessity 45%, cost appropriateness 30%, policy compliance 25%.Round to the nearest integer.Text summaries — specify length and what evidence must be cited:
2–3 sentence explanation citing at least one specific finding from the clinical notes or prior treatment history.
Be Consistent With Null
If one field returns null when data is missing and another returns "unknown" and a third returns an empty string, your downstream Decision Table needs three separate conditions to handle each case — and will still break silently when the model returns something unexpected. Pick one fallback for the whole rule and apply it everywhere. null is the recommended choice because it is the easiest to catch with a single IS NULL condition.
Annotation Comparison
Good annotations
commercial_terms
Object
Key commercial terms extracted directly from the contract document.
initial_term_months
Number
Duration of the initial contract term in months as stated in the document. Return null if not specified.
auto_renewal
Boolean
True if the contract renews automatically at the end of each term without affirmative action. False if renewal requires explicit agreement. Return null if the renewal clause is absent.
fee_increase_cap_pct
Number
Maximum percentage by which fees may increase annually as stated in the document. Return null if no cap is defined.
foreign_jurisdiction
Boolean
True if the governing law is outside the Czech Republic. False if governed by Czech law. Return null if no governing law clause is present.
Bad annotations
commercial_terms
Auto
Commercial info.
initial_term_months
Auto
The term.
auto_renewal
Auto
True if auto renewal.
fee_increase_cap_pct
Auto
Fee cap.
foreign_jurisdiction
Auto
True if foreign.
What is wrong:
Auto on every field —
initial_term_monthsis a Number,auto_renewalis a Boolean. The model has no type contract to follow"The term" — is this months, years, days, or a string like "twelve months"? The model will decide for itself, differently on every call
"True if auto renewal" — circular. Does not define what auto renewal means or what evidence to look for in the document
"Fee cap" — no unit, no instruction on what to return if no cap exists in the document
"True if foreign" — foreign to what? The model does not know your jurisdiction
Last updated
Was this helpful?

