Instructions & Automation
Instructions are the backbone of your Sales Assistantβs behavior. They go far beyond simple text prompts β each instruction has a category that determines how and when it executes. Some instructions add context to the AIβs brain, others register tools it can use, and some run as deterministic code that triggers actions before or after every customer message.
The Problem With Simple Instructions
Most chatbot platforms let you write a list of instructions. The AI reads them all every time and tries to follow them. This works for simple cases, but it breaks down fast:
- βSend the menu when someone asks about pricesβ β the AI might send it, might forget, or might send the wrong thing. Itβs not reliable.
- βMark the customer as βpaidβ after they receive an invoiceβ β the AI has to remember to do this every time. Itβs easy to forget.
- βOnly show weekend pricing rules on Fridaysβ β the AI gets confused when instructions contradict each other.
QuotyAI fixes this by giving each instruction a category that determines exactly how it behaves β as a prompt, a tool, a pre-condition, or automated code. You simply write what you want in plain language β the system determines the category, generates the deterministic code, and lets you preview it before it goes live.
Meet the Six Categories
Every instruction you write has a category. The category controls what happens with that instruction at runtime:
| Category | Where It Runs | What It Does |
|---|---|---|
| Consistent Prompt | Inside the AIβs system prompt | Always included β gives the AI permanent context |
| Conditional Prompt | Inside the AIβs system prompt | Only included when a condition is true |
| Consistent Prompt + Tool | Prompt + LangChain tool registered | Permanent context + a callable function the AI can use |
| Conditional Prompt + Tool | Prompt + LangChain tool registered | Context + tool, but only when the condition is true |
| Deterministic Router | Before AI processes message | Runs code that can intercept, send messages, or short-circuit the AI entirely |
| Deterministic Callback | After AI responds | Runs code that updates state, flags customers, or notifies your team |
A Real Business Story
A small restaurant in Ho Chi Minh City uses QuotyAI to handle their Facebook and Telegram orders. Hereβs how they use each instruction category:
Consistent Prompt:
βOur pho is 65,000 VND for a regular bowl, 85,000 VND for a large. Banh mi is 35,000 VND.β
This is always in the AIβs brain β the customer can ask anytime and get the right answer.
Conditional Prompt:
βAfter 10 PM, only takeout is available. Dine-in closes at 9:30 PM.β
The condition function checks the current time. Before 9:30 PM, this instruction is invisible to the AI. After 9:30 PM, it appears automatically and the AI starts telling customers about takeout only.
Deterministic Router:
βIf a customer says βmenuβ or βwhat do you haveβ, send them the PDF menu and donβt make them wait for the AI to respond.β
No forms, no action builders, no dropdowns β just write what you want. The AI coding agent reads your instruction, infers the appropriate actions (send message, send attachment, short-circuit, etc.), and generates the TypeScript function automatically. The router function checks every inbound message. When it detects a menu request, it immediately sends the attachment and short-circuits the AI β the customer gets the menu in under 100ms instead of waiting for the AI to generate a response.
Deterministic Callback:
βWhen a customer confirms a paid order, mark them as βhas_paid_orderβ in their conversation state.β
After the AI successfully processes an order and sends a confirmation, the callback fires. It updates the conversation state so the next time this customer messages, the AI knows theyβre a paying customer.
Consistent Prompt + Tool:
βYou can look up todayβs specials using the getDailySpecial tool. Specials change daily based on fresh ingredients.β
The AI knows about this capability from the prompt, and it has a registered function it can call to check what todayβs specials are.
How It Works
The Automation Pipeline
Every customer message goes through a deterministic pipeline before and after the AI processes it:
Customer sends a message
β
βββββββββββββββββββββββββββββββββ
β 1. Extract Order Details β
β Your assistant extracts β
β structured order data β
β from the message β
βββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββ
β 2. Deterministic Router β β Pre-AI automation
β Runs BEFORE the AI sees β
β the message. Can intercept,β
β send attachments, or β
β short-circuit entirely. β
βββββββββββββββββββββββββββββββββ
β (if not intercepted)
βββββββββββββββββββββββββββββββββ
β 3. AI Agent Processes β
β β’ Consistent prompts β β Always in brain
β β’ Conditional prompts β β Only if condition met
β β’ Tools registered β β AI can call functions
βββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββ
β 4. Deterministic Callback β β Post-AI automation
β Runs AFTER the AI replies. β
β Updates state, flags β
β customers, notifies team. β
βββββββββββββββββββββββββββββββββ
Deterministic Means Guaranteed
Unlike the AI which might forget or misinterpret an instruction, Deterministic Router and Deterministic Callback instructions run as actual TypeScript code generated by our AI coding agents. You donβt need to configure actions manually β just write what you want to happen. When you write:
βIf the total is over 1,000,000 VND, offer free deliveryβ
The AI coding agent analyzes your instruction, infers the appropriate route actions and conditions, and generates an individual TypeScript function at assistant build time. You can preview the generated code directly in the editor β expand any router instruction to see the TypeScript with syntax highlighting, copy it, or regenerate it. Each function runs independently β if one fails, the others continue without interruption.
function route(ctx: RouterContext): RouterAction[] {
const actions: RouterAction[] = [];
if (ctx.order.totalAmount && ctx.order.totalAmount > 1000000) {
actions.push({
type: 'send_message',
message: 'Your order qualifies for free delivery! π',
});
}
actions.push({ type: 'noop' });
return actions;
}
The RouterContext gives each router function everything it needs to make smart decisions:
| Field | Type | Description |
|β|β|β|β|
| ctx.order | Record<string, unknown> | Structured order parameters extracted from conversation |
| ctx.message | string | Raw customer message text β use for keyword detection |
| ctx.state | Record<string, unknown> | Full conversation state (customer flags, past data) |
| ctx.business | object | Business info: timezone, name, industries, customAttributes β company-wide user-defined attributes that apply to all contacts/conversations |
| ctx.contact | object (optional) | Customer contact: name, email, phone, customAttributes, channelIdentifiers β build conditions based on who the customer is |
| ctx.conversation | object (optional) | Conversation metadata: channel, externalId, lastInboundMessage, lastOutboundMessage β build channel-specific logic |
| ctx.now | Date | Current time (deterministic β same value for all functions in a run) |
Each generated function runs in a sandboxed environment β it never forgets, never guesses, and never hallucinates. Because the context includes contact information, conversation metadata, and business-level custom attributes, functions can build rich conditions like βif this is a VIP customer from Telegramβ, βif the customerβs email domain indicates a corporate accountβ, or βif the business-wide allow_discounts flag is set to trueβ.
Router Actions
The router function returns actions. The platform interprets them:
| Action | Effect |
|---|---|
send_message |
Send a static message to the customer immediately |
send_linked_attachments |
Send all linked attachments for this instruction |
update_state |
Change a value in the conversation state |
handover |
Escalate to a human agent with a reason |
short_circuit |
Skip the AI entirely β no AI response generated |
noop |
Do nothing, continue normally |
Callback Actions
The callback function runs after the AI replies:
| Action | Effect |
|---|---|
update_state |
Change a value in the conversation state (e.g., mark as paid) |
notify_agent |
Send a private notification to your team |
noop |
Do nothing |
Category Reference
Consistent Prompt
Purpose: Instructions that should always be part of the AIβs system prompt. Use for permanent business info, policies, and standard responses.
When to use:
- Your menu, price list, or service catalog
- Standard operating hours
- Frequently asked questions and their answers
- Business policies (cancellation, refund, etc.)
Example:
βOur spa offers: 60-min Swedish massage β 500,000 VND, 90-min deep tissue β 750,000 VND, add hot stone β 150,000 VND.β
Conditional Prompt
Purpose: Instructions that should only appear in the AIβs context when a condition is true. The condition is a generated TypeScript function that evaluates at runtime.
When to use:
- Time-based rules (after hours, weekends, holidays)
- Customer status rules (returning customer, VIP, flagged)
- Order state rules (only after items are selected)
- Seasonal promotions and limited-time offers
Under the hood, a shouldInclude function is generated from your instruction with the signature function shouldInclude(ctx: ConditionContext): boolean. The ConditionContext provides:
| Field | Type | Description |
|---|---|---|
ctx.order |
Record<string, unknown> |
Structured order input parameters from the conversation |
ctx.contact |
object (optional) |
Customer contact: name, email, phone, customAttributes |
ctx.conversation |
object (optional) |
Conversation metadata: channel, externalId, lastInboundMessage |
ctx.state |
Record<string, unknown> (optional) |
Conversation state snapshot (customer flags, past data) |
ctx.business |
object (optional) |
Business info: timezone, name, industries, customAttributes |
ctx.now |
Date |
Current time (deterministic) |
If the function returns false, the instruction content is invisible to the AI.
Consistent Prompt + Tool
Purpose: Gives the AI permanent context AND registers a deterministic tool function it can call. Use when the AI needs both knowledge about something and the ability to act on it.
When to use:
- Looking up dynamic data (inventory, daily specials, availability)
- Performing validations (check address, verify coupon code)
- Calculating derived values (tax rates, shipping costs)
The tool is registered as a LangChain function tool with a generated JSON schema. The AI can choose to call it during the conversation.
Conditional Prompt + Tool
Purpose: Same as above, but both the prompt content and the tool are only available when the condition is true.
When to use:
- Weekend-only tools (e.g., a brunch menu lookup)
- VIP-customer-only capabilities (e.g., apply loyalty discount)
- Post-order tools (e.g., track delivery status β only available after order is placed)
The condition is evaluated once β if true, both the prompt content AND the tool are available to the AI.
Deterministic Router
Purpose: Runs as actual TypeScript code before the AI agent sees the message. Can intercept, send attachments, update state, trigger handover, or short-circuit the AI entirely.
When to use:
- Instant responses: βIf customer asks for hours, send hours attachment immediatelyβ β no AI latency
- Guardrails: βIf customer mentions competitor names, block and handoverβ
- Pre-processing: βIf customer says βmenuβ in any language, send the PDFβ
- Compliance: βIf customer is in a blocked region, send sorry message and short-circuitβ
- Time-based: βAfter 10 PM, send a βclosedβ message and short-circuitβ
- Customer-state: βIf customer already has pending order, block re-orderingβ
All router instructions generate individual TypeScript functions at build time β one per instruction. Each function receives a full RouterContext object with the raw customer message, structured order data, conversation state, business info, customer contact information, conversation metadata, and current time. This means each rule can independently evaluate against all available context.
Per-instruction isolation: If one router function has an error, the rest continue running. No single buggy instruction can break all your routing rules.
How it works in the pipeline:
- Extract structured order data from the customer message
- Build
RouterContextwith the raw message, order data, conversation state, business info, customer contact, conversation metadata, and current time - Run each
route(ctx)function in order β ifshort_circuitis returned, remaining functions still run but the AI is skipped - Process accumulated actions: send messages, send attachments, update state, handover, or short-circuit
Deterministic Callback
Purpose: Runs as actual TypeScript code after the AI has responded. For side effects that shouldnβt interrupt the customer experience.
When to use:
- State tracking: βAfter confirming an order, set
hasActiveOrderflagβ - Notifications: βAfter a failed payment attempt, notify the teamβ
- Audit logging: βAfter any quote is provided, log it to the conversation stateβ
- Customer segmentation: βAfter a customer places their 5th order, flag them as VIPβ
Each callback instruction generates its own function with the signature function callback(ctx: CallbackContext): CallbackAction[]. The CallbackContext provides:
| Field | Type | Description |
|---|---|---|
ctx.order |
Record<string, unknown> |
Structured order input parameters from the conversation |
ctx.aiResponse |
string |
The AI-generated response that was just sent to the customer |
ctx.contact |
object (optional) |
Customer contact: name, email, phone, customAttributes β condition on who the customer is |
ctx.conversation |
object (optional) |
Conversation metadata: channel, externalId β condition on which channel the conversation came from |
ctx.state |
Record<string, unknown> (optional) |
Conversation state snapshot for reading or updating customer flags |
ctx.business |
object (optional) |
Business info: timezone, name, industries, customAttributes |
In addition to the standard actions, callbacks can write back to contacts and conversations:
| Action | Effect |
|---|---|
set_contact_attribute |
Set a user-defined attribute on the contact document (persisted across conversations) |
set_conversation_attribute |
Set a user-defined attribute on the conversation document |
This allows the AI-generated callback to persist data like βmark this customer as VIPβ or βflag this conversation as handledβ by writing to ctx.contact.customAttributes or ctx.conversation.customAttributes.
Callbacks are non-blocking β the customer gets their response while callbacks process in the background.
Technical Implementation Details
Build Pipeline Integration
During assistant build, instructions go through code generation:
Instructions from DB
β
Builder fetches active instructions
β
ββββββββββββββββββββββββββββββββββββββ
β Category Check β
β β
β CONSISTENT_* β No code generation β
β (prompt-only) β
β β
β CONDITIONAL_* β Generate β
β shouldInclude() per instruction β
β β
β DETERMINISTIC_ROUTER β Generate β
β route() PER instruction β β
β each function runs independently β
β with full RouterContext β
β β
β DETERMINISTIC_CALLBACK β Generate β
β callback() per instruction β
β β
β *_PROMPT_AND_TOOL β Generate β
β tool function + JSON schema β
β per instruction β
ββββββββββββββββββββββββββββββββββββββ
β
Code written back to instruction doc (for preview)
β
Code stored in aiExecutableSource (source of truth)
β
Snapshot frozen in immutable assistant
Generated Code Storage
Generated code is written to two places for different purposes:
- Instruction doc (
generatedCodefield) β for instant preview in the editor with syntax highlighting - Assistantβs
aiExecutableSourceβ the source of truth that gets frozen in immutable snapshots and used at runtime
The storage layout in aiExecutableSource, keyed by instruction ID:
| Category | Storage | Example Key |
|---|---|---|
| Router (per instruction) | deterministicRouterInstructions[instructionId].code |
One entry per router instruction β isolated and independent |
| Callback (per instruction) | deterministicCallbacks[instructionId].code |
One entry per callback instruction |
| Condition (per instruction) | conditionFunctions[instructionId].code |
One entry per conditional instruction |
| Tool (per instruction) | instructionTools[instructionId] |
One entry per tool instruction |
Sandboxed Execution
All generated TypeScript functions run in the same sandboxed environment as pricing formulas:
- Bunβs native
Functionconstructor - No file system access, no network, no environment variables
- Overridden
consolemethods for audit logging - Returns typed action objects that the pipeline interprets
Custom Attributes at Three Levels
All three context types (RouterContext, CallbackContext, ConditionContext) expose custom attributes at three levels, letting you build conditions based on business-wide settings, customer identity, or conversation context:
| Level | Access Path | Scope | Example |
|---|---|---|---|
| Business | ctx.business.customAttributes |
All contacts and conversations inherit these | allow_discounts: true, max_order_limit: 5000000 |
| Contact | ctx.contact.customAttributes |
Specific customer across all their conversations | vip_tier: 'gold', preferred_language: 'vi' |
| Conversation | ctx.conversation.customAttributes |
Specific conversation only | no_ai: true, priority: 'urgent' |
Attributes are entirely user-defined β you decide the keys and values. The AI-generated code reads them directly:
// Router β offer discount only if business and contact both allow it
if (ctx.business.customAttributes?.allow_discounts && ctx.contact.customAttributes?.vip_tier === 'gold') {
actions.push({ type: 'send_message', message: 'You get a 10% discount!' });
}
// Callback β persist a flag after successful order
if (ctx.order.totalAmount > 1000000) {
actions.push({ type: 'set_contact_attribute', key: 'high_value', value: true });
}
// Condition β hide instructions for flagged conversations
if (ctx.conversation.customAttributes?.no_ai) {
return false;
}
Best Practices
For Consistent Prompts
- Keep them factual and precise: exact prices, exact hours, exact policies
- One concept per instruction (donβt bundle unrelated rules)
- Use the same language your customers will use
For Conditional Prompts
- Make the condition obvious in the instruction content: βAfter 10 PMβ¦β works better than βLate night rulesβ
- Donβt overuse conditions β the AI works best with a stable context
- Test your conditions by simulating different scenarios
For Deterministic Routers
- Use for anything time-sensitive β the router runs in milliseconds, the AI takes seconds
- Preview the generated code in the editor to verify the AI inferred the right actions
- If the generated code doesnβt match your intent, rewrite the instruction in clearer language
short_circuitis powerful: use it when the AI has nothing useful to add- Routers canβt access the AIβs knowledge β they work with structured order data only
For Deterministic Callbacks
- Use for state mutations that should always happen after specific events
- Donβt use callbacks for customer-facing actions β the customer already got their response
- Callbacks are ideal for compliance and audit trails
General
- The order of instructions doesnβt matter for routers (theyβre consolidated into one function)
- For prompts, order matters β earlier instructions appear higher in the AIβs context
- If an instruction doesnβt fit a category, itβs skipped β every instruction needs a valid category