Zenn
🙌

Vercel AI SDK + Next.jsで簡易チャットページ(OllamaとOpenAI)

に公開

はじめに

Next.jsでVercel AI SDKを使用した簡単なテキストチャットページを作成します。
以前にも記事を書きましたが、実装内容が古くなりましたので新たに記事にしてみました。

LLMは、OpenAIのGPT-4o miniと、ローカルLLMであるOllamaでgemma3:4bを使ってみました。
※ マシンスペックが足りないなど環境がない方は、Ollamaの部分は読み飛ばしてください。

以前の記事
https://zenn.dev/nari007/articles/9c178246dfafd5

読者対象

  • Next.js開発の経験者

事前準備

  • OpenAI プラットフォームから API キーを作成&取得しておく。
    https://platform.openai.com/

  • Ollamaも使いたい場合は起動しておきます。

参考ドキュメント

https://sdk.vercel.ai/docs/ai-sdk-ui/chatbot
https://sdk.vercel.ai/providers/community-providers/ollama#ollama-provider

セットアップ

環境
node v23.1.0

ステップ

  • Next.jsを作成する
  • .env.localにOPENAI_API_KEYを記述する
    OPENAI_API_KEY=your-api-key
  • app/api/chat/route.tsを作成する
  • app/page.tsxを作成する
  • components/Chat.tsを作成する

Next.jsを作成
Typescript + App router

yarn create next-app simple-ai-chat --typescript
cd simple-ai-chat

yarn add ai @ai-sdk/openai zod
yarn add ollama-ai-provider

package.json(抜粋)

    "dependencies": {
    "@ai-sdk/openai": "^1.2.5",
    "ai": "^4.1.61",
    "next": "15.2.2",
    "ollama-ai-provider": "^1.2.0",
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
    "zod": "^3.24.2"
  },
  "devDependencies": {
    "@eslint/eslintrc": "^3",
    "@tailwindcss/postcss": "^4",
    "@types/node": "^20",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "eslint": "^9",
    "eslint-config-next": "15.2.2",
    "tailwindcss": "^4",
    "typescript": "^5"
  }

コード
OpenAIの場合

  • app/api/chat/route.ts
import { openai } from "@ai-sdk/openai";
import { streamText } from "ai";

// Vercel edgeを使う場合
export const runtime = "edge";

// ストリーミング応答を最大 30 秒まで許可
export const maxDuration = 30;

export const POST = async (req: Request) => {
  const { messages } = await req.json();

  // システムプロンプトの設定
  const systemPrompt =
    "フレンドリーに会話してください。語尾は必ず「にゃん」をつけて。";
  // temperatureの設定
  const temperature = 0.5;

  const result = streamText({
    model: openai("gpt-4o-mini"),
    messages,
    system: systemPrompt,
    temperature: temperature,
  });

  return result.toDataStreamResponse();
}

Ollamaの場合

  • app/api/chat/route.ts
  • OllamaのデフォルトURLはhttp://localhost:11434/apiになっています。
import { streamText } from "ai";
import { ollama } from "ollama-ai-provider";

// ストリーミング応答を最大 30 秒まで許可
export const maxDuration = 30;

export const POST = async (req: Request) => {
  const { messages } = await req.json();

  // システムプロンプトの設定
  const systemPrompt =
    "フレンドリーに会話してください。語尾は必ず「にゃん」をつけて。";
  // temperatureの設定
  const temperature = 0.5;

  const result = streamText({
    model: ollama("gemma3:4b"),
    messages,
    system: systemPrompt,
    temperature: temperature,
  });

  return result.toDataStreamResponse();
}
  • app/page.ts
import { Chat } from "@/components/Chat";

const Top = () => {
  return (
    <>
      <Chat />
    </>
  );
};
export default Top;
  • conponents/Chat.tsx
"use client";

import { useChat } from "@ai-sdk/react";
import { Message } from "ai";

export const Chat = () => {
  const { messages, input, handleInputChange, handleSubmit } = useChat({});

  // チャット履歴をコンソール出力
  const logMessages = () => {
    if (messages.length > 0) console.log(messages);
  };

  return (
    <>
      <div className="mx-auto w-full max-w-md py-24 flex flex-col">
        <p className="font-bold text-lg">AI CHAT</p>
        {messages.map((m: Message) => (
          <div key={m.id} className="w-96 mb-2 p-2">
            {m.role === "user" ? "User: " : "AI: "}
            {m.content}
          </div>
        ))}
        <form onSubmit={handleSubmit}>
          <input
            name="box"
            className="w-96 flex rounded bottom-0 border border-gray-300 text-gray-200 mb-2 p-2"
            value={input}
            onChange={handleInputChange}
            placeholder="メッセージを入力..."
          />
          <button
            type="submit"
            className="w-96 rounded bg-sky-500 hover:bg-sky-700 mb-2 p-2"
          >
            Send message
          </button>
        </form>
        <button
          onClick={logMessages}
          className="w-96 rounded bg-gray-700 hover:bg-gray-500 mb-2 p-2"
        >
          メッセージをコンソールに表示
        </button>
        <p className="w-96 text-slate-500 text-xs">
          Chromeブラウザで画面を右クリックして「検証」を選ぶと、
          DEVツールのコンソールからメッセージの値が確認できます。
          会話の履歴の状態や格納内容が確認できます。
        </p>
      </div>
    </>
  );
};

動作確認

ストリーミング回答するシンプルなテキストチャット

メッセージをコンソールに表示をクリックしたあとに、DEVツールのコンソールでメッセージの値が確認できます。どんなふうに格納されているのか確認できます。

まとめ

クライアント側のuseChatの仕様が変わっていたので、今回また新しく記事にしてみました。
Ollama利用でもollama-ai-providerを使う実装にしています。
エラー処理や細かいところは未実装なものですが、はじめてチャットページを作成するときの参考になればと思います。

Discussion

ログインするとコメントできます