Best Practices
AI — Best Practices
The recommended way to build a fast, correct, safe AI chat.
✓ Stream over the WebSocket for the interactive chat
/ai-v2/chat streams tokens (assistant:token) and live tool progress. Use it for
the chat surface; reserve the blocking REST POST …/messages for scripts or
back-office calls that don't need live output.
✓ Always surface the confirmation gate — never auto-run writes
When you get assistant:confirm_action (v1) / assistant:confirmation_required (v2),
render an explicit Approve/Decline card and echo the id back. The gate is
backend-enforced, but a UI that hides it just makes the change silently never happen.
✓ Confirm exactly what you were shown
Echo the actionId from the latest assistant:confirm_action (v1) or the card's
approvedCallIds (v2). Don't cache and replay old ids — a stale confirm is refused
("no longer pending"), fail-closed.
✓ Reconcile the conversation id
If you send without a conversationId, the server mints one — adopt it from
assistant:thinking / assistant:conversation_replaced so the next turn continues the
same conversation instead of starting a new one.
✓ Handle rate-limit and budget separately
RATE_LIMIT is a short back-off (retry after retryAfter); TOKEN_BUDGET_EXCEEDED is
the monthly cap (won't clear until reset). Show different UX for each — retrying a
budget error just fails again.
✓ Configure providers by env, not code
Set the keys you have and pick AI_DEFAULT_PROVIDER; the factory registers only
configured providers and fails over automatically. Override models with
<PROVIDER>_DEFAULT_MODEL. Don't hardcode a provider or model in a caller.
✓ Let the level do the scoping
The assistant only reads and writes records the caller's A/G/M/D level allows — the same fine-grained filter the rest of the CRM uses. Don't try to pre-filter or widen access in the prompt; authorization is at the tool boundary, not the prompt (see Authorization).
✓ Use the right mode / channel for the tools you need
A mode is a tool whitelist. The WhatsApp channel deliberately excludes web-only tools (schema edits, email, views, export). If a tool "isn't available", you're on a mode/channel that doesn't expose it — switch, don't work around it.
✗ Don't add a tool without classifying it
New tools are @AiToolDef and auto-discovered; in v2 an unclassified tool fails
closed at the strictest tier. Register it in the mode/agent and give it a policy —
adding and classifying a tool are the same step. See
Backend Integration.
✗ Don't poll for the answer
The turn streams; don't re-fetch the conversation on a timer. Fall back to
assistant:history only on reconnect.
Next
- When things break → Troubleshooting
- The design → Architecture