A stateful, graph-based customer support agent built with LangGraph and LangChain that classifies queries, analyses sentiment, routes to specialist handlers (Technical, Billing, General), and auto-escalates negative-sentiment cases to a human agent.
LangGraph Support Agent is a production-ready, stateful AI agent that handles customer queries end-to-end using a directed graph workflow. Each incoming query passes through three sequential stages- categorisation, sentiment analysis, and smart routing before reaching the right specialist. Negative-sentiment queries are automatically escalated to a human agent, preventing frustrated customers from falling through the cracks.
The project is fully modular, cleanly tested with pytest, and designed to be extended - swap the LLM, add new query categories, or connect to a real ticketing system with changes confined to a single file.
- ๐ง Multi-stage intelligent workflow: Categorise โ Sentiment โ Route โ Handle, all in one stateful graph
- ๐จ Auto-escalation: negative-sentiment queries bypass handlers and go straight to a human agent
- ๐ Three specialist handlers: Technical, Billing, and General support, each with tailored prompts
- ๐ Graph visualisation: export the entire workflow as a PNG or view it live in Jupyter
- ๐งฉ Fully modular: add categories, swap LLMs, or connect APIs by editing one file
- โ
Unit tested:
pytestsuite covering all node functions and routing logic - ๐ Secure config: all credentials loaded from
.env, never hardcoded
graph TD
A[User Query] --> B[Categorize Query]
B --> C[Analyze Sentiment]
C --> D{Routing Decision}
D -->|Negative Sentiment| E[Escalate to Human]
D -->|Technical| F[Technical Support Handler]
D -->|Billing| G[Billing Support Handler]
D -->|General| H[General Support Handler]
E --> I[End]
F --> I
G --> I
H --> I
Each node is a pure function in agents/nodes.py that reads from and writes to the shared AgentState:
| Node | Role | LLM Used | Output to State |
|---|---|---|---|
categorise_query |
Classifies the query into Technical, Billing, or General | GPT (chat) | category |
analyse_sentiment |
Detects Positive, Neutral, or Negative sentiment | GPT (chat) | sentiment |
route_query |
Conditional edge - reads category + sentiment, returns next node name | (logic only) | (routing) |
handle_technical |
Generates a technical support response | GPT (chat) | response |
handle_billing |
Generates a billing support response | GPT (chat) | response |
handle_general |
Generates a general support response | GPT (chat) | response |
escalate_to_human |
Flags the case and generates an escalation message | GPT (chat) | response, escalated=True |
The route_query function applies this decision matrix:
| Sentiment | Category | โ Routed To |
|---|---|---|
| Negative | Any | ๐จ Escalate to Human |
| Positive / Neutral | Technical | ๐ง Technical Handler |
| Positive / Neutral | Billing | ๐ณ Billing Handler |
| Positive / Neutral | General | ๐ฌ General Handler |
Sentiment check always runs first a frustrated user with a technical issue gets a human, not a bot.
| Tool | Purpose |
|---|---|
LangGraph |
Stateful directed graph - defines the agent's nodes and edges |
LangChain |
LLM abstraction, prompt templates, chain orchestration |
OpenAI |
GPT models for all LLM nodes (swappable via config/settings.py) |
Pydantic |
State schema definition and validation |
python-dotenv |
Secure .env API key loading |
pytest |
Unit test suite for all node functions |
- Python 3.8+
- An OpenAI API key (get one here)
1. Clone the repository
git clone https://github.com/MusaIslamFahad/Customer_Support_Agent.git
cd Customer_Support_Agent2. Create and activate a virtual environment
python -m venv .venv
source .venv/bin/activate # macOS / Linux
.venv\Scripts\activate # Windows3. Install dependencies
pip install -r requirements.txt4. Configure your API key
cp .env.example .envOpen .env and add your key:
OPENAI_API_KEY=your_openai_api_key_here
โ ๏ธ Security:.envis already in.gitignoreโ never commit your API key.
5. Run the agent
python main.pymain.py ships with sample queries that demonstrate each routing path:
# Technical query โ routed to Technical Handler
"My application keeps crashing when I try to upload files larger than 10MB."
# Billing query with negative sentiment โ escalated to Human Agent
"This is outrageous! You charged me twice this month and nobody is responding!"
# General query โ routed to General Handler
"What are your business hours and do you offer weekend support?"Sample output:
============================================================
Query: My application keeps crashing when I try to upload files...
------------------------------------------------------------
Category : Technical
Sentiment : Neutral
Routed to : Technical Handler
------------------------------------------------------------
Response : Thank you for reaching out. This sounds like it may be
related to file size limits on our upload service...
============================================================
Export the full workflow as a PNG:
from utils import save_graph
save_graph("workflow.png")View it live in Jupyter:
from utils import show_graph
show_graph()pytest tests/The test suite in tests/test_nodes.py covers:
- Query categorisation for all three categories
- Sentiment detection (Positive / Neutral / Negative)
- Routing decisions - including the negative-sentiment escalation path
- Individual handler node outputs
langgraph-support-agent/
โ
โโโ main.py # Entry point โ runs sample queries end-to-end
โโโ requirements.txt # Python dependencies
โโโ .env.example # Template โ copy to .env and add your API key
โโโ .env # Your API key โ DO NOT commit (gitignored)
โโโ .gitignore
โ
โโโ config/
โ โโโ __init__.py
โ โโโ settings.py # Loads .env, exposes MODEL_NAME and other constants
โ
โโโ agents/
โ โโโ __init__.py
โ โโโ state.py # AgentState TypedDict โ shared state across all nodes
โ โโโ nodes.py # All node functions + route_query conditional logic
โ โโโ support_agent.py # Graph assembly โ compiles and runs the LangGraph workflow
โ
โโโ utils/
โ โโโ __init__.py
โ โโโ visualize.py # show_graph() and save_graph() helpers
โ
โโโ tests/
โ โโโ __init__.py
โ โโโ test_nodes.py # pytest unit tests for nodes and routing
โ
โโโ screenshots/ # CLI output screenshots for README
The modular design means every extension is confined to one file:
| Goal | Where to Change |
|---|---|
| Add a new category (e.g. Returns & Refunds) | agents/nodes.py โ new handle_returns function + update route_query |
| Swap the LLM (e.g. Claude, Gemini) | config/settings.py โ change MODEL_NAME and update the LLM initialisation |
| Add conversation memory | agents/state.py โ add a history: list field; update nodes to append to it |
| Connect to a real ticketing system | agents/nodes.py โ call your ticket API inside the handler nodes |
| Add a new sentiment tier (e.g. Very Positive) | agents/nodes.py โ update sentiment prompt + extend route_query logic |
| Deploy as an API | Wrap run_customer_support() in a FastAPI endpoint |
- ๐ FastAPI endpoint: expose the agent as a REST API for integration with any frontend
- ๐ฌ Multi-turn conversations: add conversation history to state for context-aware follow-ups
- ๐ Ticket system integration: auto-create Jira / Zendesk tickets for escalated cases
- ๐ Analytics dashboard: log query category, sentiment, and routing per session
- ๐ Feedback loop: let users rate responses; feed ratings back to improve routing prompts
- ๐ Multi-language support: add a language detection node before categorisation
Contributions are welcome!
- Fork the repository
- Create a feature branch (
git checkout -b feature/your-feature) - Commit your changes (
git commit -m 'Add your feature') - Push to the branch (
git push origin feature/your-feature) - Open a Pull Request
MIT - free to use, adapt, and share with attribution. See LICENSE for details.
- LangGraph - for the stateful graph execution engine
- LangChain - for LLM abstraction and prompt orchestration
- OpenAI - for the GPT models powering all agent nodes
Musa Islam Fahad
- GitHub: @MusaIslamFahad
โญ If you found this useful for building your own agents, a star goes a long way. Tthank you!