L'assistant de fiches produit, de bout en bout
L'architecture complète
flowchart TB F["produits.txt : une description par ligne"] --> M["main() : lit le fichier"] M --> R["rediger_fiche(client, description)"] R --> C["build_contents : exemples + demande"] R --> G["build_config : prompt système, schéma, réglages"] C --> API["client.models.generate_content"] G --> API API -->|"429 / 5xx"| W["attente puis nouvel essai"] --> API API --> V["FicheProduit.model_validate_json"] V --> O["JSON sur la sortie standard"]
| Fichier | Rôle |
|---|---|
| fiche_produit.py | le script : prompt, exemples, schéma, appel, réessais, ligne de commande |
| test_fiche_produit.py | 8 tests avec un faux client, sans clé ni réseau |
| produits.txt | 3 descriptions fictives pour l'essai |
| requirements.txt | les dépendances |
files/.env.example | modèle de fichier .env (valeurs factices) |
Étape 1 : les tests, sans clé
Un test automatisé est une petite fonction qui vérifie un comportement et échoue bruyamment s'il change. Ici, un faux client (mock) imite client.models.generate_content : il renvoie une réponse préparée à l'avance, ou lève une erreur 429 pour simuler un quota dépassé.
def test_reessaie_sur_429(monkeypatch):
monkeypatch.setattr(fp.time, "sleep", lambda s: None) # pas de vraie attente
quota = errors.ClientError(429, {"error": {"message": "quota", "status": "RESOURCE_EXHAUSTED"}})
client = fake_client(quota, json.dumps(FICHE))
assert fp.rediger_fiche(client, "Gourde").titre
assert len(client.models.appels) == 2 # un échec, puis un succèspytest -qÉtape 2 : le vrai modèle
cp .env.example .env # puis collez votre clé dans .env
python fiche_produit.py --fichier produits.txt > fiches.jsonPourquoi les tests utilisent-ils un faux client plutôt que la vraie API ?
Le faux client rend les tests rapides et déterministes. La qualité des fiches, elle, se juge en lançant le vrai modèle sur des exemples et en les relisant.
Une fiche affiche « garantie 5 ans » alors que la description n'en parle pas. Par quoi commencer ?
Une invention vient souvent d'un prompt flou ou d'un exemple qui contredit la règle. On corrige la consigne, on rejoue les 3 à 5 produits de test, puis seulement on envisage un autre modèle.
Résultat obtenu
Exécution réelle le 2026-10-15. D'abord les tests :
test_fiche_produit.py::test_config_reprend_le_prototype PASSED [ 12%]
test_fiche_produit.py::test_temperature_et_top_p_optionnels PASSED [ 25%]
test_fiche_produit.py::test_exemples_puis_demande PASSED [ 37%]
test_fiche_produit.py::test_rediger_fiche_valide_le_json PASSED [ 50%]
test_fiche_produit.py::test_json_incomplet_refuse PASSED [ 62%]
test_fiche_produit.py::test_description_vide_refusee PASSED [ 75%]
test_fiche_produit.py::test_reessaie_sur_429 PASSED [ 87%]
test_fiche_produit.py::test_ne_reessaie_pas_sur_400 PASSED [100%]
============================== 8 passed in 0.30s ===============================Puis le catalogue de 3 produits avec gemini-3.5-flash (troisième fiche, environ 18 secondes pour les trois) :
{
"titre": "Lampe de bureau LED à bras articulé et port USB-C",
"accroche": "Illuminez efficacement votre espace de travail au quotidien.",
"description": "Cette lampe de bureau LED est dotée d'un bras articulé pour orienter la lumière à votre convenance. Elle propose 3 températures de couleur afin de personnaliser votre éclairage selon vos besoins. Son port USB-C intégré apporte une praticité appréciable au quotidien.",
"points_forts": ["Bras articulé pratique", "3 températures de couleur", "Port USB-C intégré"],
"mots_cles": ["lampe de bureau", "led", "bras articule", "usb-c"]
}Les trois fiches respectent le schéma et n'ajoutent aucune caractéristique absente des descriptions. Un défaut à corriger : le mot-clé « bras articule » a perdu son accent. C'est typiquement le genre de détail qu'on règle par une règle (« garde les accents dans les mots-clés ») puis un nouvel essai.
Exercice final
Faites évoluer l'assistant : (1) ajoutez la règle sur les accents, (2) vérifiez dans le code que chaque titre fait au plus 70 caractères et, sinon, levez une erreur, (3) ajoutez le test correspondant.
Voir la solution
SYSTEM_INSTRUCTION += "\nGarde les accents dans les mots-clés."
def rediger_fiche(client, description: str, essais: int = 4) -> FicheProduit:
...
fiche = FicheProduit.model_validate_json(response.text)
if len(fiche.titre) > 70:
raise ValueError(f"Titre trop long ({len(fiche.titre)} caractères)")
return fichedef test_titre_trop_long_refuse():
longue = dict(FICHE, titre="x" * 71)
with pytest.raises(ValueError):
fp.rediger_fiche(fake_client(json.dumps(longue)), "Gourde")La règle du prompt réduit le problème ; la vérification dans le code le garantit. Le test empêche qu'un futur changement retire cette garantie sans que personne ne s'en aperçoive.
Pour aller plus loin
Prochaine étape : Premiers pas avec l'API Gemini en Python, pour maîtriser le streaming, les conversations, le comptage des tokens et le passage à Vertex AI sur Google Cloud.
Je sais faire
- Lancer les tests du lab sans clé
- Générer les fiches d'un fichier de descriptions avec le vrai modèle
- Repérer un défaut dans une sortie et le corriger par le prompt ou par le code
- Ajouter une vérification et son test