Firecrawl と AI SDK で作る AI リサーチアシスタント
ウェブスクレイピングと検索機能を備えたフル機能の AI リサーチアシスタントを構築する
Web サイトのウェブスクレイピングやウェブ検索で質問に答える、フル機能の AI リサーチアシスタントを構築します。アシスタントは情報収集にウェブスクレイピングと検索ツールのどちらを使うべきかを自動で判断し、収集したデータに基づいて包括的な回答を返します。
ユーザーがあらゆるトピックについて質問できるAIチャットインターフェース。AIアシスタントは、情報収集のためにウェブスクレイピングや検索ツールをいつ使うかを自動で判断し、取得したデータに基づいて網羅的な回答を返します。
- Node.js 18 以降がインストールされていること
- platform.openai.com で取得した OpenAI API キー
- firecrawl.dev で取得した Firecrawl API キー
- React と Next.js の基礎知識
新規の Next.js プロジェクトを作成
まずは新しい Next.js アプリケーションを作成し、プロジェクトディレクトリに移動します。
npx create-next-app@latest ai-sdk-firecrawl && cd ai-sdk-firecrawlプロンプトが表示されたら、次のオプションを選択してください:
- TypeScript: はい
- ESLint: はい
- Tailwind CSS: はい
- App Router: はい
- 「src/」ディレクトリを使用: いいえ
- インポートエイリアス: はい (@/*)
依存関係をインストール
AI SDK パッケージのインストール
AI SDK は、さまざまな LLM プロバイダーを統一的に扱える API を提供する TypeScript 製のツールキットです。
npm i ai @ai-sdk/react zodこれらのパッケージでは次の機能を提供します:
ai: ストリーミング、ツール呼び出し、レスポンス処理に対応したコア SDK@ai-sdk/react: チャットインターフェース構築のためのuseChatなどの React フックzod: ツール入力向けのスキーマ検証
詳細は ai-sdk.dev/docs を参照してください。
AI Elements をインストールする
AI Elements は、AI アプリケーション向けのプリビルト UI コンポーネントを提供します。必要なコンポーネント一式を用意するには、次のコマンドを実行します:
npx ai-elements@latestこれは、会話コンポーネント、メッセージ表示、プロンプト入力、ツール呼び出しの可視化など、AI Elements をプロジェクトにセットアップします。
ドキュメント: ai-sdk.dev/elements/overview
OpenAI プロバイダーのインストール
OpenAI のモデルと接続するために、OpenAI プロバイダーをインストールします:
npm install @ai-sdk/openaiフロントエンドのチャットインターフェースを実装する
app/page.tsxにメインページを作成し、下記のCodeタブからコードをコピーします。これがユーザーがAIアシスタントとやり取りするチャットインターフェースになります。
フロントエンドを理解する
フロントエンドはAI Elementsコンポーネントを使用して完全なチャットインターフェースを提供します:
主な機能:
- 会話表示:
Conversationコンポーネントはメッセージのスクロールと表示を自動で処理します - メッセージのレンダリング: 各メッセージの構成要素は、そのタイプ (text、reasoning、tool calls) に応じてレンダリングされます
- ツールの可視化: ツール呼び出しは、入力と出力を示す折りたたみセクションとして表示されます
- インタラクティブコントロール:ユーザーはWeb検索のオン/オフ、モデルの選択、ファイルの添付が可能
- メッセージのアクション: アシスタントメッセージのコピーと再試行
Markdown レンダリングのサポートを追加
LLM からの Markdown を正しく表示するため、app/globals.css ファイルに次の import を追加してください:
@source "../node_modules/streamdown/dist/index.js";メッセージ応答内の Markdown コンテンツをレンダリングするために必要なスタイルをインポートします。
基本的なAPIルートを作成する
app/api/chat/route.ts にチャットAPIエンドポイントを作成します。このルートは受信メッセージを処理し、AIからの応答をストリーミング配信します。
import { streamText, UIMessage, convertToModelMessages } from "ai";
import { createOpenAI } from "@ai-sdk/openai";
const openai = createOpenAI({
apiKey: process.env.OPENAI_API_KEY!,
});
// ストリーミングレスポンスを最大5分まで許可
export const maxDuration = 300;
export async function POST(req: Request) {
const {
messages,
model,
webSearch,
}: {
messages: UIMessage[];
model: string;
webSearch: boolean;
} = await req.json();
const result = streamText({
model: openai(model),
messages: convertToModelMessages(messages),
system:
"You are a helpful assistant that can answer questions and help with tasks.",
});
// ソースと推論結果をクライアントに返送
return result.toUIMessageStreamResponse({
sendSources: true,
sendReasoning: true,
});
}この基本的なルートは次を行います:
- フロントエンドからメッセージを受け取る
- ユーザーが選択した OpenAI モデルを使用する
- レスポンスをクライアントへストリーミングする
- まだツールは未対応 — 次で追加します
環境変数を設定する
プロジェクトのルートに「.env.local」ファイルを作成します:
touch .env.localOpenAI の API キーを追加する:
OPENAI_API_KEY=sk-your-openai-api-keyOPENAI_API_KEY は AI モデルの動作に必須です。
基本的なチャットをテストする
Firecrawl との統合なしで AI SDK のチャットボットをテストできるようになりました。開発サーバーを起動してください:
npm run devブラウザで localhost:3000 を開き、基本的なチャット機能をテストしてください。アシスタントはメッセージに応答しますが、まだWebスクレイピングや検索機能はありません。
Firecrawl のツールを追加
それでは、Firecrawl を使って、ウェブスクレイピングと検索機能でアシスタントを強化しましょう。
Firecrawl SDK のインストール
Firecrawl は、スクレイピングと検索機能により、ウェブサイトを LLM 向けのフォーマットに変換します。
npm i firecrawlTools ファイルを作成する
lib フォルダを作成し、その中に tools.ts ファイルを作成します:
mkdir lib && touch lib/tools.ts次のコードを追加して、ウェブスクレイピングと検索ツールを定義します:
import { Firecrawl } from "firecrawl";
import { tool } from "ai";
import { z } from "zod";
const firecrawl = new Firecrawl({ apiKey: process.env.FIRECRAWL_API_KEY });
export const scrapeWebsiteTool = tool({
description: 'Scrape content from any website URL',
inputSchema: z.object({
url: z.string().url().describe('The URL to scrape')
}),
execute: async ({ url }) => {
console.log('Scraping:', url);
const result = await firecrawl.scrape(url, {
formats: ['markdown'],
onlyMainContent: true,
timeout: 30000
});
console.log('Scraped content preview:', result.markdown?.slice(0, 200) + '...');
return { content: result.markdown };
}
});
export const searchWebTool = tool({
description: 'Search the web using Firecrawl',
inputSchema: z.object({
query: z.string().describe('The search query'),
limit: z.number().optional().describe('Number of results'),
location: z.string().optional().describe('Location for localized results'),
tbs: z.string().optional().describe('Time filter (qdr:h, qdr:d, qdr:w, qdr:m, qdr:y)'),
sources: z.array(z.enum(['web', 'news', 'images'])).optional().describe('Result types'),
categories: z.array(z.enum(['github', 'research', 'pdf'])).optional().describe('Filter categories'),
}),
execute: async ({ query, limit, location, tbs, sources, categories }) => {
console.log('Searching:', query);
const response = await firecrawl.search(query, {
...(limit && { limit }),
...(location && { location }),
...(tbs && { tbs }),
...(sources && { sources }),
...(categories && { categories }),
}) as { web?: Array<{ title?: string; url?: string; description?: string }> };
const results = (response.web || []).map((item) => ({
title: item.title || item.url || 'Untitled',
url: item.url || '',
description: item.description || '',
}));
console.log('Search results:', results.length);
return { results };
},
});ツールの概要
Scrape Website Tool:
- 入力としてURLを受け取る (Zodスキーマで検証)
- Firecrawlの
scrapeメソッドでページをMarkdownとして取得 - トークン使用量を抑えるために主要コンテンツのみを抽出
- 取得したコンテンツをAIの分析用に返す
Search Web Tool:
- 任意のフィルター付き検索クエリを受け取る
- Firecrawlの
searchメソッドで関連するWebページを検索 - 位置情報、期間、コンテンツカテゴリなどの高度なフィルターに対応
- タイトル、URL、説明を含む構造化結果を返す
Firecrawl のツールで API ルートを更新する
先ほど作成した Firecrawl のツールを組み込むように、app/api/chat/route.ts を更新します。
基本的なルートからの主な変更点:
- AI SDK から
stepCountIsをインポート @/lib/toolsから Firecrawl のツールをインポートscrapeWebsiteとsearchWebの両ツールを含むtoolsオブジェクトを追加- 実行ステップ数を制限するために
stopWhen: stepCountIs(5)を追加 - Web 検索が有効な場合は
toolChoiceを “auto”、それ以外は “none” に設定
streamText の詳細: ai-sdk.dev/docs/reference/ai-sdk-core/stream-text.
Firecrawl APIキーを追加
.env.local ファイルに Firecrawl の API キーを追加してください。
OPENAI_API_KEY=sk-your-openai-api-key
FIRECRAWL_API_KEY=fc-your-firecrawl-api-keyfirecrawl.dev から Firecrawl の API キーを取得してください。
アプリケーション全体のテスト
開発サーバーを再起動してください:
npm run devlocalhost:3000 を開き、拡張されたアシスタントを試します:
- 「Search」ボタンをオンにしてウェブ検索を有効化
- 次のように質問します: 「firecrawl.dev の最新機能は何ですか?」
- AI が
searchWebまたはscrapeWebsiteツールを呼び出す様子を確認 - 入力と出力とともに、UI 上でのツールの実行を確認
- スクレイピングしたデータに基づく AI の分析を読む
- ユーザーがメッセージを送信: ユーザーが質問を入力し、送信をクリック
- フロントエンドがリクエストを送信:
useChatが選択したモデルとウェブ検索設定を添えてメッセージを/api/chatに送信 - バックエンドがメッセージを処理: API ルートがメッセージを受け取り、
streamTextを呼び出す - AI がツールを選択: モデルが質問を分析し、
scrapeWebsiteかsearchWebを使うかを判断(ウェブ検索が有効な場合のみ) - ツールを実行: ツールが呼び出された場合、Firecrawl がウェブをスクレイピングまたは検索
- AI が応答を生成: モデルがツールの結果を分析し、自然言語の応答を生成
- フロントエンドが結果を表示: UI がツール呼び出しと最終応答をリアルタイムに表示
AI SDK のツール呼び出しシステム(ai-sdk.dev/docs/foundations/tools)は以下のように動作します。
- モデルがユーザーのメッセージと利用可能なツールの説明を受け取る
- ツールが必要と判断した場合、モデルがパラメータ付きのツール呼び出しを生成する
- SDK がそのパラメータでツール関数を実行する
- ツールの結果がモデルに返される
- モデルがその結果を用いて最終的な応答を生成する
これらはすべて単一の streamText 呼び出し内で自動的に行われ、結果はリアルタイムでフロントエンドへストリーミングされます。
このアプリケーションは複数の OpenAI モデルに対応しています:
- GPT-5 Mini (Thinking): 高度な推論機能を備えた最新の OpenAI モデル
- GPT-4o Mini: 高速でコスト効率に優れたモデル
モデルはドロップダウンセレクターで切り替えられます。
Search ボタンは、AI が Firecrawl のツールを利用できるかどうかを切り替えます:
- 有効: 必要に応じて
scrapeWebsiteとsearchWebツールを呼び出せます - 無効: AI は学習済みの知識のみで応答します
これにより、ユーザーはモデルの内蔵知識とウェブデータのどちらをいつ使うかを制御できます。
アシスタントに追加のツールを組み込みましょう:
- 社内データのデータベース参照
- 顧客情報を取得するためのCRM連携
- メール送信機能
- ドキュメント生成
各ツールは同じパターンに従います。Zodでスキーマを定義し、execute関数を実装し、toolsオブジェクトに登録します。
OpenAI を別のプロバイダーに切り替える:
import { anthropic } from "@ai-sdk/anthropic";
const result = streamText({
model: anthropic("claude-4.5-sonnet"),
// ... その他の設定
});AI SDK は同一の API で 20 以上のプロバイダに対応しています。詳しくは次をご覧ください: ai-sdk.dev/docs/foundations/providers-and-models。
AI Elements のコンポーネントは shadcn/ui をベースに構築されているため、次のことができます。
- コンポーネントファイル内でスタイルを変更する
- 既存のコンポーネントに新しいバリアントを追加する
- デザインシステムに合わせたカスタムコンポーネントを作成する
-
適切なツールを使う: まず関連ページの発見には
searchWeb、単一ページにはscrapeWebsiteを使うか、AI に任せる -
API 使用量を監視する: 予期せぬコストを避けるために Firecrawl と OpenAI の API 使用量を把握する
-
エラーを適切に処理する: ツールにはエラーハンドリングが含まれているが、ユーザー向けのエラーメッセージの表示も検討する
-
パフォーマンスを最適化する: ストリーミングで即時にフィードバックを返し、頻繁にアクセスされるコンテンツはキャッシュを検討する
-
妥当な制限を設定する:
stopWhen: stepCountIs(5)は過剰なツール呼び出しやコストの暴走を防ぐ