À quoi ressemble un bon prompt
Une même demande d'aide confuse d'un utilisateur, traitée une fois avec un prompt écrit à la va-vite et une fois avec un prompt bien structuré, et les résultats comparés section par section. Le rôle de chaque bloc – identité, tâche, contexte, exigences, format de sortie.
- Environ 35 minutes
- Niveau : Débutant
- Testé : 2026-09-14 deepseek-flash
Le code et les sorties des programmes sont reproduits tels qu’ils ont tourné : commentaires et sorties sont donc en chinois.
« Prompt engineering », cela sonne mystérieux, et le web regorge de « modèles universels » et de « formules magiques ». Au fond, pourtant, c'est très simple : écrire clairement ce que vous avez en tête. Le modèle ne connaît pas votre contexte, ne sait pas à qui s'adresse le résultat, ne sait pas à quoi ressemble le « bien » dans votre esprit. Ce que vous n'écrivez pas, il ne peut que le deviner.
Cette leçon prend un cas réel, écrit deux prompts pour la même tâche et regarde où est la différence.
Le cas : transformer une demande d'aide en issue
Vous êtes mainteneur de httpx, et un utilisateur écrit ceci dans le groupe de discussion :
用httpx下载大文件老是断 我用的stream 下到一半就报错了 ReadTimeout
代码大概是 with httpx.stream("GET", url) as r: for chunk in r.iter_bytes(): f.write(chunk)
文件2个G左右,网不太好,公司内网。之前用requests好像没这问题??
版本是0.27 python3.11 mac
急!!!明天要交
Vous voulez que le modèle en fasse une issue GitHub propre, pour que d'autres mainteneurs puissent enquêter.
Première version : écrite à la va-vite
v1 = [{"role": "user", "content": "帮我整理一下这个问题:\n" + REPORT}]
La réponse du modèle (réflexion désactivée ; c'est le texte exact que j'ai obtenu, le vôtre sera différent) :
# 问题整理
## 一句话概述
用 httpx 的 `stream` 下载 ~2GB 大文件,网络环境较差(公司内网),下载中途抛 `ReadTimeout`,改用 requests 则无此问题,需要尽快解决。
## 环境信息
- 库/版本:httpx **0.27**
- Python:**3.11**
- 系统:**macOS**
- 网络:公司内网,网速较差/不稳定
- 文件大小:约 **2 GB**
## 复现代码
```python
with httpx.stream("GET", url) as r:
for chunk in r.iter_bytes():
f.write(chunk)
```
## 现象
- 下载过程**中途中断**
- 报错类型:**ReadTimeout**
- 相同 URL 和网络下,**requests 不报错**(或很少报错)
## 关键疑问
1. 为什么 httpx 会 ReadTimeout 而 requests 不会?两者的超时/重试默认行为差异在哪?
2. httpx 的 `timeout` 参数在 stream 场景下具体作用于什么?(连接 / 单个 chunk 间隔 / 整体?)
3. 有没有办法在 stream 时**禁用或放宽读超时**,让慢速下载不被打断?
4. 是否需要加**重试 / 断点续传(Range)**来兜底?
## 目标
- 让 2GB 大文件在弱网内网环境下能稳定下完
- 时间紧急,明早交付
## 可能的排查方向(供参考)
- httpx 默认 `timeout=5.0`,requests 默认是"无超时"(会一直等),这很可能是根因
- 可尝试:`httpx.stream("GET", url, timeout=httpx.Timeout(connect=10, read=None, write=10, pool=10))`
- 或加大 read timeout,加 `transport=httpx.HTTPTransport(retries=3)`
- 断点续传:记录已下字节,用 `Range: bytes=N-` 续传
---
需要我帮你针对哪个方向继续深挖(比如给出可直接替换的稳定下载代码)?
Cela a l'air professionnel, et la mise en page est soignée. Mais en comparant avec le message d'origine, les problèmes sont nombreux :
- Une supposition devient un fait. L'utilisateur disait « 之前用 requests 好像没这问题?? » (avant, avec requests, je crois que ce problème n'existait pas ??) ; chez le modèle, cela devient « avec requests, ce problème n'existe pas » et « avec la même URL et le même réseau, requests ne produit pas d'erreur ». Un mainteneur lisant cela pourrait suivre une piste jamais confirmée.
- Il fait ce qu'on ne lui a pas demandé. Je voulais seulement mettre le problème en forme, et il ajoute une section « pistes d'investigation », plus un « objectif » et des « questions clés ». Pour une issue, c'est du bruit.
- Un conseil est faux. Il suggère d'ajouter
HTTPTransport(retries=3), mais la documentation de httpx est très claire : ce paramètre ne réessaie qu'en cas d'échec de connexion (ConnectError,ConnectTimeout) et n'a aucun effet sur unReadTimeouten plein téléchargement. Un conseil trompeur. - Le format n'est pas celui d'une issue. Il se termine même par « voulez-vous que je creuse davantage ? », une phrase bien étrange dans une issue.
Ce n'est pas la faute du modèle. « Aide-moi à mettre ça en forme » peut se comprendre de cent façons : pour qui ? sous quelle forme ? peut-il ajouter son propre avis ? Il a choisi l'interprétation « être aussi utile que possible », donc plus il en écrit, mieux c'est.
Deuxième version : écrire clairement les exigences
v2 = [
{
"role": "system",
"content": """你是 httpx 开源项目的维护者,负责把用户在群里的求助整理成 GitHub issue。
任务:把 <report> 标签里的用户原话整理成一个 issue,给其他维护者看。
要求:
- 只使用原话里有的信息,不要猜测或补充原话里没有的细节。
- 原话里没提到、但排查需要的信息,列在"待确认"里。
- 不要给解决方案,这一步只整理问题。
输出格式(Markdown,只输出 issue 本身):
## 标题
一句话,说清现象和场景
## 环境
## 复现代码
## 现象
## 待确认""",
},
{"role": "user", "content": f"<report>\n{REPORT}\n</report>"},
]
La réponse du modèle :
## 标题
使用 httpx.stream 下载约 2G 大文件时,中途报 ReadTimeout 错误
## 环境
- httpx 版本:0.27
- Python 版本:3.11
- 操作系统:macOS
- 网络环境:公司内网,网络状况较差
## 复现代码
```python
with httpx.stream("GET", url) as r:
for chunk in r.iter_bytes():
f.write(chunk)
```
## 现象
- 使用 `httpx.stream` 下载约 2G 的大文件时,下载到一半报错 `ReadTimeout`。
- 网络状况较差(公司内网)。
- 用户反馈之前使用 requests 时似乎没有该问题。
## 待确认
- 是否设置了自定义 timeout(尤其是 read timeout)?具体值是多少?
- 报错时的完整 traceback 信息。
- 下载中断是必现还是偶发?中断位置是否固定?
- `url` 指向的服务端类型(如是否支持分块传输、是否有响应延迟等)。
- requests 与 httpx 测试时是否为同一 URL、同一网络环境,以及 requests 是否也配置过 timeout。
- 是否使用代理。
« 似乎没有该问题 » (ce problème semble ne pas exister) conserve l'incertitude de l'utilisateur. Pas de solution improvisée, donc pas de mauvais conseil. La section « à confirmer » est très utile : la traceback complète, si le problème se reproduit à chaque fois, si requests avait alors un délai d'expiration défini, voilà ce qu'il faut vraiment demander à l'utilisateur pour enquêter. Ce texte peut être collé tel quel dans GitHub.
Ce que la deuxième version ajoute
Décomposé, le prompt de la deuxième version contient cinq blocs.
Identité : « Tu es mainteneur du projet open source httpx ». Cette phrase indique au modèle avec quel point de vue et quel niveau d'expertise traiter la tâche. Ce n'est pas magique : écrire « tu es le meilleur expert mondial » ne rend pas le modèle plus intelligent. Son rôle est de donner un contexte, pour qu'il sache quelles connaissances sont pertinentes et quel style le résultat doit avoir.
Tâche : « mettre le message d'origine sous forme d'issue, pour d'autres mainteneurs ». L'essentiel est de dire ce qu'on produit et pour qui. Une issue pour des mainteneurs et une réponse pour l'utilisateur ne s'écrivent pas du tout de la même façon.
Exigences : trois règles. Remarquez qu'elles sont toutes très concrètes : « n'utilise que les informations présentes dans le message d'origine », « ne donne pas de solution ». Des exigences comme « sois professionnel » ou « sois précis » ne servent presque à rien, car le modèle se croit déjà professionnel et précis.
Format de sortie : donner directement la structure. Pour obtenir un format, le dessiner est plus fiable que de le décrire en mots (« divise en titre, environnement, symptômes… »).
Délimiteurs : le message de l'utilisateur est placé entre <report> et </report>. Le modèle distingue ainsi clairement vos instructions du matériau à traiter. Si le matériau contient par hasard une phrase « ignore les consignes ci-dessus », elle risque moins d'être prise pour une instruction. (La leçon 8 du module 05 est consacrée à ce type d'attaque.) Balises de style XML, trois accents graves ou """ : tout convient, l'important est d'être cohérent.
Par ailleurs, la deuxième version met les règles fixes dans le message system et le matériau variable dans le message user. C'est plus clair, et cela permet aussi de toucher le cache : le message system est toujours identique et, comme vu à la leçon précédente, il est facturé au prix avec cache.
Dire « quoi faire », mais aussi « quoi ne pas faire »
Un conseil fréquent sur le web : « dites au modèle ce qu'il doit faire, pas ce qu'il ne doit pas faire ». Il est fondé : si vous écrivez seulement « ne sois pas bavard », le modèle ne sait pas jusqu'où être concis ; « réponds en une phrase » est bien plus clair.
Mais « ne pas faire » est indispensable dans un cas : quand le modèle a une forte habitude par défaut. Dans la première version, donner spontanément une solution était une telle habitude. Dans la deuxième, la phrase « ne donne pas de solution » suffit à l'arrêter.
La leçon 3 du module 00 avait un autre exemple : quand on demandait de « décrire l'automne en cinq caractères », le modèle donnait cinq caractères, plus six en bonus. J'ai réessayé avec ce prompt :
ask([{"role": "user", "content": "用五个字形容秋天"}])
ask([{"role": "user", "content": "用五个字形容秋天。只输出这五个字,不要标点,不要解释,不要给其他选项。"}])
原提示词: **金风送爽时**
(也可以换成:**霜叶红于花**、**一叶知秋意**、**秋高气爽天**,看你喜欢哪种意境。)
改进后: 秋高气爽时
« Ne produis que ces cinq caractères » est une exigence positive ; « pas de ponctuation, pas d'explication, pas d'autres options » bloque d'avance les trois choses que le modèle ferait le plus probablement en trop. La combinaison des deux marche le mieux.
Dans quel ordre écrire un prompt
Quand j'écris un prompt, je réfléchis en général dans cet ordre :
- Qui utilise le résultat ? Un humain ou un programme qui l'analyse ? Un expert ou un débutant ?
- À quoi ressemble un bon résultat ? Le mieux est d'écrire à la main la sortie idéale. Si vous n'y arrivez pas, c'est que vous ne savez pas encore vous-même ce que vous voulez.
- Où le modèle risque-t-il le plus de se tromper ? Essayez d'abord un prompt d'une phrase, voyez ce qui ne va pas, puis ajoutez des règles ciblées.
- Séparer le matériau des instructions.
L'étape 3 est importante : n'écrivez pas d'emblée un « prompt parfait » de plusieurs centaines de mots. Écrivez d'abord la version la plus simple, voyez où elle échoue, puis complétez. Chaque règle doit correspondre à un problème que vous avez vu de vos yeux. Le prompt obtenu est court, et chaque phrase sert.
Quand ce n'est pas nécessaire
Pour une question ponctuelle ou faire retoucher un texte, une phrase suffit ; complétez si le résultat ne vous convient pas. Les prompts structurés servent surtout dans du code, là où ils sont appelés encore et encore : vous ne pouvez pas être là à chaque fois pour corriger, alors il faut tout écrire clairement une bonne fois.
Exercices
- Lancez
code/02-prompting/prompt_structure.pyet voyez en quoi vos première et deuxième versions diffèrent des miennes. - Supprimez une des « exigences » de la deuxième version (par exemple « ne donne pas de solution ») et relancez plusieurs fois : le modèle recommence-t-il à donner des conseils ?
- Prenez un texte réel de votre travail qui aurait besoin d'être mis en forme (compte rendu de réunion, e-mail de client, journal d'erreurs), traitez-le d'abord avec un prompt d'une phrase, repérez les défauts du résultat, puis réécrivez le prompt selon la structure en cinq blocs de cette leçon. Notez quel problème résout chaque règle ajoutée.
Auto-test
1. Le résultat de la première version est joliment mis en page. Pourquoi dit-on qu'il pose problème ?
Il présente une supposition de l'utilisateur (« je crois que ce problème n'existait pas ») comme un fait établi, fait ce qu'on ne lui a pas demandé (des pistes d'investigation), et l'un de ses conseils (utiliser HTTPTransport(retries=3) contre un délai de lecture) est faux. Le format ne convient pas non plus à une issue telle quelle. Une jolie mise en page ne garantit pas un contenu fiable ; il faut vérifier par rapport au matériau d'origine.
2. Écrire « tu es le meilleur expert Python au monde » dans un prompt rend-il les réponses du modèle plus précises ?
Pratiquement pas. La description d'identité donne un contexte au modèle : quel point de vue, quel style, quelles connaissances sont pertinentes. Elle ne lui donne pas de capacités supplémentaires. Plutôt que d'exagérer l'identité, écrivez clairement la tâche, le destinataire, les exigences concrètes et le format de sortie.
3. Pourquoi envelopper le matériau de l'utilisateur dans une balise comme <report> ?
Pour que le modèle distingue clairement ce qui est votre instruction de ce qui est le matériau à traiter. Le contenu du matériau risque ainsi moins d'être pris pour une instruction, y compris quand quelqu'un y écrit volontairement une attaque du type « ignore les consignes précédentes ».
Questions et discussion
Bloqué sur cette leçon ? Posez votre question ici. Et si vous pouvez répondre à quelqu'un, n'hésitez pas.
Une question rapporte 3 points, une réponse 6. Les messages paraissent après vérification.
Chargement de la discussion…