- HERENCIA-SIMCO.md actualizado con directivas v3.7 y v3.8 - Cambios en backend y frontend Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
7.2 KiB
7.2 KiB
| id | type | title | status | decision_date | updated_at | simco_version | stakeholders | tags | |||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ADR-0003 | ADR | Estrategia LLM Agnostica | Accepted | 2026-01-04 | 2026-01-10 | 3.8.0 |
|
|
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
- Flexibilidad: Cambiar modelo con una variable de entorno
- Resiliencia: Fallback si un modelo falla
- Costos optimizados: Usar modelo mas economico cuando baste
- Futuro-proof: Nuevos modelos disponibles inmediatamente
- Multi-tenant: Cada tenant puede tener su configuracion
Negativas
- Dependencia: OpenRouter como intermediario
- Costo: Pequeno markup sobre precio base
- Complejidad: Manejar diferencias entre modelos
Neutrales
- Prompts: Deben funcionar con multiples modelos
- 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
// 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
// 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
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
Fecha decision: 2026-01-06 Autores: Backend Team