← Back to DevBytes

Building a Customer Support Bot with LangChain: Complete Guide

What is a Customer Support Bot with LangChain?

A customer support bot built with LangChain is an intelligent conversational agent that leverages large language models (LLMs) to understand, process, and resolve customer inquiries. LangChain provides the orchestration layer that connects the LLM to your company's knowledge base, APIs, databases, and business logic, creating a support system that goes far beyond simple FAQ matching.

Unlike traditional rule-based chatbots that follow rigid decision trees, a LangChain-powered bot uses chain-of-thought reasoning, semantic search over documents, and tool-calling capabilities to handle complex, multi-step customer issues. It can look up order statuses, initiate returns, check inventory, and escalate to human agents—all while maintaining context across the conversation.

Core Architecture Overview

At its heart, a LangChain customer support bot consists of several interconnected components:

Why Building a LangChain Support Bot Matters

Customer support is undergoing a fundamental transformation. Organizations that deploy intelligent support bots see measurable impact across multiple dimensions:

Business Impact

Technical Advantages of LangChain Specifically

Project Setup and Dependencies

Let's start by setting up a complete project environment. Create a new directory and initialize your Python project:

# Create project directory
mkdir customer-support-bot
cd customer-support-bot

# Create virtual environment
python -m venv venv
source venv/bin/activate  # On Windows: venv\Scripts\activate

# Install core dependencies
pip install langchain langchain-openai langchain-community
pip install chromadb tiktoken sentence-transformers
pip install python-dotenv fastapi uvicorn pydantic
pip install langsmith  # For observability and tracing

Create a .env file for your API keys and configuration:

# .env file
OPENAI_API_KEY=your-openai-api-key-here
LANGCHAIN_TRACING_V2=true
LANGCHAIN_API_KEY=your-langsmith-key-here
LANGCHAIN_PROJECT=customer-support-bot

# Optional: for production, add other provider keys
ANTHROPIC_API_KEY=your-anthropic-key
COHERE_API_KEY=your-cohere-key

Building the Knowledge Base with Document Loading and Chunking

The foundation of any effective support bot is a rich knowledge base. You'll load your support documentation, product manuals, FAQ pages, and historical ticket resolutions, then index them for semantic search.

Loading Documents from Multiple Sources

from langchain_community.document_loaders import (
    TextLoader,
    CSVLoader,
    PyPDFLoader,
    WebBaseLoader,
    UnstructuredMarkdownLoader,
)
from langchain.text_splitter import RecursiveCharacterTextSplitter
from pathlib import Path

# Load documents from various sources
documents = []

# 1. Load FAQ markdown files
faq_directory = Path("./knowledge_base/faqs")
for md_file in faq_directory.glob("*.md"):
    loader = UnstructuredMarkdownLoader(str(md_file))
    documents.extend(loader.load())

# 2. Load product documentation PDFs
pdf_directory = Path("./knowledge_base/product_docs")
for pdf_file in pdf_directory.glob("*.pdf"):
    loader = PyPDFLoader(str(pdf_file))
    documents.extend(loader.load())

# 3. Load support ticket history CSV
ticket_loader = CSVLoader(
    file_path="./knowledge_base/resolved_tickets.csv",
    source_column="ticket_description",
)
documents.extend(ticket_loader.load())

# 4. Load policy pages from company website
web_loader = WebBaseLoader([
    "https://docs.yourcompany.com/returns",
    "https://docs.yourcompany.com/shipping",
    "https://docs.yourcompany.com/warranty",
])
documents.extend(web_loader.load())

print(f"Loaded {len(documents)} documents")

Strategic Chunking for Support Content

Chunking strategy dramatically impacts retrieval quality. For support content, you need chunks that preserve complete problem-solution pairs:

from langchain.text_splitter import (
    RecursiveCharacterTextSplitter,
    MarkdownHeaderTextSplitter,
)

# Primary splitter: respect markdown structure
markdown_splitter = MarkdownHeaderTextSplitter(
    headers_to_split_on=[
        ("#", "h1_title"),
        ("##", "h2_section"),
        ("###", "h3_topic"),
    ],
    strip_headers=False,
)

# Secondary splitter for oversized sections
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=800,          # Larger chunks to preserve context
    chunk_overlap=150,       # Overlap prevents mid-concept splits
    separators=["\n\n", "\n", ". ", "? ", "! ", " ", ""],
    length_function=len,
    add_start_index=True,
)

# Process documents
split_docs = []
for doc in documents:
    # First, split by markdown headers
    md_splits = markdown_splitter.split_text(doc.page_content)
    for split in md_splits:
        # Then split oversized sections
        if len(split.page_content) > 1000:
            sub_splits = text_splitter.split_documents([split])
            split_docs.extend(sub_splits)
        else:
            split_docs.append(split)

print(f"Created {len(split_docs)} chunks after splitting")

Creating the Vector Store with Embeddings

Now embed your chunks into a vector database for fast semantic retrieval:

from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
import chromadb

# Initialize embeddings model
embeddings = OpenAIEmbeddings(
    model="text-embedding-3-small",  # Cost-effective, high quality
    dimensions=1536,
)

# Set up persistent Chroma client
chroma_client = chromadb.PersistentClient(
    path="./vector_store/customer_support_db"
)

# Create collection with metadata filtering capability
collection = chroma_client.get_or_create_collection(
    name="support_knowledge",
    metadata={"hnsw:space": "cosine"},
)

# Create vector store
vector_store = Chroma(
    client=chroma_client,
    collection_name="support_knowledge",
    embedding_function=embeddings,
)

# Batch insert with metadata for filtering
batch_size = 100
for i in range(0, len(split_docs), batch_size):
    batch = split_docs[i:i + batch_size]
    vector_store.add_documents(
        documents=batch,
        ids=[f"doc_{j}" for j in range(i, i + len(batch))],
    )
    print(f"Inserted batch {i//batch_size + 1}, total: {i+len(batch)}")

print(f"Vector store ready with {vector_store._collection.count()} embeddings")

Building the Core Retrieval Chain

With the vector store populated, build a retrieval-augmented generation (RAG) chain that forms the backbone of your support bot:

from langchain_openai import ChatOpenAI
from langchain.chains import create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.retrievers import ContextualCompressionRetriever
from langchain.retrievers.document_compressors import LLMChainFilter

# Initialize the LLM
llm = ChatOpenAI(
    model="gpt-4o",
    temperature=0.3,        # Lower temperature for factual consistency
    max_tokens=1000,
)

# Create a sophisticated retriever with relevance filtering
base_retriever = vector_store.as_retriever(
    search_type="similarity",
    search_kwargs={
        "k": 8,              # Retrieve 8 candidates
        "filter": None,      # Add metadata filters if needed
    },
)

# Add contextual compression to filter irrelevant chunks
compressor = LLMChainFilter.from_llm(llm)
compressed_retriever = ContextualCompressionRetriever(
    base_compressor=compressor,
    base_retriever=base_retriever,
)

# Define the support-specific prompt template
support_system_prompt = """You are a helpful customer support agent for OurCompany.
Use the following retrieved context to answer the customer's question.
Follow these rules strictly:

1. If the context contains the answer, provide it clearly and concisely.
2. If the context is partial, acknowledge gaps and offer to escalate.
3. If the context is irrelevant, say: "I don't have that information in my knowledge base.
   Let me connect you with a human agent."
4. Always maintain a friendly, empathetic tone.
5. Never fabricate policies, prices, or procedures.
6. When quoting policies, cite the source document section.

Retrieved context:
{context}

Customer query: {input}
"""

rag_prompt = ChatPromptTemplate.from_messages([
    ("system", support_system_prompt),
    ("human", "{input}"),
])

# Build the document combination chain
combine_docs_chain = create_stuff_documents_chain(
    llm=llm,
    prompt=rag_prompt,
)

# Assemble the full retrieval chain
retrieval_chain = create_retrieval_chain(
    retriever=compressed_retriever,
    combine_docs_chain=combine_docs_chain,
)

# Test the chain
response = retrieval_chain.invoke({
    "input": "What is your return policy for electronics purchased online?"
})
print(response["answer"])

Adding Conversation Memory and Session Management

Customer support conversations often span multiple exchanges. You need persistent memory that tracks the full dialogue history and any actions taken:

from langchain.memory import ConversationSummaryBufferMemory
from langchain_core.runnables import RunnableLambda, RunnablePassthrough
from langchain_core.messages import HumanMessage, AIMessage
from datetime import datetime, timedelta
import json

class CustomerSessionManager:
    """Manages per-customer conversation sessions with TTL-based expiry."""
    
    def __init__(self, llm, vector_store, ttl_minutes=30):
        self.llm = llm
        self.vector_store = vector_store
        self.ttl_minutes = ttl_minutes
        self.sessions = {}  # In production, use Redis or a database
        
    def get_or_create_session(self, session_id: str, customer_info: dict = None):
        """Retrieve existing session or create a new one."""
        now = datetime.now()
        
        if session_id in self.sessions:
            session = self.sessions[session_id]
            # Check if session has expired
            if (now - session["last_accessed"]) < timedelta(minutes=self.ttl_minutes):
                session["last_accessed"] = now
                return session
            else:
                # Session expired, create new one
                del self.sessions[session_id]
        
        # Create fresh session
        memory = ConversationSummaryBufferMemory(
            llm=self.llm,
            max_token_limit=2000,     # Summarize when history exceeds this
            return_messages=True,
            memory_key="chat_history",
        )
        
        session = {
            "session_id": session_id,
            "memory": memory,
            "created_at": now,
            "last_accessed": now,
            "customer_info": customer_info or {},
            "escalation_count": 0,
            "resolved_issues": [],
        }
        self.sessions[session_id] = session
        return session
    
    def add_escalation(self, session_id: str):
        """Track escalations to detect problematic sessions."""
        if session_id in self.sessions:
            self.sessions[session_id]["escalation_count"] += 1
            
    def mark_resolved(self, session_id: str, issue: str):
        """Record resolved issues for analytics."""
        if session_id in self.sessions:
            self.sessions[session_id]["resolved_issues"].append({
                "issue": issue,
                "resolved_at": datetime.now().isoformat(),
            })

# Initialize session manager
session_manager = CustomerSessionManager(llm, vector_store)

Memory-Integrated Chat Chain

from langchain_core.runnables import RunnableLambda
from langchain_core.output_parsers import StrOutputParser

def build_memory_aware_chain(retrieval_chain, session_manager):
    """Wrap the retrieval chain with memory injection."""
    
    def inject_memory(inputs: dict):
        session_id = inputs.get("session_id", "default")
        session = session_manager.get_or_create_session(session_id)
        
        # Load conversation history
        memory = session["memory"]
        history = memory.load_memory_variables({})
        chat_history = history.get("chat_history", [])
        
        # Format history for the prompt
        history_text = "\n".join([
            f"{'Customer' if isinstance(m, HumanMessage) else 'Agent'}: {m.content}"
            for m in chat_history[-10:]  # Last 10 exchanges
        ])
        
        # Augment the input with history context
        augmented_input = f"""Previous conversation:
{history_text}

Current customer message: {inputs['input']}"""
        
        return {"input": augmented_input, "session_id": session_id}
    
    def store_interaction(outputs: dict):
        session_id = outputs.get("session_id", "default")
        session = session_manager.get_or_create_session(session_id)
        memory = session["memory"]
        
        # Store the exchange in memory
        memory.save_context(
            {"input": outputs.get("original_input", "")},
            {"output": outputs.get("answer", "")}
        )
        return outputs
    
    # Build the full chain
    full_chain = (
        RunnableLambda(inject_memory)
        | retrieval_chain
        | RunnableLambda(lambda x: {
            **x,
            "original_input": x.get("input", ""),
            "session_id": x.get("session_id", "default"),
        })
        | RunnableLambda(store_interaction)
    )
    
    return full_chain

# Create the memory-aware chain
memory_chain = build_memory_aware_chain(retrieval_chain, session_manager)

# Test conversation continuity
response1 = memory_chain.invoke({
    "input": "I need to return a laptop I bought last week.",
    "session_id": "customer_42",
})
print("Agent:", response1["answer"])

response2 = memory_chain.invoke({
    "input": "Yes, I have the order number. It's #ORD-98765.",
    "session_id": "customer_42",
})
print("Agent:", response2["answer"])

Building the Tool Framework for Action Execution

The true power of a LangChain support bot comes from its ability to take action—looking up orders, processing returns, checking inventory. Here's how to build a comprehensive tool suite:

from langchain.tools import Tool, tool, StructuredTool
from langchain_core.tools import ToolException
from pydantic import BaseModel, Field
from typing import Optional, List
import json
import httpx

# ---- Tool Input Schemas ----

class OrderLookupInput(BaseModel):
    order_id: str = Field(description="The order ID (e.g., ORD-12345)")
    customer_email: Optional[str] = Field(
        default=None,
        description="Customer email for verification"
    )

class ReturnInitiateInput(BaseModel):
    order_id: str = Field(description="Order ID for the return")
    item_sku: str = Field(description="SKU of the item to return")
    reason: str = Field(description="Return reason code: DEFECTIVE, WRONG_ITEM, CHANGED_MIND, DAMAGED")
    customer_notes: Optional[str] = Field(default="", description="Additional customer notes")

class InventoryCheckInput(BaseModel):
    sku: str = Field(description="Product SKU to check")
    warehouse_id: Optional[str] = Field(default="MAIN", description="Warehouse identifier")

# ---- Tool Implementations ----

@tool("order_lookup", args_schema=OrderLookupInput)
def lookup_order(order_id: str, customer_email: str = None) -> str:
    """
    Look up order details including status, items, and shipping information.
    Use this when a customer asks about their order status or details.
    """
    # In production, this calls your order management system API
    # Simulated response for demonstration
    orders_db = {
        "ORD-98765": {
            "status": "DELIVERED",
            "order_date": "2024-12-15",
            "delivery_date": "2024-12-22",
            "items": [
                {"sku": "LAPTOP-XPS-15", "name": "XPS 15 Laptop", "price": 1299.99},
                {"sku": "CASE-LEATHER", "name": "Leather Laptop Case", "price": 49.99},
            ],
            "total": 1349.98,
            "eligible_return": True,
            "return_deadline": "2025-01-22",
        },
        "ORD-12345": {
            "status": "SHIPPED",
            "order_date": "2024-12-18",
            "estimated_delivery": "2024-12-26",
            "items": [
                {"sku": "HEADPHONES-PRO", "name": "Pro Headphones", "price": 249.99},
            ],
            "total": 249.99,
            "eligible_return": False,
        },
    }
    
    order = orders_db.get(order_id)
    if not order:
        return f"Order {order_id} not found. Please verify the order ID and try again."
    
    return json.dumps(order, indent=2)

@tool("initiate_return", args_schema=ReturnInitiateInput)
def initiate_return(order_id: str, item_sku: str, reason: str, customer_notes: str = "") -> str:
    """
    Initiate a return for an item in an order.
    Use this when a customer wants to return a product.
    Requires the order ID, item SKU, and a valid reason code.
    """
    valid_reasons = ["DEFECTIVE", "WRONG_ITEM", "CHANGED_MIND", "DAMAGED"]
    if reason.upper() not in valid_reasons:
        return f"Invalid reason code. Must be one of: {', '.join(valid_reasons)}"
    
    # In production, this calls your returns management API
    # Generate return authorization
    import random
    rma_number = f"RMA-{random.randint(100000, 999999)}"
    
    return json.dumps({
        "rma_number": rma_number,
        "status": "INITIATED",
        "order_id": order_id,
        "item_sku": item_sku,
        "reason": reason,
        "next_steps": [
            f"Print return label for RMA {rma_number}",
            "Package item securely with original accessories",
            "Drop off at nearest carrier location within 14 days",
        ],
        "refund_timeline": "Refund processed within 5-7 business days after carrier scan",
    }, indent=2)

@tool("check_inventory", args_schema=InventoryCheckInput)
def check_inventory(sku: str, warehouse_id: str = "MAIN") -> str:
    """
    Check current inventory levels for a product SKU.
    Use this when customers ask about product availability or stock status.
    """
    # Simulated inventory database
    inventory_db = {
        "LAPTOP-XPS-15": {
            "warehouses": {
                "MAIN": 42,
                "EAST": 15,
                "WEST": 8,
            },
            "status": "IN_STOCK",
            "next_restock": None,
        },
        "HEADPHONES-PRO": {
            "warehouses": {
                "MAIN": 0,
                "EAST": 3,
                "WEST": 0,
            },
            "status": "LOW_STOCK",
            "next_restock": "2025-01-10",
        },
    }
    
    item = inventory_db.get(sku)
    if not item:
        return f"SKU {sku} not found in inventory system."
    
    wh_qty = item["warehouses"].get(warehouse_id, 0)
    return json.dumps({
        "sku": sku,
        "status": item["status"],
        "warehouse_quantity": wh_qty,
        "total_available": sum(item["warehouses"].values()),
        "next_restock": item.get("next_restock"),
    }, indent=2)

@tool("escalate_to_human", return_direct=True)
def escalate_to_human(summary: str) -> str:
    """
    Escalate the conversation to a human agent.
    Use this when the bot cannot resolve the issue, the customer explicitly
    requests a human, or the issue requires manual intervention.
    Provide a concise summary of the conversation.
    """
    return json.dumps({
        "action": "ESCALATE",
        "status": "TRANSFERRING",
        "summary": summary,
        "estimated_wait": "2-5 minutes",
        "ticket_id": f"TKT-{hash(summary) % 1000000:06d}",
    }, indent=2)

# Assemble the tool collection
support_tools = [
    lookup_order,
    initiate_return,
    check_inventory,
    escalate_to_human,
]

print(f"Registered {len(support_tools)} support tools")

Building the Agent with Tool-Calling and Routing

Now assemble a full agent that can reason about when to use tools, retrieve knowledge, or escalate:

from langchain.agents import AgentExecutor, create_openai_tools_agent
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.callbacks import StdOutCallbackHandler

# Define the agent's system prompt with routing logic
agent_system_prompt = """You are Aurora, a senior customer support agent for OurCompany.
You have access to knowledge base articles and several tools.

Your workflow:
1. Greet the customer warmly and understand their issue.
2. For order-related questions, use the order_lookup tool.
3. For return requests, first verify the order with order_lookup, then use initiate_return.
4. For product availability, use check_inventory.
5. For general policy questions, rely on your knowledge base context.
6. If you cannot resolve the issue after 2 attempts, or if the customer asks for a human,
   use escalate_to_human with a detailed summary.

IMPORTANT RULES:
- Always verify order eligibility before initiating returns.
- Never promise refund amounts—defer to the returns system.
- If a tool fails, try an alternative approach once before escalating.
- Be empathetic: acknowledge frustration, apologize for issues.
- Protect customer data: never expose raw system responses with PII.
"""

# Create the agent prompt
agent_prompt = ChatPromptTemplate.from_messages([
    ("system", agent_system_prompt),
    ("system", "You have access to the following context from the knowledge base:\n{context}"),
    MessagesPlaceholder(variable_name="chat_history", optional=True),
    ("human", "{input}"),
    MessagesPlaceholder(variable_name="agent_scratchpad"),
])

# Initialize the LLM with tool-calling capabilities
agent_llm = ChatOpenAI(
    model="gpt-4o",
    temperature=0.2,
    max_tokens=2000,
)

# Create the OpenAI tools agent
agent = create_openai_tools_agent(
    llm=agent_llm,
    tools=support_tools,
    prompt=agent_prompt,
)

# Build the agent executor
agent_executor = AgentExecutor(
    agent=agent,
    tools=support_tools,
    verbose=True,
    max_iterations=6,          # Prevent infinite loops
    max_execution_time=30,     # 30-second timeout
    handle_parsing_errors=True,
    return_intermediate_steps=False,
)

print("Agent executor ready for operation")

Integrating Retrieval with the Agent

def build_full_support_agent(agent_executor, retrieval_chain, session_manager):
    """
    Combine the agent with retrieval and memory for a complete support system.
    """
    def retrieve_and_agent(inputs: dict):
        session_id = inputs.get("session_id", "default")
        customer_query = inputs["input"]
        
        # First, retrieve relevant knowledge
        retrieval_result = retrieval_chain.invoke({"input": customer_query})
        context = retrieval_result.get("answer", "")
        
        # Get session memory
        session = session_manager.get_or_create_session(session_id)
        memory = session["memory"]
        history = memory.load_memory_variables({})
        chat_history = history.get("chat_history", [])
        
        # Run the agent with context and history
        result = agent_executor.invoke({
            "input": customer_query,
            "context": context,
            "chat_history": chat_history,
        })
        
        # Store the interaction
        memory.save_context(
            {"input": customer_query},
            {"output": result.get("output", "")}
        )
        
        # Check for escalation and update session
        if "ESCALATE" in result.get("output", ""):
            session_manager.add_escalation(session_id)
        
        return result
    
    return retrieve_and_agent

# Build the complete agent
full_support_agent = build_full_support_agent(
    agent_executor,
    retrieval_chain,
    session_manager,
)

# Test with a complex scenario
test_result = full_support_agent({
    "input": "Hi, I received my order ORD-98765 yesterday but the laptop has a dead pixel cluster. "
             "I want to return just the laptop and get a replacement. What are my options?",
    "session_id": "customer_42",
})
print("\nFinal Response:", test_result.get("output", "No output"))

Implementing Intent Classification and Intelligent Routing

Before the agent engages, classify the customer's intent to route them efficiently and apply the right business logic:

from langchain_core.prompts import PromptTemplate
from langchain_core.output_parsers import PydanticOutputParser
from pydantic import BaseModel, Field
from enum import Enum
from typing import Optional

class IntentCategory(str, Enum):
    ORDER_STATUS = "order_status"
    RETURN_REQUEST = "return_request"
    PRODUCT_INQUIRY = "product_inquiry"
    TECHNICAL_SUPPORT = "technical_support"
    BILLING_ISSUE = "billing_issue"
    POLICY_QUESTION = "policy_question"
    COMPLAINT = "complaint"
    GENERAL = "general"

class IntentClassification(BaseModel):
    primary_intent: IntentCategory = Field(description="Primary customer intent")
    confidence: float = Field(description="Confidence score 0-1", ge=0, le=1)
    urgency: str = Field(description="LOW, MEDIUM, or HIGH")
    requires_escalation: bool = Field(description="True if this likely needs a human")
    summary: str = Field(description="One-line summary of the customer's issue")

# Create the intent classifier
intent_parser = PydanticOutputParser(pydantic_object=IntentClassification)

intent_prompt = PromptTemplate(
    template="""Analyze this customer message and classify the intent.

Customer message: {message}

{format_instructions}

Classify carefully. If the customer is angry or mentions legal action, mark as HIGH urgency.""",
    input_variables=["message"],
    partial_variables={
        "format_instructions": intent_parser.get_format_instructions(),
    },
)

intent_chain = intent_prompt | llm | intent_parser

# Test classification
test_messages = [
    "Where is my order? I placed it a week ago and haven't heard anything.",
    "The laptop you sold me is defective. The screen flickers constantly. I want my money back NOW.",
    "What's the battery life of the XPS 15 model?",
]

for msg in test_messages:
    result = intent_chain.invoke({"message": msg})
    print(f"Message: '{msg[:60]}...'")
    print(f"  Intent: {result.primary_intent}, Urgency: {result.urgency}")
    print(f"  Escalate: {result.requires_escalation}\n")

Router Implementation

from typing import Dict, Callable

class SupportRouter:
    """Routes customer queries based on intent classification."""
    
    def __init__(self, agent_executor, retrieval_chain, session_manager):
        self.agent_executor = agent_executor
        self.retrieval_chain = retrieval_chain
        self.session_manager = session_manager
        
        # Define routing rules
        self.routing_map: Dict[IntentCategory, Callable] = {
            IntentCategory.ORDER_STATUS: self.handle_order_status,
            IntentCategory.RETURN_REQUEST: self.handle_return_request,
            IntentCategory.PRODUCT_INQUIRY: self.handle_product_inquiry,
            IntentCategory.TECHNICAL_SUPPORT: self.handle_technical_support,
            IntentCategory.BILLING_ISSUE: self.handle_billing_issue,
            IntentCategory.POLICY_QUESTION: self.handle_policy_question,
            IntentCategory.COMPLAINT: self.handle_complaint,
            IntentCategory.GENERAL: self.handle_general,
        }
    
    def route(self, message: str, session_id: str, customer_info: dict = None):
        """Classify intent and route to appropriate handler."""
        # Classify the intent
        classification = intent_chain.invoke({"message": message})
        
        # Immediate escalation for high-urgency complaints
        if classification.urgency == "HIGH" and classification.primary_intent == IntentCategory.COMPLAINT:
            return self.immediate_escalation(message, classification, session_id)
        
        # Route to handler
        handler = self.routing_map.get(
            classification.primary_intent,
            self.handle_general,
        )
        
        return handler(message, session_id, classification, customer_info)
    
    def handle_order_status(self, message, session_id, classification, customer_info):
        """Order status queries get priority tool access."""
        return self.agent_executor.invoke({
            "input": message,
            "context": f"Customer wants order status information.",
            "chat_history": self._get_history(session_id),
        })
    
    def handle_return_request(self, message, session_id, classification, customer_info):
        """Return requests require verification steps."""
        session = self.session_manager.get_or_create_session(session_id)
        context = self.retrieval_chain.invoke({"input": "return policy requirements"})
        
        return self.agent_executor.invoke({
            "input": f"{message}\n\nIMPORTANT: Verify order eligibility before processing return. "
                     f"Follow return policy from context.",
            "context": context.get("answer", ""),
            "chat_history": self._get_history(session_id),
        })
    
    def handle_complaint(self, message, session_id, classification, customer_info):
        """Complaints get empathetic handling with escalation readiness."""
        return self.agent_executor.invoke({
            "input": f"{message}\n\nThis customer may be frustrated. Be extra empathetic. "
                     f"If resolution isn't immediate, escalate.",
            "context": "Handle complaint with priority.",
            "chat_history": self._get_history(session_id),
        })
    
    def handle_policy_question(self, message, session_id, classification, customer_info):
        """Policy questions rely heavily on the knowledge base."""
        context = self.retrieval_chain.invoke({"input": message})
        return self.agent_executor.invoke({
            "input": message,
            "context": context.get("answer", ""),
            "chat_history": self._get_history(session_id),
        })
    
    def handle_product_inquiry(self, message, session_id, classification, customer_info):
        return self.agent_executor.invoke({
            "input": message,
            "context": "",
            "chat_history": self._get_history(session_id),
        })
    
    def handle_technical_support(self, message, session_id, classification, customer_info):
        context = self.retrieval_chain.invoke({"input": message})
        return self.agent_executor.invoke({
            "input": message,
            "context": context.get("answer", ""),
            "chat_history": self._get_history(session_id),
        })
    
    def handle_billing_issue(self, message, session_id, classification, customer_info):
        """Billing issues often need human intervention."""
        return self.agent_executor.invoke({
            "input": f"{message}\n\nIf billing details are involved, recommend escalation "
                     f"for security verification.",
            "context": "",
            "chat_history": self._get_history(session_id),
        })
    
    def handle_general(self, message, session_id, classification, customer_info):
        return self.agent_executor.invoke({
            "input": message,
            "context": "",
            "chat_history": self._get_history(session_id),
        })
    
    def immediate_escalation(self, message, classification, session_id):
        """Bypass agent for urgent complaints."""
        return {
            "output

— Ad —

Google AdSense will appear here after approval

← Back to all articles