Infrastructure
Local LLM Setup
Run AI models locally with Ollama for zero-cost inference, with automatic fallback to Claude API when needed.
Ollama Installation
Ollama runs LLMs locally on your Mac Mini. Install it and pull the recommended models:
# Install Ollama
brew install ollama
# Start Ollama service
ollama serve
# Pull recommended models
ollama pull mistral:7b # Intent classification (~4GB)
ollama pull llama3:8b # Text generation (~4.7GB)
# Verify installation
ollama listHealth Check
# Check Ollama health
curl http://localhost:11434/api/tags
# Expected response:
# {
# "models": [
# { "name": "mistral:7b", "size": 4109865472 },
# { "name": "llama3:8b", "size": 4661211648 }
# ]
# }
# Test a generation
curl http://localhost:11434/api/generate -d '{
"model": "mistral:7b",
"prompt": "Classify this message intent: I want to create a 20% off deal",
"stream": false
}'Claude API Setup
Get your API key from console.anthropic.com and add it to your environment. Claude serves as the fallback LLM when Ollama is unavailable.
# Get your API key from https://console.anthropic.com
# Add to your .env file:
ANTHROPIC_API_KEY=sk-ant-api03-your-key-here
# The system uses Claude as a fallback when Ollama is unavailable
# or for complex tasks that require stronger reasoning.
# Recommended model: claude-sonnet-4-20250514Auto-Fallback System
If Ollama fails or is unavailable, the system automatically falls back to Claude API. This ensures your agents are always responsive, even during Ollama maintenance or high load.
// Auto-fallback: Ollama → Claude API
const generateResponse = async (prompt, options = {}) => {
// Try Ollama first (free, local)
try {
const ollamaHealth = await fetch('http://localhost:11434/api/tags');
if (ollamaHealth.ok) {
const response = await fetch('http://localhost:11434/api/generate', {
method: 'POST',
body: JSON.stringify({
model: options.model || 'mistral:7b',
prompt,
stream: false
})
});
if (response.ok) {
const data = await response.json();
return { text: data.response, provider: 'ollama' };
}
}
} catch (err) {
console.warn('Ollama unavailable, falling back to Claude API');
}
// Fallback to Claude API
const response = await fetch('https://api.anthropic.com/v1/messages', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': process.env.ANTHROPIC_API_KEY,
'anthropic-version': '2023-06-01'
},
body: JSON.stringify({
model: 'claude-sonnet-4-20250514',
max_tokens: 1024,
messages: [{ role: 'user', content: prompt }]
})
});
const data = await response.json();
return { text: data.content[0].text, provider: 'claude' };
};Intent Classification System Prompt
The INTENT_SYSTEM_PROMPT is used to classify incoming messages into actionable intents. This is the core of the messaging-to-action pipeline:
// INTENT_SYSTEM_PROMPT used for message classification
const INTENT_SYSTEM_PROMPT = `You are an intent classifier for a commerce platform.
Classify the user's message into one of these intents:
INTENTS:
- create_deal: User wants to create a discount or promotion
- check_inventory: User wants to check stock levels
- update_price: User wants to change a product price
- view_analytics: User wants to see sales/performance data
- manage_agent: User wants to configure AI agent settings
- approve_action: User wants to approve a pending agent action
- reject_action: User wants to reject a pending agent action
- general_query: General question about the platform
- unknown: Cannot determine intent
Respond with ONLY the intent name, nothing else.`;
// Usage with Ollama
const classifyIntent = async (message) => {
const response = await fetch('http://localhost:11434/api/generate', {
method: 'POST',
body: JSON.stringify({
model: 'mistral:7b',
prompt: `${INTENT_SYSTEM_PROMPT}\n\nUser: ${message}\nIntent:`,
stream: false
})
});
const data = await response.json();
return data.response.trim().toLowerCase();
};Model Selection Guide
| Model | Size | Use Case | Speed |
|---|---|---|---|
| mistral:7b Recommended | ~4 GB | Intent classification, quick responses | Fast (~50 tok/s on M2) |
| llama3:8b Recommended | ~4.7 GB | Text generation, complex reasoning | Medium (~35 tok/s on M2) |
| llama3:70b | ~40 GB | Advanced reasoning (requires 64GB+ RAM) | Slow (~5 tok/s on M4 Pro) |
| phi3:3.8b | ~2.3 GB | Lightweight tasks, low-memory devices | Very fast (~80 tok/s on M2) |
Cost Comparison
| Provider | Monthly Cost | Latency | Privacy | Limits |
|---|---|---|---|---|
| Ollama (Local) | $0 | ~100-300ms | Full (on-device) | Hardware only |
| Claude API | ~$5-15 | ~500-2000ms | Anthropic servers | Rate limits apply |
Health Check Endpoint
Monitor the LLM subsystem health via the dedicated health endpoint:
// LLM Health Check endpoint
// GET /v1/messaging/llm/health
curl http://localhost:3000/v1/messaging/llm/health
// Response:
{
"status": "ok",
"ollama": {
"status": "connected",
"url": "http://localhost:11434",
"models": ["mistral:7b", "llama3:8b"],
"gpu_available": true
},
"claude": {
"status": "configured",
"model": "claude-sonnet-4-20250514",
"api_key_set": true
},
"active_provider": "ollama",
"fallback_enabled": true
}