Design principles — read this first
How to configure agents that actually work — one job per agent, few skills, grounded, verified.
get_agentsearch_knowledge_baseMost "the agent gives bad answers" problems are configuration, not the model. Follow these before you build, and the results hold up. Ignore them and it is easy to assemble something that looks plausible and behaves badly — then mistake that for a platform limit.
Discover before you build — interview, don't just execute
"Create a support agent" is the start of the conversation, not the spec. Never answer it with a single
create_agent and hand back a blank shell. Work like the setup wizard: interview first, plan the whole
setup, play it back, then build. Ask in plain language — a few questions at a time, about their real
situation, not tool parameters — until you can picture the finished thing:
- The job & who it serves. What is this agent for, who talks to it, what does a good answer look like? One job per agent — if they describe three, that's three agents.
- What it must answer from → a knowledge base. Do they have documents, a site, policies, a price list? If facts have to be right, those become a knowledge base you build and attach — not prompt text. If they have nothing yet, tell them what to gather.
- Procedures it runs → skills. Any "when X, do these steps" behaviours (book, screen, quote)? Each becomes a load-on-demand skill with a sharp "use this when…".
- Things it must do in other systems → MCP. Check a calendar, look up an order, open a ticket? Those are MCP tools to bind — ask which system and whether they can connect it.
- A guided, stateful flow → a Storyline. Is it a process with memory (intake → qualify → follow-up) rather than one-shot Q&A? That's a Storyline, not just an agent.
- How it opens and its hard limits. The first line visitors see (→ a page playbook)
and the boundaries that go in
task("never quote a price", "never give legal advice").
Then play the plan back before touching a tool — "so I'll build: agent Support, a knowledge base from your policy PDFs, a booking skill, and bind your calendar over MCP — right?" — and build only once they confirm. Skipping this is exactly how you end up with an empty agent nobody wanted. A vague request is a cue to ask, never a cue to guess.
One agent, one job
Give each agent a single, clearly bounded task. A "does everything" agent — support and sales
and scheduling — has a diffuse task, competes with itself for attention, and answers each thing
worse. If you have three jobs, build three agents.
Keep the skill count small
Attach only the skills this agent's job needs — roughly five or fewer. Skills are chosen by the model
from their one-line description; the more you attach, the harder that choice, and the more often it
loads the wrong one or none. A focused set with sharp "use this when…" descriptions beats a big pile.
- Each skill's
descriptionsays when to reach for it, in one line. - Procedures live in
instructions(loaded on demand), never insoul(paid for every turn). - If two skills overlap in when, merge them — overlapping triggers make the choice a coin-flip.
Ground facts; put boundaries in the task
- Facts (rates, policy, catalogue) belong in a knowledge base, which is retrieved every turn — not in the prompt, where they go stale and un-cited. See Build a knowledge base.
- Constraints belong in
task, as negatives: "never promise a date", "for a quote, call a tool". Negative boundaries stop drift better than positive description. Don't put safety rules insoul— the platform appends global moderation for you. - Turn on
grounding_requiredwhen answers must come from the material, not the model's priors.
Never hand determinism to probability
The single most useful design rule on this platform: if something can be computed, generated, or verified by the system, never leave it to the model. A model asked to produce a deterministic artifact doesn't fail loudly — it produces a plausible one. The user gets a ticket number that doesn't exist, a callback booked seven hours off, a colour palette that fails contrast. Nothing errors; it's just quietly wrong.
Every one of these started as a real production failure and became a platform feature:
| Deterministic thing | Wrong way (probability) | Right way (system) |
|---|---|---|
| Reference / ticket numbers | Instructions say "tell the user the ticket number" → model invents one | save_contact / schedule_followup return a real stored code — instruct the model to relay it verbatim |
| Absolute times | Model hand-computes delay_seconds for "tomorrow 10am" → off by hours | Pass run_at (local ISO time); the server resolves the timezone |
| Text colours on a brand colour | Model picks "matching" colours → unreadable in one theme | Send theme_color only; WCAG-contrast foregrounds are derived server-side |
| Routing in a flow | An ai exit for "if the user agreed" | rule / user_choice exits; reserve ai exits for genuine judgment. Loops get an explicit counter and cap — never "the LLM will stop eventually" |
| "Did the trigger fire?" | Assume the prompt works | test_skill_trigger measures it; the platform also injects a deterministic hint when a phone/email is detected |
| The wording that reaches a tool | Hope the model turns "any others like that?" into the right call | The platform works out what was asked, then composes the instruction from the tool's own definition and shows that turn one tool. Measured 80% → 97%; letting the model rewrite its own request did nothing (82%) |
| A phrase that must never appear | Add another "do not say X" line to the prompt | Delete it after the fact. A rule you can check on the finished answer is a rule you can enforce; a rule in the prompt is a request |
| Machine-readable output | "Reply with valid JSON only" and parse strictly | Ask for the shape, then parse for the one field that matters. Strictness throws away answers that were right |
The corollary for writing skills: your instructions should tell the model which system facility
to use ("pass run_at, relay the returned code"), not teach it to imitate the facility ("compute
the seconds, format a ticket number"). If you find yourself scripting the output of a
deterministic process, look for the tool that produces it — or ask for one.
Two corollaries about output
A prompt rule is a request; a post-check is a rule. "Never say X" belongs in the prompt — it lowers the rate — but it is not enforcement. A model that agrees with the instruction can still reach the same forbidden idea by a phrasing your wording didn't anticipate, and each rewrite of the rule tends to catch only the phrasings you already saw. Wording cannot police wording. So ask a different question: is the unwanted sentence recognisable in the finished answer? If it is, remove it there, and keep the prompt line as well.
Get the content right first; do not let syntax cost you the content. A smaller model will often make the correct choice and then write it in a shape your parser rejects — one object per line instead of an array, a trailing comma, prose wrapped around the block. Parsing strictly means a correct answer is discarded for a misplaced brace, and the symptom looks like a model that isn't capable enough. Decide which field in the response is load-bearing — usually exactly one: an id, a reference, a choice — and extract that field however it arrives. The rest of the payload is typically data you were going to replace with your own anyway, so strictness about it protects nothing.
Show the agent good patterns only
When you hand an agent examples, make them correct examples. Don't paste a "here's the wrong way" snippet next to the right one — the model may imitate the nearest example rather than read the caveat. Describe what to avoid in words; keep runnable examples exemplary.
Verify — writing is not working
After every change, check it did what you intended. Creating an agent doesn't mean it's configured the way you think; adding to a knowledge base doesn't mean the question retrieves.
get_agent(name="Support") # confirm the config that landed
search_knowledge_base(kb_name="Company policy", query="…") # confirm the answer is retrievableAn empty search result means that question will be answered as "not covered" — find that now, not from
a customer.
Hand back a deliverable — and the next step
After every action, don't just report that you finished. Hand back four things: what you produced, a clickable console link to view it, one line on how to use it, and the natural next step — proposed, and offered to do. They will ask "where do I see it?", "how do I use it?" and "what now?" anyway; answer all three up front. URL-encode names that contain spaces.
| After you… | Hand back |
|---|---|
| Create or import into a knowledge base | Its page — which includes the knowledge starmap (a 3D view of what was ingested): https://console.agent4.io/#/knowledge-bases/<name> |
| Create an agent | Its page to review/test: https://console.agent4.io/#/agents/<name> — and note that to let end users reach it, they create a share link in the console |
| Create a skill | https://console.agent4.io/#/skills/<name> |
| Publish a Storyline | https://console.agent4.io/#/storylines/<id> (the id from create_storyline) |
| Register an MCP server | https://console.agent4.io/#/mcp/<id> |
For example, after importing documents into a knowledge base, reply with how many chunks landed, the link above so they can open its knowledge starmap and see exactly what was ingested, and confirm whether it's now attached to an agent (or how to attach it). A bare "done" just makes them ask.
Always end on the next step, and offer to do it — a setup is a chain, not a single action:
- Built a knowledge base? → offer to attach it to an agent (ask which).
- Created an agent? → offer to attach a knowledge base, add a skill, or create a share link so end users can reach it (an empty space = off).
- Authored a skill? → offer to attach it to the agent that needs it.
- Published a Storyline? → offer to set it as the agent's default, or wire its enrollment trigger.
- Registered an MCP server? → offer to grant an agent its tools.
"Here's what I made, here's the link, here's how to use it, and here's what I'd do next — want me to?" keeps the build moving; a bare "done" leaves the tenant guessing.