2023 年後半、フィンテック系のクライアントから電話がありました。パニック状態で、彼の開発チームが OpenAI の API 上にストリーミング チャット インターフェイスを構築するのに 6 週間を費やしていて、それでも iOS Safari でうまく動作していなかったのです。競合状態。トークン チャンキングの問題。UI が応答の途中でフリーズします。6 週間です。私は彼らのコードベースを見ました。そしてそれはまさにあなたが予想するもそのもの。自作の ReadableStream 実装、ストリーミング トークン用のカスタム状態管理レイヤー、3 年前の Stack Overflow の回答からコピーされたリトライ ロジック。それはテープで一緒に保持されていました。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.
私は Vercel AI SDK を使ってその中核を約 2 日間で書き直しました。ストリーミングが動きました。iOS Safari が動きました。クライアントからの電話が来なくなりました。Vercel AI SDK in about two days. Streaming worked. Mobile Safari worked. The client stopped ringing.
これは宣伝ではありません。これはただ起きたことです。だから、この SDK が実際に何をするのか、どこで時間を本当に節約できるのか、そしてどこでまだ壁にぶつかるのかを説明したいのです。
Vercel AI SDK が実際に何であるか
「Vercel AI SDK」と聞くと、Vercel インフラに限定されていると考える人もいます。違います。Node.js サーバー上の任意の場所(自分の VPS、Railway、Render など)で実行できます。実際のところ、それは TypeScript ライブラリで、LLM を搭載した機能を Web アプリに構築する際の最も厄介な部分を抽象化します。
メインで使う2つのパッケージがあります。ai はコアランタイムです。@ai-sdk/openai(または @ai-sdk/anthropic、@ai-sdk/google など)がプロバイダアダプターです。SDKは統一されたインターフェースを使うため、GPT-4o から Claude 3.5 Sonnet に切り替えることは、ほとんどの場合本当に1行の変更です。私は検証済みです。確実に動きます。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.
コアプリミティブ
SDKの核となる3つのものがあります:
streamText は、テキスト応答をトークンごとにストリーミングします。チャットインターフェース向けです。, streams a text response token by token, ideal for chat interfacesgenerateText は、フル応答を待ちます。バックグラウンドジョブや、ストリーミングUXが不要な場合に適しています。, waits for the full response, good for background jobs or when you don't need streaming UXgenerateObject は、Zodスキーマに対して検証された構造化JSON を返します。ここが本当に面白くなるところです。, returns structured JSON validated against a Zod schema, which is where things get properly interesting
streamText がほとんどの人が最初に欲しいものです。ReadableStream の配管、エンコーディング、チャンキングを処理します。私が言及した6週間のフィンテック悪夢? それはまさに streamText が初日に解決したはずのものです。 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.
頭を抱える前にセットアップする
Next.js 14または15 でApp Router を使っていると仮定すると(2025年にこれを読んでいるなら、おそらくそうでしょう)、セットアップは本当に素早いです。
- パッケージをインストール: npm install ai @ai-sdk/openai
npm install ai @ai-sdk/openai - app/api/chat/route.ts にルートハンドラーを作成します
app/api/chat/route.ts - streamText とプロバイダーをインポートし、モデルとメッセージを渡して、結果を StreamingTextResponse として返します
streamTextand your provider, pass in your model and messages, return the result as aStreamingTextResponse - クライアント側では、ai/react パッケージの useChat フックを使用します
useChathook from theai/reactpackage
これで完了です。Next.js をそこそこ理解していれば、1 時間以内に機能するストリーミングチャットが完成します。useChat フックはメッセージ配列、ローディング状態、入力フィールドのバインディング、ストリーミング更新を管理するため、それらを自分で書く必要がありません。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.
本番環境のセットアップに即座に追加すべきもの:適切なシステムプロンプト、レート制限(私は Upstash を使用しており、彼らの Redis ベースのレート制限は Edge Functions との相性が良好です)、そしてある種のトークン使用量ログです。SDK は応答オブジェクトの使用量データを公開しているため、追加の API 呼び出しなしにプロンプトトークンと完了トークンをデータベースにログできます。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 は見落としている機能です
正直な意見:streamText はすべての注目を集めていますが、generateObject こそが私の AI 機能アーキテクチャの設計方法を変えた機能です。streamText gets all the press but generateObject is the one that's changed how I architect AI features.
考え方は簡単です。Zod スキーマを定義し、それを generateObject に渡すと、SDK はモデルに対してスキーマに準拠した JSON を返すよう指示します。解析して祈る必要がある文字列ではなく、型付けされたオブジェクトが返されます。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 は昨年、物件管理会社向けのプロジェクトを手掛けました。彼らはアップロードされたリース契約書から構造化されたデータを抽出したかったのです。テナント名、賃貸料、解約特約、更新日。従来のアプローチなら:モデルにプロンプトを入れ、テキストを得て、正規表現パーサーを書いて、泣きます。generateObject では、Zod で LeaseSchema を定義し、モデルが毎回きれいな型付けデータを返しました。1 週間以内に抽出されたリース契約を Postgres テーブルにプッシュしていました。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.
人々がつまずくポイント:すべてのモデルが構造化出力を同じように適切にサポートしているわけではありません。GPT-4o と Claude 3.5 Sonnet は確実に対応しています。小さいモデルや古いモデルの中には、フィールドを幻覚させたり、スキーマを完全に無視したりするものもあります。少なくともユースケースを検証するまでは、フラグシップモデルに固執してください。
ツール呼び出し:真の力が宿る場所
generateObjectが過小評価されているなら、ツール呼び出しは積極的に誤解されている。ほとんどの開発者は「ツール呼び出し」とはAIがインターネットを閲覧したり、コードを実行したりできることだと考えている。時にはそうだ。しかし実際には、ツールはあなたが定義した関数に過ぎず、モデルが応答中に呼び出すことを選択できるだけだ。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.
どう考えるべきか
SaaSプロダクト向けのサポートボットを構築しているとしよう。ユーザーが「現在のサブスクリプションプランは何ですか?」と尋ねる。モデルはそれを知ることができない。しかしあなたはgetUserSubscriptionツールを定義できます。このツールはユーザーIDを受け取り、データベースからプランデータを返します。モデルは意図を認識し、ツールを呼び出し、データを受け取り、それを応答に組み込みます。ユーザーは一貫性のある回答を見るだけです。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.
SDKのtools パラメータはstreamText とgenerateText で利用でき、オブジェクトを受け取ります。各キーはツール名で、各値にはdescription (モデルが呼び出すタイミングを決めるのに使う)、parameters スキーマ(これもZod)、およびexecute 関数があります。execute 関数はサーバー側で安全に実行され、クライアントから離れています。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.
このやり方で複数ステップのエージェントを構築しました。SDKはmaxStepsをサポートしているので、モデルはツール呼び出しをチェーンし、1つのツールを呼び出し、その結果を使い、別のツールを呼び出し、すべてを合成して、応答することができます。これは魔法ではありません。ツールの説明とシステムプロンプトについて慎重に考える必要があります。しかし配線はすべてあなたのために処理されます。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.
ミドルウェア、ラッピング、およびパイプラインモデル
ドキュメントをもっと詳しく読むまで気づかなかったことの1つは、SDKにはミドルウェアの概念があり、実装特性のコードに触れずにモデル呼び出しをラップしてログ、キャッシング、またはカスタム動作を追加できるということです。
wrapLanguageModelはモデルとミドルウェアオブジェクトを受け取ります。リクエストがAPIに到達する前に割り込み、パラメータを修正し、レスポンスをキャッシュできます。パブリッシャー向けに構築したコンテンツ生成ツールでこれを使用しました。彼らのユースケースでは繰り返されるプロンプトが多くありました(同じ記事の要約が1日に複数回リクエストされる)。ミドルウェアレイヤーを使用してRedisでレスポンスをキャッシュしました。最初の月でコストは約35%削減されました。 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.
これは通常、API呼び出しの外側に不器用に取り付けるようなことです。SDKでこれをファーストクラスの概念として持つことは、それが合成可能でテスト可能であることを意味します。
それが出来ないこと(自分に正直になろう)
よし。痛い目を見ないよう先に言っておこう。
このSDKはメモリや長期の会話履歴を管理してくれない。useChatはクライアント側の状態でメッセージ配列を保持するが、ユーザーがリフレッシュした瞬間にそれは消える。永続的な会話が必要なら、自分で構築する必要がある。メッセージストレージ用のPostgresやMongoDB、マウント時に取得してチャットをリハイドレートするロジック。そういったものは提供されていない。実はこれは正しい動作だ。SDKがあなたのデータレイヤーを所有すべきではない。だが新参者はよくそれを期待する。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.
また、複数形式の入力をネイティブに、複雑さをすべて隠蔽する形では処理しない。メッセージ配列に画像URLを渡すことはできるし、GPT-4oのようなモデルは問題なく処理するが、プレビュー、圧縮、ストレージを備えた適切な画像アップロードフローの構築はやはり自分の仕事だ。Next.jsを使っているときはUploadthingを使う。配線するのに30分くらいかかる。Uploadthing for this when I'm on Next.js, takes about 30 minutes to wire up.
RAG(retrieval-augmented generation)も含まれていない。ベクトル検索も、エンベディングパイプラインも、チャンキングロジックも組み込まれていない。そういったことにはPostgresのpgvectorか、Pineconeのような専用サービスを使う。SDKはLLMコールを処理する。その他の検索アーキテクチャは自分で構築する必要がある。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.
デプロイ:Vercel対その他すべて
そう、このSDKはVercelで最も良く動く。StreamingTextResponseはVercelのEdge Runtimeとそのまま動作し、Edge FunctionsのコールドスタートはサーバーレスNode.js関数よりもはるかに低い。チャット応答が遅く感じるなら、最初のトークンまでのレイテンシが重要で、Edgeがそれを削減する。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.
だが僕はRailway上(Node.js、Edge以外)でデプロイしたし、うまく動く。適切なストリーミングヘッダーを持つResponseを使うだけで、Vercel固有のヘルパーの代わりになり、SDKドキュメントでもそれを説明している。「Vercel AI SDK」という名前でロックインされていると思い込まないこと。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.
本気で勧めることが一つある。AIルートハンドラは薄く保つこと。データベースクエリ、認証チェック、LLMコールをすべて一つの関数に詰め込まない。ミドルウェア(SDKミドルウェアではなくNext.jsミドルウェア)がリクエストがAIルートに到達する前に認証を処理する。動きが速く、デバッグしやすくなる。
FAQ
Vercel AI SDKは无料で使用できますか?
SDK自体はオープンソースで無料です。費用が発生するのは基盤となるモデルAPI、つまりOpenAI、Anthropic、Google、どのプロバイダーを呼び出すかによって決まります。その費用はあなたが管理する必要があります。SDKはマークアップを追加しません。
OpenAI以外のモデルで使用できますか?
はい、これが本当の強みの一つです。Anthropic、Google(Gemini)、Mistral、Groq、Cohereなど、公式なアダプターがあります。コミュニティもローカルモデルを実行したい場合のOllamaアダプターを構築しています。統一されたインターフェースにより、プロバイダーを切り替える際にフィーチャーコードを変更する必要がありません。
React Server Componentsで動作しますか?
部分的です。generateTextとgenerateObjectはServer Componentsで問題なく実行できます。単なる非同期関数です。streamTextとuseChat hookはクライアント側の状態が必要なため、その部分はClient Componentに存在します。これは標準的なNext.jsアーキテクチャで、境界を理解していれば問題は発生しません。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.
APIがダウンした場合、エラーをどのように処理しますか?
SDKは型付きエラーをスローし、それをキャッチして処理できます。実際には、LLM呼び出しをtry/catchでラップし、ストリームエラーがUIに不完全なレスポンスとして波及するのを防ぐため、グレースフルなフォールバックメッセージを返すようにしています。SDKのエラー処理に関するドキュメントはエラータイプの詳細をカバーしており、本番環境に移行する前に読む価値があります。SDK docs on error handling cover the error types in detail and are worth reading before you go to production.
トークンリミットの状況はどうなっていますか?
SDKはコンテキストウィンドウのリミットを管理しません。長時間実行される会話を構築していて、メッセージ履歴をトリミングしない場合、モデルのコンテキストウィンドウに到達してエラーが発生します。簡単な修正方法は、最後のNメッセージ(私は通常20個)とピンドされたシステムプロンプトを保持することです。ほとんどのチャットアプリケーションで十分です。
聞いてくれ、Vercel AI SDKは魔法の杖じゃない。つまるところ、本当に退屈な配管作業を上手に抽象化したものに過ぎない。ストリーミングを正しく処理する、型付きの構造化出力をくれる、ツール呼び出しを使いやすくする、複数のプロバイダーに対応する。AI機能の仕事の80%では、これが正しい出発点だ。
聞いてくれ、Vercel AI SDKは魔法の杖じゃない。つまるところ、本当に退屈な配管作業を上手に抽象化したものに過ぎない。ストリーミングを正しく処理する、型付きの構造化出力をくれる、ツール呼び出しを使いやすくする、複数のプロバイダーに対応する。AI機能の仕事の80%では、これが正しい出発点だ。
ちなみに、その金融系クライアントは、私がチャットを書き直してから2週間後にはもう本番運用を始めていた。モバイルのSafariでも問題なし。競合状態もなし。6週間が2日になった。時には適切な抽象化の価値は本当に大きいんだ。
