跳转到主要内容
构建一款完整的 AI 驱动研究助手,能够抓取网站并在网上搜索来回答问题。助手会自动判断何时使用网页抓取或搜索工具来收集信息,并基于汇总的数据提供全面的答案。 AI 研究助手聊天界面,展示使用 Firecrawl 实时进行网页抓取以及由 OpenAI 驱动的对话式回复

你将构建的内容

一个 AI 聊天界面,用户可以就任意主题提问。AI 助手会自动判断何时使用网页爬取或搜索工具收集信息,并基于所获取的数据提供全面答案。

前置条件

  • 已安装 Node.js 18 或更高版本
  • 来自 platform.openai.com 的 OpenAI API 密钥
  • 来自 firecrawl.dev 的 Firecrawl API 密钥
  • 具备 React 和 Next.js 的基础知识
1

创建新的 Next.js 项目

先创建一个全新的 Next.js 应用,并进入项目目录:
npx create-next-app@latest ai-sdk-firecrawl && cd ai-sdk-firecrawl
在出现提示时,选择以下选项:
  • TypeScript:是
  • ESLint:是
  • Tailwind CSS:是
  • App Router:是
  • 使用 src/ 目录:否
  • 导入别名:是(@/*)
2

安装依赖项

安装 AI SDK 包

AI SDK 是一个 TypeScript 工具包,提供统一的 API,便于与不同的 LLM 服务提供商对接:
npm i ai @ai-sdk/react zod
这些软件包提供:
  • ai:包含流式传输、工具调用和响应处理的核心 SDK
  • @ai-sdk/react:用于构建聊天界面的 React Hooks(如 useChat
  • zod:用于工具输入的架构校验
ai-sdk.dev/docs 了解更多。

安装 AI Elements

AI Elements 提供用于 AI 应用的预构建 UI 组件。运行以下命令以生成所有必需的组件脚手架:
npx ai-elements@latest
这会在你的项目中初始化 AI Elements,包括会话组件、消息展示、提示输入和工具调用可视化。文档:ai-sdk.dev/elements/overview

安装 OpenAI Provider

安装 OpenAI Provider 以连接到 OpenAI 的模型:
npm install @ai-sdk/openai
3

构建前端聊天界面

app/page.tsx 创建主页面,并从下方的 Code 标签页复制代码。这将是用户与 AI 助手交互的聊天界面。
  • 预览
  • 代码
AI 研究助理聊天机器人界面:借助 Firecrawl 实时抓取网页,并由 OpenAI 驱动的对话式回答

了解前端

前端使用 AI Elements 组件提供完整的聊天界面:核心功能:
  • 会话显示Conversation 组件会自动处理消息的滚动与呈现
  • 消息渲染:每个消息片段会根据其类型(文本、推理、工具调用)进行渲染
  • 工具可视化:工具调用以可折叠的部分展示其输入和输出
  • 交互控件:用户可切换网页搜索、选择模型并附加文件
  • 消息 actions:为 assistant 消息提供复制与重试的 actions
4

添加 Markdown 渲染功能

为确保 LLM 输出的 Markdown 正确渲染,请在你的 app/globals.css 文件中添加以下 import:
@source "../node_modules/streamdown/dist/index.js";
这将导入在消息回复中渲染 Markdown 内容所需的样式。
5

构建基础型 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:
      "你是一个实用的助手,可以回答问题并协助完成任务。",
  });

  // 将数据源和推理过程返回给客户端
  return result.toUIMessageStreamResponse({
    sendSources: true,
    sendReasoning: true,
  });
}
这个基础路由:
  • 接收来自前端的消息
  • 使用用户选择的 OpenAI 模型
  • 将响应以流式方式返回给客户端
  • 尚未包含工具——我们稍后会添加
6

配置环境变量

在项目根目录下创建一个 .env.local 文件:
touch .env.local
添加你的 OpenAI API Key:
OPENAI_API_KEY=sk-your-openai-api-key
OPENAI_API_KEY 是 AI 模型正常运行所必需的。
7

测试基础聊天

现在你可以在未集成 Firecrawl 的情况下测试 AI SDK 聊天机器人。启动开发服务器:
npm run dev
在浏览器中打开 localhost:3000 并测试基础聊天功能。助手应能回复消息,但暂不具备网页抓取或搜索能力。不具备网页抓取功能的基础 AI 聊天机器人
8

添加 Firecrawl 工具

现在,让我们使用 Firecrawl 为助手增强网页抓取和搜索功能。

安装 Firecrawl SDK

Firecrawl 通过抓取和搜索,将网站转换为适用于 LLM 的 formats:
npm i @mendable/firecrawl-js

创建工具文件

创建一个 lib 文件夹,并在其中添加一个 tools.ts 文件:
mkdir lib && touch lib/tools.ts
添加以下代码以定义网页抓取与搜索工具:
lib/tools.ts
import FirecrawlApp from "@mendable/firecrawl-js";
import { tool } from "ai";
import { z } from "zod";

const firecrawl = new FirecrawlApp({ apiKey: process.env.FIRECRAWL_API_KEY });

export const scrapeWebsiteTool = tool({
  description: '抓取任意网站 URL 的内容',
  inputSchema: z.object({
    url: z.string().url().describe('要抓取的 URL')
  }),
  execute: async ({ url }) => {
    console.log('正在抓取:', url);
    const result = await firecrawl.scrape(url, {
      formats: ['markdown'],
      onlyMainContent: true,
      timeout: 30000
    });
    console.log('抓取内容预览:', result.markdown?.slice(0, 200) + '...');
    return { content: result.markdown };
  }
});

export const searchWebTool = tool({
  description: '使用 Firecrawl 搜索网页',
  inputSchema: z.object({
    query: z.string().describe('搜索查询内容'),
    limit: z.number().optional().describe('返回结果数量'),
    location: z.string().optional().describe('本地化结果的地理位置'),
    tbs: z.string().optional().describe('时间过滤器 (qdr:h, qdr:d, qdr:w, qdr:m, qdr:y)'),
    sources: z.array(z.enum(['web', 'news', 'images'])).optional().describe('结果类型'),
    categories: z.array(z.enum(['github', 'research', 'pdf'])).optional().describe('筛选类别'),
  }),
  execute: async ({ query, limit, location, tbs, sources, categories }) => {
    console.log('正在搜索:', 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 || '无标题',
      url: item.url || '',
      description: item.description || '',
    }));

    console.log('搜索结果:', results.length);
    return { results };
  },
});

了解这些工具

Scrape Website 工具:
  • 接收一个 URL 作为输入(由 Zod 架构验证)
  • 使用 Firecrawl 的 scrape 方法以 Markdown 获取页面内容
  • 仅提取主要内容以减少 token 消耗
  • 返回抓取的内容供 AI 分析
Search Web 工具:
  • 接收带可选筛选条件的搜索查询
  • 使用 Firecrawl 的 search 方法查找相关网页
  • 支持位置、时间范围、内容类别等高级筛选
  • 返回包含标题、URL 和描述的结构化结果
了解更多关于工具的信息:ai-sdk.dev/docs/foundations/tools
9

使用 Firecrawl 工具更新 API 路径

现在更新你的 app/api/chat/route.ts,把我们刚创建的 Firecrawl 工具加入进去。
import { streamText, UIMessage, stepCountIs, convertToModelMessages } from "ai";
import { createOpenAI } from "@ai-sdk/openai";
import { scrapeWebsiteTool, searchWebTool } from "@/lib/tools";

const openai = createOpenAI({
  apiKey: process.env.OPENAI_API_KEY!,
});

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.",
    // 在此添加 Firecrawl 工具
    tools: {
      scrapeWebsite: scrapeWebsiteTool,
      searchWeb: searchWebTool,
    },
    stopWhen: stepCountIs(5),
    toolChoice: webSearch ? "auto" : "none",
  });

  return result.toUIMessageStreamResponse({
    sendSources: true,
    sendReasoning: true,
  });
}
与基础路由相比的关键改动:
  • 从 AI SDK 引入 stepCountIs
  • @/lib/tools 引入 Firecrawl 工具
  • 添加包含 scrapeWebsitesearchWebtools 对象
  • 添加 stopWhen: stepCountIs(5) 以限制执行步数
  • 启用网页搜索时将 toolChoice 设为 “auto”,否则为 “none”
了解更多关于 streamTextai-sdk.dev/docs/reference/ai-sdk-core/stream-text
10

添加你的 Firecrawl API 密钥

将你的 .env.local 文件更新为包含 Firecrawl API 密钥:
OPENAI_API_KEY=sk-your-openai-api-key
FIRECRAWL_API_KEY=fc-your-firecrawl-api-key
firecrawl.dev 获取你的 Firecrawl API 密钥。
11

测试完整应用程序

重启开发服务器:
npm run dev
启用了 Firecrawl 工具的 AI 聊天机器人打开 localhost:3000 测试增强版助手:
  1. 切换“Search”按钮以启用网页搜索
  2. 提问:“What are the latest features from firecrawl.dev?”
  3. 观察 AI 调用 searchWebscrapeWebsite 工具
  4. 在 UI 中查看工具执行的输入与输出
  5. 阅读基于抓取数据的 AI 分析

工作机制

消息流

  1. 用户发送消息:用户输入问题并点击提交
  2. 前端发送请求useChat 携带所选模型和网页搜索设置,将消息发送到 /api/chat
  3. 后端处理消息:API 路由接收消息并调用 streamText
  4. AI 选择工具:模型分析问题并决定是否使用 scrapeWebsitesearchWeb(仅在启用网页搜索时)
  5. 工具执行:若调用了工具,Firecrawl 将执行网页抓取或搜索
  6. AI 生成响应:模型分析工具结果并生成自然语言响应
  7. 前端展示结果:UI 实时显示工具调用与最终响应

工具调用流程

AI SDK 的工具调用系统(ai-sdk.dev/docs/foundations/tools)工作方式如下:
  1. 模型接收用户消息和可用的工具描述
  2. 如果模型判断需要使用工具,则生成带参数的工具调用
  3. SDK 使用这些参数执行工具函数
  4. 将工具结果返回给模型
  5. 模型基于结果生成最终响应
以上过程会在一次 streamText 调用中自动完成,并将结果实时流式传输到前端。

核心功能

模型选择

该应用支持多种 OpenAI 模型:
  • GPT-5 Mini(Thinking):OpenAI 的新近模型,具备更强的推理能力
  • GPT-4o Mini:速度快、成本友好的模型
用户可以通过下拉菜单在不同模型之间切换。

Web Search 开关

Search 按钮用于控制 AI 是否可以使用 Firecrawl 工具:
  • 启用:AI 可按需调用 scrapeWebsitesearchWeb 工具
  • 禁用:AI 仅基于其训练知识作答
这让用户可以掌控何时使用网页数据,而非依赖模型的内置知识。

自定义方案思路

添加更多工具

使用其他工具扩展助手:
  • 查询公司内部数据的数据库
  • 集成 CRM 以获取客户信息
  • 发送电子邮件
  • 生成文档
每个工具遵循相同模式:使用 Zod 定义模式(schema),实现 execute 函数,并将其注册到 tools 对象中。

更换 AI 模型

将 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

自定义 UI

AI Elements 组件基于 shadcn/ui 构建,因此你可以:
  • 在组件文件中修改组件样式
  • 为现有组件添加新的变体
  • 创建符合设计体系的自定义组件

最佳实践

  1. 使用合适的工具:优先用 searchWeb 搜索相关页面,单页用 scrapeWebsite,或交由 AI 决定
  2. 监控 API 使用:跟踪你的 Firecrawl 与 OpenAI API 使用量,以避免意外开销
  3. 优雅处理错误:工具内置错误处理,但建议补充面向用户的错误提示
  4. 优化性能:使用流式输出提供即时反馈,并考虑缓存高频访问的内容
  5. 设置合理的限制:通过 stopWhen: stepCountIs(5) 避免过多工具调用与成本失控