My App

ADR-10 — AI provider + RAG strategy

Claude Haiku 4.5 default concierge, Sonnet 4.6 Enterprise, quotas par plan, prompt caching ON, RAG via pgvector prêt mais désactivé MVP, pas de fine-tuning

Statut : Accepté Date : 2026-04-20 Sujet : Choix du modèle LLM, stratégie RAG, gestion des coûts pour le concierge IA Bell

Contexte

L'ancien projet Bell utilisait Azure OpenAI (GPT-4 class) sans stratégie claire. Coûts non trackés, pas de prompt caching, fine-tuning jamais fait bien qu'évoqué. Maintenant qu'on reconçoit proprement, il faut trancher :

  1. Quel provider LLM ? (OpenAI / Anthropic / Azure / Google / multi)
  2. Quel modèle par défaut ? (Sonnet, Haiku, GPT-4o, GPT-4o-mini)
  3. Fine-tuning ou pas ?
  4. RAG ou pas ?
  5. Comment contrôler les coûts pour rester rentable même à 100 hôtels ?

Le business model HOAIY s'appuie sur un plan Pro à ~99 €/hôtel/mois. Le coût AI ne doit JAMAIS manger la marge. Un hôtel actif doit coûter < 5 $/mois en AI.

Alternatives considérées

Option 1 — Azure OpenAI (GPT-4o) uniquement

Ce que le collègue faisait. Même stack, pas de friction.

Pour :

  • Écosystème Azure (si on est déjà dessus pour d'autres raisons)
  • Azure OpenAI "on your data" = RAG empaqueté
  • Conformité RGPD via régions EU d'Azure
  • GPT-4o : latence faible, bonne qualité

Contre :

  • Lock-in Azure
  • Prompt caching Azure OpenAI limité vs Anthropic (factor 10 vs factor 3)
  • Tool use de GPT-4o plus faible que Claude (hallucinations "j'ai fait X" sans appeler le tool)
  • Prix GPT-4o : $2.50/M input, $10/M output — mange la marge à 100+ hôtels

Option 2 — Anthropic Claude uniquement

Pour :

  • Prompt caching natif (divise le coût input par 10 sur les system prompts répétés)
  • Tool use fiable — Claude respecte les schémas de tools, rare les hallucinations
  • Instructions strictes respectées ("Ne JAMAIS inventer, escalade si pas sûr")
  • Claude Haiku 4.5 : $1/M input, $5/M output — très compétitif
  • Qualité raisonnement sur demandes ambiguës

Contre :

  • Pas de fine-tuning public (sauf Bedrock pour Haiku, niche)
  • Un seul provider = single point of failure si Anthropic a un incident
  • Azure OpenAI plus ancien et plus testé en prod Enterprise

Option 3 — Multi-provider via Vercel AI SDK (retenu)

@ai-sdk/openai + @ai-sdk/anthropic via @elysiajs/ai-sdk. Le code ne dépend pas du provider — c'est une config runtime.

Pour :

  • Aucun lock-in — on swap de provider sans changer le code
  • Possibilité d'A/B tester par hôtel (feature flag)
  • Fallback automatique si un provider est down
  • Le modèle par défaut peut être différent selon la tâche (concierge ≠ auxiliaire)
  • Embeddings OpenAI + concierge Claude : chacun sa force

Contre :

  • Légère complexité de config (2 API keys, 2 monitoring, 2 pricings à suivre)
  • Test dev plus coûteux si on test contre les 2 APIs (mock en tests)

Option 4 — LLM self-hosted (Ollama, vLLM, Llama 3)

Pour :

  • Coût par token = 0 après amortissement infra
  • Data 100 % chez nous
  • Pas de quota, pas de rate limit externe

Contre :

  • Infra GPU dédiée : ~200-500 €/mois pour un GPU correct
  • Amortit à partir de ~15 k conversations/mois (donc ~50 hôtels actifs)
  • Qualité Llama 3 70B < Claude Sonnet 4.6 sur tool use
  • Latence et stabilité à gérer
  • Pas MVP — à considérer à 50+ hôtels si les coûts AI deviennent le 1er poste

Option 5 — Fine-tuning sur domaine hôtelier

Fine-tuner GPT-4o-mini ou Claude Haiku sur des conversations hôtel synthétiques.

Contre :

  • Gain souvent marginal vs prompt engineering + tools + RAG
  • Dette opérationnelle permanente : chaque nouveau menu/hôtel/procédure = re-fine-tune
  • Précision factuelle (prix, horaires, dispo) = tools, pas fine-tuning
  • Azure OpenAI fine-tuning GPT-4o-mini dispo, Claude fine-tuning limité à Bedrock Haiku
  • Rejeté — on ne fine-tune pas.

Option 6 — RAG uniquement (pas de LLM custom)

RAG = on stocke des docs, on embedding, au runtime on retrieve + inject dans le prompt. Le LLM reste inchangé.

Pour :

  • Factuellement précis : l'IA répond depuis la vraie doc hôtel
  • Pas de ré-entraînement : on met à jour les docs, live
  • Compatible avec tous les providers
  • Sensibilité RGPD : data chez nous (pgvector)

Contre :

  • Ajoute une complexité (pipeline embeddings + vector search)
  • Latence légèrement supérieure (retrieve avant de prompt)
  • Pas nécessaire au MVP si organization.aiContext (markdown édité par le GM) couvre déjà 95 % des cas

Décision

Multi-provider via Vercel AI SDK, stack hybride, pas de fine-tuning, RAG prévu mais désactivé MVP.

Répartition par tâche

packages/api/src/services/ai/models.ts
import { openai } from "@ai-sdk/openai";
import { anthropic } from "@ai-sdk/anthropic";

export const models = {
  // Concierge principal : ultra-fiable sur tool use, prompt caching puissant
  concierge: anthropic("claude-haiku-4-5"),              // default

  // Concierge premium (Enterprise plan) : raisonnement plus fin
  conciergeStrong: anthropic("claude-sonnet-4-6"),

  // Tâches auxiliaires (titre conversation, résumé, classification simple)
  auxiliary: openai("gpt-4o-mini"),

  // Embeddings RAG (quand activé)
  embeddings: openai("text-embedding-3-small"),
};
TâcheModèle defaultPrix inputPrix outputPourquoi
Concierge guest (Pro plan)Claude Haiku 4.5$1/M$5/Msweet spot qualité/prix, tool use fiable
Concierge guest (Enterprise plan)Claude Sonnet 4.6$3/M$15/Mraisonnement premium
Génération titre/résuméGPT-4o-mini$0.15/M$0.60/M10× moins cher, qualité suffisante
Embeddings RAGtext-embedding-3-small$0.02/Mstandard communauté
Content moderation (futur)GPT-4o-mini ou Azure Content Safetyà décider plus tard

Prompt caching Anthropic ON par défaut

Le system prompt Bell (~3 k tokens) + organization.aiContext (~2 k tokens) + menu featured (~1 k tokens) = ~6 k tokens quasi-statiques à chaque turn.

Avec Anthropic prompt caching :

  • Cache write : 25 % plus cher que input normal (facturé une fois)
  • Cache read : 10 % du prix input normal (facturé à chaque hit)
  • TTL : 5 minutes (ou 1h avec écriture premium)

Sur notre pattern (5 turns/conversation, cache TTL 5 min) :

  • Turn 1 : write cache → facturé 125 % sur 6k tokens
  • Turns 2-5 : cache hit → facturé 10 % sur 6k tokens

Économie estimée : 50-70 % sur les tokens input selon le pattern réel.

Activé via l'option cacheControl dans @ai-sdk/anthropic.

RAG prévu mais désactivé MVP

MVP : zéro RAG. Le champ organization.metadata.aiContext (markdown édité par le GM, ~2-5 k tokens) est injecté dans le system prompt. Couvre :

  • Horaires (spa, resto, bar, réception)
  • Infos pratiques (wifi, parking, pets, fumeurs)
  • Spécialités de l'hôtel
  • Règles custom ("ne jamais proposer de remboursement")

Quand activer le RAG (déclenchable par org via feature flag ai_rag_enabled) :

  • Un hôtel veut charger brochure PDF 30+ pages
  • FAQ longue (50+ entrées)
  • Procédures internes volumineuses
  • Contenu multi-langue

Stack RAG quand activé :

  • Upload PDF → RustFS
  • Extract text (pdf-parse) → chunks ~500 tokens
  • Embed chunks via text-embedding-3-small (OpenAI, $0.02/M)
  • Store vectors dans Postgres vector(1536) via pgvector (déjà chargé)
  • Runtime : embed question → cosine similarity top-5 → inject dans prompt

Pas de Pinecone / Qdrant / Weaviate. pgvector suffit jusqu'à 1M+ chunks par hôtel.

Routing dynamique (optionnel phase 2)

Pour plan Pro, on peut router 80 % du trafic vers Haiku et 20 % complexes vers Sonnet :

export function pickConcierge(ctx: { turnCount: number; intent?: string; plan: Plan }) {
  if (ctx.plan === "enterprise") return models.conciergeStrong;       // Sonnet systématique
  if (ctx.turnCount > 6) return models.conciergeStrong;               // conversation longue
  if (ctx.intent === "complex_booking") return models.conciergeStrong; // tool use sensible
  return models.concierge;                                             // default Haiku
}

Pas MVP — on start full Haiku, on mesure la qualité, on ajoute le routing si signaux d'insatisfaction.

Quotas par plan (hard limits)

PlanPrixMessages AI/mois/hôtelModèle
Free0 €0 IA (FAQ statique + tools UI seulement)
Pro99 €/mois2 000 msgHaiku 4.5 (+ caching)
Enterprisedevis (≥ 299 €/mois)illimitéSonnet 4.6 + RAG dispo

Enforced côté API :

// packages/api/src/modules/ai/ai.service.ts
export async function checkQuota(opts: { organizationId: string }) {
  const org = await loadOrganization(opts.organizationId);
  const monthlyUsed = await countAIMessagesThisMonth(opts.organizationId);
  const limit = quotaByPlan[org.plan];
  if (limit !== Infinity && monthlyUsed >= limit) {
    throw new QuotaExceededError({ plan: org.plan, used: monthlyUsed, limit });
  }
}

Si quota dépassé : l'IA est remplacée par un message "Le concierge IA n'est plus disponible pour ce mois — parlez directement à notre staff" + escalade automatique vers staff.

Rentabilité calculée

Pour un hôtel Pro (2 000 msg/mois) :

  • Input moyen : 400 tokens (system caché 300 + user message 100)
  • Output moyen : 200 tokens
  • Total : 800 k input + 400 k output

Coût Haiku 4.5 avec caching 70 % :

  • Non-cached input (30 %) : 240 k × $1/M = $0.24
  • Cached input (70 %) : 560 k × $0.10/M = $0.06
  • Output : 400 k × $5/M = $2.00
  • Total : ~$2.30/mois/hôtel

Sur 99 € facturé → marge 97 %.

Pour un Enterprise (illimité, Sonnet 4.6) :

  • Supposons 10 000 msg/mois
  • Coût estimé : ~$30/mois
  • Facturé ≥ 299 € → marge 90 %+

Monitoring cost

Métriques obligatoires Signoz :

bell.ai.cost_usd{org_id, model}
bell.ai.tokens.input{org_id, model, cached}
bell.ai.tokens.output{org_id, model}
bell.ai.quota.usage{org_id, plan}
bell.ai.quota.exceeded{org_id}

Alertes :

AlerteSeuilCanal
Org dépasse $5/jourwarningSlack #bell-alerts
Org dépasse $10/jourcriticalSlack + email admin HOAIY
Total HOAIY dépasse $50/jourcriticalSlack + email
Spike 3× moyenne sur un hôtelinfoSlack
Quota atteint par un hôtel Proinfolog + email GM

Env vars

apps/server/.env
# Providers
ANTHROPIC_API_KEY=sk-ant-api03-...
OPENAI_API_KEY=sk-...

# Models (config-driven pour swap sans redeploy)
AI_MODEL_CONCIERGE=claude-haiku-4-5
AI_MODEL_CONCIERGE_STRONG=claude-sonnet-4-6
AI_MODEL_AUXILIARY=gpt-4o-mini
AI_MODEL_EMBEDDINGS=text-embedding-3-small

# Feature flags
AI_PROMPT_CACHING_ENABLED=true
AI_ROUTING_DYNAMIC=false                # phase 2
AI_RAG_GLOBAL_ENABLED=false             # activable par org via DB flag

# Limits absolues (safety net)
AI_MAX_TOKENS_PER_TURN=4000
AI_MAX_TURNS_PER_CONVERSATION=100
AI_MAX_CONVERSATION_PER_USER=20

Dev / test avec vraie API

  • Clé API dev avec spend limit $5 côté Anthropic console
  • Mock providers via Vercel AI SDK dans les tests unitaires/intégration (MockLanguageModelV1)
  • Appels réels uniquement sur smoke tests manuels ciblés
  • bun scripts/ai-cost-simulator.ts <scenario> pour estimer le coût d'un flow avant prod

Conséquences

Positives :

  • Marge business préservée (97 %+ sur Pro, 90 %+ sur Enterprise)
  • Zéro lock-in provider (swap via env var)
  • Prompt caching divise les coûts par 2-3× sur Claude
  • RAG infrastructure prête (pgvector déjà chargé), activable à la demande
  • Quota hard limit = pas de surprise de facturation
  • Monitoring par hôtel pour détecter abuse ou bug

Négatives :

  • 2 API keys à gérer (Anthropic + OpenAI) vs 1 seule
  • Légère complexité dans la config models.ts (plusieurs variantes)
  • Tests dev coûtent un peu (mitigé par mock + spend limit)
  • Dépendance partielle à Anthropic (single-point si leur API est down → fallback OpenAI configurable via env)

Métriques à surveiller

  • Coût AI moyen par hôtel (cible : < $5/mois/hôtel Pro, < $40/mois Enterprise)
  • Marge AI sur revenue plan (cible : > 95 % sur Pro, > 90 % sur Enterprise)
  • Cache hit rate Anthropic (cible : > 70 %)
  • Taux d'escalade vers staff (cible : < 20 % — si plus, signale que l'IA est insuffisante)
  • Taux de quota exceeded (cible : < 5 % des hôtels Pro — si plus, le quota est trop bas)
  • Latence p95 TTFT (cible : < 1s)

Lien

On this page