La première version d'une fonctionnalité IA est presque toujours un appel API direct. Vous envoyez un prompt, obtenez une réponse, la parsez avec une regex ou un décodeur JSON fragile, et passez à autre chose. Ça fonctionne pour une démo. Ça s'effondre à l'échelle.
Le piège du prototype
Les appels LLM bruts ont une simplicité séduisante. Il n'y a rien à configurer, rien à apprendre, et ils vous mènent à un résultat fonctionnel en quelques minutes. Mais dès que vous essayez de construire par-dessus cette sortie — la passer à une autre étape, la valider ou l'exposer via une API — les failles apparaissent :
- Le format de sortie est implicite. Vous savez ce que vous attendez, mais le modèle n'est pas toujours d'accord.
- La gestion des erreurs est fragile. Une formulation légèrement différente produit une structure complètement différente.
- La composition est manuelle. Passer la sortie d'un appel comme entrée d'un autre nécessite du code de parsing ad-hoc dans chaque intégration.
- Les tests sont impossibles. Sans schéma défini, vous ne pouvez pas écrire d'assertions déterministes.
Le cas des schémas de sortie typés
Le changement le plus impactant que vous puissiez apporter à une fonctionnalité LLM est de définir explicitement son schéma de sortie. Dans Waiflo, chaque étape a un ws_output_schema — une définition JSON Schema de ce que l'étape produit exactement :
"ws_output_schema": {
"properties": {
"priority": { "type": "string", "enum": ["low", "medium", "high", "critical"] },
"category": { "type": "string" },
"summary": { "type": "string", "maxLength": 280 }
},
"required": ["priority", "category"]
}Ce schéma sert trois objectifs simultanément. Il indique au modèle le format exact à produire (injecté dans le prompt au runtime). Il valide la sortie avant qu'elle n'atteigne les étapes en aval. Et il sert de documentation pour toute personne qui intègre la sortie de l'étape.
La composition d'étapes comme design de premier rang
Une fois les sorties typées, la composition devient simple. Une étape en aval déclare les entrées dont elle a besoin, et le runtime peut vérifier que les sorties des étapes en amont satisfont ces exigences. Les dépendances deviennent explicites :
{ "step_id": "draft_response", "ws_ref": "draft_response",
"depends_on": ["classify_ticket", "enrich_customer"] }Ce graphe de dépendances n'est pas seulement de la documentation — c'est le plan d'exécution. Le runtime résout l'ordre, gère la concurrence là où c'est possible, et propage les sorties automatiquement. Vous définissez l'intention ; le moteur gère le séquençage.
Réutilisabilité entre projets
Quand les étapes sont des unités autonomes avec des contrats d'entrée et de sortie explicites, elles deviennent des actifs réutilisables. Une étape classify_sentiment écrite pour un pipeline de support client peut être transplantée directement dans un pipeline de modération de contenu sans modifications. L'étape ne sait pas et ne se soucie pas de son contexte — seulement de ses entrées et sorties.
Cette composabilité est ce qui transforme une collection d'appels LLM ponctuels en une discipline d'ingénierie. Les étapes deviennent des blocs de construction. Les pipelines deviennent des compositions de composants éprouvés. La charge cognitive de construire une nouvelle fonctionnalité IA diminue drastiquement quand vous pouvez partir d'une bibliothèque d'étapes testées et typées.
La discipline en vaut la peine
Ajouter des schémas explicites et des frontières d'étapes ressemble à une surcharge quand vous écrivez votre premier prompt. Mais c'est la même discipline qui rend tout système logiciel maintenable : définir les interfaces clairement, cacher les détails d'implémentation, rendre les dépendances explicites. Les patterns qui ont fonctionné en génie logiciel depuis des décennies fonctionnent tout aussi bien pour les pipelines IA — parce que les pipelines IA sont du logiciel.