0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Next.js】AIチャットを個人開発してVercelにデプロイした話

Posted at

はじめに

こんにちは、遠藤太一 です。
自身のポートフォリオサイト(Next.js製)に、
**「医療分野のユースケースを想定した情報提供AIチャット」**を
技術検証目的で実装しました。

本記事では、その実装過程で遭遇した Vercel AI SDK 利用時のトラブル と、
最終的に ライブラリに依存しないシンプルな実装(Vanilla Fetch) に切り替えて
安定動作させた経験を共有します。

※本記事は個人による技術検証・個人開発の記録です
※医療行為・診断を目的としたものではありません
※特定の医療機関・実案件・契約業務とは一切関係ありません

開発環境

Framework: Next.js 16(App Router)

Language: TypeScript

Styling: Tailwind CSS

AI API: OpenAI API(GPT-4o)

Deployment: Vercel

実装した機能概要(技術検証)

本実装では、
**「医療相談UIを想定したAIチャット」**として、
以下のような情報整理を行う構成を試しました。

状況の整理(緊急性の目安)

受診先の一般的な案内

想定されるリスク要因の整理

一般的な注意点・セルフケア情報

あくまで 情報提供・整理を目的としたUI検証 であり、
診断や医療判断を行うものではありません。

利用範囲は メンバー限定エリア に制限し、
技術検証用途としてのみアクセスできる形にしています。

実装のポイント:システムプロンプト設計

単なる Chat UI ではなく、
AIの振る舞いを制御するためのシステムプロンプトを設計しました。

const systemPrompt = `
あなたは医療分野の一般的な情報整理を支援するAIアシスタントです。
ユーザーの入力内容をもとに、以下の形式で一般的な情報提供を行ってください。

1. 【状況整理の目安】
2. 【受診先の一般的な案内】
3. 【考えられるリスク要因】
4. 【一般的な注意点・アドバイス】

※必ず「これはAIによる情報提供であり、医師の診断ではありません」
という注意書きを含めてください。
`;

役割を「診断」ではなく「情報整理・案内」に限定している点が重要です。

直面した問題:ライブラリ依存の落とし穴

当初は、Next.js で AI チャットを簡単に構築できる
Vercel AI SDK(ai / @ai-sdk/react) を利用しました。

しかし、デプロイ後に以下の問題が発生しました。

① Module not found エラー

Module not found: Can't resolve 'ai/react'

SDK のバージョンアップ(v6系)により import パスが変更されており、
@ai-sdk/react を別途導入する必要がありました。

② クライアントサイドでの予期せぬクラッシュ

一見正常に動作しているように見えたものの、
デプロイ後に以下のエラーで画面が真っ暗に。

Uncaught TypeError: Cannot read properties of undefined (reading 'trim')

useChat フックが返す input が、
特定のタイミングで undefined になるケースがあり、
.trim() 呼び出しでクラッシュしていました。

ライブラリ内部の挙動がブラックボックス化しており、
原因特定が難しい状態でした。

解決策:ライブラリを使わず自前実装する

最終的に、

「必要なことは fetch と state 管理だけ」

と割り切り、
ライブラリをすべて外して 自前実装 に切り替えました。

シンプルなチャット実装(骨子)

const [messages, setMessages] = useState<Message[]>([]);
const [input, setInput] = useState("");

const handleSubmit = async (e: React.FormEvent) => {
  e.preventDefault();
  if (!input.trim()) return;

  const userMessage = { role: 'user', content: input };
  setMessages(prev => [...prev, userMessage]);
  setInput("");

  const response = await fetch('/api/chat', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ messages: [...messages, userMessage] }),
  });

  const reader = response.body?.getReader();
  const decoder = new TextDecoder();

  setMessages(prev => [...prev, { role: 'assistant', content: '' }]);

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    const chunk = decoder.decode(value, { stream: true });

    setMessages(prev => {
      const newMessages = [...prev];
      newMessages[newMessages.length - 1].content += chunk;
      return newMessages;
    });
  }
};

結果

自前実装に切り替えたことで、

✅ 不定期に発生していたクラッシュが解消

✅ ライブラリ依存による破壊的変更リスクがゼロ

✅ UI・挙動を完全にコントロール可能

というメリットが得られました。

まとめ

便利なライブラリは開発を加速してくれますが、
AI系ライブラリは進化が速く、挙動が不安定になることも多いと感じました。

シンプルな要件であれば、

「一度、素の React / Fetch に立ち返る」

ことが、
結果的に最も堅牢で理解しやすい実装になる場合もあります。

ポートフォリオとしても
**「実際に動く AI アプリ」**は強いアピール材料になりますね。

セキュリティに関する注意

本番環境で AI API を利用する場合は、
必ずサーバーサイド(API Routes / Edge Functions)で API Key を管理し、
クライアントに直接露出させないようにしましょう。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?