L’intelligence artificielle ne peut pas résoudre le problème agricole ivoirien.
Vrai, si l’on pense qu’il s’agit de prédire les récoltes ou de fixer les prix. Faux, si le vrai problème n’a jamais été la production, mais l’information.
Un grossiste cherche deux tonnes de tomates pour vendredi à Abidjan. Un producteur à trois cents kilomètres a une récolte qui va pourrir faute d’acheteur. Les deux ne se croiseront jamais.
C’est exactement ce problème que Logodougou résout : une plateforme WhatsApp de coordination du commerce agricole ivoirien, conçue, testée et déployée en neuf jours de travail effectif, avec un seul assistant de code du début à la fin, Claude Sonnet 5, jamais Opus, jamais Fable.
Ce document est le retour d’expérience complet : le problème réel que le projet adresse, le rôle exact de l’intelligence artificielle dans le système et ses limites assumées, les décisions techniques qui ont structuré le projet, la manière dont chaque mécanisme critique a été validé, et la chronologie réelle du travail.
Le problème : des acteurs qui existent, un système d’information qui n’existe pas
Dans le commerce agricole ivoirien, les producteurs, les acheteurs (collecteurs ruraux, urbains ou inter-zones), les transporteurs, les grossistes et les détaillants existent tous, et fonctionnent déjà, sans plateforme numérique. Le problème n’est pas l’absence d’acteurs. C’est l’absence d’un système partagé de coordination et d’information.
Chaque acteur ne voit qu’une partie du marché :
- un producteur peut ignorer qu’une forte demande existe à deux cents kilomètres ;
- un grossiste peut chercher plusieurs tonnes d’un produit sans connaître les volumes réellement disponibles dans les différentes zones ;
- un acheteur peut devoir appeler des dizaines de contacts pour constituer un seul chargement ;
- un transporteur peut effectuer un trajet à vide alors qu’un chargement complet l’attendait à proximité de sa route ;
- un marché peut connaître une pénurie pendant qu’un autre marché est saturé du même produit, à quelques dizaines de kilomètres.
Logodougou ne cherche pas à remplacer ce marché par une marketplace numérique classique. L’objectif est de le rendre plus visible, plus coordonné et progressivement plus organisé, sans supprimer artificiellement l’acheteur, sans imposer de formulaires complexes et sans supposer une connexion internet permanente.
Pourquoi WhatsApp, pourquoi le langage naturel
L’interface principale est conversationnelle, sur l’API Cloud WhatsApp Business officielle de Meta, le canal déjà présent dans la poche de chaque acteur, sans installation et sans formation. Ce choix a été documenté (ADR-0001) contre deux alternatives écartées : Baileys, une émulation non officielle de WhatsApp Web qui viole les conditions d’utilisation de Meta et expose au bannissement du numéro, et Twilio, qui repose sur la même API officielle mais ajoute un coût récurrent par message et un format de webhook propriétaire, avec un mode d’essai inutilisable pour tester un scénario multi-acteurs réel. Un utilisateur n’a pas besoin de comprendre les bases de données, les formulaires, le matching ou l’intelligence artificielle : il doit pouvoir simplement parler, y compris en messages vocaux et en argot local.
« J’ai environ 800 kg de tomates disponibles à partir de vendredi à Sinématiali. »
C’est cette phrase, telle quelle, que le système doit transformer en donnée structurée exploitable, sans jamais imposer à l’utilisateur le langage informatique.
Le rôle réel de l’intelligence artificielle
Un principe fondamental structure tout le projet : l’IA comprend, elle ne décide jamais seule. La compréhension du langage est déléguée au modèle ; le contrôle des opérations (matching, quantités, dates, distances, droits, états) reste entièrement dans du code métier déterministe, testé et prévisible.
Dans le code
- compréhension du langage naturel et extraction d’intention ;
- transcription vocale ;
- détection des informations manquantes et formulation des questions de relance nécessaires (« dans quel village se trouvent les tomates ? ») ;
- accès multi-modèles via OpenRouter plutôt qu’un fournisseur unique (ADR-0011), une décision mesurée et non supposée, détaillée plus bas.
Dans l’orchestration
- routage intelligent des notifications : chaque événement détermine qui a réellement besoin de cette information (rôle, produit, zone, urgence), jamais une diffusion générale ;
- matching partiel : une demande de deux tonnes peut être satisfaite par l’agrégation de plusieurs offres plus petites, réparties entre plusieurs producteurs ;
- orchestration de bout en bout : demande, disponibilités, agrégation, acheteur, collecte, transport, destination.
Les décisions qui ont structuré le système
Décision 1. Ne jamais faire confiance au texte d’une réponse du modèle, et vérifier l’état réel en base après chaque action annoncée.
En testant des modèles moins chers que le modèle de production contre le système réel (vraies bases de données, vrais appels API, sans aucun mock, sur un lot de douze scénarios), deux modèles différents ont chacun affirmé, à deux reprises, avoir enregistré une action, par exemple : « Confirmation reçue, j’enregistre l’offre… », sans jamais avoir appelé le moindre outil. La base restait vide. Un troisième modèle a inventé un rôle et une zone géographique que l’utilisateur n’avait jamais mentionnés.
Ce comportement n’est pas un cas isolé : il a été reproduit sur quatre actions distinctes (modification, annulation, prise en charge de transport), avec quatre formulations différentes, sur plusieurs appels réels au modèle de production. Un LLM face à une action qu’il ne peut pas exécuter ne répond presque jamais « je ne peux pas ». Il confirme au contraire avec la même assurance qu’un vrai succès. La seule protection fiable identifiée est un contrôle systématique et automatisé de l’état réel en base, jamais une consigne de prompt seule, qu’un modèle peut ignorer sans prévenir.
Décision 2. Un second appel IA, court et dédié, pour vérifier qu’une donnée vient explicitement de l’utilisateur avant toute écriture.
Un utilisateur qui décrit son offre en plusieurs messages fragmentés (« j’ai des ignames », puis « 1 tonne, pas mûr », puis « prêt mercredi », sans jamais préciser où) a vu le modèle compléter silencieusement la localité manquante en réutilisant une autre donnée de son contexte : une hallucination plausible, reproduite sur un second modèle sans qu’aucune donnée visible n’explique la valeur inventée. La correction retenue est un appel IA dédié à une seule question binaire (« cette valeur vient-elle explicitement de ce que l’utilisateur a dit ? ») avant toute action qui écrit en base.
Décision 3. Un accès multi-modèles (ADR-0011), pas un fournisseur unique.
Le coût réel par appel IA s’est révélé quatre à cinq fois plus élevé que documenté initialement. La tentation naturelle était de basculer vers un modèle moins cher. Testés contre le système réel, sur les mêmes scénarios, les candidats les plus économiques se sont révélés être ceux qui mentaient le plus sur leurs propres actions, comme décrit dans la décision précédente. Le prix par token ne dit rien de la fiabilité. La seule façon de le savoir est de faire tourner le modèle contre le système réel et de vérifier en base, jamais de se fier à sa documentation commerciale. D’où le choix architectural d’un accès multi-modèles, permettant d’évaluer et de changer de modèle sans réécrire le système autour.
Piloter des agents de code sur un projet de production : ce qu’il ne faut jamais négliger
Discipline de test (TDD, couverture 100 %)
Test rouge avant code, sur le domaine métier et la couche application, avec une couverture de 100 % imposée en intégration continue (ADR-0008). Cette discipline n’est pas décorative : c’est elle qui a permis de détecter, par exemple, qu’un décrément non atomique (lecture puis écriture séparées) sur une quantité disponible laissait une fenêtre de course ouverte, corrigée avant que le comportement n’atteigne la production.
Traçabilité systématique des décisions
Trente-trois décisions d’architecture ont été documentées une par une (ADR), chacune avec sa justification explicite, y compris les décisions de ne pas faire quelque chose. Un exemple révélateur : une question de sécurité pure (faut-il lever la règle qui interdit tout partage direct de numéro entre deux parties ?) a en réalité été tranchée par une question de modèle économique. Le seul revenu documenté à terme repose sur la construction d’un graphe des relations réelles entre acteurs, un graphe qui n’existe que si les transactions continuent de passer par le système. Une règle de sécurité tient mieux reliée à ce que le produit doit devenir que défendue seule, dans l’abstrait.
Ce que la formulation d’une consigne change dans le comportement d’un modèle
Un test délibéré en argot local a révélé qu’un modèle collait une expression d’ambiance (« chap chap », qui signifie « vite fait ») directement dans le nom du produit envoyé à l’outil d’enregistrement. La première correction a consisté à citer l’expression exacte comme exemple interdit dans la consigne. Résultat inattendu : le taux d’appel de l’outil est tombé à zéro sur vingt essais consécutifs. Face à une consigne citant mot pour mot une expression présente dans le message qu’il devait traiter, le modèle préférait ne rien faire plutôt que trancher. La correction retenue a décrit la catégorie à exclure plutôt que de citer l’exemple littéral. Sur vingt nouveaux essais, douze appels ont été effectués, tous propres. La façon de formuler une interdiction compte donc autant que l’interdiction elle-même, et un seul essai ne suffit jamais à distinguer un vrai effet d’un aléa.
À l’inverse, une catégorie fourre-tout ajoutée pour couvrir les messages hors-sujet a immédiatement été sur-utilisée par le modèle sur de vrais messages métier. Une consigne abstraite (« ne classe pas ici si le message mentionne un produit ») n’a réduit le problème qu’à moitié ; des exemples concrets et contrastés, directement dans le prompt, ont supprimé l’erreur complètement sur le même lot de test.
Mesurer plutôt que supposer
Un message WhatsApp de quatre mots (« J’ai des tomates. ») coûtait 7169 tokens en entrée, quasiment autant qu’un message contenant une offre complète. Le texte des règles ne représentait qu’un septième du total ; le reste était le catalogue complet d’une vingtaine d’outils, renvoyé en entier à chaque message, utile ou non à ce tour précis. La correction n’a pas été de raccourcir des phrases, mais de changer l’architecture : un premier appel détermine la catégorie du message, un second, limité aux seuls outils pertinents, fait le travail réel. Le coût par tour est passé d’environ 14 000 tokens avant correctif à moins de 3 000 après.
La validation : la preuve plutôt que la supposition
Concurrence réelle, pas simulée
Sur une plateforme où deux acheteurs peuvent tenter de réserver la même marchandise, ou deux transporteurs accepter le même mandat au même instant, la question n’est pas « est-ce que ça peut arriver ? » mais « qu’est-ce qui se passe si ça arrive ? ». Deux vraies transactions Postgres concurrentes, lancées contre la même ressource, et non un test séquentiel qui simule la concurrence en théorie, ont confirmé deux doubles réservations réelles possibles avant correctif. La correction a consisté en un décrément conditionnel atomique directement dans la requête SQL (WHERE quantité >= X). Le même test, rejoué avec les deux mêmes transactions concurrentes, a montré qu’une seule réussit désormais, jamais les deux, jamais aucune. Une race condition qu’on croit avoir corrigée par la seule relecture de code n’est pas corrigée tant qu’on ne l’a pas reproduite, puis fait échouer, avec de vraies transactions concurrentes.
Le test qui prouvait un succès qu’aucun utilisateur n’aurait jamais pu obtenir
L’acceptation d’une demande par un acheteur, l’un des mécanismes les plus centraux du produit, était testée et validée. En relisant les scripts de test après une remarque extérieure, l’identifiant utilisé par l’acteur simulé venait en réalité d’une requête faite directement en base de données, jamais d’un numéro que le système avait réellement communiqué à quelqu’un. Reproduit sans cette aide, en suivant exactement le chemin que l’agent proposait lui-même, le scénario aboutissait à une impasse totale, sans aucune sortie possible. Le mécanisme le plus central du produit ne fonctionnait pour personne dans la vraie vie, seulement dans un test qui trichait sans le savoir. La correction a consisté à exposer systématiquement l’identifiant de toute ressource dans chaque réponse de recherche et de confirmation.
Le silence n’est jamais un compromis acceptable
Sur une plateforme conçue pour une population qui n’a souvent qu’un seul canal de recours, une panne en plein traitement d’un message (base de données, service IA, type de message non géré) ne produisait initialement aucun message, aucune trace côté utilisateur. La correction a consisté en un message de repli explicite dans tous les cas d’échec, y compris quand l’envoi du message de repli lui-même échoue une première fois, testé pour chaque branche, pas seulement le chemin heureux.
Chronologie réelle du projet
D’après l’historique git complet, du premier au dernier commit :
- premier commit : 22 août 2026 ;
- dernier commit : 2 septembre 2026 ;
- durée calendaire : douze jours ;
- neuf jours d’activité effective, du 22 au 25 août pour le bot écho et l’inscription conversationnelle, un creux du 26 au 28 août, une reprise intensive du 29 au 31 août pour le gros du moteur métier (matching, transport, notifications, sécurité), puis les 1er et 2 septembre pour les correctifs et l’isolation des données de démonstration ;
- cent huit commits au total, dont soixante-neuf concentrés sur les deux seules journées du 29 et du 30 août.
Neuf jours de travail effectif ne veut pas dire neuf jours de code approximatif. Ce sont neuf jours avec test rouge avant code, couverture de 100 % imposée sur le cœur métier, trente-trois décisions d’architecture documentées une par une, des situations de concurrence prouvées puis corrigées avec de vraies transactions simultanées, et un principe de sécurité non négociable (aucun numéro de tiers jamais exposé) tenu du premier au dernier commit.
Stack technique
Node.js et TypeScript strict, Fastify, Prisma, Vitest, PostgreSQL, et un accès IA multi-modèles via OpenRouter. Cette stack est figée par décision d’architecture (ADR-0009) : toute nouvelle dépendance ou tout nouveau composant d’infrastructure passe par une décision documentée avant implémentation, jamais après. L’architecture en couches est stricte (domaine, puis application, puis infrastructure et interface, ADR-0007), jamais dans l’autre sens. Le déploiement est conteneurisé, avec intégration continue (ADR-0025).
Ce que ce projet prouve
L’intelligence artificielle est un levier de vitesse réel. Ce projet le prouve avec un système complet, du langage naturel non structuré jusqu’à l’orchestration logistique multi-acteurs, livré en neuf jours de travail effectif, à condition de ne jamais confondre vitesse et absence de rigueur. La vraie maîtrise de l’intelligence artificielle n’est pas de lui faire écrire du code : c’est de savoir précisément où lui faire confiance, où la vérifier systématiquement, et où l’architecture doit garder le dernier mot.
J’ai conçu et développé ce projet seul, avec un seul assistant de code du début à la fin : Claude Sonnet 5, jamais Opus, jamais Fable.
Le site du projet est disponible à cette adresse : logodougou.ivoire.io. Je peux partager le dépôt et le détail des décisions d’architecture sur demande.