Knowledge > Products > Chatbot > Architecture > Chatbot Agent Routing
Chatbot Agent Routing
How the chatbot selects, configures, and switches between specialized agents. The chatbot uses 4 marketing agent types, each mapping to multiple personas with professional domain rulesets.
Agent Type Hierarchy
Marketing Agent Type Persona Types (CareAgentType)
──────────────────── ──────────────────────────────
care → pastoral_care, prayer
discipleship → bible_study, sermon_prep, small_group
stewardship → giving
coordinator → welcome, new_member, volunteer, youth
Each marketing agent type is a container. The persona type is the specific specialist the user interacts with.
Tier Gating
Not all plans get all agents.
TIER_AGENTS:
starter: [care, coordinator] (2 agents)
pro: [care, discipleship, stewardship, coordinator] (4 agents)
suite: [care, discipleship, stewardship, coordinator] (4 agents)
Request Flow
Step 1: Receive Request
POST /api/chatbot/stream
body: {
message: string,
churchId: string,
sessionId: string,
history?: ChatMessage[],
agentType?: string, # Optional persona type (e.g., "pastoral_care")
lensOverride?: number, # Optional theological lens ID (demo mode)
lensNameOverride?: string
}
Step 2: Early Gates
1. RATE LIMIT: 30 requests / 60 seconds per IP
2. INPUT VALIDATION: message required, churchId required, sessionId required
3. MESSAGE LENGTH: max 2000 characters
4. CHATBOT ENABLED: query premium_churches WHERE church_id AND chatbot_enabled=true
5. MODERATION CHECK: checkRestriction(churchId, sessionId)
→ cooldown (5 min), temp_block (24 hr), or permanent_block
6. FAQ SHORT-CIRCUIT:
matchFAQ(message, churchId, agentType)
IF exact match with exact_response=true:
RETURN canned response immediately (zero LLM cost)
IF fuzzy match:
faqPreferredContext = matched answer (injected into LLM context)
7. USAGE LIMIT: checkUsageLimit(churchId, planTier)
→ Monthly message cap varies by tier
Step 3: Resolve Agent Config
1. LOAD ORGANIZATION SETTINGS
query organization_settings WHERE organization_id = churchId
→ agent_tool_config: { chatbot_tools: [...enabled tool IDs] }
→ agent_config: { care: {enabled, voiceId, personalityOverrides, handoffRules}, ... }
2. RESOLVE EFFECTIVE CONFIG
resolveAgentConfig(savedAgentConfig, planTier)
FOR each marketing agent type:
IF not in TIER_AGENTS[tier]: force enabled=false (preserve saved config)
IF saved config exists: use saved enabled state + all overrides
IF no saved config: default enabled=true for allowed agents
3. CHECK AGENT ENABLED
IF agentType provided:
marketingAgent = getMarketingAgentForPersona(agentType)
IF marketingAgent AND NOT effectiveAgentConfig[marketingAgent].enabled:
RETURN "This care agent is not currently active."
4. DETECT CHATBOT MODE
IF chatbotSource == "pewsearch_auto_provision": isBasicChatbot = true
→ Simple Q&A, no tools, concise responses
IF plan == "pro_website" AND NOT isBasicChatbot: isProWebsite = true
→ Richer than basic but restricted vs full CWA plan
Step 4: Build Persona-Aware System Prompt
The prompt is layered, built by buildAgentSystemPrompt() in agent-prompts.ts.
LAYER 1: BASE (built in route.ts, passed to buildAgentSystemPrompt)
Church facts: name, address, denomination, phone, website
Hours: custom_hours or working_hours
Staff: name — title list
Ministries: name, description list
What to Expect: dress code, parking, kids, music style
Pastor Pulse: sermon_topic, sermon_series, theme_verse, weekly_announcement
Contact info: pastor name, callback availability
Theological lens: lens name + doctrinal rules + vocabulary block
RAG context: theological hits + church KB hits
Product knowledge: injected from product_knowledge table
FAQ preferred context: if fuzzy FAQ matched
LAYER 2: AGENT PERSONA (from persona-templates.ts)
SYSTEM_PROMPTS[agentType]:
Role description, core responsibilities
Example: pastoral_care → grief counselor, crisis support, prayer ministry
LAYER 3: DOMAIN RULESET (from agent-prompts.ts DOMAIN_RULESETS)
Professional methodology specific to the persona:
pastoral_care → Worden's Four Tasks, Dual Process Model, Lament Theology
welcome → Visitor Research (Rainer), Three Visitor Fears, Progressive Engagement
prayer → ACTS Framework, Listening Prayer, Confidentiality Protocol
youth → Safe Sanctuaries, OARS Technique, Mental Health Literacy
bible_study → Inductive Method (OIA), Historical-Grammatical, Theological Triage
new_member → Assimilation Research, Progressive Discipleship Pathway
sermon_prep → Haddon Robinson's Big Idea, Multiple Homiletical Forms
small_group → Tuckman's Group Dynamics, Facilitation Not Teaching
volunteer → Gift-Based Placement, Volunteer Lifecycle
giving → (stewardship methodology)
LAYER 4: PERSONALITY OVERRIDES (from organization_settings.agent_config)
Church-specific personality customization:
tone: warm|empathetic|encouraging|professional|pastoral|friendly|energetic
formality: casual|moderate|formal
responseLength: concise|moderate|thorough
emojiUsage: none|minimal|moderate|frequent
specialInstructions: free-text custom behavior
customInstructions: additional free-text
suggestedQuestions: string[] for conversation starters
LAYER 5: TOOL INSTRUCTIONS
Filtered tool list = intersection of:
- Church-enabled tools (from agent_tool_config.chatbot_tools)
- Agent-allowed tools (from TOOL_AGENT_MAP — which tools each agent type can use)
Each tool gets a schema definition injected into the prompt
LAYER 6: SAFETY LAYER (universal, same for all agents)
- Crisis resources: 988, 741741, 911 — always appended
- Never provide medical/legal/financial advice
- Never share other members' information
- Always offer to connect with a real person
Step 5: LLM Call
MODEL SELECTION (via llm-provider.ts):
Primary: Anthropic Claude Haiku 4.5
Fallback: OpenAI gpt-4o-mini (if Anthropic fails)
MODEL PARAMS (from getModelParams):
temperature, max_tokens, top_p
Adjusted per persona type for appropriate creativity level
TOOL LOOP:
IF LLM returns tool_calls:
FOR each tool_call:
result = executeTool(tool_call, toolContext)
Append tool result to messages
Re-call LLM with tool results
(Loop up to 3 iterations to prevent infinite tool chains)
Step 6: Post-Processing
1. CRISIS PATTERN CHECK (regex in route.ts)
Scan response for crisis indicators
IF detected: append 988/741741/911 resources (non-negotiable)
2. MODERATION LOGGING
IF safety flag detected in message:
logViolation(churchId, sessionId, violationType, message)
autoEscalate(churchId, sessionId)
# Escalation thresholds:
# 2 violations → 5-min cooldown
# 4 violations → 24-hr temp block
# 7 violations → permanent block
3. USAGE TRACKING
trackUsage(churchId, sessionId, agentType, responseSource,
inputTokens, outputTokens, model)
4. CONVERSATION TRACKING (fire-and-forget)
upsert chatbot_conversations: session_id, organization_id,
agent_type, persona_type, last_message_at
5. ESCALATION CHECK
shouldEscalate(message, response):
IF caller asking for human → suggest handoff
IF topic outside agent expertise → suggest different agent
Handoff Flow
Agents can suggest transferring to another agent type.
HANDOFF RULES (from agent_config.handoffRules per agent):
{
care: { enabled: true, customMessage: "..." },
discipleship: { enabled: true, customMessage: "..." },
humanEscalation: {
contactName: "Pastor John",
contactMethod: "Call the church office at (555) 123-4567",
whenToEscalate: "Crisis, complex counseling, membership decisions"
}
}
WHEN agent suggests transfer:
Response includes suggested agentType
Client UI shows transfer button
Next message sent with new agentType param
→ New persona, new domain ruleset, same session history