A multi-turn conversational system with dynamic routing between specialized agents for technical support and billing inquiries.
This system provides intelligent routing between two specialized agents:
- Tech Agent: Answers technical questions using documentation snippets. Always cites sources and asks for clarification when documentation doesn't cover the question.
- Billing Agent: Handles billing-related inquiries using tool-calling with three tools:
- Open refund cases
- Get plan information
- Estimate refund timelines
The system maintains conversation history and uses LLM-based routing to classify incoming messages into TECH, BILLING, or OUT_OF_SCOPE categories.
- Dynamic Routing: LLM-based classification of user messages
- Multi-turn Conversations: Maintains conversation history by conversationId
- Retrieval Pipeline: Document loading, chunking, and embedding-based retrieval (RAG)
- Tool Calling: Billing agent uses OpenAI tool calling for structured operations
- Citation Support: Tech agent cites documentation sources in [docId:sectionTitle] format
- In-memory Storage: Fast, ephemeral storage for conversations and billing data
- Java 17 or higher
- Maven 3.6+
- Docker (optional, for containerized deployment)
- OpenAI API key
Set the following environment variables:
export OPENAI_API_KEY=your_openai_api_key_here
export OPENAI_MODEL=gpt-4o-mini # Optional, defaults to gpt-4o-mini
export OPENAI_EMBEDDING_MODEL=text-embedding-3-small # Optional, defaults to text-embedding-3-small./mvnw spring-boot:runmvn spring-boot:runmvn clean package
java -jar target/multi-agent-support-ai-1.0.0.jarThe application will start on http://localhost:8080.
docker build -t multi-agent-support-ai .docker run -p 8080:8080 \
-e OPENAI_API_KEY=your_openai_api_key_here \
-e OPENAI_MODEL=gpt-4o-mini \
multi-agent-support-aiThe application will be available at http://localhost:8080.
Sends a message to the multi-agent system.
Request:
{
"conversationId": "string",
"message": "string"
}Response:
{
"conversationId": "string",
"agent": "TECH|BILLING|OUT_OF_SCOPE",
"response": "string",
"citations": ["docId:sectionTitle", ...],
"toolUsed": "string|null",
"caseId": "string|null",
"meta": {
"snippetsFound": 4,
"needsClarification": false
}
}Initial Question:
curl -X POST http://localhost:8080/chat \
-H "Content-Type: application/json" \
-d '{
"conversationId": "conv-001",
"message": "How do I authenticate API requests?"
}'Response:
{
"conversationId": "conv-001",
"agent": "TECH",
"response": "All API requests require authentication using an API key. Include your API key in the Authorization header as: Authorization: Bearer YOUR_API_KEY. You can generate an API key from Settings > API Keys in your account dashboard.",
"citations": ["authentication:API Keys"],
"toolUsed": null,
"meta": {
"snippetsFound": 3,
"needsClarification": false
}
}Follow-up Question:
curl -X POST http://localhost:8080/chat \
-H "Content-Type: application/json" \
-d '{
"conversationId": "conv-001",
"message": "What about webhooks? How do I set them up?"
}'Response:
{
"conversationId": "conv-001",
"agent": "TECH",
"response": "To set up webhooks, log in to your account dashboard, navigate to Settings > Webhooks, add a new webhook URL, select the events you want to receive, and save your webhook secret for signature verification. All webhook requests are authenticated using a signature in the X-Webhook-Signature header.",
"citations": ["api_webhooks:Webhook Configuration", "api_webhooks:Authentication"],
"toolUsed": null,
"meta": {
"snippetsFound": 4,
"needsClarification": false
}
}Initial Request:
curl -X POST http://localhost:8080/chat \
-H "Content-Type: application/json" \
-d '{
"conversationId": "conv-002",
"message": "I want a refund"
}'Response:
{
"conversationId": "conv-002",
"agent": "BILLING",
"response": "I'd be happy to help you with a refund. To process your refund, I'll need some information. Could you please provide:\n1. Your email address\n2. Your order ID\n3. The reason for the refund request",
"toolUsed": null,
"meta": {}
}Providing Information:
curl -X POST http://localhost:8080/chat \
-H "Content-Type: application/json" \
-d '{
"conversationId": "conv-002",
"message": "My email is user1@example.com, order ID is ORD-12345, and I want to cancel my subscription"
}'Response:
{
"conversationId": "conv-002",
"agent": "BILLING",
"response": "I've opened a refund case for you. Your case ID is REF-1000. Please complete the refund form using this link: https://example.com/refund-form/uuid-here. Our team will review and process your request within 2-3 business days.",
"toolUsed": "openRefundCase",
"caseId": "REF-1000",
"meta": {
"caseId": "REF-1000",
"formLink": "https://example.com/refund-form/uuid-here",
"status": "OPEN"
}
}Checking Plan Info:
curl -X POST http://localhost:8080/chat \
-H "Content-Type: application/json" \
-d '{
"conversationId": "conv-002",
"message": "What is my current plan?"
}'Response:
{
"conversationId": "conv-002",
"agent": "BILLING",
"response": "Your current plan is Premium, priced at $29.99/month. Your next renewal date is 2024-02-15.",
"toolUsed": "getPlanInfo",
"meta": {
"email": "user1@example.com",
"planName": "Premium",
"price": 29.99,
"renewalDate": "2024-02-15"
}
}curl -X POST http://localhost:8080/chat \
-H "Content-Type: application/json" \
-d '{
"conversationId": "conv-003",
"message": "What is the weather today?"
}'Response:
{
"conversationId": "conv-003",
"agent": "OUT_OF_SCOPE",
"response": "I apologize, but I'm specifically trained to help with technical questions and billing inquiries. Could you please rephrase your question, or let me know if you have a technical or billing-related question I can help with?",
"citations": null,
"toolUsed": null
}curl -X POST http://localhost:8080/chat \
-H "Content-Type: application/json" \
-d '{
"conversationId": "conv-004",
"message": "How do I authenticate API requests?"
}'Наступне повідомлення (та сама conversationId) вже про оплату/рефанд:
curl -X POST http://localhost:8080/chat \
-H "Content-Type: application/json" \
-d '{
"conversationId": "conv-004",
"message": "Also, I think I was charged twice this month. I want a refund."
}'Очікувана поведінка:
- перший turn →
agent: "TECH" - другий turn →
agent: "BILLING"(BillingAgent попроситьemail,orderId,reasonі далі може відкрити refund кейс через tool-calling)
The system uses documentation files in the ./docs directory:
api_webhooks.md- Webhook setup and configurationauthentication.md- API authentication methodsintegration_guide.md- Integration patterns and best practicestroubleshooting.md- Common setup and error fixesbilling_policy.md- Refund policies and processing times (used by BillingTools only)
The Tech Agent retrieves relevant snippets from these documents to answer questions (billing docs are excluded from TechAgent retrieval).
- ConversationOrchestrator: Main orchestrator that routes messages and coordinates agents
- Router: LLM-based routing classifier (TECH/BILLING/OUT_OF_SCOPE)
- TechAgent: Answers technical questions using retrieved documentation snippets
- BillingAgent: Handles billing inquiries with tool-calling
- Retriever: Embedding-based document retrieval (DocLoader → Chunker → Embeddings → Retriever)
- Storage: In-memory stores for conversations and billing data
- User sends message with conversationId
- Orchestrator gets conversation history
- Router classifies message using LLM
- Based on route:
- TECH: Retrieve docs → TechAgent answers with citations
- BILLING: BillingAgent handles with tool calling
- OUT_OF_SCOPE: Polite decline response
- Save conversation history
- Return response
The system includes seed data for testing:
user1@example.com: Premium plan ($29.99/month)user2@example.com: Basic plan ($9.99/month)user3@example.com: Enterprise plan ($99.99/month)
- In-memory storage (data is lost on restart)
- Embedding-based retrieval (RAG over local docs)
- Basic error handling
- No authentication/authorization on API
MIT