---
title: "Kalceo : construire un SaaS B2B avec des agents IA"
description: "Retour concret sur la construction de Kalceo, un SaaS devis/facturation pour le BTP, du premier ticket à la mise en production avec une fleet d'agents Claude."
created: 2026-05-05T10:00:00+02:00
updated: 2026-05-13T12:00:00+02:00
tags: ["IA", "SaaS", "KittyClaw", "Agents", "BTP", "Retex"]
image: /images/blog/kalceo-mvp-saas-b2b-agents-cover.webp
card: /images/blog/kalceo-mvp-saas-b2b-agents-card.webp
---

## Le problème qu'on a choisi

Le BTP, c'est un secteur qui tourne encore aux e-mails et aux classeurs Excel. Les artisans et petites entreprises du bâtiment passent des heures à rédiger des devis à la main, à relancer les clients, à suivre les paiements. Les outils existent — mais ils sont soit trop complexes, soit pensés pour des PME structurées, pas pour le carreleur ou le plâtrier solo.

Kalceo part d'un constat simple : un devis, ça devrait prendre 10 minutes, pas deux heures.

Le périmètre MVP est délibérément étroit — pas de module comptabilité, pas d'intégration bancaire, pas de signature électronique dans la première version. Juste : créer un devis, le transformer en facture, le PDF, l'email au client. Le strict nécessaire pour que quelqu'un puisse s'en servir dès aujourd'hui.

---

## Du ticket à la production

Kalceo a été construit avec la même infrastructure qu'Ekioo.com lui-même : **KittyClaw** comme kanban orchestrateur, et une fleet d'agents Claude spécialisés qui prennent les tickets un par un.

Le découpage du MVP a commencé par une liste de fonctionnalités brutes, qu'on a ensuite affinée en tickets atomiques. Chaque ticket a une portée précise : "Créer le formulaire de saisie d'un devis", "Générer le PDF depuis le modèle HTML", "Envoyer le devis par email via Brevo". Rien de flou, rien de transversal. Les agents fonctionnent bien avec des périmètres clairs.

Le workflow pour chaque feature ressemble à ça :

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 260" style="width:100%;max-width:720px;display:block;margin:2rem auto;font-family:monospace">
  <defs>
    <filter id="kc-glow-g" x="-30%" y="-30%" width="160%" height="160%">
      <feGaussianBlur stdDeviation="3" result="blur"/>
      <feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge>
    </filter>
    <filter id="kc-glow-p" x="-30%" y="-30%" width="160%" height="160%">
      <feGaussianBlur stdDeviation="3" result="blur"/>
      <feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge>
    </filter>
    <filter id="kc-glow-b" x="-30%" y="-30%" width="160%" height="160%">
      <feGaussianBlur stdDeviation="3" result="blur"/>
      <feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge>
    </filter>
    <!-- dot grid -->
    <pattern id="kc-dots" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse">
      <circle cx="1" cy="1" r="1" fill="#ffffff08"/>
    </pattern>
  </defs>
  <!-- background -->
  <rect width="720" height="260" rx="12" fill="#0d1117"/>
  <rect width="720" height="260" rx="12" fill="url(#kc-dots)"/>
  <!-- Ticket box -->
  <rect x="20" y="90" width="120" height="80" rx="8" fill="#0d2818" stroke="#22c55e" stroke-width="1.5" filter="url(#kc-glow-g)"/>
  <rect x="20" y="90" width="120" height="4" rx="4" fill="#22c55e"/>
  <text x="80" y="120" text-anchor="middle" fill="#22c55e" font-size="11" font-weight="bold">KittyClaw</text>
  <text x="80" y="138" text-anchor="middle" fill="#94a3b8" font-size="10">Ticket Todo</text>
  <text x="80" y="155" text-anchor="middle" fill="#94a3b8" font-size="10">→ assignee</text>
  <!-- Arrow 1 -->
  <line x1="140" y1="130" x2="188" y2="130" stroke="#475569" stroke-width="1.5"/>
  <polygon points="188,125 196,130 188,135" fill="#475569"/>
  <!-- Dispatcher box -->
  <rect x="196" y="90" width="120" height="80" rx="8" fill="#1a0d2e" stroke="#a855f7" stroke-width="1.5" filter="url(#kc-glow-p)"/>
  <rect x="196" y="90" width="120" height="4" rx="4" fill="#a855f7"/>
  <text x="256" y="120" text-anchor="middle" fill="#a855f7" font-size="11" font-weight="bold">Dispatch</text>
  <text x="256" y="138" text-anchor="middle" fill="#94a3b8" font-size="10">Lain / Automation</text>
  <text x="256" y="155" text-anchor="middle" fill="#94a3b8" font-size="10">→ lance l'agent</text>
  <!-- Arrow 2 -->
  <line x1="316" y1="130" x2="364" y2="130" stroke="#475569" stroke-width="1.5"/>
  <polygon points="364,125 372,130 364,135" fill="#475569"/>
  <!-- Agent box -->
  <rect x="372" y="90" width="130" height="80" rx="8" fill="#0d1a2e" stroke="#3b82f6" stroke-width="1.5" filter="url(#kc-glow-b)"/>
  <rect x="372" y="90" width="130" height="4" rx="4" fill="#3b82f6"/>
  <text x="437" y="116" text-anchor="middle" fill="#3b82f6" font-size="11" font-weight="bold">Agent Claude</text>
  <text x="437" y="132" text-anchor="middle" fill="#94a3b8" font-size="10">programmer</text>
  <text x="437" y="147" text-anchor="middle" fill="#94a3b8" font-size="10">qa-tester</text>
  <text x="437" y="162" text-anchor="middle" fill="#94a3b8" font-size="10">content-writer</text>
  <!-- Arrow 3 -->
  <line x1="502" y1="130" x2="550" y2="130" stroke="#475569" stroke-width="1.5"/>
  <polygon points="550,125 558,130 550,135" fill="#475569"/>
  <!-- PR / Review box -->
  <rect x="558" y="90" width="130" height="80" rx="8" fill="#0d2818" stroke="#22c55e" stroke-width="1.5" filter="url(#kc-glow-g)"/>
  <rect x="558" y="90" width="130" height="4" rx="4" fill="#22c55e"/>
  <text x="623" y="120" text-anchor="middle" fill="#22c55e" font-size="11" font-weight="bold">PR → Review</text>
  <text x="623" y="138" text-anchor="middle" fill="#94a3b8" font-size="10">Owner merge</text>
  <text x="623" y="155" text-anchor="middle" fill="#94a3b8" font-size="10">→ Production</text>
  <!-- Labels bottom -->
  <text x="360" y="230" text-anchor="middle" fill="#475569" font-size="10">Kalceo MVP — workflow par ticket</text>
</svg>

Chaque agent travaille dans son propre **git worktree** isolé. Il crée une branche, code la feature, commit, ouvre une PR. L'owner (moi) review et merge. Quand l'agent a fini, le ticket passe en Review dans KittyClaw.

---

## Ce que les agents ont construit

En quelques semaines, la fleet a produit l'essentiel du MVP :

- **Authentification** — inscription, connexion, gestion de compte
- **Catalogue de prestations** — chaque entreprise crée ses propres lignes de devis réutilisables
- **Création de devis** — ajout de prestations, calcul des totaux HT/TVA/TTC, statuts (brouillon, finalisé, envoyé, accepté, refusé)
- **Export PDF** via impression navigateur (page HTML print-ready générée côté serveur)
- **Export DOCX** pour édition dans Word

L'agent `programmer` a géré la majorité des features backend (JavaScript, Cloudflare Workers + D1). Le `qa-tester` a écrit les tests d'intégration et repéré plusieurs bugs sur les calculs de totaux — notamment des écarts d'arrondi sur les lignes avec des quantités décimales.

---

## Ce qui a fonctionné, ce qu'on a dû corriger

**Ce qui a bien fonctionné :**

Les agents sont excellents sur les tâches bien délimitées. Donner un ticket avec un périmètre clair ("générer un PDF à partir du composant Razor Devis"), c'est souvent un aller-retour. L'agent lit le code existant, propose une implémentation cohérente, et produit quelque chose de testable.

La structure en worktrees isolés a aussi simplifié la parallélisation : deux ou trois features avancaient en même temps sans interférences.

**Ce qu'on a dû corriger :**

Les agents ratent parfois les effets de bord — surtout sur la logique métier complexe. Le calcul des totaux en est le meilleur exemple : le premier jet gérait les arrondis correctement pour des quantités entières, mais il y avait un bug subtil sur les quantités décimales (ex : 1,5 unité × prix) qui produisait un total HT erroné au centime. Le `qa-tester` l'a détecté, le `programmer` a corrigé — mais ça illustre pourquoi la review humaine reste indispensable.

Il y a aussi eu des régressions UI entre features. Un agent ajoute un composant, un autre modifie le layout, et le CSS se casse dans un coin qu'aucun des deux n'a testé. On a institué une règle simple : chaque PR include une capture d'écran de la page concernée avant de passer en Review.

---

## Où en est Kalceo aujourd'hui

Le MVP est **en production** sur [kalceo.fr](https://kalceo.fr). Une campagne Facebook est active depuis quelques semaines, ciblant les artisans et TPE du BTP en France.

Les premiers retours terrain confirment le problème : les utilisateurs qui s'inscrivent sont effectivement des solo ou micro-entreprises BTP qui gèrent encore leurs devis sous Excel ou Word. La friction principale qu'ils signalent, c'est l'import de leur catalogue existant — un point d'amélioration clairement identifié pour la V2.

Le reste du backlog tourne autour de deux axes : **la relance client automatique** (envoyer un email de rappel X jours après l'envoi d'un devis sans réponse) et **l'export comptable** (export au format compatible avec les logiciels de compta populaires).

---

## Ce que ça change

Kalceo n'aurait pas existé en mode solo classique — pas dans ce délai. Pas parce que les features sont impossibles à coder, mais parce que la somme des tâches de fond (auth, modèles de données, génération PDF, emails, tests, déploiement) aurait pris plusieurs mois à temps partiel.

Avec la fleet, ça se passe différemment : tu passes du temps à cadrer les tickets, à reviewer les PRs, à décider ce qui est dans le MVP et ce qui ne l'est pas. Le travail de fond est délégué. Ça ne signifie pas "sans erreurs" — mais ça signifie que tu peux itérer vite, corriger vite, et rester concentré sur le produit plutôt que sur le code.

C'est le vrai changement : **l'accélération ne vient pas des agents qui codent plus vite, mais de toi qui décides plus vite** parce que la friction d'exécution a disparu.
