Chatbot with EscalationAI Workflow
A conversational chatbot that follows scripted flows, escalates to an LLM when stuck, validates all AI responses with guardrails, and notifies support on Slack when it can't resolve the issue.
Prerequisites
Environment variables
OPENAI_KEYSLACK_BOT_TOKENInstall
npx radzor@latest recipe add chatbot-escalationAI Prompt
“Run `npx radzor@latest add chatbot-flow llm-completion guardrails slack-bot` to install 4 Radzor components. Then read components/radzor/chatbot-flow/radzor.manifest.json, components/radzor/llm-completion/radzor.manifest.json, components/radzor/guardrails/radzor.manifest.json, components/radzor/slack-bot/radzor.manifest.json and each component's llm/integration.md. Wire them together to a conversational chatbot that follows scripted flows, escalates to an LLM when stuck, validates all AI responses with guardrails, and notifies support on Slack when it can't resolve the issue. Use the manifest's inputs (check envVar for required environment variables), outputs (check fields for object shapes), composability (check mapField for field extraction), and actions — don't invent custom interfaces.”
Paste this into Claude Code, Cursor, Windsurf, or any AI coding agent.
Pipeline
ChatbotFlow
Handles scripted conversation flow
LLMCompletion
Generates AI response on fallback
Guardrails
Validates AI response for safety
SlackBot
Escalates unresolved issues to support
Scaffolded Code
// npx radzor@latest add chatbot-flow llm-completion guardrails slack-bot
import { ChatbotFlow } from "./components/radzor/chatbot-flow"
import { LLMCompletion } from "./components/radzor/llm-completion"
import { Guardrails } from "./components/radzor/guardrails"
import { SlackBot } from "./components/radzor/slack-bot"
const bot = new ChatbotFlow({
fallbackMessage: "Let me think about that...",
maxHistory: 20,
flow: {
startNode: "greeting",
nodes: {
greeting: {
message: "Hi! How can I help you today?",
options: [
{ label: "Billing question", next: "billing" },
{ label: "Technical issue", next: "technical" },
{ label: "Something else", next: "freeform" },
],
},
billing: {
message: "For billing, I can help with invoices, refunds, or subscription changes. What do you need?",
options: [
{ label: "Get invoice", next: "invoice_help" },
{ label: "Request refund", next: "refund_help" },
],
},
technical: {
message: "Let me connect you with our AI assistant for technical questions.",
next: "freeform",
},
invoice_help: { message: "You can download invoices from Settings > Billing. Need anything else?" },
refund_help: { message: "Refunds are processed within 5-7 days. I'll escalate this to our team." },
freeform: { message: null }, // triggers fallback → LLM
},
},
})
const llm = new LLMCompletion({
provider: "openai",
apiKey: process.env.OPENAI_KEY!,
model: "gpt-4o",
systemPrompt: "You are a helpful customer support agent. Be concise and friendly.",
maxTokens: 256,
})
const guard = new Guardrails({ enableBuiltinRules: true, maxOutputLength: 1000 })
const slack = new SlackBot({ botToken: process.env.SLACK_BOT_TOKEN! })
// Escalate to LLM when chatbot flow has no matching node
bot.on("onFallback", async (event) => {
const history = bot.getHistory(event.sessionId)
const context = history.map(m => `${m.role}: ${m.content}`).join("\n")
const { content } = await llm.complete(`Conversation so far:\n${context}\n\nUser: ${event.message}\nAssistant:`)
// Validate AI response
const validation = guard.validateOutput(content)
if (!validation.passed) {
await slack.sendMessage("#support-escalations",
`Guardrails blocked AI response for session ${event.sessionId}: ${validation.violations?.join(", ")}`
)
return { message: "I'll connect you with a human agent. One moment please." }
}
return { message: content }
})
// Notify support on flow completion (when issue needs human follow-up)
bot.on("onFlowComplete", async (event) => {
if (event.nodeId === "refund_help") {
const history = bot.getHistory(event.sessionId)
await slack.sendMessage("#support-escalations",
`Refund request from session ${event.sessionId}:\n${history.map(m => m.content).join("\n")}`
)
}
})
// Handle incoming messages
async function handleMessage(sessionId: string, message: string) {
return bot.processMessage(sessionId, message)
}Components used
LLM tip
Pass all 4 radzor.manifest.json files to your agent at once. It will read the outputs of each step and match them against the inputs of the next — wiring the full pipeline without any extra instructions.