Status: Accepted
Date: 2025-01-01
AutoBot's primary interface is a chat-based interaction where users can:
- Ask questions and get AI-powered responses
- Execute workflows and automations
- Query the knowledge base
- Interact with multi-modal content (images, voice)
The chat system must handle:
- Streaming responses: Real-time token-by-token output
- Context management: Maintaining conversation history
- Tool execution: Running workflows triggered by chat
- Multi-LLM routing: Choosing appropriate model for each task
- Session persistence: Saving and resuming conversations
We implement a layered chat workflow architecture:
User Message
│
▼
┌──────────────────┐
│ Message Router │ ─── Determines intent and routing
└────────┬─────────┘
│
┌────┴────┐
▼ ▼
┌───────┐ ┌───────────┐
│ Chat │ │ Workflow │
│ Flow │ │ Executor │
└───┬───┘ └─────┬─────┘
│ │
▼ ▼
┌──────────────────┐
│ LLM Interface │ ─── Unified LLM abstraction
└────────┬─────────┘
│
┌────┴────┐
▼ ▼
┌───────┐ ┌───────┐
│ Cloud │ │ Local │
│ (API) │ │ (NPU) │
└───────┘ └───────┘
- Message Router: Analyzes intent, routes to appropriate handler
- Chat Flow: Standard conversational responses
- Workflow Executor: Runs automations triggered by commands
- LLM Interface: Unified API for multiple LLM providers
- Session Manager: Handles conversation state and history
-
Direct LLM Calls
- Pros: Simple, low latency
- Cons: No routing, no workflow integration, single provider lock-in
-
LangChain/LangGraph
- Pros: Feature-rich, community support
- Cons: Heavy abstraction, version instability, opinionated design
-
Custom Layered Architecture (Chosen)
- Pros: Full control, tailored to needs, clear separation
- Cons: More code to maintain, no community plugins
- Flexibility: Easy to add new LLM providers
- Streaming: Native support for real-time responses
- Context Control: Fine-grained conversation management
- Testability: Each layer can be tested independently
- Observability: Clear points for logging and monitoring
- Maintenance: Custom code requires ongoing maintenance
- Learning Curve: New developers must understand architecture
- No Plugins: Can't use LangChain community integrations
- WebSocket for streaming, REST for non-streaming
- Session state stored in Redis
src/llm_interface.py- Unified LLM abstraction layerautobot-backend/api/chat.py- Chat API endpointsbackend/services/chat_service.py- Chat business logicbackend/services/session_manager.py- Session state managementsrc/enhanced_orchestrator.py- Workflow orchestration
# 1. Receive message via WebSocket
async def handle_message(message: ChatMessage, session: Session):
# 2. Route based on intent
if message.is_workflow_trigger():
return await workflow_executor.execute(message)
# 3. Standard chat flow
context = await session_manager.get_context(session.id)
# 4. Stream response
async for token in llm_interface.stream(message, context):
await websocket.send(token)
# 5. Update session
await session_manager.append_message(session.id, message)from src.llm_interface import LLMInterface
llm = LLMInterface()
# Synchronous completion
response = await llm.complete(prompt, model="gpt-4")
# Streaming completion
async for token in llm.stream(prompt, model="claude-3"):
yield token
# Embedding (routes to NPU when available)
embedding = await llm.embed(text)from backend.services.session_manager import SessionManager
session_mgr = SessionManager()
# Create session
session = await session_mgr.create(user_id="user123")
# Get conversation context
context = await session_mgr.get_context(session.id, limit=10)
# Persist message
await session_mgr.append_message(session.id, message)// Frontend WebSocket handler
const ws = new WebSocket('ws://localhost:8001/ws/chat');
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'token') {
appendToken(data.content);
} else if (data.type === 'complete') {
finishMessage();
}
};Author: mrveiss Copyright: © 2025 mrveiss