Gemini
使用 Gemini 构建流式聊天机器人并部署到 Vercel
本文介绍如何构建一个基于 Gemini 的聊天机器人,后端使用 Vercel Serverless Function 调用 Gemini API 并流式返回响应,前端使用 React 组件实时显示文本。涵盖架构、后端实现、接口测试、前端对接和部署步骤。
Gemini 是 Google 推出的多模态大语言模型,通过 API 可以方便地将 AI 能力集成到自己的应用中。本文介绍如何构建一个聊天机器人,后端使用 Vercel Serverless Function 调用 Gemini API,并将响应以文本流的形式逐步返回给浏览器;前端使用 React 组件读取该流并实时更新聊天窗口。这种流式传输方式让用户无需等待完整回复生成,就能看到文字逐字出现,体验更接近现代 AI 聊天界面。
本教程适合有一定 React 和 Node.js 基础、希望将 AI 对话能力集成到 Web 应用中的开发者。你将了解如何设置 Vercel Serverless Function、如何调用 Gemini 的流式生成接口、如何用 React 处理流式响应,以及如何将整个应用部署到 Vercel。文中还会用 curl 测试接口,确保后端正常工作后再编写前端代码。
关于 Gemini 的更多信息,可以参考 Gemini 工具介绍。
准备工作
开始之前,请确保具备以下条件:
- 本地安装 Node.js 18 或更高版本。
- 从 Google AI Studio 获取 Gemini API 密钥。
- 拥有 Vercel 账号,并安装 Vercel CLI。
- 在项目中安装
@google/genai包:npm install @google/genai。
将 API 密钥存储在环境变量 GOOGLE_API_KEY 中。本地开发时,可以在项目根目录的 .env 文件中设置;部署到 Vercel 时,需要在 Vercel 项目控制台的 Settings → Environment Variables 中添加同名变量。
操作步骤
1. 架构概览
整个应用由两部分组成:
- 前端:React 聊天组件,用户输入消息,AI 回复以流式方式渲染。
- 后端:Vercel Serverless Function(例如
api/chat),负责验证请求、调用 Gemini、并将输出流式返回。
工作流程:浏览器向 Vercel Function 发送请求 → Function 调用 Gemini API → Gemini 的响应以文本块形式逐步返回给浏览器。
2. 定义 API 契约
前端发送 POST 请求到 /api/chat,请求体为 JSON,包含用户消息和简历数据(本教程的聊天机器人充当简历教练)。后端结合两者生成针对性回复。
POST /api/chat
{
"message": "How can I improve my resume summary?",
"resume": {
"name": "...",
"experience": [...],
"skills": [...]
}
}
3. 实现后端 Serverless Function
在项目根目录创建 api/chat.js(或 api/chat/index.js),代码如下:
const { GoogleGenAI } = require("@google/genai");
const ai = new GoogleGenAI({ apiKey: process.env.GOOGLE_API_KEY });
const MAX_TEXT_LENGTH = 2000;
const MAX_ARRAY_LENGTH = 50;
function sanitizeString(input = "") {
// 实现字符串清洗逻辑,例如去除多余空白、限制长度
return input.slice(0, MAX_TEXT_LENGTH);
}
function sanitizeObject(obj = {}) {
// 实现对象清洗逻辑,例如限制数组长度、去除危险字段
return obj;
}
const allowCors = fn => async (req, res) => {
res.setHeader('Access-Control-Allow-Origin', '<<你的Web应用URL>>');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
if (req.method === 'OPTIONS') {
return res.status(200).end();
}
return await fn(req, res);
};
const handler = async (req, res) => {
if (req.url === '/api/chat' && req.method === 'POST') {
const { message, resume } = req.body || {};
const safeMessage = sanitizeString(message);
const safeResume = sanitizeObject(resume);
if (!safeMessage || !safeResume) {
return res.status(400).json({ error: 'Missing message or resume data' });
}
try {
const stream = await ai.models.generateContentStream({
model: "<<Gemini模型名称>>",
contents: `You are a professional Resume Coach AI.
- Always respond clearly, politely, and professionally.
- <<根据需求添加描述性指令>>
- Resume data: ${JSON.stringify(safeResume, null, 2)}
User question: ${safeMessage}`,
});
res.setHeader("Content-Type", "text/plain; charset=utf-8");
res.setHeader("Cache-Control", "no-cache");
for await (const chunk of stream) {
const text = chunk.text;
if (text) {
res.write(text);
}
}
res.end();
} catch (error) {
console.error('Gemini Error:', error);
res.status(500).json({ error: 'AI request failed' });
}
}
// 健康检查端点
if (req.url === '/api/chat?type=healthcheck' && req.method === 'GET') {
res.status(200).json({ message: 'Hello from the chat endpoint!' });
}
};
module.exports = allowCors(handler);
代码要点:
- 初始化客户端:使用
GoogleGenAI和 API 密钥创建客户端。 - 输入清洗:对
message和resume进行清洗,避免将不可信的用户输入直接传给 AI。 - CORS 处理:
allowCors包装函数设置跨域头,并处理浏览器预检请求(OPTIONS)。 - 请求验证:若消息或简历数据缺失,返回 400 错误,避免无效调用。
- 流式调用:使用
generateContentStream获取异步迭代器,用for await...of循环读取每个文本块,并通过res.write(text)立即发送给浏览器。 - 错误处理与健康检查:捕获异常并返回 500;提供 GET 健康检查端点便于测试。
4. 测试接口
在编写前端之前,先用 curl 验证流式响应是否正常:
curl -N -X POST "http://localhost:3000/api/chat" \
-H "Content-Type: application/json" \
-d '{"message":"Give me 3 resume summary tips","resume":{"name":"Test"}}'
-N 参数禁用 curl 的输出缓冲,你会看到文本在终端中逐步出现,这证明流式传输已生效。
5. 实现前端 React 组件
前端核心是 sendMessage 函数,它负责发送用户输入并读取流式响应。组件外壳如下:
function ChatWidget({ resume }) {
const [messages, setMessages] = useState([]);
const [input, setInput] = useState("");
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
async function sendMessage() {
// 实现见下文
}
return (
<div className="chat-widget">
{/* 消息列表、输入框和发送按钮 */}
</div>
);
}
sendMessage 的完整实现:
async function sendMessage() {
const messageText = input.trim();
if (!messageText || loading) return;
const userMessage = { role: "user", text: messageText };
setMessages((prev) => [...prev, userMessage]);
setInput("");
setLoading(true);
setError(null);
try {
const res = await fetch("<<你的Vercel函数URL>>/api/chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ message: messageText, resume })
});
if (!res.ok) {
throw new Error(`Failed to get response: ${res.status}`);
}
const reader = res.body.getReader();
const decoder = new TextDecoder();
let fullText = "";
// 添加一个空的助手消息,用于逐步填充
setMessages((prev) => [...prev, { role: "assistant", text: "" }]);
while (true) {
const { value, done } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
fullText += chunk;
// 更新最后一条助手消息的文本
setMessages((prev) => {
const newMessages = [...prev];
newMessages[newMessages.length - 1] = { role: "assistant", text: fullText };
return newMessages;
});
}
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}
该函数将用户消息添加到列表,清空输入框,然后发送 POST 请求。通过 res.body.getReader() 读取响应流,每次读取到新文本块时更新最后一条助手消息,实现实时显示。
6. 部署到 Vercel
在项目根目录运行 vercel 命令,按照提示登录并部署。确保在 Vercel 项目设置中添加 GOOGLE_API_KEY 环境变量。部署后,将前端中的 <<你的Vercel函数URL>> 替换为实际部署的 URL。
一个完整示例
假设我们要构建一个简历教练聊天机器人,完整流程如下:
- 创建项目目录并初始化 Node.js:
npm init -y。 - 安装依赖:
npm install @google/genai和vercel(全局或本地)。 - 创建
api/chat.js,写入上述后端代码,将模型名称替换为可用的 Gemini 模型(如gemini-2.0-flash),并将 CORS 中的<<你的Web应用URL>>替换为你的前端域名(本地开发可用http://localhost:3000)。 - 在
.env文件中设置GOOGLE_API_KEY=你的密钥。 - 本地启动 Vercel 开发服务器:
vercel dev。 - 用 curl 测试接口,确认流式响应正常。
- 创建 React 应用(例如使用 Vite 或 Next.js),实现
ChatWidget组件,并将fetch的 URL 指向本地或部署后的函数地址。 - 部署:运行
vercel --prod,并在 Vercel 控制台设置环境变量。
注意事项
- API 密钥安全:切勿将密钥硬编码在前端代码中,务必通过环境变量管理。
- 输入清洗:示例中的
sanitizeString和sanitizeObject需要根据实际需求实现,防止提示注入或超长输入。 - CORS 配置:如果聊天组件嵌入在不同域名,必须正确设置
Access-Control-Allow-Origin,否则浏览器会拦截请求。 - 模型选择:代码中的
<<Gemini模型名称>>需要替换为当前可用的模型 ID,不同模型可能有不同的速率限制和计费标准,请参考 Gemini 官方文档。 - 配额与计费:Gemini API 有调用配额和费用,具体限制以 Google AI Studio 或官方文档为准。
- 错误处理:前端应处理网络错误、HTTP 错误状态,并给用户适当反馈。
- 流式传输的浏览器兼容性:
fetch的流式读取需要现代浏览器支持,旧浏览器可能无法正常工作。