Guide technique

Concevoir des Systemes IA sans Vendor Lock-In : Quoi Abstraire (et Quoi Ne Pas Abstraire)

Comment concevoir des architectures IA qui survivent aux changements de fournisseur. Couches d'abstraction, portabilite des prompts, routage multi-modeles, developpement pilote par l'evaluation, et quand le lock-in est le bon choix.

26 mars 202614 min de lectureÉquipe d'Ingénierie Oronts

Le Piege du Lock-In

Tout projet IA commence avec un seul fournisseur. OpenAI, Anthropic, ou un modele local. Le SDK entre dans le code. Les fonctionnalites specifiques au fournisseur (format d'appel de fonctions, mode JSON, comportement du prompt systeme) se retrouvent integrees dans la logique metier. Six mois plus tard, tu veux changer de fournisseur (cout, performance, conformite) et tu decouvres que la migration necessite de reecrire la moitie de l'application.

Nous avons construit des systemes IA qui utilisent plusieurs fournisseurs simultanement et avons change de fournisseur en cours de projet sans toucher a la logique metier. Cet article couvre l'architecture qui rend cela possible et les vrais compromis que ca implique.

Pour des patterns d'architecture IA plus larges, consulte notre guide sur les systemes IA et notre guide d'orchestration IA.

Ce Qui Cree le Lock-In

Source de Lock-InExempleSeverite
Couplage SDKopenai.chat.completions.create() dans 50 fichiersElevee
Format de promptPrompts optimises pour le comportement de GPT-4, echouent sur ClaudeElevee
Modeles fine-tunesModele entraine sur tes donnees, heberge par le fournisseurTres Elevee
Fonctionnalites proprietairesAssistants API, format d'appel de fonctions, mode JSONMoyenne
Lock-in des embeddings100K documents indexes avec text-embedding-3-small, incompatibles avec d'autres modelesTres Elevee
Architecture liee aux rate limitsSysteme concu autour des rate limits et du batching specifiques a OpenAIBasse

La Couche d'Abstraction Qui Fonctionne

Une bonne couche d'abstraction a trois composants : une interface unifiee pour les appels de modeles, une couche de gestion des prompts, et un framework d'evaluation.

// Interface LLM unifiee
interface LlmProvider {
    generate(request: LlmRequest): Promise<LlmResponse>;
    stream(request: LlmRequest): AsyncIterable<LlmChunk>;
    embed(texts: string[]): Promise<number[][]>;
}

interface LlmRequest {
    model: string;
    messages: Message[];
    temperature?: number;
    maxTokens?: number;
    responseFormat?: 'text' | 'json';
    tools?: ToolDefinition[];
}

interface LlmResponse {
    text: string;
    usage: { promptTokens: number; completionTokens: number };
    finishReason: string;
    model: string;
    provider: string;
    latencyMs: number;
}

Chaque fournisseur implemente cette interface :

class OpenAiProvider implements LlmProvider {
    async generate(request: LlmRequest): Promise<LlmResponse> {
        const response = await this.client.chat.completions.create({
            model: request.model,
            messages: this.convertMessages(request.messages),
            temperature: request.temperature,
            max_tokens: request.maxTokens,
            response_format: request.responseFormat === 'json'
                ? { type: 'json_object' } : undefined,
        });
        return this.convertResponse(response);
    }
}

class AnthropicProvider implements LlmProvider {
    async generate(request: LlmRequest): Promise<LlmResponse> {
        const response = await this.client.messages.create({
            model: request.model,
            system: this.extractSystemMessage(request.messages),
            messages: this.convertMessages(request.messages),
            temperature: request.temperature,
            max_tokens: request.maxTokens ?? 4096,
        });
        return this.convertResponse(response);
    }
}

La logique metier utilise l'interface, jamais le SDK du fournisseur directement :

// Logique metier : agnostique du fournisseur
async function generateCustomerResponse(ctx: Context, ticket: string): Promise<string> {
    const provider = ctx.getLlmProvider(); // resolu depuis la config
    const response = await provider.generate({
        model: ctx.getModelForUseCase('customer-support'),
        messages: [
            { role: 'system', content: SUPPORT_SYSTEM_PROMPT },
            { role: 'user', content: ticket },
        ],
        temperature: 0.3,
    });
    return response.text;
}

Passer d'OpenAI a Anthropic : change la config. Zero modification de code dans la logique metier.

Le Probleme de la Portabilite des Prompts

Les prompts ne sont pas portables d'un modele a l'autre. Un prompt optimise pour GPT-4 peut donner de moins bons resultats sur Claude, et inversement. Les modeles interpretent les instructions differemment, gerent les cas limites differemment, et ont des forces differentes.

La solution : des variantes de prompt par famille de modeles.

const SUPPORT_PROMPTS: Record<string, string> = {
    'openai': `You are a customer support assistant. Be concise and helpful.
When you don't know the answer, say so clearly.
Always reference the customer's order number.`,

    'anthropic': `You are a customer support assistant.
<instructions>
- Be concise and helpful
- When you don't know the answer, say so clearly
- Always reference the customer's order number
</instructions>`,

    'local': `### System
You are a customer support assistant.
### Rules
1. Be concise and helpful
2. When you don't know the answer, say so clearly
3. Always reference the customer's order number`,
};

function getPrompt(useCase: string, provider: string): string {
    return PROMPTS[useCase][provider] || PROMPTS[useCase]['default'];
}

Ca ajoute un cout de maintenance (plusieurs versions de prompts), mais c'est la realite. Un seul prompt pour tous les modeles signifie accepter une qualite degradee sur au moins un modele.

Routage Multi-Modeles

Chaque tache a des exigences differentes. La classification a besoin de rapidite. La generation a besoin de qualite. L'embedding a besoin de coherence. Dirige chaque tache vers le bon modele.

const MODEL_ROUTING: Record<string, ModelConfig> = {
    'classification': {
        primary: { provider: 'openai', model: 'gpt-4o-mini' },
        fallback: { provider: 'anthropic', model: 'claude-haiku-4-5-20251001' },
        reason: 'Fast, cheap, good enough for classification',
    },
    'generation': {
        primary: { provider: 'anthropic', model: 'claude-sonnet-4-20250514' },
        fallback: { provider: 'openai', model: 'gpt-4o' },
        reason: 'Best quality for long-form generation',
    },
    'embedding': {
        primary: { provider: 'openai', model: 'text-embedding-3-small' },
        fallback: null, // Impossible de changer de modele d'embedding sans re-indexer
        reason: 'Consistency: all embeddings must use the same model',
    },
    'summarization': {
        primary: { provider: 'anthropic', model: 'claude-haiku-4-5-20251001' },
        fallback: { provider: 'openai', model: 'gpt-4o-mini' },
        reason: 'Fast and cheap for summaries',
    },
};

La config de routage est separee de la logique metier. Changer le modele qui gere le resume est un changement de config, pas un changement de code.

Lock-In des Embeddings : Le Plus Difficile a Briser

Les embeddings sont la forme de lock-in la plus forte. Une fois que tu as indexe 100K documents avec text-embedding-3-small, changer de modele d'embedding necessite de re-indexer tout. Les vecteurs sont incompatibles d'un modele a l'autre (dimensions differentes, espaces semantiques differents).

Mesures d'attenuation :

  • Stocke les documents sources a cote des embeddings (pour pouvoir re-indexer)
  • Trace quel modele d'embedding a ete utilise par document
  • Prevois le budget pour le re-embedding lors d'un changement de fournisseur
  • Envisage des modeles d'embedding open-source (sentence-transformers) pour la portabilite

Pour approfondir les patterns de recherche vectorielle et la gestion des embeddings, consulte notre guide d'architecture de recherche vectorielle et notre guide des systemes RAG d'entreprise.

Developpement Pilote par l'Evaluation

La cle pour changer de fournisseur en toute securite : evalue par rapport aux taches, pas par rapport aux modeles. Si ta suite d'evaluation teste "est-ce que la reponse traite correctement le probleme du client ?" plutot que "est-ce que la sortie correspond au format de GPT-4 ?", tu peux changer de modele en toute confiance.

interface EvalCase {
    input: string;
    expectedBehavior: string;  // Ce que la sortie devrait FAIRE, pas ce qu'elle devrait DIRE
    criteria: EvalCriterion[];
}

interface EvalCriterion {
    name: string;
    check: (output: string, context: EvalCase) => boolean | number;
}

const SUPPORT_EVALS: EvalCase[] = [
    {
        input: "I haven't received my order #12345",
        expectedBehavior: "Acknowledge the issue, reference order number, offer next steps",
        criteria: [
            { name: 'references_order', check: (out) => out.includes('12345') },
            { name: 'acknowledges_issue', check: (out) => /sorry|apologize|understand/i.test(out) },
            { name: 'offers_action', check: (out) => /check|investigate|track|follow up/i.test(out) },
            { name: 'reasonable_length', check: (out) => out.length > 50 && out.length < 500 },
        ],
    },
];

// Lancer les evals sur n'importe quel modele
async function runEvalSuite(provider: LlmProvider, model: string): Promise<EvalResults> {
    const results = [];
    for (const evalCase of SUPPORT_EVALS) {
        const output = await provider.generate({
            model,
            messages: [
                { role: 'system', content: SUPPORT_SYSTEM_PROMPT },
                { role: 'user', content: evalCase.input },
            ],
        });
        const scores = evalCase.criteria.map(c => ({
            name: c.name,
            passed: c.check(output.text, evalCase),
        }));
        results.push({ input: evalCase.input, scores });
    }
    return summarize(results);
}

Lance la suite d'evaluation avant de changer de modele. Si le nouveau modele passe avec des scores acceptables, la migration est sure. Sinon, tu sais exactement quels comportements se sont degrades.

Notre guide sur l'observabilite IA couvre les metriques a surveiller en production pour ce type de transitions. Tu peux aussi explorer notre approche du design de workflows IA pour structurer tes pipelines de maniere modulaire.

Ce Qu'il Ne Faut PAS Abstraire

Tout ne doit pas etre abstrait. La sur-abstraction ajoute de la complexite sans benefice.

Ne Pas AbstrairePourquoi
Modele d'embedding (dans un projet)Les vecteurs sont incompatibles. L'abstraction n'aide pas.
Specificites du fine-tuningLe fine-tuning est intrinsequement lie au fournisseur
Optimisations specifiques au fournisseurLe batching, le cache, la gestion des rate limits different par fournisseur
Details du format de streamingA gerer dans l'adaptateur du fournisseur, pas dans l'abstraction

L'abstraction devrait couvrir : la selection du modele, le routage des prompts, le parsing des reponses, le suivi des couts, et la logique de fallback. Elle ne devrait PAS essayer de faire en sorte que tous les fournisseurs se comportent de maniere identique. Ce n'est pas le cas, et pretendre que si cree des bugs.

Ce pattern s'inscrit dans une approche plus large d'architecture logicielle et de conception de logiciels robustes. Si tu travailles sur des systemes multi-agents, notre guide d'architecture multi-agents couvre les strategies de routage a un niveau superieur.

Quand le Lock-In Est le Bon Choix

Parfois, se verrouiller chez un fournisseur est la bonne decision business :

  • Le fine-tuning necessite un engagement chez un seul fournisseur. Le modele entraine n'est pas portable.
  • Les remises sur volume d'un seul fournisseur peuvent reduire les couts de maniere significative.
  • La conformite peut exiger un fournisseur specifique (residence des donnees, certifications).
  • La rapidite de mise sur le marche implique d'utiliser les fonctionnalites specifiques au fournisseur sans le surcharge de l'abstraction.

L'essentiel : prends la decision de lock-in consciemment, pas accidentellement. Si tu choisis d'utiliser l'Assistants API d'OpenAI parce que ca t'economise 3 mois de developpement, c'est un compromis valide. Si tu l'utilises parce que tu n'as pas reflechi a l'abstraction, c'est de la dette technique.

Pour en savoir plus sur la conformite et les implications RGPD de tes choix de fournisseur IA, consulte notre guide de conformite RGPD pour l'IA et notre page RGPD.

Erreurs Courantes

  1. SDK du fournisseur dans la logique metier. openai.chat.completions.create() disperse dans 50 fichiers rend la migration impossible. Utilise une couche d'abstraction.

  2. Un seul prompt pour tous les modeles. Les prompts sont specifiques au modele. Maintiens des variantes par famille de modeles. Accepte le cout de maintenance.

  3. Tout abstraire. La sur-abstraction ajoute de la complexite. Abstrait l'interface, pas les mecanismes internes.

  4. Pas de suite d'evaluation. Sans evals automatisees, tu ne peux pas savoir si un changement de modele degrade la qualite avant que les utilisateurs se plaignent.

  5. Ignorer le lock-in des embeddings. Changer de modele d'embedding necessite de re-indexer chaque document. Prevois ce cout.

  6. Le lock-in accidentel. Le pire. Utiliser des fonctionnalites specifiques au fournisseur sans realiser que tu crees une dependance.

Notre guide sur les modes de defaillance de l'IA couvre d'autres pieges courants dans les systemes IA en production, et le guide de gouvernance IA t'aide a structurer les decisions autour du choix de fournisseur.

Points Cles a Retenir

  • Abstrait l'interface, pas l'implementation. Une interface LlmProvider unifiee avec des adaptateurs specifiques par fournisseur. La logique metier appelle l'interface.

  • Les prompts ne sont pas portables. Maintiens des variantes de prompt par famille de modeles. Un seul prompt pour tous les modeles signifie une qualite degradee quelque part.

  • Dirige chaque tache vers le bon modele. La classification vers un modele rapide, la generation vers un modele precis, les embeddings vers un modele coherent. Pilote par la config, pas par le code.

  • Evalue par rapport aux taches, pas aux modeles. Si ta suite d'evaluation teste le comportement ("est-ce qu'il reference le numero de commande ?") plutot que le format, tu peux changer de modele en confiance.

  • Les embeddings sont le lock-in le plus difficile a briser. Stocke les documents sources. Trace quel modele a ete utilise. Prevois le budget pour le re-embedding.

  • Parfois le lock-in est le bon choix. Fine-tuning, remises sur volume et conformite peuvent le justifier. Que ce soit une decision consciente.

Nous concevons des architectures IA agnostiques des fournisseurs dans le cadre de nos services IA et de notre practice de consulting. Explore nos solutions pour voir comment nous appliquons ces patterns, ou consulte nos cas d'utilisation concrets. Si tu as besoin d'aide pour ton architecture IA, parle a notre equipe ou demande un devis.

Sujets couverts

vendor lock-in IAcouche dabstraction LLMstrategie multi-modelesindependance fournisseur IAroutage de modelesportabilite des promptsarchitecture IA

Prêt à construire des systèmes IA prêts pour la production ?

Notre équipe est spécialisée dans les systèmes IA prêts pour la production. Discutons de comment nous pouvons aider.

Démarrer une conversation