Ingenierie des prompts pour la generation de code
Pourquoi l'ingenierie des prompts compte
La difference entre une reponse IA inutile et un extrait de code pret pour la production tient souvent a une seule phrase dans votre prompt. L'ingenierie des prompts est l'art de communiquer efficacement avec les modeles IA pour obtenir un code precis et exploitable.
Imaginez embaucher un developpeur freelance a distance. Si vous lui envoyez un message vague comme « construis-moi un site web », vous obtiendrez quelque chose de generique. Mais si vous dites « cree un endpoint FastAPI qui accepte un payload JSON avec les champs name (string, requis) et age (int, 1–120), valide l'entree et renvoie une reponse 201 avec l'objet utilisateur cree », vous obtiendrez exactement ce dont vous avez besoin. Solliciter une IA repose sur la meme discipline.
Anatomie d'un bon prompt pour le code
Un prompt bien structure comporte cinq composants cles :
| Composant | Description | Exemple |
|---|---|---|
| Role | Definir la persona et le niveau d'expertise de l'IA | « Tu es un developpeur backend Python senior » |
| Contexte | Fournir le contexte du projet et de la stack | « Je construis une app FastAPI avec SQLAlchemy et PostgreSQL » |
| Tache | Definir clairement ce que vous voulez | « ecris une fonction qui pagine les resultats de requete » |
| Contraintes | Specifier les regles, limites et preferences | « Utilise async/await, renvoie des modeles Pydantic, gere les resultats vides » |
| Format | Definir la structure attendue de la sortie | « Inclus les annotations de type, la docstring et un exemple d'utilisation » |
Le prompt a cinq composants en action
❌ Mauvais prompt :
Write a pagination function
✅ Bon prompt :
You are a senior Python backend developer.
I'm building a FastAPI application with SQLAlchemy (async) and PostgreSQL.
Write an async function called `paginate_query` that:
- Accepts a SQLAlchemy select statement, page number (int, default 1),
and page size (int, default 20, max 100)
- Returns a Pydantic model with fields: items (list), total (int),
page (int), pages (int), has_next (bool), has_prev (bool)
- Handles edge cases: page < 1, page_size < 1, empty results
- Uses SQLAlchemy 2.0 style with `select()` and `session.execute()`
Include type hints, a docstring, and the Pydantic response model definition.
Strategies de prompting
Strategie 1 : Zero-shot prompting
Demandez directement sans fournir d'exemples. Fonctionne mieux pour les taches standard et bien connues.
Write a Python decorator that measures the execution time of a function
and logs it using the `logging` module at DEBUG level.
Include the function name and elapsed time in milliseconds.
Sortie attendue :
import functools
import logging
import time
logger = logging.getLogger(__name__)
def timing_decorator(func):
"""Measure and log the execution time of a function."""
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
elapsed_ms = (time.perf_counter() - start) * 1000
logger.debug(f"{func.__name__} executed in {elapsed_ms:.2f}ms")
return result
return wrapper
Strategie 2 : Few-shot prompting
Fournissez des exemples du pattern entree/sortie souhaite. L'IA imite votre style.
I have a pattern for creating FastAPI route handlers. Follow this pattern exactly:
Example 1:
@router.get("/users", response_model=list[UserResponse])
async def list_users(
db: AsyncSession = Depends(get_db),
skip: int = Query(0, ge=0),
limit: int = Query(20, ge=1, le=100),
):
"""List all users with pagination."""
result = await db.execute(select(User).offset(skip).limit(limit))
return result.scalars().all()
Example 2:
@router.get("/users/{user_id}", response_model=UserResponse)
async def get_user(
user_id: int = Path(..., ge=1),
db: AsyncSession = Depends(get_db),
):
"""Get a specific user by ID."""
result = await db.execute(select(User).where(User.id == user_id))
user = result.scalar_one_or_none()
if not user:
raise HTTPException(status_code=404, detail="User not found")
return user
Now, following the same pattern, create:
1. POST /users (create user, return 201)
2. PUT /users/{user_id} (update user)
3. DELETE /users/{user_id} (delete user, return 204)
Strategie 3 : Chain-of-Thought (CoT)
Demandez a l'IA de raisonner etape par etape avant d'ecrire le code. Produit de meilleurs resultats pour les taches complexes.
I need to implement a rate limiter for my API. Before writing code:
1. List 3 common rate limiting algorithms and their trade-offs
2. Recommend the best one for a FastAPI app handling ~1000 req/sec
3. Explain how you would implement it step by step
4. Then write the complete implementation
Use Redis as the backend store. The rate limiter should support
per-user limits with configurable windows.
Comparaison des strategies
| Strategie | Complexite | Tokens utilises | Ideal pour | Qualite |
|---|---|---|---|---|
| Zero-shot | Faible | Peu | Taches standard, boilerplate | Bonne |
| Few-shot | Moyenne | Modere | Patterns personnalises, style specifique au projet | Tres bonne |
| Chain-of-thought | elevee | Beaucoup | Algorithmes complexes, architecture | Excellente |
Modeles de prompts pour les taches courantes
Modele 1 : ecrire une fonction
Write a [language] function called `[name]` that:
- Input: [describe parameters with types]
- Output: [describe return value with type]
- Behavior: [describe what it does step by step]
- Edge cases to handle: [list them]
- Constraints: [performance, style, libraries allowed]
Include type hints and a docstring.
Exemple : Utilisation du modele « ecrire une fonction »
Write a Python function called `chunk_list` that:
- Input: a list of any type (items: list[T]) and chunk size (size: int)
- Output: a list of lists, each containing at most `size` elements (list[list[T]])
- Behavior: split the input list into chunks of the given size;
last chunk may be smaller
- Edge cases: empty list → return [], size <= 0 → raise ValueError,
size > len(list) → return [list]
- Constraints: use only standard library, must be generic (TypeVar)
Include type hints and a docstring.
Resultat :
from typing import TypeVar
T = TypeVar('T')
def chunk_list(items: list[T], size: int) -> list[list[T]]:
"""Split a list into chunks of the given size.
Args:
items: The list to split.
size: Maximum number of elements per chunk.
Returns:
A list of lists, each containing at most `size` elements.
Raises:
ValueError: If size is less than or equal to 0.
"""
if size <= 0:
raise ValueError(f"Chunk size must be positive, got {size}")
return [items[i:i + size] for i in range(0, len(items), size)]
Modele 2 : Deboguer une erreur
I'm getting this error in my [language/framework] application:
[paste the full error traceback]
Here is the relevant code:
```[language]
[paste the code that triggers the error]
Environment: [Python version, OS, key library versions]
Please:
- Explain what is causing this error
- Show the corrected code
- Explain what you changed and why
<details>
<summary>Exemple : Utilisation du modele « Deboguer une erreur »</summary>
```text
I'm getting this error in my FastAPI application:
TypeError: Object of type datetime is not JSON serializable
Here is the relevant code:
```python
@app.get("/events")
async def get_events():
events = await db.fetch_all(query)
return {"events": events}
Environment: Python 3.11, FastAPI 0.104, SQLAlchemy 2.0
Please:
- Explain what is causing this error
- Show the corrected code
- Explain what you changed and why
</details>
### Modele 3 : Refactoriser du code
```text
Refactor the following [language] code for:
- [ ] Better readability
- [ ] Improved performance
- [ ] Following [convention/standard] conventions
- [ ] Reducing code duplication
Current code:
```[language]
[paste the code]
Requirements:
- Preserve the exact same external behavior (inputs/outputs)
- Add type hints if missing
- [Any specific constraints]
### Modele 4 : ecrire des tests
```text
Write [test framework] tests for the following function:
```[language]
[paste the function to test]
Cover these scenarios:
- Normal/happy path with typical inputs
- Edge cases: [list them: empty input, None, boundaries]
- Error cases: [list expected exceptions]
- [Any specific test patterns to follow]
Use [fixtures/mocks/parametrize] as appropriate. Follow the Arrange-Act-Assert pattern.
### Modele 5 : Expliquer du code
```text
Explain the following [language] code in detail:
```[language]
[paste the code]
Please:
- What is the overall purpose of this code?
- Walk through it line by line
- What are the time and space complexities?
- Are there any potential issues or improvements?
- Explain any non-obvious design choices
---
## Bons vs mauvais prompts — Exemples concrets
### Exemple 1 : Validation de donnees
| Aspect | ❌ Mauvais prompt | ✅ Bon prompt |
|--------|------------------|---------------|
| **Prompt** | « valide les donnees utilisateur » | « ecris un BaseModel Pydantic appele UserCreate avec les champs : email (format email valide), password (min 8 caracteres, doit inclure majuscule, minuscule et chiffre), age (int, 13–120). Inclus des validateurs personnalises avec des messages d'erreur clairs. » |
| **Resultat** | Fonction generique, entrees floues | Modele Pydantic precis avec validateurs |
| **Pourquoi** | Pas de precisions sur les donnees, format, regles | Champs, types, contraintes et format clairs |
### Exemple 2 : Requete base de donnees
| Aspect | ❌ Mauvais prompt | ✅ Bon prompt |
|--------|------------------|---------------|
| **Prompt** | « ecris une requete SQL pour les utilisateurs » | « ecris une requete SQLAlchemy 2.0 async qui recupere les utilisateurs inscrits dans les 30 derniers jours, ayant verifie leur email et ayant au moins une commande terminee. Renvoie user id, email, signup_date et order_count. Trie par order_count decroissant. Utilise le style select(), pas l'API Query legacy. » |
| **Resultat** | `SELECT * FROM users` | Requete async complete avec jointures et filtres |
| **Pourquoi** | Quels utilisateurs ? Tous ? Certains ? | Criteres, champs de sortie et style clairs |
### Exemple 3 : Gestion des erreurs
| Aspect | ❌ Mauvais prompt | ✅ Bon prompt |
|--------|------------------|---------------|
| **Prompt** | « ajoute la gestion des erreurs » | « Ajoute la gestion des erreurs a cet endpoint FastAPI. Intercepte ValueError (renvoie 422), SQLAlchemyError (renvoie 500 avec message generique, log l'erreur complete) et HTTPException (relance). Utilise un bloc try/except et renvoie des reponses JSON structurees avec les champs `detail` et `error_code`. » |
| **Resultat** | `except Exception: pass` | Gestion des erreurs specifique et structuree |
---
## Patterns avances de prompting
### Pattern 1 : Pattern Persona
Definir un niveau d'expertise et une perspective specifiques pour l'IA :
```text
You are a staff-level Python developer with 15 years of experience,
specializing in high-performance API design. You follow PEP 8 strictly,
always write comprehensive docstrings, and prefer composition over inheritance.
Review this code and suggest improvements:
[code]
Pattern 2 : Pattern Template
Definir la structure exacte que doit suivre la sortie :
Generate a FastAPI CRUD router following this exact template:
# File: routers/{resource_name}.py
# Imports: [list needed imports]
router = APIRouter(prefix="/{resource_name}", tags=["{Resource Name}"])
# GET /{resource_name} - List all
# GET /{resource_name}/{id} - Get one
# POST /{resource_name} - Create
# PUT /{resource_name}/{id} - Update
# DELETE /{resource_name}/{id} - Delete
Generate this for a "Product" resource with fields:
name (str), price (float), category (str), in_stock (bool)
Pattern 3 : Pattern Contraintes
enoncer explicitement ce que l'IA doit et ne doit PAS faire :
Write a file upload handler for FastAPI.
DO:
- Accept only PNG, JPG, and PDF files
- Limit file size to 5MB
- Generate a UUID-based filename
- Save to an /uploads directory
- Return the file URL in the response
DO NOT:
- Use synchronous file I/O
- Store the original filename (security risk)
- Allow path traversal in filenames
- Skip MIME type validation (don't rely only on extension)
Pattern 4 : Pattern Raffinement iteratif
Construire des solutions complexes en plusieurs tours :
Tour 1 :
I need to build a JWT authentication system for FastAPI.
First, outline the architecture: what modules, classes, and functions
do we need? Don't write code yet.
Tour 2 :
Good. Now implement the `auth/jwt_handler.py` module with the
create_access_token and verify_token functions.
Tour 3 :
Now add proper error handling: expired tokens, invalid signatures,
missing claims. Use custom exception classes.
Tour 4 :
Write pytest tests for all the functions in jwt_handler.py.
Cover happy paths, expired tokens, tampered tokens, and missing fields.
Conversations multi-tours pour les taches complexes
Quand utiliser le multi-tour
Les prompts uniques conviennent aux taches simples. Pour les fonctionnalites complexes, utilisez des conversations multi-tours pour :
- Explorer l'espace du probleme d'abord
- Concevoir l'architecture de la solution
- Implementer de facon incrementale
- Reviser et affiner chaque partie
Strategies multi-tours efficaces
| Strategie | Description | Quand l'utiliser |
|---|---|---|
| Scaffold d'abord | Demander la structure/squelette, puis remplir | Grandes fonctionnalites |
| Test-driven | Demander les tests d'abord, puis l'implementation | Logique metier critique |
| Boucle de revue | Generer → reviser → affiner → repeter | Code sensible aux performances |
| Incremental | Construire la fonctionnalite piece par piece | Apprendre un nouveau framework |
Dans les longues conversations, l'IA peut « oublier » le contexte precedent. Resumez periodiquement les decisions et contraintes cles :
To recap: we're building a JWT auth system for FastAPI with:
- Access tokens (15 min expiry) and refresh tokens (7 days)
- Stored in HTTP-only cookies
- RS256 signing algorithm
- Custom User model with SQLAlchemy
Now, let's implement the refresh token rotation logic.
Anti-patterns de l'ingenierie des prompts
evitez ces erreurs courantes :
| Anti-pattern | Probleme | Meilleure approche |
|---|---|---|
| « ecris-moi une app » | Beaucoup trop vague, perimetre enorme | Decouper en taches specifiques et petites |
| Pas de contexte | L'IA devine votre stack technique | Toujours preciser langage, framework, versions |
| Accepter la premiere sortie | La premiere tentative est rarement optimale | Iterer et affiner |
| Ignorer les erreurs dans la sortie | Le code IA peut ne pas s'executer | Toujours tester avant de committer |
| Sur-prompting | Prompts de 2000 mots pour des taches simples | Adapter la longueur du prompt a la complexite |
| Pas de contraintes | L'IA choisit des defauts que vous ne voulez pas | enoncer ce que vous voulez et ne voulez pas |
Ne tombez pas dans le piege du prompt-and-pray — ecrire un prompt, accepter la sortie sans revue et passer a la suite. Le code genere par l'IA doit toujours etre traite comme un brouillon necessitant une validation humaine.
Mesurer l'efficacite des prompts
Comment savoir si vos prompts fonctionnent bien ? Suivez ces metriques :
| Metrique | Ce qu'elle mesure | Cible |
|---|---|---|
| Taux d'acceptation | % des suggestions IA utilisees telles quelles | 30–50 % |
| Nombre d'iterations | Nombre de raffinements de prompt necessaires | 1–3 |
| Temps jusqu'au code fonctionnel | Du prompt au code teste et fonctionnel | < 2× le codage manuel |
| Taux d'introduction de bugs | Bugs trouves dans le code IA lors de la revue | < 10 % des suggestions |
| Changements de contexte | Fois ou vous abandonnez l'IA et codez manuellement | < 20 % |
Points cles a retenir
| Concept | Resume |
|---|---|
| Prompt a 5 composants | Role, Contexte, Tache, Contraintes, Format |
| Zero-shot | Instruction directe, pas d'exemples — pour les taches standard |
| Few-shot | Fournir des exemples du pattern souhaite — pour le style personnalise |
| Chain-of-thought | Raisonnement etape par etape — pour les problemes complexes |
| Modeles | Structures de prompts reutilisables pour les taches courantes |
| Pattern Persona | Definir le niveau d'expertise et le style de l'IA |
| Raffinement iteratif | Construire des solutions complexes via un dialogue multi-tours |
| Anti-patterns | eviter les prompts vagues, l'absence de contexte et l'acceptation sans revue |
Exercices pratiques
- Prenez une fonction de votre projet actuel et ecrivez un prompt qui la genererait
- Essayez la meme tache en zero-shot, few-shot et chain-of-thought — comparez les resultats
- ecrivez un modele de prompt pour la tache la plus courante de votre flux de travail
- Pratiquez le pattern de raffinement iteratif sur une fonctionnalite multi-fichiers