< BACK Notes de code écrites à la main dans un carnet sur un bureau en bois avec une lumière grise douce et une tasse d'espresso

Vercel AI SDK en pratique : Livrez plus intelligent, pas plus dur

Un client fintech m'a appelé fin 2023, vraiment paniqué, parce que son équipe dev avait passé six semaines à construire une interface de chat en streaming sur l'API d'OpenAI et c'était toujours cassé sur Mobile Safari. Des conditions de course. Des problèmes de chunking de tokens. L'interface se gelait en pleine réponse. Six semaines. J'ai regardé leur codebase et c'était exactement ce qu'on s'attend à voir : une implémentation ReadableStream bricolée, une couche de gestion d'état sur mesure pour les tokens en streaming, de la logique de retry copiée d'une réponse Stack Overflow vieille de trois ans. Le truc tenait avec du scotch.ReadableStream implementation, a bespoke state management layer for the streaming tokens, retry logic copied from a three-year-old Stack Overflow answer. The thing was held together with tape.

J'ai réécrit le cœur avec Vercel AI SDK en environ deux jours. Le streaming fonctionnait. Mobile Safari fonctionnait. Le client a arrêté d'appeler.Vercel AI SDK in about two days. Streaming worked. Mobile Safari worked. The client stopped ringing.

Ce n'est pas un argument de vente. C'est juste ce qui s'est passé. Et c'est pourquoi je veux vous montrer ce que ce SDK fait vraiment, où il vous fait vraiment gagner du temps, et où vous allez quand même buter sur des murs.

Ce que Vercel AI SDK est réellement

Les gens entendent « Vercel AI SDK » et supposent que c'est verrouillé à l'infrastructure Vercel. Ce n'est pas le cas. Vous pouvez le lancer sur n'importe quel serveur Node.js, y compris votre propre VPS, Railway, Render, peu importe. Ce que c'est vraiment : une bibliothèque TypeScript qui abstrait les parties les plus pénibles de la construction de fonctionnalités alimentées par LLM dans une app web.

Il y a deux packages principaux que vous utiliserez. ai est le runtime principal. @ai-sdk/openai (ou @ai-sdk/anthropic, @ai-sdk/google, etc.) sont les adaptateurs de fournisseur. Le SDK utilise une interface unifiée, donc passer de GPT-4o à Claude 3.5 Sonnet est vraiment un changement d'une ligne dans la plupart des cas. Je l'ai testé. Ça tient la route.ai is the core runtime. @ai-sdk/openai (or @ai-sdk/anthropic , @ai-sdk/google, etc.) are the provider adapters. The SDK uses a unified interface so switching from GPT-4o to Claude 3.5 Sonnet is genuinely a one-line change in most cases. I've tested this. It holds up.

Les primitives essentielles

Trois choses se trouvent au cœur du SDK :

  • streamText, diffuse une réponse textuelle token par token, idéal pour les interfaces de chat, streams a text response token by token, ideal for chat interfaces
  • generateText, attend la réponse complète, bon pour les tâches en arrière-plan ou quand vous n'avez pas besoin d'une UX de streaming, waits for the full response, good for background jobs or when you don't need streaming UX
  • generateObject, retourne du JSON structuré validé contre un schéma Zod, c'est là que les choses deviennent vraiment intéressantes, returns structured JSON validated against a Zod schema, which is where things get properly interesting

streamText est ce que la plupart des gens veulent en premier. Il gère la plomberie ReadableStream, l'encodage et le chunking. Ce cauchemar fintech de six semaines dont j'ai parlé ? C'est exactement ce que streamText aurait résolu dès le premier jour. is what most people want first. It handles the ReadableStream plumbing, the encoding, and the chunking. That six-week fintech nightmare I mentioned? That's exactly what streamText would have solved on day one.

La mettre en place sans perdre la tête

Si vous êtes sur Next.js 14 ou 15 avec l'App Router (ce que vous êtes probablement si vous lisez ceci en 2025), la configuration est vraiment rapide.

  1. Installez les packages : npm install ai @ai-sdk/openainpm install ai @ai-sdk/openai
  2. Créez un gestionnaire de route à app/api/chat/route.tsapp/api/chat/route.ts
  3. Importez streamText et votre fournisseur, passez votre modèle et vos messages, retournez le résultat sous la forme d'une StreamingTextResponsestreamText and your provider, pass in your model and messages, return the result as a StreamingTextResponse
  4. Sur le client, utilisez le hook useChat du package ai/reactuseChat hook from the ai/react package

C'est tout. Vous aurez un chat avec streaming fonctionnel en moins d'une heure si vous maîtrisez raisonnablement bien Next.js. Le hook useChat gère le tableau de messages, l'état de chargement, la liaison du champ d'entrée et les mises à jour en streaming. Vous n'écrivez rien de tout cela vous-même.useChat hook manages the message array, the loading state, the input field binding, and the streaming updates. You don't write any of that yourself.

Ce que j'ajouterais immédiatement à tout déploiement en production : une invite système appropriée, une limitation de débit (j'utilise Upstash pour cela, leur limiteur de débit Redis-backed fonctionne bien avec les Edge Functions), et une sorte de journalisation de l'utilisation des tokens. Le SDK expose les données d'utilisation sur l'objet de réponse, vous pouvez donc enregistrer les tokens d'invite et les tokens de complétion dans votre base de données sans aucun appel API supplémentaire.Upstash for this, their Redis-backed rate limiter plays nicely with Edge Functions), and some kind of token usage logging. The SDK exposes usage data on the response object, so you can log prompt tokens and completion tokens to your database without any extra API calls.

generateObject Est la Fonctionnalité Sur Laquelle Vous Dormez

Avis honnête : streamText reçoit toute la publicité mais generateObject est celle qui a changé ma façon de concevoir les fonctionnalités d'IA.streamText gets all the press but generateObject is the one that's changed how I architect AI features.

L'idée est simple. Vous définissez un schéma Zod, vous le passez à generateObject, et le SDK demande au modèle de retourner un JSON qui s'y conforme. Vous récupérez un objet typé, pas une chaîne que vous devez parser et croiser les doigts.generateObject, and the SDK instructs the model to return JSON that conforms to it. You get back a typed object, not a string you have to parse and pray over.

Seahawk a eu un projet l'année dernière pour une entreprise de gestion immobilière. Ils voulaient extraire des données structurées de documents de bail uploadés, noms des locataires, montants de loyer, clauses de rupture, dates de renouvellement. L'ancienne approche aurait été : demander au modèle, récupérer du texte, écrire un parser regex, pleurer. Avec generateObject, nous avons défini un LeaseSchema avec Zod, et le modèle a retourné des données typées propres à chaque exécution. Nous poussions les baux extraits dans une table Postgres en une semaine.generateObject , we defined a LeaseSchema with Zod, and the model returned clean typed data on every run. We were pushing extracted leases into a Postgres table within a week.

Ce qui intrigue les gens : pas tous les modèles supportent la sortie structurée aussi bien. GPT-4o et Claude 3.5 Sonnet la gèrent de façon fiable. Certains modèles plus petits ou plus anciens vont inventer des champs ou ignorer complètement le schéma. Restez avec les modèles phares pour celle-ci, du moins jusqu'à ce que vous ayez validé votre cas d'usage.

L'appel d'outils : où réside le vrai pouvoir

Si generateObject est sous-utilisé, l'appel d'outils est activement mal compris. La plupart des développeurs pensent que « l'appel d'outils » signifie que l'IA peut naviguer sur internet ou exécuter du code. Parfois c'est le cas. Mais en pratique, les outils ne sont que des fonctions que vous définissez et que le modèle peut choisir d'invoquer pendant une réponse.generateObject is underused, tool calling is actively misunderstood. Most developers think "tool calling" means the AI can browse the internet or run code. Sometimes it does. But in practice, tools are just functions you define that the model can choose to invoke during a response.

Comment y réfléchir

Supposons que vous construisez un bot de support pour un produit SaaS. L'utilisateur demande « quel est mon plan d'abonnement actuel ? » Le modèle ne peut pas le savoir. Mais vous pouvez définir un outil getUserSubscription qui prend un identifiant utilisateur et retourne les données du plan depuis votre base de données. Le modèle reconnaît l'intention, appelle l'outil, récupère les données et les incorpore dans la réponse. L'utilisateur voit simplement une réponse cohérente.getUserSubscription tool that takes a user ID and returns the plan data from your database. The model recognises the intent, calls the tool, gets the data back, and incorporates it into the response. The user just sees a coherent answer.

Le paramètre tools du SDK sur streamText et generateText prend un objet où chaque clé est un nom d'outil, et chaque valeur a une description (que le modèle utilise pour décider quand l'appeler), un schéma parameters (Zod à nouveau), et une fonction execute. La fonction execute s'exécute côté serveur, en toute sécurité, loin du client.tools parameter on streamText and generateText takes an object where each key is a tool name, and each value has a description (which the model uses to decide when to call it), a parameters schema (Zod again), and an execute function. The execute function runs server-side, safely, away from the client.

J'ai construit des agents multi-étapes de cette façon. Le SDK supporte maxSteps pour que le modèle puisse chaîner les appels d'outils, appeler un outil, utiliser le résultat, appeler un autre outil, synthétiser tout, répondre. Ce n'est pas de la magie. Vous devez toujours réfléchir attentivement à vos descriptions d'outils et à votre system prompt. Mais tout le câblage est géré pour vous.maxSteps so the model can chain tool calls, call one tool, use the result, call another tool, synthesise everything, respond. It's not magic. You still need to think carefully about your tool descriptions and your system prompt. But the wiring is all handled for you.

Middleware, Wrapping, et le modèle Pipeline

Une chose que je n'ai pas bien appréciée jusqu'à ce que je creuse davantage la documentation : le SDK a un concept de middleware qui vous permet de wrapper les appels au modèle pour ajouter de la journalisation, du caching, ou un comportement personnalisé sans toucher à votre code métier.

wrapLanguageModel prend un modèle et un objet middleware. Vous pouvez intercepter les requêtes avant qu'elles ne frappent l'API, modifier les paramètres, mettre en cache les réponses. J'ai utilisé cela sur un outil de génération de contenu que nous avons construit pour un éditeur, leur cas d'usage avait beaucoup de prompts répétés (le même résumé d'article étant demandé plusieurs fois par jour), et nous avons mis en cache les réponses dans Redis en utilisant la couche middleware. Le coût a baissé d'environ 35 % le premier mois. takes a model and a middleware object. You can intercept requests before they hit the API, modify parameters, cache responses. I used this on a content generation tool we built for a publisher, their use case had a lot of repeated prompts (same article summary being requested multiple times a day), and we cached responses in Redis using the middleware layer. Cost dropped by about 35% in the first month.

C'est le genre de chose que vous brancheriez normalement maladroitement autour de vos appels API. L'avoir comme un concept de première classe dans le SDK signifie que c'est composable et testable.

Ce Qu'Il Ne Fait Pas (Soyez Honnête Avec Vous-Même)

D'accord. Laissez-moi vous épargner quelques déboires.

Le SDK ne gère pas la mémoire ni l'historique des conversations à long terme pour vous. useChat maintient le tableau des messages dans l'état client, mais dès que l'utilisateur actualise, c'est parti. Si vous avez besoin de conversations persistantes, vous devez les construire vous-même. Postgres ou MongoDB pour le stockage des messages, fetch au montage pour réhydrater le chat, rien de cela n'est fourni. C'est en réalité le bon comportement. Le SDK ne devrait pas posséder votre couche de données. Mais les nouveaux venus s'y attendent souvent.useChat maintains the message array in client state, but the moment the user refreshes, that's gone. If you need persistent conversations, you're building that yourself. Postgres or MongoDB for message storage, fetch on mount to rehydrate the chat, none of that is provided. This is correct behaviour, actually. The SDK shouldn't own your data layer. But newcomers often expect it to.

Il ne gère pas non plus les entrées multimodales de manière native d'une façon qui abstrait toute la complexité. Vous pouvez passer des URLs d'images dans le tableau des messages et les modèles comme GPT-4o les géreront bien, mais construire un vrai flux d'upload d'image avec aperçu, compression et stockage reste votre responsabilité. J'utilise Uploadthing pour cela quand je travaille sur Next.js, ça prend environ 30 minutes à configurer.Uploadthing for this when I'm on Next.js, takes about 30 minutes to wire up.

La RAG (génération augmentée par récupération) n'est pas non plus incluse. Il n'y a pas de recherche vectorielle intégrée, pas de pipeline d'embedding, pas de logique de chunking. Pour cela, vous vous tournez vers quelque chose comme pgvector sur Postgres ou un service dédié comme Pinecone. Le SDK gère l'appel LLM. Le reste de l'architecture de récupération, c'est à vous de le construire.pgvector on Postgres or a dedicated service like Pinecone. The SDK handles the LLM call. The rest of the retrieval architecture is yours to build.

Le Déployer : Vercel vs Partout Ailleurs

Oui, le SDK fonctionne mieux sur Vercel. StreamingTextResponse fonctionne avec l'Edge Runtime de Vercel directement, et les cold starts sur les Edge Functions sont bien inférieurs à ceux des fonctions serverless Node.js. Si vos réponses de chat semblent lentes, la latence jusqu'au premier token compte énormément, et Edge réduit cela.StreamingTextResponse works with Vercel's Edge Runtime out of the box, and cold starts on Edge Functions are much lower than on serverless Node.js functions. If your chat responses feel sluggish, the latency until the first token matters a lot, and Edge cuts that down.

Mais je l'ai déployé sur Railway (Node.js, pas Edge) et ça fonctionne bien. Vous utilisez simplement Response avec les bons en-têtes de streaming au lieu des helpers spécifiques à Vercel, et la documentation du SDK couvre cela. Ne vous laissez pas impressionner par « Vercel AI SDK » en pensant que vous êtes enfermé.Response with proper streaming headers instead of the Vercel-specific helpers, and the SDK docs cover this. Don't let "Vercel AI SDK" make you think you're locked in.

Une chose que je recommanderais vraiment : gardez vos gestionnaires de route IA minimalistes. Ne faites pas de requêtes de base de données, de vérifications d'authentification et d'appels LLM tous dans une seule fonction. Les middlewares (middleware Next.js, pas le middleware du SDK) doivent gérer l'authentification avant même que la requête ne parvienne à votre route IA. Ça garde tout rapide, ça garde tout débogable.

FAQ

Le Vercel AI SDK est-il gratuit ?

Le SDK lui-même est open source et gratuit. Vous payez pour les API des modèles sous-jacents : OpenAI, Anthropic, Google, peu importe qui vous appelez. Ces coûts sont votre responsabilité. Le SDK ne majore rien.

Puis-je l'utiliser avec d'autres modèles qu'OpenAI ?

Oui, et c'est l'une de ses véritables forces. Il y a des adaptateurs officiels pour Anthropic, Google (Gemini), Mistral, Groq, Cohere, et d'autres. La communauté a aussi construit des adaptateurs pour Ollama si vous voulez exécuter des modèles locaux. L'interface unifiée signifie que votre code métier ne change pas quand vous changez de fournisseur.

Fonctionne-t-il avec les React Server Components ?

Partiellement. generateText et generateObject peuvent s'exécuter dans les Server Components sans problème, ce sont juste des fonctions asynchrones. streamText avec le hook useChat nécessite un état côté client, donc cette partie vit dans un Client Component. C'est l'architecture Next.js standard et ne devrait pas vous causer de problème si vous comprenez la limite.generateText and generateObject can run in Server Components without issues, they're just async functions. streamText with the useChat hook requires client-side state, so that part lives in a Client Component. This is standard Next.js architecture and shouldn't cause you any grief if you understand the boundary.

Comment gérer les erreurs quand l'API est hors ligne ?

Le SDK lance des erreurs typées que vous pouvez attraper et traiter. En pratique, j'enveloppe mes appels LLM dans un try/catch et je retourne un message de secours gracieux plutôt que de laisser l'erreur du stream remonter à l'UI comme une réponse partielle cassée. La documentation du SDK sur la gestion des erreurs couvre les types d'erreurs en détail et vaut le coup d'être lue avant de passer en production.SDK docs on error handling cover the error types in detail and are worth reading before you go to production.

Qu'en est-il des limites de tokens ?

Le SDK ne gère pas les limites de contexte pour vous. Si vous construisez une conversation longue et que vous ne supprimez pas l'historique des messages, vous allez atteindre la fenêtre de contexte du modèle et obtenir une erreur. Un correctif simple : gardez les N derniers messages (je fais généralement 20) plus une invite système épinglée. C'est suffisant pour la plupart des applications de chat.

---

Écoutez, le Vercel AI SDK n'est pas un miracle. C'est une abstraction bien pensée sur des tuyauteries vraiment fastidieuses. Il gère le streaming correctement, il vous donne des sorties structurées typées, il rend l'appel d'outils accessible, et il fonctionne sur plusieurs fournisseurs. Pour 80% des travaux de fonctionnalités IA que j'entreprends, c'est le bon point de départ.

Le client fintech, d'ailleurs, s'est lancé avec le chat reconstruit deux semaines après que je l'aie réécrit. Aucun problème sur mobile Safari. Aucune condition de concurrence. Six semaines sont devenues deux jours. Parfois, la bonne abstraction vaut vraiment le coup.

< BACK