For structured use cases (collections, motor-insurance renewal), CXB API builds the live system prompt from a typed policy config instead of a free-text prompt. This keeps compliance language, objection handling, and the disposition schema consistent across calls while interpolating per-customer facts. Assembly lives in the conversation-policy service.

Entry point

build_policy_system_prompt(bot_system_prompt, policy, variables) dispatches on policy.type:
policy.typeBuilder
collection_recoverybuild_collection_recovery_prompt
collection_early_dpdbuild_collection_early_dpd_prompt
collection_pre_duebuild_collection_pre_due_prompt
insurance_renewal_motorbuild_insurance_renewal_motor_prompt
(missing/unknown type, or enabled: false)Falls back to rendering bot_system_prompt with render_template
The config service’s build_config_for_call treats a bot as a policy bot when conversation_policy is a dict and not explicitly enabled: false. Policy bots use the policy builder; non-policy bots use the direct static/dynamic prompt path with live caching.

Variable resolution inside policies

Policy builders read facts through _variable(variables, field_name), which tries the exact key and then crm., call., system. prefixes (for bare field names). _variable_from_fields walks a candidate list and _customer_names resolves a Hindi + English name pair, preferring *_HI / FIRSTNAME variants and falling back to the literal customer. _scenario_matches gates optional snippets (e.g. compliance clauses) on a CRM/call flag field. A disabled scenario (enabled: false) always wins over always: true, so toggling a scenario off never leaks its static text into the prompt.

Number and date localisation

A core job of the policy engine is converting raw numeric/date CRM values into spoken words so TTS reads them naturally in Hindi and English. The relevant helpers:
HelperInput → Output
indian_amount_to_hindi_words50000पचास हज़ार रुपये
indian_amount_to_english_words50000fifty thousand rupees
_days_to_hindi_words / _days_to_english_words5पाँच दिन / five days
_date_to_spoken_wordsparsed date → (hindi, english) spoken pair
_tomorrow_fact_linetoday’s date → a “Tomorrow: …” fact line
_spoken_product_name / _spoken_product_name_enPLपर्सनल लोन / personal loan
_spoken_vehicle_brand_hi / _spoken_vehicle_brand_enbrand code → spoken brand name
Amounts use the Indian numbering system (crore / lakh / thousand). _parse_int strips non-digit characters before conversion, and _parse_date accepts multiple formats (YYYY-MM-DD, DD Month YYYY, DD/MM/YYYY, ISO, etc.); unparseable values fall back to the raw string.
The renewal builder computes a renewal window from policy_expiry vs today (_renewal_window) and selects objection-handling snippets via _objection_lines with _RENEWAL_OBJECTION_DEFAULTS.

Name transliteration

Name localisation is shared with the runtime-config builder and lives in the name-transliteration service:
  1. Dictionary lookupHINDI_NAME_MAP covers common North/Central-India surnames and first names. Instant and exact.
  2. External transliteration fallbacktransliterate_word (itc hi-t-i0-und, 2s timeout) for dictionary misses.
  3. Pass-through — if the external service fails, the original English word is kept (TTS handles it acceptably).
to_devanagari strips S/O, W/O, D/O suffixes, skips text already in Devanagari (_is_devanagari), and transliterates word-by-word. to_firstname_devanagari returns only the first token.

Callback prompt injection

When callback detection is active, the post-call analysis prompt is augmented so the LLM also reports callback intent. In the config service:
  • Injection runs only when bot.callback_prompt_injection is set and the linked campaign has config.callback_detection.enabled = true (_callback_detection_enabled).
  • _with_callback_analysis_prompt appends CALLBACK_ANALYSIS_INSTRUCTIONS to the raw analysis prompt, but only if the prompt does not already define callback_requested and callback_preferred_time_text.
  • The instruction block mandates four JSON keys: callback_requested, callback_preferred_time_text, callback_reason, callback_confidence.
On a callback attempt, _callback_call_vars also injects call.callback_requested, call.callback_sequence, call.callback_preferred_time_text, and call.callback_reason so the live prompt can acknowledge the prior request.
The injected instructions only ask the LLM to emit the keys. Actual scheduling/persistence happens later in the campaign-result service (the second gate). See Callbacks.

Runtime config

Where the policy prompt fits in the call-time contract.

Live prompt cache

Static-prompt caching for direct-prompt (non-policy) bots.

Prompts and variables

Operator-facing guide to prompt variables.

Campaign callbacks

Callback extraction, scheduling, and the double gate.