OpenAI a publié le Agents SDK pour offrir aux développeurs TypeScript une primitive officielle et industrielle pour construire des systèmes IA autonomes. Fini les boucles d'outils câblées à la main, fini les transferts entre agents spécialisés réinventés à chaque projet, fini les garde-fous dont on ne sait jamais s'ils se sont vraiment déclenchés.
Dans ce tutoriel pratique, vous allez construire une plateforme complète de support client multi-agents avec le SDK OpenAI Agents en TypeScript — couvrant les outils, les transferts, les garde-fous, le streaming et le traçage — et terminer avec des patterns directement utilisables en production.
Pourquoi le Agents SDK plutôt qu'une boucle maison ? OpenAI a conçu le SDK autour de quatre primitives — Agents, Handoffs, Guardrails et Tracing — qui se composent en workflows arbitrairement complexes tout en restant lisibles. Vous passez zéro temps sur la plomberie et 100 pour cent sur le comportement de l'agent.
Ce que vous allez apprendre
À la fin de ce tutoriel, vous saurez :
- Initialiser un projet TypeScript avec
@openai/agents - Définir des agents avec instructions, modèles et outils
- Ajouter des outils typés et validés par Zod
- Orchestrer des workflows multi-agents avec des handoffs
- Valider entrées et sorties avec des guardrails
- Streamer la sortie de l'agent vers l'utilisateur en temps réel
- Inspecter chaque étape avec le tableau de bord de tracing intégré
- Déployer des agents avec gestion des retries, des coûts et de l'observabilité
Prérequis
Avant de commencer, assurez-vous d'avoir :
- Node.js 20+ installé (
node --version) - Une clé API OpenAI avec accès à la Responses API
- Une connaissance pratique de TypeScript et de
async/await - Un éditeur de code (VS Code recommandé)
- Environ 30 minutes de temps concentré
Ce que vous allez construire
Une plateforme modulaire de support IA avec trois agents spécialisés :
- Un agent de triage qui route les demandes
- Un agent facturation qui répond aux questions sur les factures via un outil
- Un agent remboursements qui traite les demandes de remboursement avec garde-fous
Le système streame les réponses à l'utilisateur, transfère le contrôle entre agents selon l'intention, et journalise chaque décision via le dashboard de tracing OpenAI.
Étape 1 : initialisation du projet
Créez un nouveau projet TypeScript et installez le SDK.
mkdir openai-agents-tutorial && cd openai-agents-tutorial
npm init -y
npm install @openai/agents zod
npm install -D typescript tsx @types/node
npx tsc --initMettez à jour tsconfig.json pour les paramètres Node modernes :
{
"compilerOptions": {
"target": "ES2022",
"module": "ES2022",
"moduleResolution": "bundler",
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true,
"outDir": "./dist"
},
"include": ["src/**/*"]
}Ajoutez "type": "module" dans votre package.json, puis créez un fichier .env avec votre clé API :
OPENAI_API_KEY=sk-proj-...Ajoutez un script dev :
{
"scripts": {
"dev": "tsx --env-file=.env src/index.ts"
}
}Étape 2 : votre premier agent
Créez src/index.ts et définissez un agent minimal.
import { Agent, run } from "@openai/agents";
const helpAgent = new Agent({
name: "Help Assistant",
instructions:
"Tu es un assistant support amical pour Noqta. Réponds de manière concise en deux phrases ou moins.",
model: "gpt-5",
});
async function main() {
const result = await run(helpAgent, "Comment réinitialiser mon mot de passe ?");
console.log(result.finalOutput);
}
main();Lancez-le :
npm run devVous devriez voir une réponse courte et pertinente. La fonction run exécute la boucle de l'agent jusqu'à produire une sortie finale ou atteindre sa limite de tours.
Le choix du modèle compte. Utilisez gpt-5 pour le raisonnement complexe et l'orchestration d'outils, et gpt-5-mini pour des agents de triage rapides et économiques. Le SDK vous permet de mélanger les modèles par agent, vous ne payez l'intelligence que là où c'est nécessaire.
Étape 3 : un outil typé avec Zod
Les vrais agents doivent appeler vos APIs. Les outils du SDK OpenAI Agents sont définis avec des schémas Zod, offrant sûreté à la compilation et validation à l'exécution.
Créez src/tools.ts :
import { tool } from "@openai/agents";
import { z } from "zod";
const fakeInvoices = new Map([
["INV-001", { amount: 89.0, status: "paid", date: "2026-04-12" }],
["INV-002", { amount: 240.5, status: "pending", date: "2026-05-03" }],
]);
export const getInvoice = tool({
name: "get_invoice",
description: "Récupère les détails d'une facture par son identifiant.",
parameters: z.object({
invoiceId: z.string().describe("Identifiant de facture comme INV-001"),
}),
async execute({ invoiceId }) {
const invoice = fakeInvoices.get(invoiceId);
if (!invoice) return { error: `Facture ${invoiceId} introuvable.` };
return invoice;
},
});Maintenant, branchez-le dans un agent facturation :
import { Agent } from "@openai/agents";
import { getInvoice } from "./tools.js";
export const billingAgent = new Agent({
name: "Billing Agent",
instructions: [
"Tu réponds aux questions de facturation des clients Noqta.",
"Appelle toujours get_invoice avant de citer un montant.",
"Si un outil retourne une erreur, excuse-toi et suggère de contacter le support.",
].join(" "),
model: "gpt-5",
tools: [getInvoice],
});Remplacez votre appel main() pour tester :
const result = await run(
billingAgent,
"Quel est le statut de la facture INV-002 ?"
);
console.log(result.finalOutput);L'agent appellera de manière autonome get_invoice, parsera le résultat et répondra avec le statut et le montant corrects.
Étape 4 : transferts multi-agents (handoffs)
Les handoffs sont la primitive phare du SDK. Au lieu d'un prompt géant, vous composez des agents spécialisés et les laissez se transférer le contrôle selon l'intention utilisateur.
Créez src/refunds.ts :
import { Agent, tool } from "@openai/agents";
import { z } from "zod";
const issueRefund = tool({
name: "issue_refund",
description: "Émet un remboursement pour une facture spécifique.",
parameters: z.object({
invoiceId: z.string(),
reason: z.string().describe("Raison communiquée au client pour le remboursement"),
}),
async execute({ invoiceId, reason }) {
return {
ok: true,
refundId: `RF-${Date.now()}`,
invoiceId,
reason,
};
},
});
export const refundsAgent = new Agent({
name: "Refunds Agent",
instructions:
"Tu traites les demandes de remboursement. Confirme toujours l'identifiant de la facture et la raison avant d'émettre.",
model: "gpt-5",
tools: [issueRefund],
});Construisez maintenant un agent de triage qui transfère vers facturation ou remboursements :
import { Agent, run } from "@openai/agents";
import { billingAgent } from "./billing.js";
import { refundsAgent } from "./refunds.js";
const triageAgent = new Agent({
name: "Triage Agent",
instructions: [
"Tu es l'accueil du support Noqta.",
"Transfère vers le Billing Agent pour les questions de facture et de paiement.",
"Transfère vers le Refunds Agent si l'utilisateur demande un remboursement.",
"Sinon, réponds directement en une phrase polie.",
].join(" "),
model: "gpt-5-mini",
handoffs: [billingAgent, refundsAgent],
});
const result = await run(
triageAgent,
"Je veux un remboursement pour la facture INV-002, le service n'a pas fonctionné."
);
console.log(result.finalOutput);L'agent de triage détecte l'intention de remboursement, transfère à l'agent remboursements, qui appelle alors issue_refund et retourne la confirmation — sans que vous orchestriez la moindre machine à états.
Étape 5 : garde-fous (guardrails)
Les guardrails s'exécutent en parallèle de l'agent principal et peuvent court-circuiter les requêtes dangereuses ou hors politique. Utilisez-les pour la sécurité, le périmètre ou la conformité.
Créez un garde-fou d'entrée qui bloque les requêtes hors périmètre support :
import { Agent, InputGuardrail, run } from "@openai/agents";
import { z } from "zod";
const scopeCheckAgent = new Agent({
name: "Scope Check",
instructions:
"Décide si l'entrée utilisateur est une question de support client. Retourne du JSON.",
model: "gpt-5-mini",
outputType: z.object({
isSupport: z.boolean(),
reason: z.string(),
}),
});
const supportScopeGuardrail: InputGuardrail = {
name: "support_scope_guardrail",
async execute({ input }) {
const result = await run(scopeCheckAgent, input);
return {
outputInfo: result.finalOutput,
tripwireTriggered: !result.finalOutput?.isSupport,
};
},
};Attachez le garde-fou à votre agent de triage :
const triageAgent = new Agent({
name: "Triage Agent",
instructions: "...",
model: "gpt-5-mini",
handoffs: [billingAgent, refundsAgent],
inputGuardrails: [supportScopeGuardrail],
});Quand l'utilisateur pose une question hors sujet, le tripwireTriggered passe à true et le SDK lève une erreur InputGuardrailTripwireTriggered que vous pouvez attraper et gérer proprement.
Les output guardrails fonctionnent pareillement — ils encapsulent la sortie finale de l'agent avant qu'elle n'atteigne l'utilisateur. Utilisez-les pour masquer des PII, forcer une forme JSON ou bloquer du contenu interdit.
Étape 6 : réponses en streaming
Pour les UI de chat, vous voulez streamer les tokens à mesure qu'ils arrivent. Le SDK expose une API d'événements de streaming :
import { run } from "@openai/agents";
const stream = await run(triageAgent, "Donne-moi des infos sur la facture INV-001.", {
stream: true,
});
for await (const event of stream) {
if (event.type === "raw_model_stream_event") {
if (event.data.type === "output_text_delta") {
process.stdout.write(event.data.delta);
}
}
if (event.type === "agent_updated_stream_event") {
console.error(`\n[transfert vers : ${event.agent.name}]`);
}
}
await stream.completed;
console.log("\n\nFinal :", stream.finalOutput);Vous obtenez des événements granulaires pour les deltas de tokens, les appels d'outils, les handoffs et la sortie finale. Diffusez output_text_delta vers votre front via Server-Sent Events ou WebSockets.
Étape 7 : tracing et observabilité
Chaque exécution d'agent est automatiquement tracée vers le dashboard OpenAI à platform.openai.com/traces. Vous voyez l'arbre complet des appels LLM, invocations d'outils, transferts et évaluations de garde-fous — colorisés et alignés sur une timeline.
Pour ajouter des métadonnées personnalisées et filtrer plus tard :
import { withTrace, run } from "@openai/agents";
await withTrace(
"support_session",
async () => {
await run(triageAgent, userMessage);
},
{
metadata: {
userId: "user_42",
tier: "pro",
sessionId: "sess_abc123",
},
}
);En production, vous pouvez aussi transférer les traces vers Logfire, Langfuse, Braintrust ou AgentOps en installant leurs packages de processeurs. Le pipeline de tracing est extensible.
Étape 8 : patterns de production
Voici les patterns que nous utilisons chez Noqta lors du déploiement d'agents en trafic réel.
Limiter les tours et le coût
Définissez un plafond maxTurns pour qu'un agent qui dérape ne brûle pas votre budget en boucle :
await run(triageAgent, userMessage, { maxTurns: 8 });Combinez cela avec des budgets de tokens au niveau de l'organisation OpenAI pour des plafonds durs.
Persister l'historique de conversation
Le SDK accepte un tableau history de messages précédents pour des conversations multi-tours :
const result = await run(triageAgent, userMessage, {
history: previousMessages,
});
const updatedHistory = result.history;Persistez updatedHistory dans votre base de données (Postgres, Redis, Convex) indexé par session ID.
Retry en cas d'erreurs transitoires
Encapsulez run() dans un helper de retry pour les erreurs de rate-limit et réseau :
import { run } from "@openai/agents";
async function runWithRetry<T>(fn: () => Promise<T>, attempts = 3): Promise<T> {
let lastError: unknown;
for (let i = 0; i < attempts; i++) {
try {
return await fn();
} catch (err) {
lastError = err;
await new Promise((r) => setTimeout(r, 1000 * 2 ** i));
}
}
throw lastError;
}
await runWithRetry(() => run(triageAgent, userMessage));Utiliser des modèles moins chers pour le triage
Faites passer les requêtes par un agent de triage rapide sur gpt-5-mini, et n'escaladez vers gpt-5 que lors des handoffs. Cela réduit le coût moyen de 60 à 80 pour cent sur les charges réelles.
Sandboxer les effets de bord des outils
Si un outil écrit en base ou facture une carte, mettez-le derrière un mode dry-run en développement et exigez un flag d'environnement pour activer les écritures réelles en production.
Tester votre implémentation
Vérifications rapides :
# Triage vers facturation
npm run dev -- "Quel est le statut de INV-001 ?"
# Triage vers remboursements
npm run dev -- "Rembourse-moi la facture INV-002 s'il te plaît."
# Déclenchement du garde-fou
npm run dev -- "Écris-moi un poème sur les croissants."Ouvrez le dashboard de tracing OpenAI et confirmez que l'arbre de trace correspond à vos attentes : agent de triage à la racine, exécutions enfants pour chaque transfert, appels d'outils sous l'agent responsable.
Dépannage
L'agent n'appelle jamais l'outil. Vérifiez que la description de votre outil commence par un verbe ("Récupère", "Émet", "Crée") et que le schéma Zod correspond aux paramètres que le LLM produit. Inspectez la trace pour voir les arguments bruts.
Les handoffs sont ignorés. L'agent de triage a besoin d'instructions explicites lui disant quand transférer et vers quel agent. Des instructions vagues comme "délègue de manière appropriée" ne marchent que rarement — soyez spécifique.
Le garde-fou lève une erreur mais vous ne pouvez pas l'attraper. Encapsulez votre appel run() dans un try/catch et vérifiez InputGuardrailTripwireTriggered depuis @openai/agents. Le SDK exporte ces classes d'erreurs explicitement.
L'usage de tokens explose. Baissez maxTurns, passez le tier de triage à gpt-5-mini, et auditez la trace pour des appels d'outils redondants. Un bug fréquent est un agent qui appelle le même outil en boucle parce que sa sortie n'est pas claire.
Étapes suivantes
Maintenant que le cœur fonctionne, voici où aller ensuite :
- Ajouter le support vocal avec l'API Realtime d'OpenAI — les agents peuvent être enveloppés dans une boucle vocale
- Connecter des serveurs MCP comme sources d'outils via le support MCP intégré du SDK
- Empiler des évaluations Langfuse ou Braintrust par-dessus vos traces
- Déplacer l'état de conversation vers Convex ou Postgres pour la persistance multi-session
- Déployer le runtime d'agent sur Vercel, Cloudflare Workers ou AWS Lambda
Si vous ne l'avez pas encore vu, notre guide compagnon sur le Claude Agent SDK en TypeScript couvre les primitives Anthropic équivalentes, utile pour comparer les compromis entre fournisseurs.
Conclusion
Vous avez construit une plateforme de support client multi-agents avec le SDK OpenAI Agents — outils typés, handoffs intelligents, garde-fous d'entrée, streaming et tracing complet — en moins de 300 lignes de TypeScript. Le SDK élimine le boilerplate pour vous laisser vous concentrer sur ce que l'agent doit faire plutôt que sur la manière dont il s'exécute.
Déployez-le, instrumentez-le, et itérez sur les données de trace. C'est la boucle qui transforme un prototype astucieux en un agent sur lequel vos clients comptent vraiment.