En 45 minutes, on construit une petite API qui envoie la photo d'une feuille de maïs à Gemma 4 et renvoie un diagnostic en JSON. On applique au passage le guide « Coder avec l'IA : le guide complet, du premier prompt à la production » : fichiers de contexte, prompts rangés, sortie structurée, tests.
Ce qu'on construit
AgriScan : une API qui reçoit la photo d'une feuille de maïs et répond avec un diagnostic simple, en français, au format JSON.
{"maladie": "rouille commune", "confiance": "moyenne", "conseil": "Retire les feuilles touchées et surveille les plants voisins."}
Le trajet d'une requête : l'utilisateur envoie une photo → FastAPI la vérifie → gemma.py l'envoie à Gemma 4 avec le prompt de prompts.py → on lit le JSON → on le renvoie.
Gemma 4 est un modèle open-weight de Google qui comprend le texte et les images. On l'appelle ici via l'API Gemini, ce qui évite de le faire tourner sur ton ordinateur.
Ce projet sert d'exemple. Pendant le Hack Day, remplace le maïs par ton propre problème : la structure reste la même.
Prérequis
- Python 3.11 ou plus récent (
python --version) - Git et un compte GitHub
- Un éditeur, idéalement VS Code avec GitHub Copilot
- Une clé d'API Gemini, gratuite, créée sur Google AI Studio (bouton Get API key)
- Une ou deux photos de feuilles pour tester (n'importe quelle plante convient)
Étape 1 : créer le dépôt et ses fichiers de contexte
Sur GitHub, crée un dépôt public agriscan avec un README, une licence MIT et le .gitignore Python. Puis :
git clone https://github.com/TON-PSEUDO/agriscan.git
cd agriscan
mkdir app tests
touch app/__init__.py app/main.py app/gemma.py app/prompts.py tests/test_main.py
Ajoute AGENTS.md à la racine, avant d'écrire le moindre code. C'est lui qui guidera Copilot pendant tout l'atelier (modèle complet dans le guide) :
# Instructions pour l'assistant IA
## Le projet
AgriScan : une API qui diagnostique une maladie du maïs à partir d'une photo.
## Stack
- Python 3.11, FastAPI
- Gemma 4 via l'API Gemini, SDK google-genai, modèle gemma-4-26b-a4b-it
## Commandes
- Lancer : fastapi dev app/main.py
- Tester : pytest
## Règles
- La clé d'API vient de GEMINI_API_KEY, jamais du code.
- Tous les appels au modèle passent par app/gemma.py.
- Tous les prompts sont dans app/prompts.py.
- Chaque route a un test pytest qui n'appelle pas le vrai modèle.
Crée aussi requirements.txt :
fastapi[standard]
google-genai
pytest
Étape 2 : installer et configurer la clé d'API
python -m venv .venv
source .venv/bin/activate # Windows : .venv\Scripts\activate
pip install -r requirements.txt
Le SDK lit la clé dans la variable d'environnement GEMINI_API_KEY :
export GEMINI_API_KEY="ta-cle" # macOS / Linux
$env:GEMINI_API_KEY="ta-cle" # Windows PowerShell
Ne l'écris jamais dans un fichier du dépôt. Si tu la mets dans un fichier .env, vérifie que .env figure bien dans .gitignore.
Étape 3 : premier appel à Gemma 4
Crée un fichier essai.py à la racine :
from google import genai
client = genai.Client() # lit GEMINI_API_KEY
response = client.models.generate_content(
model="gemma-4-26b-a4b-it",
contents="Explique en deux phrases ce qu'est un modèle open-weight.",
)
print(response.text)
python essai.py
Si une réponse s'affiche, tout est en place. Deux modèles Gemma 4 sont disponibles via l'API Gemini : gemma-4-26b-a4b-it (celui qu'on utilise) et gemma-4-31b-it. Commit maintenant :
git add . && git commit -m "feat: premier appel a Gemma 4"
Étape 4 : ranger le prompt dans prompts.py
Le prompt est du code : on le met à un seul endroit, on le versionne, on le teste. Dans app/prompts.py :
DIAGNOSTIC_PROMPT = """Tu es un agronome qui aide des petits producteurs au Togo.
On te montre la photo d'une feuille de maïs.
Réponds uniquement avec un objet JSON, sans texte autour, avec les clés :
- "maladie" : le nom probable de la maladie, ou "inconnue"
- "confiance" : "faible", "moyenne" ou "forte"
- "conseil" : une action simple, en une phrase, en français
Si la photo ne montre pas une feuille de maïs, réponds
{"maladie": "photo non valide", "confiance": "forte", "conseil": "Envoie une photo nette d'une feuille de maïs."}"""
On applique ici trois techniques du guide : un rôle et un public, un format de sortie imposé, et un cas limite prévu à l'avance.
Étape 5 : envoyer une image à Gemma 4
Dans app/gemma.py, un seul endroit appelle le modèle. Le prompt passe en system instruction ; l'image est envoyée comme une partie du contenu.
import json
from google import genai
from google.genai import types
from app.prompts import DIAGNOSTIC_PROMPT
MODEL = "gemma-4-26b-a4b-it"
def analyser_image(image_bytes: bytes, mime_type: str) -> dict:
"""Envoie la photo à Gemma 4 et renvoie le diagnostic sous forme de dict."""
client = genai.Client() # créé ici pour que les tests n'aient pas besoin de clé
response = client.models.generate_content(
model=MODEL,
config=types.GenerateContentConfig(system_instruction=DIAGNOSTIC_PROMPT),
contents=[
types.Part.from_bytes(data=image_bytes, mime_type=mime_type),
"Analyse cette photo.",
],
)
return extraire_json(response.text)
Étape 6 : lire une sortie JSON fiable
Même quand on demande « uniquement du JSON », un modèle ajoute parfois des balises jsonou une phrase autour. On extrait donc l'objet entre la première accolade et la dernière, et on refuse proprement le reste. Ajoute dansapp/gemma.py` :
python
def extraire_json(texte: str) -> dict:
"""Extrait le premier objet JSON d'une réponse du modèle."""
debut, fin = texte.find("{"), texte.rfind("}")
if debut == -1 or fin == -1:
raise ValueError(f"Pas de JSON dans la réponse : {texte[:200]}")
return json.loads(texte[debut : fin + 1])
Teste-le vite dans un terminal Python avec une de tes photos :
python
from app.gemma import analyser_image
print(analyser_image(open("feuille.jpg", "rb").read(), "image/jpeg"))
Essaie aussi une photo qui n'a rien à voir (ton chat, un ordinateur) pour vérifier que le cas « photo non valide » fonctionne. Si la réponse ne te plaît pas, c'est le prompt qu'on corrige, pas le code.
Étape 7 : exposer une API avec FastAPI
On transforme la fonction en route web. app/main.py vérifie l'entrée (type et taille de la photo), appelle Gemma et renvoie une erreur claire si quelque chose casse :
`python
from fastapi import FastAPI, File, HTTPException, UploadFile
from app import gemma
app = FastAPI(title="AgriScan")
TAILLE_MAX = 5 * 1024 * 1024 # 5 Mo
@app.post("/diagnostic")
async def diagnostic(photo: UploadFile = File(...)):
if photo.content_type not in {"image/jpeg", "image/png"}:
raise HTTPException(415, "Envoie une photo JPEG ou PNG.")
contenu = await photo.read()
if len(contenu) > TAILLE_MAX:
raise HTTPException(413, "Photo trop lourde : 5 Mo maximum.")
try:
return gemma.analyser_image(contenu, photo.content_type)
except ValueError:
raise HTTPException(502, "Le modèle n'a pas renvoyé un résultat lisible. Réessaie.")
`
Note : on écrit gemma.analyser_image(...) et pas from app.gemma import analyser_image. Ce détail permet au test de l'étape 8 de remplacer la fonction.
Lance le serveur :
bash
fastapi dev app/main.py
Ouvre http://127.0.0.1:8000/docs, clique sur POST /diagnostic, puis Try it out, choisis une photo et clique Execute. Tu dois voir le JSON du diagnostic. FastAPI génère cette page tout seul : c'est aussi ta démo pour la vidéo.
Étape 8 : écrire un test qui ne dépend pas du modèle
Un test qui appelle vraiment Gemma est lent, coûte du quota et échoue sans réseau. On remplace donc l'appel au modèle par une fausse réponse avec monkeypatch, et on teste notre code à nous. tests/test_main.py :
`
from fastapi.testclient import TestClient
from app import gemma
from app.main import app
client = TestClient(app)
def test_diagnostic(monkeypatch):
faux = {"maladie": "rouille", "confiance": "moyenne", "conseil": "..."}
monkeypatch.setattr(gemma, "analyser_image", lambda data, mime: faux)
r = client.post("/diagnostic", files={"photo": ("feuille.jpg", b"fake", "image/jpeg")})
assert r.status_code == 200
assert r.json()["maladie"] == "rouille"
def test_refuse_un_pdf():
r = client.post("/diagnostic", files={"photo": ("doc.pdf", b"%PDF", "application/pdf")})
assert r.status_code == 415
def test_extraire_json_avec_texte_autour():
texte = 'Voici le résultat :\n```
json\n{"maladie": "inconnue"}\n
```'
assert gemma.extraire_json(texte) == {"maladie": "inconnue"}
`
Lance les tests, puis commite :
bash
pytest
git add .
git commit -m "Ajoute la route /diagnostic et ses tests"
git push
Trois tests verts, aucune clé d'API nécessaire. C'est exactement ce que le guide appelait « vérifier avant de faire confiance ».
Étape 9 : avancer plus vite avec Copilot (et ton AGENTS.md)
Ton AGENTS.md de l'étape 2 travaille maintenant pour toi : Copilot le lit et connaît déjà le modèle, la structure et les commandes. Tes prompts peuvent donc être courts et précis. Quelques exemples à coller dans Copilot Chat (mode agent) :
text
Ajoute une page HTML simple dans app/static/index.html avec un champ pour choisir
une photo et un bouton « Analyser ». Elle appelle POST /diagnostic et affiche
maladie, confiance et conseil. Pas de framework JavaScript. Sers-la avec FastAPI.
text
Dans app/main.py, si le modèle renvoie "photo non valide", réponds avec un code 422
et le conseil du modèle. Ajoute le test correspondant dans tests/test_main.py.
text
Relis app/gemma.py et liste les cas d'erreur non gérés. Ne modifie rien,
explique seulement.
Après chaque modification : relis le diff, lance pytest, essaie dans /docs, puis commite. Si Copilot part dans une mauvaise direction, annule (git restore .) et reformule plutôt que de corriger à la main un code que tu ne comprends pas.
Étape 10 : README, vidéo et soumission
Le jury regarde ton repo avant ton pitch. Ton README.md doit répondre à ces questions :
- Le problème : qui en souffre, en une ou deux phrases.
- La solution : ce que fait l'app et le rôle exact de Gemma 4.
- Lancer le projet : les commandes de l'étape 3 et de l'étape 7, copiables.
-
Le modèle :
gemma-4-26b-a4b-itvia l'API Gemini, et sa licence. - Outils IA utilisés : par exemple « GitHub Copilot pour la page HTML et les tests ; Gemma 4 pour le diagnostic ».
- L'équipe : noms et profils GitHub.
- La vidéo démo : le lien.
Pour la vidéo (2 minutes maximum) : enregistre ton écran pendant que tu envoies une vraie photo et que le diagnostic s'affiche. Dépose-la sur Google Drive, règle le partage sur « Tous les utilisateurs disposant du lien » et colle le lien dans le README. Ouvre ce lien dans une fenêtre privée pour vérifier qu'il marche.
Enfin, sur OrganizerHQ : Challenges, puis Add Submission, coche les défis visés (Best Open-Source AI Project, Best Use of Gemma, Best Use of GitHub Copilot), colle l'URL du repo, puis Submit Project. Le Hack Day ferme les soumissions à 12:35 : vise 12:20 pour garder une marge.
Dernière vérification avant de soumettre :
bash
git status # rien à commiter
git log --oneline | head # tes commits sont là
Puis ouvre ton repo sur GitHub depuis un autre navigateur : il doit être public, avec un fichier LICENSE.
Dépannage
| Symptôme | Cause probable | Que faire |
|---|---|---|
401, API key not valid
|
Clé absente ou mal copiée | Relance export GEMINI_API_KEY=... dans le terminal qui lance le serveur, sans guillemets en trop. |
429, RESOURCE_EXHAUSTED
|
Quota gratuit dépassé | Attends une minute, évite les appels en boucle, et utilise les tests avec monkeypatch pendant le développement. |
404, modèle introuvable |
Mauvais identifiant | Vérifie le nom exact du modèle dans Google AI Studio et dans MODEL. |
ValueError: Pas de JSON |
Le modèle a répondu en texte libre | Renforce le prompt (« uniquement un objet JSON »), affiche response.text pour voir ce qu'il a dit. |
ModuleNotFoundError: app |
Mauvais dossier | Lance les commandes depuis la racine du repo, là où se trouve app/. |
415 sur une vraie photo |
Format HEIC (iPhone) ou WebP | Convertis en JPEG, ou ajoute le type dans la liste autorisée. |
Ressources
- Gemma sur l'API Gemini, documentation officielle : https://ai.google.dev/gemma/docs/core/gemma_on_gemini_api
- Google AI Studio, pour créer ta clé et tester tes prompts : https://aistudio.google.com
- Démarrer avec Gemma (MLH) : https://mlh.link/gemma-quickstart
- Guide débutant Gemma (MLH) : https://mlh.link/gemma-beginnerguide
- Documentation Gemma (MLH) : https://mlh.link/gemma-docs
- Gemma 4 avec l'API Gemini, tutoriel de Philipp Schmid : https://www.philschmid.de/gemma-4-gemini-api
- GitHub Copilot gratuit pour les étudiants : https://mlh.link/GitHub
- Documentation FastAPI : https://fastapi.tiangolo.com
- Le guide compagnon de cet atelier : Coder avec l'IA : le guide complet, du premier prompt à la production
Une question pendant le hack ? Lève la main, un mentor passe. Après le Hack Day, on continue sur le Discord de Python Togo : https://pytogo.org/discord
Top comments (0)