De Dev Web a Ingenieur IA
De Dev Web à Ingénieur IA #2 — Premier appel LLM & Prompt Engineering
System prompt, température : comment parler à un LLM

Intro
L’article précédent nous a donné un environnement complet : Ollama avec qwen3:4b (ou le modèle de ton choix), PostgreSQL avec pgvector, un projet TypeScript prêt à l’emploi.
Cet article est indépendant : pas besoin de base de données, juste un LLM qui tourne. Si tu viens de l’article 1, copie simplement le dossier :
cp -r article-01-setup article-02-prompt-engineering
cd article-02-prompt-engineering
rm -rf node_modules package-lock.json
npm install
Si tu démarres ici, initialise un projet TypeScript minimal :
mkdir -p article-02-prompt-engineering/src
cd article-02-prompt-engineering
npm init -y
npm i -D @types/node tsx typescript
npm pkg set scripts.start="tsx src/index.ts"
Le modèle doit être disponible. Vérifie avec :
ollama ls | grep qwen3
Prêt ? On va enfin parler à un LLM.
System prompt vs User prompt
Un appel à un LLM peut contenir deux types de messages :
- System prompt (
role: "system") — instruction permanente qui définit le comportement du modèle. C’est le “mode d’emploi” : ton, format, règles. - User prompt (
role: "user") — la requête, ce qu’on lui demande à cet instant précis.
L’analogie : le system prompt est le chef de cuisine qui définit la recette et les règles. Le user prompt est le client qui passe commande.
On va écrire un script qui appelle le même user prompt avec et sans system prompt, pour voir la différence.
Le code
const MODEL = "qwen3:4b";
const OLLAMA_BASE = process.env.OLLAMA_HOST ?? "http://localhost:11434";
interface ChatMessage {
role: "system" | "user" | "assistant";
content: string;
}
interface LLMResponse {
message: ChatMessage;
}
async function chat(
model: string,
messages: ChatMessage[],
temperature?: number,
): Promise<string> {
const body: Record<string, unknown> = { model, messages, stream: false, keep_alive: 0 };
if (temperature !== undefined) {
body.options = { temperature };
}
const res = await fetch(`${OLLAMA_BASE}/api/chat`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
});
const data = (await res.json()) as LLMResponse;
return data.message.content;
}
async function withSpinner<T>(label: string, fn: () => Promise<T>): Promise<T> {
const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
let i = 0;
const interval = setInterval(() => {
process.stdout.write(`\r${frames[i]} ${label}`);
i = (i + 1) % frames.length;
}, 80);
try {
const result = await fn();
process.stdout.write(`\r✓ ${label}\n`);
return result;
} catch (e) {
process.stdout.write(`\r✗ ${label}\n`);
throw e;
} finally {
clearInterval(interval);
}
}
async function main() {
console.log("");
console.log("=== 1. System prompt vs User prompt ===\n");
const question = "Explique ce qu'est un LLM en deux phrases maximum.";
const sansSystem = await withSpinner("Appel sans system prompt...", () =>
chat(MODEL, [{ role: "user", content: question }]),
);
console.log(` ${sansSystem}\n`);
const avecSystem = await withSpinner("Appel avec system prompt...", () =>
chat(MODEL, [
{
role: "system",
content:
"Tu es un professeur d'informatique. Réponds de façon pédagogique et utilise une analogie.",
},
{ role: "user", content: question },
]),
);
console.log(` ${avecSystem}\n`);
console.log("---\n");
console.log("=== 2. Température ===\n");
const creativePrompt =
"Trouve un nom original pour un assistant IA personnel.";
for (const temp of [0, 0.7, 1.5]) {
const reponse = await withSpinner(`Temp = ${temp}...`, () =>
chat(MODEL, [{ role: "user", content: creativePrompt }], temp),
);
console.log(` ${reponse}\n`);
}
}
main().catch((err) => {
console.error(`\nErreur : ${err.message}`);
process.exit(1);
});
Exécution
npm start

Explication
chat() — notre fonction centrale. Elle envoie une requête POST à l’API /api/chat d’Ollama avec le modèle, les messages (tableau avec rôles system/user/assistant), et optionnellement la température via body.options. On utilise fetch natif, pas de SDK.
keep_alive: 0 — Ollama garde par défaut le modèle chargé en mémoire pendant 5 minutes après chaque appel (on dit qu’il le “garde chaud”). C’est pratique pour des conversations interactives : les réponses sont instantanées. Mais sur une machine avec 16 Go de RAM, multiplier les appels sans libérer la mémoire finit par saturer — et Ollama crashe.
En passant keep_alive: 0, on force Ollama à décharger le modèle immédiatement après la réponse. Chaque appel est un peu plus lent (le modèle doit être rechargé depuis le disque, 1 à 2 secondes), mais on ne risque plus de saturer la RAM. C’est le bon compromis pour des scripts d’apprentissage où l’on enchaîne plusieurs appels.
withSpinner() — une animation dans le terminal pour montrer que le script tourne. Elle affiche un spinner, exécute la fonction, puis remplace par ✓ ou ✗.
Première partie : System vs User prompt — on appelle le LLM deux fois avec la même question. Sans system prompt, le modèle répond par défaut. Avec un system prompt qui le définit comme “professeur d’informatique”, la réponse change de ton et utilise une analogie. Le même modèle, deux comportements différents.
Deuxième partie : Température — on demande un nom créatif avec trois températures différentes :
| Température | Effet |
|---|---|
| 0 | Toujours la même réponse (déterministe) |
| 0.7 | Créatif mais cohérent |
| 1.5 | Très aléatoire, peut devenir incohérent |
À température 0, le modèle choisit toujours le token le plus probable. Plus on monte, plus il explore des tokens moins probables, donc plus de variété mais aussi plus de risque de hors-sujet.
Voici ce qu’on obtient avec la question “Trouve un nom original pour un assistant IA personnel.” :
| Température | Réponse |
|---|---|
| 0 | Luma |
| 0.7 | Kairos |
| 1.5 | ZephyrMind |
Ce qu’on a appris
- System prompt — le comportement du modèle se contrôle via
role: "system". Un bon system prompt change radicalement la qualité de la réponse. - Température — un paramètre simple qui contrôle la créativité. 0 = précis et reproductible, 0.7 = équilibre, 1.5+ = exploration aléatoire.
- API Ollama — une API REST simple, compatible avec
fetch. Pas de SDK propriétaire, juste du JSON.
Ces trois concepts (rôles, température, appel API) sont les briques de base de tout ce qui va suivre : tool calling, RAG, agents.
Prochain article
Comment obtenir des réponses structurées et fiables avec Zod et le JSON mode d’Ollama. On arrête de parser du texte brut, on force le modèle à répondre en JSON typé.