Aller au contenu principal

Ingenierie des prompts pour la generation de code

Theorie 60 min

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.

Analogie concrete

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 :

ComposantDescriptionExemple
RoleDefinir la persona et le niveau d'expertise de l'IA« Tu es un developpeur backend Python senior »
ContexteFournir le contexte du projet et de la stack« Je construis une app FastAPI avec SQLAlchemy et PostgreSQL »
TacheDefinir clairement ce que vous voulez« ecris une fonction qui pagine les resultats de requete »
ContraintesSpecifier les regles, limites et preferences« Utilise async/await, renvoie des modeles Pydantic, gere les resultats vides »
FormatDefinir 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

StrategieComplexiteTokens utilisesIdeal pourQualite
Zero-shotFaiblePeuTaches standard, boilerplateBonne
Few-shotMoyenneModerePatterns personnalises, style specifique au projetTres bonne
Chain-of-thoughteleveeBeaucoupAlgorithmes complexes, architectureExcellente

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:

  1. Explain what is causing this error
  2. Show the corrected code
  3. 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:

  1. Explain what is causing this error
  2. Show the corrected code
  3. 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:

  1. Normal/happy path with typical inputs
  2. Edge cases: [list them: empty input, None, boundaries]
  3. Error cases: [list expected exceptions]
  4. [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:

  1. What is the overall purpose of this code?
  2. Walk through it line by line
  3. What are the time and space complexities?
  4. Are there any potential issues or improvements?
  5. 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 :

  1. Explorer l'espace du probleme d'abord
  2. Concevoir l'architecture de la solution
  3. Implementer de facon incrementale
  4. Reviser et affiner chaque partie

Strategies multi-tours efficaces

StrategieDescriptionQuand l'utiliser
Scaffold d'abordDemander la structure/squelette, puis remplirGrandes fonctionnalites
Test-drivenDemander les tests d'abord, puis l'implementationLogique metier critique
Boucle de revueGenerer → reviser → affiner → repeterCode sensible aux performances
IncrementalConstruire la fonctionnalite piece par pieceApprendre un nouveau framework
Conseil pro — Gestion du contexte

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-patternProblemeMeilleure approche
« ecris-moi une app »Beaucoup trop vague, perimetre enormeDecouper en taches specifiques et petites
Pas de contexteL'IA devine votre stack techniqueToujours preciser langage, framework, versions
Accepter la premiere sortieLa premiere tentative est rarement optimaleIterer et affiner
Ignorer les erreurs dans la sortieLe code IA peut ne pas s'executerToujours tester avant de committer
Sur-promptingPrompts de 2000 mots pour des taches simplesAdapter la longueur du prompt a la complexite
Pas de contraintesL'IA choisit des defauts que vous ne voulez pasenoncer ce que vous voulez et ne voulez pas
Piege courant

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 :

MetriqueCe qu'elle mesureCible
Taux d'acceptation% des suggestions IA utilisees telles quelles30–50 %
Nombre d'iterationsNombre de raffinements de prompt necessaires1–3
Temps jusqu'au code fonctionnelDu prompt au code teste et fonctionnel< 2× le codage manuel
Taux d'introduction de bugsBugs trouves dans le code IA lors de la revue< 10 % des suggestions
Changements de contexteFois ou vous abandonnez l'IA et codez manuellement< 20 %

Points cles a retenir

ConceptResume
Prompt a 5 composantsRole, Contexte, Tache, Contraintes, Format
Zero-shotInstruction directe, pas d'exemples — pour les taches standard
Few-shotFournir des exemples du pattern souhaite — pour le style personnalise
Chain-of-thoughtRaisonnement etape par etape — pour les problemes complexes
ModelesStructures de prompts reutilisables pour les taches courantes
Pattern PersonaDefinir le niveau d'expertise et le style de l'IA
Raffinement iteratifConstruire des solutions complexes via un dialogue multi-tours
Anti-patternseviter les prompts vagues, l'absence de contexte et l'acceptation sans revue

Exercices pratiques

  1. Prenez une fonction de votre projet actuel et ecrivez un prompt qui la genererait
  2. Essayez la meme tache en zero-shot, few-shot et chain-of-thought — comparez les resultats
  3. ecrivez un modele de prompt pour la tache la plus courante de votre flux de travail
  4. Pratiquez le pattern de raffinement iteratif sur une fonctionnalite multi-fichiers