Optimisation de prompts pour agents IA (sans casser la prod)

Optimiser des prompts sans incidents : versioning, tests de régression, budgets coût/latence et rollouts. Exemples Python + JS.
Sur cette page
  1. Problème (la “petite modif” qui te mange une semaine)
  2. Pourquoi ça casse en prod
  3. Diagramme : pipeline safe
  4. Code réel : versionne tes prompts comme du code (Python + JS)
  5. Panne réelle (avec chiffres)
  6. Compromis
  7. Quand NE PAS optimiser le prompt
  8. Checklist copy-paste
  9. Config safe par défaut (YAML)
  10. Implémenter dans OnceOnly (optionnel)
  11. FAQ (3–5)
  12. Pages liées (3–6 liens)

Problème (la “petite modif” qui te mange une semaine)

Tu ajustes un prompt parce que l’agent a une phrase bizarre.

En dev, ça a l’air OK.

En prod :

  • tool calls/run montent doucement (personne ne regarde)
  • la latence explose sous charge
  • l’agent “oublie” les stop conditions
  • un edge case rare devient un incident quotidien

L’optimisation de prompt, c’est de l’ingénierie. Pas du copywriting. Si tu fais “je modifie du texte jusqu’à ce que ça sonne mieux”, tu vas shipper des régressions.

Pourquoi ça casse en prod

En prod, un prompt est collé à :

  • des schémas de tools (un champ renommé et le modèle se perd)
  • des budgets (l’inflation tokens rend les boucles chères)
  • des stop reasons (un prompt peut encourager “encore un essai”)
  • de la variance externe (search change, APIs flaky, rate limits)

La vérité pas fun : tu ne peux pas optimiser un prompt sans harness. Pas besoin d’un truc fancy. Besoin d’un truc stable.

Diagramme : pipeline safe

Code réel : versionne tes prompts comme du code (Python + JS)

Le minimum viable :

  • chaque prompt a une prompt_id stable (hash ou version)
  • chaque run log la prompt_id
  • rollback = un switch de config
PYTHON
import hashlib
from dataclasses import dataclass
from typing import Any, Dict


def prompt_id(text: str) -> str:
    return hashlib.sha256(text.encode("utf-8")).hexdigest()[:12]


@dataclass(frozen=True)
class Prompt:
    name: str
    version: str
    text: str

    @property
    def id(self) -> str:
        return f"{self.name}:{self.version}:{prompt_id(self.text)}"


class Logger:
    def event(self, name: str, fields: Dict[str, Any]) -> None: ...


def build_system_prompt(p: Prompt) -> str:
    return (
        "You are a production agent. You must follow tool policies and budgets.\n"
        "Always stop with a stop_reason.\n\n"
        f"[prompt_id={p.id}]\n"
        + p.text.strip()
    )


def run_agent(task: str, *, prompt: Prompt, logger: Logger, budgets: Dict[str, Any]) -> Dict[str, Any]:
    build_system_prompt(prompt)
    logger.event("agent_start", {"prompt_id": prompt.id, "budget": budgets})
    return {"output": "ok", "prompt_id": prompt.id, "stop_reason": "finish"}


PROMPTS = {
    "support:v12": Prompt("support", "v12", "Answer using KB. If unsure, ask a clarifying question."),
    "support:v13": Prompt("support", "v13", "Answer using KB. Cite tool results. If unsure, ask a clarifying question."),
}

ACTIVE_PROMPT = PROMPTS["support:v13"]
JAVASCRIPT
import crypto from "node:crypto";

export function promptId(text) {
  return crypto.createHash("sha256").update(text, "utf8").digest("hex").slice(0, 12);
}

export function makePrompt({ name, version, text }) {
  const id = `${name}:${version}:${promptId(text)}`;
  return { name, version, text, id };
}

export function buildSystemPrompt(prompt) {
  return [
    "You are a production agent. You must follow tool policies and budgets.",
    "Always stop with a stop_reason.",
    "",
    "[prompt_id=" + prompt.id + "]",
    prompt.text.trim(),
  ].join("\n");
}

export function runAgent(task, { prompt, logger, budgets }) {
  buildSystemPrompt(prompt);
  logger.event("agent_start", { prompt_id: prompt.id, budget: budgets });
  return { output: "ok", prompt_id: prompt.id, stop_reason: "finish" };
}

const PROMPTS = {
  "support:v12": makePrompt({ name: "support", version: "v12", text: "Answer using KB. If unsure, ask a clarifying question." }),
  "support:v13": makePrompt({ name: "support", version: "v13", text: "Answer using KB. Cite tool results. If unsure, ask a clarifying question." }),
};

const ACTIVE_PROMPT = PROMPTS["support:v13"];

Panne réelle (avec chiffres)

On a “amélioré” un prompt support en ajoutant une longue consigne “sois exhaustif”.

Rien n’a crashé. Mais le modèle a fait ce qu’on lui demandait : il est devenu exhaustif.

Impact sur 36h :

  • p95 tokens/run : 7.5k → 14.2k
  • avg tool calls/run : 4 → 11
  • spend : +$620
  • astreinte : ~2h pour prouver que c’était le prompt, pas le trafic

Fix :

  1. budgets (steps/tool calls/tokens)
  2. invariant golden tasks : max tool calls + max tokens
  3. rendre “exhaustif” conditionnel (seulement quand un tool manque)

Compromis

  • Qualité vs coût : tu dois choisir ce que tu surveilles.
  • Prompts courts = rapides, mais souvent trop flous côté safety.
  • Contrats explicites = moins “joli”, plus safe.

Quand NE PAS optimiser le prompt

Ne “prompt‑optimize” pas pour remplacer :

  • budgets (/fr/governance/budget-controls)
  • validation tools (/fr/tools/input-validation)
  • logging (/fr/observability-monitoring/agent-logging)
  • tests (/fr/testing-evaluation/unit-testing-agents)

Si l’agent est instable, toucher au prompt c’est juste bouger les symptômes.

Checklist copy-paste

  • [ ] prompt_id stable loggée sur chaque run
  • [ ] Golden tasks (10–50) proches du trafic réel
  • [ ] Invariants : stop_reason, tool_calls bound, tokens bound
  • [ ] Canary + rollback switch
  • [ ] Monitor : spend/run, tool_calls/run, latency/run
  • [ ] 1 golden task par incident

Config safe par défaut (YAML)

YAML
prompts:
  active: "support:v13"
  rollback: "support:v12"
  require_prompt_id: true
testing:
  golden_tasks:
    - id: "kb_lookup"
      expect_stop_reason: "finish"
      max_tool_calls: 6
      max_tokens: 9000
budgets:
  max_steps: 25
  max_tool_calls: 12
  max_seconds: 60
observability:
  log_prompt_id: true
  alert_on_token_spike: true

Implémenter dans OnceOnly (optionnel)

Implémenter dans OnceOnly
Bloquer les changements de prompt derrière budgets + stop reasons + logging.
Utiliser dans OnceOnly
# onceonly-python: budgets + safe rollout guardrails
import os
from onceonly import OnceOnly

client = OnceOnly(api_key=os.environ["ONCEONLY_API_KEY"])
agent_id = "support-bot"

# Set budgets/limits before you ship prompt changes
client.gov.upsert_policy({
    "agent_id": agent_id,
    "max_actions_per_hour": 200,
    "max_spend_usd_per_day": 50.0,
    "max_calls_per_tool": {"kb.search": 6},
    "allowed_tools": ["kb.search", "send_email"],
})

# After rollout, watch for spend/tool spikes
m = client.gov.agent_metrics(agent_id, period="day")
print("actions=", m.total_actions, "spend_usd=", m.total_spend_usd)

FAQ (3–5)

Faut-il A/B tester des prompts en prod ?
Uniquement si tu peux rollback vite et si tu monitors spend/run et tool_calls/run. Sinon c’est un incident contrôlé.
Quelle taille pour le set de golden tasks ?
Commence à 10–20 tâches proches du trafic réel. Ajoute 1 tâche par incident. Tu arriveras naturellement vers 50.
Qu’est-ce qu’on assert dans des tests de prompt ?
Stop reasons, nombre d’appels tools, arrêts budget, contraintes simples. Pas le texte exact.
Un prompt peut remplacer des guardrails ?
Non. Un prompt change la proba. Un guardrail change ce qui est possible.

Pages liées (3–6 liens)

⏱️ 4 min de lectureMis à jour Mars, 2026Difficulté: ★★☆
Intégré : contrôle en productionOnceOnly
Ajoutez des garde-fous aux agents tool-calling
Livrez ce pattern avec de la gouvernance :
  • Budgets (steps / plafonds de coût)
  • Permissions outils (allowlist / blocklist)
  • Kill switch & arrêt incident
  • Idempotence & déduplication
  • Audit logs & traçabilité
Mention intégrée : OnceOnly est une couche de contrôle pour des systèmes d’agents en prod.
Auteur

Cette documentation est organisée et maintenue par des ingénieurs qui déploient des agents IA en production.

Le contenu est assisté par l’IA, avec une responsabilité éditoriale humaine quant à l’exactitude, la clarté et la pertinence en production.

Les patterns et recommandations s’appuient sur des post-mortems, des modes de défaillance et des incidents opérationnels dans des systèmes déployés, notamment lors du développement et de l’exploitation d’une infrastructure de gouvernance pour les agents chez OnceOnly.