- Prefijo v2: MCH - TRACEABILITY-MASTER.yml creado - Listo para integracion como submodulo Workspace: v2.0.0 | SIMCO: v4.0.0
254 lines
7.2 KiB
Markdown
254 lines
7.2 KiB
Markdown
---
|
|
id: ADR-0003
|
|
type: ADR
|
|
title: "Estrategia LLM Agnostica"
|
|
status: Accepted
|
|
decision_date: 2026-01-04
|
|
updated_at: 2026-01-10
|
|
simco_version: "3.8.0"
|
|
stakeholders:
|
|
- "Equipo MiChangarrito"
|
|
tags:
|
|
- llm
|
|
- openrouter
|
|
- ia
|
|
- integracion
|
|
- arquitectura
|
|
- multi-provider
|
|
---
|
|
|
|
# ADR-0003: Estrategia LLM Agnostica
|
|
|
|
## Metadata
|
|
|
|
| Campo | Valor |
|
|
|-------|-------|
|
|
| **ID** | ADR-0003 |
|
|
| **Estado** | Accepted |
|
|
| **Fecha** | 2026-01-06 |
|
|
| **Autor** | Backend Team |
|
|
| **Supersede** | - |
|
|
|
|
---
|
|
|
|
## Contexto
|
|
|
|
MiChangarrito usa un modelo de lenguaje (LLM) para:
|
|
- Interpretar mensajes de usuarios
|
|
- Generar respuestas contextualizadas
|
|
- Procesar comandos de voz transcritos
|
|
- Asistir en consultas de negocio
|
|
|
|
El mercado de LLMs esta en rapida evolucion:
|
|
- Nuevos modelos cada mes
|
|
- Precios cambiantes
|
|
- Calidad variable por caso de uso
|
|
- Disponibilidad y rate limits diferentes
|
|
|
|
Se necesita decidir como manejar esta dependencia.
|
|
|
|
---
|
|
|
|
## Decision
|
|
|
|
**Adoptamos una estrategia LLM-agnostica usando OpenRouter como gateway con fallback entre modelos.**
|
|
|
|
El sistema puede usar multiples proveedores de LLM:
|
|
- Claude (Anthropic) como default
|
|
- GPT-4/3.5 (OpenAI) como fallback
|
|
- Mistral como opcion economica
|
|
- Capacidad de cambiar sin modificar codigo
|
|
|
|
---
|
|
|
|
## Alternativas Consideradas
|
|
|
|
### Opcion 1: Single provider (OpenAI)
|
|
- **Pros:**
|
|
- Simplicidad
|
|
- SDK bien documentado
|
|
- Comunidad grande
|
|
- **Cons:**
|
|
- Lock-in a un proveedor
|
|
- Sin alternativa si hay caida
|
|
- Precios pueden subir
|
|
|
|
### Opcion 2: Multi-provider directo
|
|
- **Pros:**
|
|
- Control total
|
|
- Negociar precios directos
|
|
- **Cons:**
|
|
- Multiples integraciones
|
|
- Multiples cuentas
|
|
- Codigo complejo
|
|
|
|
### Opcion 3: Gateway LLM (OpenRouter) - Elegida
|
|
- **Pros:**
|
|
- Una API para multiples modelos
|
|
- Facil cambiar de modelo
|
|
- Fallback automatico
|
|
- Pay-per-use
|
|
- Nuevos modelos sin cambios
|
|
- **Cons:**
|
|
- Dependencia de OpenRouter
|
|
- Capa adicional (latencia minima)
|
|
- Markup sobre precio base
|
|
|
|
---
|
|
|
|
## Consecuencias
|
|
|
|
### Positivas
|
|
|
|
1. **Flexibilidad:** Cambiar modelo con una variable de entorno
|
|
2. **Resiliencia:** Fallback si un modelo falla
|
|
3. **Costos optimizados:** Usar modelo mas economico cuando baste
|
|
4. **Futuro-proof:** Nuevos modelos disponibles inmediatamente
|
|
5. **Multi-tenant:** Cada tenant puede tener su configuracion
|
|
|
|
### Negativas
|
|
|
|
1. **Dependencia:** OpenRouter como intermediario
|
|
2. **Costo:** Pequeno markup sobre precio base
|
|
3. **Complejidad:** Manejar diferencias entre modelos
|
|
|
|
### Neutrales
|
|
|
|
1. **Prompts:** Deben funcionar con multiples modelos
|
|
2. **Testing:** Probar con diferentes modelos
|
|
|
|
---
|
|
|
|
## Implementacion
|
|
|
|
### Arquitectura
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ MCP Server │
|
|
├─────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ ┌────────────────┐ │
|
|
│ │ LLM Service │ │
|
|
│ │ │ │
|
|
│ │ - model │──────┐ │
|
|
│ │ - fallback │ │ │
|
|
│ │ - maxTokens │ │ │
|
|
│ └────────────────┘ │ │
|
|
│ v │
|
|
│ ┌─────────────────────┐ │
|
|
│ │ OpenRouter API │ │
|
|
│ │ (Gateway) │ │
|
|
│ └──────────┬──────────┘ │
|
|
│ │ │
|
|
└─────────────────────────┼───────────────────────────────┘
|
|
│
|
|
┌───────────────┼───────────────┐
|
|
v v v
|
|
┌──────────┐ ┌──────────┐ ┌──────────┐
|
|
│ Claude │ │ GPT-4 │ │ Mistral │
|
|
│(Anthropic)│ │ (OpenAI) │ │ │
|
|
└──────────┘ └──────────┘ └──────────┘
|
|
```
|
|
|
|
### Codigo
|
|
|
|
```typescript
|
|
// llm.service.ts
|
|
@Injectable()
|
|
export class LLMService {
|
|
private readonly client: OpenAI;
|
|
private readonly defaultModel: string;
|
|
private readonly fallbackModel: string;
|
|
|
|
constructor(config: ConfigService) {
|
|
this.client = new OpenAI({
|
|
apiKey: config.get('OPENROUTER_API_KEY'),
|
|
baseURL: 'https://openrouter.ai/api/v1',
|
|
});
|
|
this.defaultModel = config.get('LLM_MODEL_DEFAULT');
|
|
this.fallbackModel = config.get('LLM_MODEL_FALLBACK');
|
|
}
|
|
|
|
async generate(prompt: string): Promise<string> {
|
|
try {
|
|
return await this.callModel(this.defaultModel, prompt);
|
|
} catch (error) {
|
|
if (this.shouldFallback(error)) {
|
|
return await this.callModel(this.fallbackModel, prompt);
|
|
}
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
private shouldFallback(error: Error): boolean {
|
|
return error.message.includes('rate_limit') ||
|
|
error.message.includes('overloaded');
|
|
}
|
|
}
|
|
```
|
|
|
|
### Configuracion por Tenant
|
|
|
|
```typescript
|
|
// Cada tenant puede tener su configuracion
|
|
interface TenantLLMConfig {
|
|
tenantId: string;
|
|
provider: 'openrouter' | 'openai' | 'anthropic';
|
|
apiKey?: string; // Si quiere usar su propia key
|
|
model: string;
|
|
maxTokens: number;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Validacion
|
|
|
|
### Criterios de Exito
|
|
|
|
| Criterio | Medicion |
|
|
|----------|----------|
|
|
| Cambio de modelo < 5 min | Tiempo de despliegue |
|
|
| Fallback funciona | Tests automatizados |
|
|
| Latencia < 3s | Metricas |
|
|
| Costo por consulta | Dashboard OpenRouter |
|
|
|
|
### Tests
|
|
|
|
```typescript
|
|
describe('LLM Service', () => {
|
|
it('should fallback on rate limit', async () => {
|
|
// Mock rate limit error
|
|
mockOpenRouter.mockRejectedValueOnce(new RateLimitError());
|
|
|
|
const response = await llmService.generate('Hola');
|
|
|
|
expect(response).toBeDefined();
|
|
expect(mockOpenRouter).toHaveBeenCalledTimes(2);
|
|
});
|
|
|
|
it('should work with different models', async () => {
|
|
const models = ['claude-3-haiku', 'gpt-3.5-turbo', 'mistral-7b'];
|
|
|
|
for (const model of models) {
|
|
const response = await llmService.generate('Test', { model });
|
|
expect(response).toBeDefined();
|
|
}
|
|
});
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## Referencias
|
|
|
|
- [OpenRouter Documentation](https://openrouter.ai/docs)
|
|
- [INT-003-openrouter.md](../02-integraciones/INT-003-openrouter.md)
|
|
- [Arquitectura Multi-Tenant](../90-transversal/ARQUITECTURA-MULTI-TENANT-INTEGRACIONES.md)
|
|
|
|
---
|
|
|
|
**Fecha decision:** 2026-01-06
|
|
**Autores:** Backend Team
|