Build notes · AI architecture & safety

Klaros Chatbot As-Is AI Architecture: Fail-Closed Design and Zero-Token Grounding

· 100% grounded in facts · Founder, Klaros

When we built the first version of the Klaros WhatsApp AI chatbot, we made a deliberate architectural decision: “Rules First, LLM Tail, Human Override Always”. Wrapping an unconstrained LLM around incoming webhooks and hoping system prompts hold the line is a recipe for disaster on WhatsApp.

In WhatsApp operations, a single hallucinated price, an ill-timed automated message over an active human sales conversation, or an unvetted freeform send outside Meta's 24-hour service window damages customer trust permanently. We designed the Klaros chatbot around fail-closed safety boundaries where deterministic rules run first, and the LLM is invoked only when non-deterministic assistance is needed.

TL;DR

From a first-hand developer perspective, the As-Is chatbot architecture prioritizes safety and cost control: (1) agent-decision.mjs runs an 8-level priority tree that yields to human staff for 24h; (2) ai-auto-reply.mjs enforces fact-fencing with mandatory chunk citations (usedFacts), downgrading uncited replies to human handoffs; (3) learned-answer.mjs intercepts repeat queries via Jaccard matching to serve verified answers at $0 token cost; and (4) retrieve.mjs scores BM25 term relevance in pure JS under $<10\text{ms}$ inside Cloudflare Workers.

Developer Rationale: Rules First, LLM Tail

On the WhatsApp Cloud API, freeform messages can only be sent inside an open 24-hour service window, while outbound template messages cost money per delivery. An unconstrained chatbot model that invents pricing or offers discounts outside approved parameters breaks Meta compliance and burns budget.

We placed the LLM at stage 9000 (last_resort). Opt-outs, keyword triggers, away rules, sequence steps, and human staff takeover detection run deterministically before an LLM API call is ever constructed.

The 8-Level Priority Tree & Human Yield

In agent-decision.mjs, outreach is evaluated through 8 explicit priority levels. Our most load-bearing developer decision was Priority 1: Offline Human Activity Check (`detectOfflineConversation()`). If a human sales or support rep interacted with a thread within 8 hours, all automated AI chatbot actions yield for 24 hours. The assistant never talks over a colleague.

Code Blueprint: agent-decision.mjs Priority Tree Structure

// Priority 1: Human rep yield check
if (await detectOfflineConversation(contactId)) {
  return buildPauseDecision("Human staff active in thread");
}
// Priority 2: Do-Not-Contact enforcement
if (contact.opted_out || contact.dnc) {
  return buildDncDecision();
}
// Priority 3-7: Deterministic sequence & qualification gates
// Priority 8: LLM Planner fallback if non-deterministic planning is required

Fact-Fenced Auto-Responses & Citation Rules

When the auto-responder (ai-auto-reply.mjs) executes, it receives at most 6 retrieved knowledge chunks. The prompt requires a strict JSON action envelope with explicit citation indices in usedFacts:

{
  "canAnswer": true,
  "reply": "Our standard plan includes 3 lines...",
  "action": "reply",
  "usedFacts": [1, 3]
}

If the model generates a text reply without citing at least one retrieved chunk index, our output validator flags missing attribution and downgrades the outcome to a human handoff (HANDOFF). The chatbot never delivers ungrounded text to a prospect.

Zero-Token Q&A Caching Mechanics

Verified operator answers are indexed in knowledge_answers. In learned-answer.mjs (stage 8000), incoming queries are tokenized and scored using Jaccard similarity. When similarity meets the $0.6$ floor with $\ge 2$ shared content words, the verified response is sent at $0 token cost, bypassing LLM inference entirely. Operators can calculate their exact token cost reduction using our WhatsApp cost savings calculator.

Dependency-Free BM25 Search Engine

Retrieval in uni-cloud/src/services/knowledge/retrieve.mjs uses pure JavaScript BM25 term scoring over SQLite chunks in D1. We applied a Discriminating Document Frequency Ratio ($1/3$) to filter out boilerplate terms without external vector DB dependencies, keeping execution fast ($<10\text{ms}$) inside Cloudflare Worker isolates.

Developer Architecture Tradeoff Matrix

The as-is chatbot design represents explicit engineering choices:

Get the next build note on WhatsApp

Message our line and type NOTES. The latest engineering note comes straight back, in the same thread, from the number that sends everything else. Reply STOP whenever you like and it stops.

Send NOTES on WhatsApp

Ask our WhatsApp number what it costs

Not a sales form. Message the line and type pricing. You will get our live catalog as a WhatsApp list, with real available slots, and a payment link on whatever you tap. The whole path is the product, demonstrating itself before you own it.

Written 19 August 2026. We append when the facts change. Related: the response asset registry that allows tool sends without hallucination, the 7 listening defects caught and fixed in auto-reply, all build notes.