Vercel AI SDKを使った生成AIチャットアプリをNext.js & Cloudflareで実装する

Next.jsとVercel AI SDKを使って、Cloudflare Workers AI上で動作する生成AIチャットアプリを実装する方法を解説。プロジェクト設定からストリーミングレスポンス対応、チャットUIの構築までの手順を紹介。

広告ここから
広告ここまで

目次

    Next.js アプリケーションを簡単に構築できる AI SDK を使ってみたので、簡単にまとめてみました。シンプルなチャットアプリをCloudflare Workers AI で作ってみています。

    アプリケーションのセットアップ

    まずは Cloudflare の CLI ツールを使ってプロジェクトを作成していきます。

    npm create cloudflare@latest

    対話形式でプロジェクトの設定を進めていきます。

    ╭ Create an application with Cloudflare Step 1 of 3
    │
    ├ In which directory do you want to create your application?
    │ dir ./cf-vercel-ai
    │
    ├ What would you like to start with?
    │ category Framework Starter
    │
    ├ Which development framework do you want to use?
    │ framework Next.js
    │
    ├ Continue with Next.js via `npx [email protected] cf-vercel-ai`
    │
    
    Need to install the following packages:
    [email protected]
    Ok to proceed? (y) 

    続いて Next.js の初期設定を行います。TypeScript や ESLint、Tailwind CSS などの使用について聞かれるので、お好みで選択してください。

    Ok to proceed? (y) y
    ✔ Would you like to use TypeScript? … No / Yes
    ✔ Would you like to use ESLint? … No / Yes
    ✔ Would you like to use Tailwind CSS? … No / Yes
    ✔ Would you like your code inside a `src/` directory? … No / Yes
    ✔ Would you like to use App Router? (recommended) … No / Yes
    ✔ Would you like to use Turbopack for `next dev`? … No / Yes
    ✔ Would you like to customize the import alias (`@/*` by default)? … No / Yes

    Vercel AI SDK のセットアップ

    次に必要なパッケージをインストールします。Workers AI を Vercel AI SDK で使うためのプロバイダーも一緒にインストールしておきましょう。

    npm i workers-ai-provider ai @ai-sdk/openai zod

    wrangler.jsonc に AI バインディングの設定を追加します。

      "ai": {
        "binding": "AI"
      }

    次のコマンドで、env.d.ts を更新します。

    wrangler types --env-interface CloudflareEnv env.d.ts

    これで Workers AI を使う準備が整いました。

    最初の API を作成

    まずはシンプルなテキスト生成 API を作ってみます。app/api/hello/route.ts を作成して、以下のコードを記述してください。

    import { getRequestContext } from "@cloudflare/next-on-pages";
    import { createWorkersAI } from 'workers-ai-provider';
    import { generateText } from 'ai';
    
    export const runtime = 'edge'
    
    export async function GET() {
      const ai = getRequestContext().env.AI
      const workersai = createWorkersAI({ binding: ai });
      const result = await generateText({
        model: workersai('@cf/meta/llama-2-7b-chat-int8'),
        prompt: 'Write a 50-word essay about hello world.',
      });
    
      return new Response(result.text);
    }

    ストリーミングレスポンスへの対応

    リアルタイムで応答を返したい場合は、streamText を使用します。先ほどのコードを少し修正してみましょう。

    import { getRequestContext } from "@cloudflare/next-on-pages";
    import { createWorkersAI } from 'workers-ai-provider';
    import { generateText, streamText } from 'ai';
    
    export const runtime = 'edge'
    
    export async function GET() {
      const ai = getRequestContext().env.AI
      const workersai = createWorkersAI({ binding: ai });
      const result = streamText({
        model: workersai('@cf/meta/llama-2-7b-chat-int8'),
        prompt: 'Write a 50-word essay about hello world.',
      })
      return result.toDataStreamResponse();
    }

    streamText を使うことで、生成されたテキストを逐次的にクライアントへ送信できるようになります。

    AI SDK を使ったチャット UI の構築

    ここからが本題です。Vercel AI SDK の useChat フックを使って、対話型のチャットインターフェースを作成していきます。

    チャット用 API の作成

    app/api/chat/route.ts を作成します。これはクライアントで使う useChat が呼び出す API のデフォルトパスが /api/chat で、POST リクエストを投げるためです。streamText の引数が prompt から messages に変わっている点にも注意してください。

    import { getRequestContext } from "@cloudflare/next-on-pages";
    import { createWorkersAI } from 'workers-ai-provider';
    import { CoreMessage, streamText } from 'ai';
    
    export const runtime = 'edge'
    
    export async function POST(req: Request) {
      const { messages } = await req.json<{messages: CoreMessage[]}>()
      const ai = getRequestContext().env.AI
      const workersai = createWorkersAI({ binding: ai });
      const result = streamText({
        model: workersai('@cf/meta/llama-2-7b-chat-int8'),
        messages,
      })
      return result.toDataStreamResponse();
    }

    messages には次のような形式でデータが送られてきます。

    [
      {
        role: 'user',
        content: 'hello my name is hidetaka',
        parts: [ { type: 'text', text: 'hello my name is hidetaka' } ]
      }
    ]

    チャット UI の実装

    Client Component を作成します。app/chat-ui.tsx というファイルを作って、以下のコードを記述しましょう。

    'use client';
    
    import { useChat } from '@ai-sdk/react';
    
    export default function Chat() {
      const { messages, input, handleInputChange, handleSubmit } = useChat();
      return (
        <div className="flex flex-col w-full max-w-md py-24 mx-auto stretch">
          {messages.map(m => (
            <div key={m.id} className="whitespace-pre-wrap">
              {m.role === 'user' ? 'User: ' : 'AI: '}
              {m.content}
            </div>
          ))}
    
          <form onSubmit={handleSubmit}>
            <input
              className="fixed dark:bg-zinc-900 bottom-0 w-full max-w-md p-2 mb-8 border border-zinc-300 dark:border-zinc-800 rounded shadow-xl"
              value={input}
              placeholder="Say something..."
              onChange={handleInputChange}
            />
          </form>
        </div>
      );
    }

    app/page.tsx で呼び出します。

    import Chat from "./chat-ui";
    
    export default function Home() {
      return (
        <div>
          <Chat />
        </div>
      );
    }

    これでチャットアプリケーションが完成しました!

    useChat の便利な機能

    useChat フックの素晴らしいところは、現在の会話履歴を自動的に管理してくれることです。

    実際の会話例を見てみましょう。

    User: hello my name is hidetaka
    AI: Nice to meet you Hidetaka! That's a unique and name. Where you from? What you here today?
    User: do you remember my name?
    AI: I remember that your name is Hidetaka! I'm impressed by memory How can I you, Hetaka?

    2回目のリクエスト時には、messages に以下のような履歴が含まれています。

    [
      {
        role: 'user',
        content: 'hello my name is hidetaka',
        parts: [ { type: 'text', text: 'hello my name is hidetaka' } ]
      },
      {
        role: 'assistant',
        content: 'Nice to meet you Hidetaka! That\'s a unique and name. Where you from? What you here today?',
        parts: [
          {
            type: 'text',
            text: 'Nice to meet you Hidetaka! That\'s a unique and name. Where you from? What you here today?'
          }
        ]
      },
      {
        role: 'user',
        content: 'do you remember my name?',
        parts: [ { type: 'text', text: 'do you remember my name?' } ]
      }
    ]

    このように、会話の文脈を保持しながらやり取りができるため、より自然な対話が可能になります。

    参考リンク

    今回の実装で参考にしたドキュメントはこちらです:

    Cloudflare Workers AI と Vercel AI SDK を組み合わせることで、エッジで動作する高速なチャットアプリケーションを簡単に構築できました。ぜひ皆さんも試してみてください!

    広告ここから
    広告ここまで
    Home
    Search
    Bookmark