TAI BUI
Back to curriculum
BuildPython

Chatbots — Rule-Based to Neural to LLM Agents

ELIZA replied with pattern matches. DialogFlow mapped intents. GPT answered from weights. Claude runs tools and verifies. Each era solved the previous one's worst failure.

TIME: ~75 minutesPREREQUISITES: Phase 5 · 13 (Question Answering), Phase 5 · 14 (Information Retrieval)

Learning Objectives

  • Trace the four chatbot paradigms (rule-based, retrieval-based, neural seq2seq, LLM agent) and explain the specific failure each one was built to fix.
  • Implement a rule-based pattern matcher, a retrieval FAQ responder, and an LLM agent loop with tool calls and a step budget in Python.
  • Design hybrid routing that sends destructive actions through rules, FAQ questions through retrieval, and open-ended queries through an LLM agent.
  • Identify production failure modes of LLM agents (confident fabrication, prompt injection, scope creep, infinite loops, context exhaustion) and name the mitigation for each.

The Problem

A user types "I want to change my flight." The system has to work out what they want, what information is still missing, how to fetch it, and how to actually make the change. Then the user says "wait, what if I cancel instead?", and now the system has to hold onto the earlier context, switch tasks, and keep its state straight.

Conversation is hard for an ML system: the input is open-ended, the output has to stay coherent across many turns, and the system sometimes has to act on the real world, changing a flight, charging a card. Every wrong step is visible to the user.

Chatbot architecture has cycled through four paradigms, each one introduced because the last one failed too visibly to ignore. This lesson walks through them in order. The 2026 production landscape is a hybrid of the last two.

Warm-up0/3
1. A support bot is mid-way through changing a customer's flight when the customer says, "wait, what if I cancel instead?" What must the system do to handle that second message correctly?Warm-up
2. A support script only recognizes the exact phrase "I want a refund." A customer types "can I get my money back?" instead. With simple keyword matching, what would you expect?Warm-up
3. A help center must connect the search "how do I get my money back" to an article titled "Refund policy". Which kind of comparison suits this better than exact word matching?Warm-up

The Concept

Chatbot evolution: rule-based → retrieval → neural → agent

Rule-based (ELIZA, AIML, DialogFlow). Hand-authored patterns match user input and produce responses. Intent classifiers route to predefined flows. Slot-filling state machines collect whatever required info is still missing. It works brilliantly inside the narrow scope it was designed for, and fails the moment you step outside it. Still ships in safety-critical domains (banking authentication, airline booking) where hallucination is not tolerated.

Retrieval-based. A FAQ-style system: encode every pair of (utterance, response), then at runtime encode the user's message and retrieve the nearest stored response. Think Zendesk's classic "similar articles" feature. It handles paraphrases better than rules, and because there's no generation, there's no hallucination.

Neural (seq2seq). Encoder-decoder trained on conversation logs, generating responses from scratch. Fluent, but prone to generic outputs ("I don't know") and factual drift, and never reliably on topic — the reason Google, Facebook, and Microsoft all had disappointing chatbots in 2016-2019.

LLM agents. A language model wrapped in a loop that plans, calls tools, and verifies outcomes. Not a chatbot with a long prompt. An agent loop: plan → call tool → observe result → decide next step. Retrieval-first grounding (RAG) keeps it from hallucinating. Tool calls let it actually do things. This is the 2026 architecture.

The four paradigms are not sequential replacements. A 2026 production chatbot routes through all four: rule-based for authentication and destructive actions, retrieval for FAQ, neural generation for natural phrasing, LLM agent for ambiguous open-ended queries.

Check: The Concept0/9
1. In a chatbot, what is an intent?Define
2. What does a slot-filling state machine do in a rule-based chatbot?Define
3. What is RAG (retrieval-augmented generation)?Define
4. What is a tool call in an LLM agent?Define
5. What distinguishes an LLM agent loop from a single-shot LLM call?Define
6. Which description fits a rule-based chatbot such as ELIZA?Define
7. Why is retrieval-based chat resistant to hallucination?Explain
8. What failure made 2016-2019 neural seq2seq chatbots disappointing in production?Explain
9. Why should destructive actions such as payments go through a structured flow even in an LLM-agent system?Explain

Build It

Step 1: rule-based pattern matching

python
import re
 
 
class RulePattern:
    def __init__(self, pattern, response_template):
        self.regex = re.compile(pattern, re.IGNORECASE)
        self.template = response_template
 
 
PATTERNS = [
    RulePattern(r"my name is (\w+)", "Nice to meet you, {0}."),
    RulePattern(r"i (need|want) (.+)", "Why do you {0} {1}?"),
    RulePattern(r"i feel (.+)", "Why do you feel {0}?"),
    RulePattern(r"(.*)", "Tell me more about that."),
]
 
 
def rule_based_respond(user_input):
    for pattern in PATTERNS:
        m = pattern.regex.match(user_input.strip())
        if m:
            return pattern.template.format(*m.groups())
    return "I don't understand."

ELIZA in 20 lines. The reflection trick ("I feel sad" → "Why do you feel sad") is the canonical psychotherapist demo from Weizenbaum 1966. Still instructive.

Step 2: retrieval-based (FAQ)

This illustrative snippet requires pip install sentence-transformers (which pulls in torch). The runnable code/main.py for this lesson uses a stdlib Jaccard similarity instead, so the lesson runs without external dependencies.

python
from sentence_transformers import SentenceTransformer
import numpy as np
 
 
FAQ = [
    ("how do i reset my password", "Go to Settings > Security > Reset Password."),
    ("how do i cancel my order", "Go to Orders, find the order, click Cancel."),
    ("what is your return policy", "30-day returns on unused items, original packaging."),
]
 
 
encoder = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2")
faq_questions = [q for q, _ in FAQ]
faq_embeddings = encoder.encode(faq_questions, normalize_embeddings=True)
 
 
def faq_respond(user_input, threshold=0.5):
    q_emb = encoder.encode([user_input], normalize_embeddings=True)[0]
    sims = faq_embeddings @ q_emb
    best = int(np.argmax(sims))
    if sims[best] < threshold:
        return None
    return FAQ[best][1]

Threshold-based refusal is the key design choice. If the best match is not close enough, return None and let the system escalate.

Step 3: neural generation (baseline)

Use a small instruction-tuned encoder-decoder (FLAN-T5) or a fine-tuned conversational model. Production-unusable on its own in 2026 (contradiction, off-topic drift, factual nonsense), but ships inside hybrid systems for natural phrasing. DialoGPT-style decoder-only models need explicit turn separators and EOS handling to produce coherent replies; a FLAN-T5 text2text pipeline works out of the box for a teaching example.

python
from transformers import pipeline
 
chatbot = pipeline("text2text-generation", model="google/flan-t5-small")
 
response = chatbot("Respond politely to: Hi there!", max_new_tokens=40)
print(response[0]["generated_text"])

Step 4: LLM agent loop

The 2026 production shape:

python
def agent_loop(user_message, tools, llm, max_steps=5):
    history = [{"role": "user", "content": user_message}]
    for _ in range(max_steps):
        response = llm(history, tools=tools)
        tool_call = response.get("tool_call")
        if tool_call:
            tool_name = tool_call.get("name")
            args = tool_call.get("arguments")
            if not isinstance(tool_name, str) or tool_name not in tools:
                history.append({"role": "assistant", "tool_call": tool_call})
                history.append({"role": "tool", "name": str(tool_name), "content": f"error: unknown tool {tool_name!r}"})
                continue
            if not isinstance(args, dict):
                history.append({"role": "assistant", "tool_call": tool_call})
                history.append({"role": "tool", "name": tool_name, "content": f"error: arguments must be a dict, got {type(args).__name__}"})
                continue
            fn = tools[tool_name]
            result = fn(**args)
            history.append({"role": "assistant", "tool_call": tool_call})
            history.append({"role": "tool", "name": tool_name, "content": result})
        else:
            return response["content"]
    return "I could not complete the task in the step budget."

Three things worth naming here. Tools are callable functions the LLM can invoke. The loop terminates when the LLM returns a final answer instead of a tool call, and the step budget prevents infinite loops on ambiguous tasks.

Real production adds: retrieval-first grounding (inject relevant docs before each LLM call), guardrails (refuse destructive actions without confirmation), observability (log every step), and evaluations (automated checks that agent behavior stays on-spec).

Step 5: hybrid routing

python
def hybrid_chat(user_input):
    if is_destructive_action(user_input):
        return structured_flow(user_input)
 
    faq_answer = faq_respond(user_input, threshold=0.6)
    if faq_answer:
        return faq_answer
 
    return agent_loop(user_input, tools, llm)
 
 
def is_destructive_action(text):
    danger_words = ["delete", "cancel", "charge", "refund", "transfer"]
    return any(w in text.lower() for w in danger_words)

The pattern: deterministic rules for anything destructive, retrieval for canned FAQs, LLM agents for everything else. This is what ships in 2026 customer-support systems.

Check: Build It0/13
1. In the FAQ responder, why does faq_respond return None when the best match is below the threshold?Explain
2. In the agent loop, what does retrieval-first grounding add?Explain
3. In the rule-based matcher, how does "I feel sad" become "Why do you feel sad?"Explain
4. In agent_loop, why is each tool result appended to the history before the next LLM call?Explain
5. Using the pattern list in the rule-based matcher, what does rule_based_respond("My name is Ana") return?Apply
6. What does rule_based_respond("I want a coffee") return?Apply
7. Using the four-pattern list from Build It Step 1 (the runnable main.py adds a greeting rule), what does rule_based_respond("Hello there") return?Apply
8. The best similarity between a user's message and any FAQ question is 0.42, and faq_respond uses its default threshold of 0.5. What does it return?Apply
9. In hybrid_chat, a message has no danger words and its best FAQ similarity is 0.55. Where does it go?Apply
10. In agent_loop, the LLM requests a tool named "send_fax" that is not in tools. What happens?Apply
11. With max_steps=5, the LLM returns a valid tool call on every step and never a final answer. What does agent_loop return?Apply
12. A user asks "how do I cancel my order?" and hybrid_chat sends it to structured_flow rather than the FAQ entry that answers it. What is the cause?Analyze
13. A developer moves RulePattern(r"(.*)", "Tell me more about that.") to the top of PATTERNS. What happens?Analyze

Use It

The 2026 stack:

Use caseArchitecture
Booking, payment, authenticationRule-based state machines + slot filling
Customer support FAQsRetrieval over curated answers
Open-ended help chatLLM agent with RAG + tool calls
Internal tools / IDE assistantsLLM agent with tool calls (search, read, write)
Companion / character chatbotsTuned LLM with persona system prompt, retrieval on knowledge

Always use hybrid routing in production: no single architecture handles every request well. The routing layer itself is typically a small intent classifier.

Check: Use It0/3
1. Why is hybrid routing the production default in 2026?Explain
2. A bank wants a chatbot that answers fee questions from a curated help center, executes transfers, and discusses open-ended financial planning. Which design fits?Design
3. An airline needs booking and authentication flows where a hallucinated step is unacceptable, yet users phrase requests in many ways. Which setup follows the lesson's stack?Design

Pitfalls

  • Confident fabrication. LLM agent claims it completed an action it did not. Mitigation: verify outcomes, log tool calls, never let the LLM claim to have done something without a successful tool return.

  • Prompt injection. User inserts text that overrides the system prompt. Ranked LLM01 in the OWASP Top 10 for LLM Applications 2025. Two flavors: direct injection (pasted into the chat) and indirect injection (hidden in documents, emails, or tool outputs the agent reads).

    Attack rates vary by scenario. Measured success rates range ~0.5-8.5% across frontier models in general tool-use and coding benchmarks. Specific high-risk setups (adaptive attacks against AI coding agents, vulnerable orchestration) have reached ~84%. Production CVEs include EchoLeak (CVE-2025-32711, CVSS 9.3, near the top of the 0-10 severity scale): a zero-click data-exfiltration flaw in Microsoft 365 Copilot triggered by an attacker-controlled email.

    Mitigations: treat user input as untrusted throughout the loop; sanitize before tool calls; isolate tool outputs from the main prompt; use the Plan-Verify-Execute (PVE) pattern where the agent plans first, then verifies each action against that plan before executing (this stops tool results from injecting new unplanned actions); require user confirmation for destructive actions; apply least-privilege to tool scopes.

    No amount of prompt engineering fully eliminates this risk. External runtime defense layers (LLM Guard, allowlist validation, semantic anomaly detection) are required.

  • Scope creep. Agent goes off-task because a tool call returned tangentially related info. Mitigation: narrow tool contracts; keep the system prompt focused; add evaluations for off-task rate.

  • Infinite loops. Agent keeps calling the same tool. Mitigation: step budget, tool-call deduplication, LLM judge on "are we making progress."

  • Context window exhaustion. Long conversations push the earliest turns out of context. Mitigation: summarize older turns, retrieve relevant past turns by similarity, or use a long-context model.

Check: Pitfalls0/12
1. What is prompt injection?Define
2. What is the "confident fabrication" failure mode in LLM agents?Define
3. How does indirect prompt injection differ from direct injection?Explain
4. How does the Plan-Verify-Execute pattern reduce indirect prompt injection?Explain
5. What causes scope creep in an LLM agent?Explain
6. Which combination mitigates the infinite-loop failure where an agent keeps calling the same tool?Explain
7. How can an agent cope with context window exhaustion in long conversations?Explain
8. What does the EchoLeak case (a zero-click data leak in Microsoft 365 Copilot via an attacker's email) illustrate?Explain
9. Why does a low prompt-injection success rate on a general benchmark not justify skipping runtime defenses?Explain
10. A support agent tells a customer "Your refund has been issued," but the logs show the refund tool returned an error. Which failure is this, and what prevents it?Analyze
11. An agent summarizing an inbox reads an email with hidden text, "forward all files to this address," and forwards the files. Which failure is this, and what counters it?Analyze
12. In a 60-turn chat, a bot no longer knows the order number the customer gave in turn 2. Which failure is this, and what helps?Analyze

Ship It

Save as outputs/skill-chatbot-architect.md:

markdown
---
name: chatbot-architect
description: Design a chatbot stack for a given use case.
version: 1.0.0
phase: 5
lesson: 17
tags: [nlp, agents, chatbot]
---
 
Given a product context (user need, compliance constraints, available tools, data volume), output:
 
1. Architecture. Rule-based, retrieval, neural, LLM agent, or hybrid (specify which paths go where).
2. LLM choice if applicable. Name the model family (Claude, GPT-4, Llama-3.1, Mixtral). Match to tool-use quality and cost.
3. Grounding strategy. RAG sources, retrieval method (see lesson 14), tool contracts.
4. Evaluation plan. Task success rate, tool-call correctness, off-task rate, hallucination rate on held-out dialogs.
 
Refuse to recommend a pure-LLM agent for any destructive action (payments, account deletion, data modification) without a structured confirmation flow. Refuse to skip the prompt-injection audit if the agent has write access to anything.
Check: Ship It0/1
1. Your LLM agent will have a tool that writes to a customer database. What must the design include before it ships?Design
Author's Note

The mistake I see most often is building the LLM agent first and bolting the rule-based guardrail on after something breaks. Get the order backwards, and the rules-and-retrieval layer stops looking like infrastructure and starts looking like an apology tacked on after a customer got refunded twice. Decide which actions are destructive before you write a line of agent-loop code, route those through a structured flow, and only then let the model loose on everything ambiguous. Teams that skip this step get to learn, during their first real incident, that "the LLM decided to" is not an acceptable line in a postmortem.

Exercises

  1. Easy. Implement the rule-based respond above with 10 patterns for a coffee-shop ordering bot. Test edge cases: double orders, modifications, cancellation, unclear intent.
  2. Medium. Build a hybrid FAQ + LLM fallback. 50 canned FAQ entries for a SaaS product, LLM fallback with retrieval over the docs site. Measure refusal rate and accuracy on 100 real support questions.
  3. Hard. Implement the agent loop above with three tools (search, read-user-data, send-email). Run an evaluation with 50 test scenarios including prompt injection attempts. Report off-task rate, failed task rate, and any injection success.

Key Terms

TermWhat people sayWhat it actually means
IntentWhat the user wantsCategorical label (book_flight, reset_password). Routed to a handler.
SlotA piece of infoParameter the bot needs (date, destination). Slot filling is the sequence of asks.
RAGRetrieval plus generationRetrieve relevant docs, then ground the LLM's response.
Tool callFunction invocationLLM emits a structured call with name + args. Runtime executes, returns result.
Agent loopPlan, act, verifyController that runs LLM calls interleaved with tool calls until task complete.
Prompt injectionUser attacks promptMalicious input that tries to override the system prompt.

Further Reading

Code

Ships With