使用 Genkit 构建 AI 驱动的 React 应用
Source: Dev.to
介绍
过去几个月我一直在使用各种 AI 框架,而 Genkit 已经成为我在生产应用中首选的工具。它是 Google 开源的用于构建 AI 工作流的框架,能够解决将大语言模型(LLM)接入真实应用时出现的诸多痛点。
如果你曾尝试在生产应用中加入 AI 功能,可能会遇到相同的问题:
- Prompt spaghetti – 提示最初是简单的字符串,随后会演变成带有上下文注入的多行模板,最终散落在各个服务中,缺乏明确的所有权。
- Debugging black holes – AI 流程出现异常。是提示词的问题?模型?还是传入的上下文?没有合适的可观测性,定位问题简直是噩梦。
- Integration mess – 需要调用外部 API,把数据传给 LLM,可能还要做后处理,最后返回结构化响应。如果没有干净的抽象,这一切会变得非常丑陋。
Genkit 通过提供 flow 原语 来解决这些问题——可以把它想象成一个函数,内部可以包含多个步骤(API 调用、LLM 调用、转换),并自带日志记录和开发者 UI 供检查。
我们将创建一个 城市分析器,它:
- 从前端获取城市名称。
- 从外部 API 获取真实的天气数据。
- 将数据传给 Gemini 进行分析。
- 返回原始数据和 AI 生成的洞察。
虽然并不革命性,但它展示了在大多数真实 AI 功能中会使用的模式。
后端设置
mkdir genkit-backend && cd genkit-backend
npm init -y
npm install @genkit-ai/core @genkit-ai/flow @genkit-ai/google-ai express cors
npm install -D typescript ts-node @types/node @types/express
Flow 实现 (src/flows/analyzeCity.ts)
// src/flows/analyzeCity.ts
import { flow } from '@genkit-ai/flow';
import { genkit } from '@genkit-ai/core';
import { googleAI } from '@genkit-ai/google-ai';
const ai = genkit({
plugins: [googleAI()],
});
interface WeatherData {
temp: number;
humidity: number;
wind_speed: number;
}
async function fetchWeather(city: string): Promise<WeatherData> {
const response = await fetch(
`https://api.api-ninjas.com/v1/weather?city=${encodeURIComponent(city)}`,
{
headers: { 'X-Api-Key': process.env.WEATHER_API_KEY! },
}
);
if (!response.ok) {
throw new Error(`Weather API failed: ${response.status}`);
}
return response.json();
}
export const analyzeCityFlow = flow(
'analyzeCityFlow',
async ({ city }: { city: string }) => {
// Step 1: Get external data
const weather = await fetchWeather(city);
// Step 2: Generate AI analysis
const result = await ai.generate({
model: 'google/gemini-pro',
prompt: `Given the current weather in ${city}:
- Temperature: ${weather.temp}°C
- Humidity: ${weather.humidity}%
- Wind Speed: ${weather.wind_speed} km/h
Write 2‑3 sentences describing what it's like there right now and any practical suggestions for someone visiting today.`,
});
return {
city,
weather,
analysis: result.text(),
};
}
);
Express 服务器 (src/server.ts)
// src/server.ts
import express from 'express';
import cors from 'cors';
import { analyzeCityFlow } from './flows/analyzeCity';
const app = express();
app.use(cors());
app.use(express.json());
app.post('/api/analyze-city', async (req, res) => {
try {
const result = await analyzeCityFlow(req.body);
res.json(result);
} catch (error) {
console.error('Flow failed:', error);
res.status(500).json({ error: 'Analysis failed' });
}
});
const PORT = process.env.PORT || 4000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
你可以使用以下命令单独测试 flows:
npx genkit dev
这会启动本地 UI,您可以在其中使用测试输入调用 flows 并检查每一步——省下大量调试时间。
前端设置
npm create vite@latest genkit-react -- --template react-ts
cd genkit-react
npm install
React 应用 (src/App.tsx)
// src/App.tsx
import { useState } from 'react';
interface AnalysisResult {
city: string;
weather: {
temp: number;
humidity: number;
wind_speed: number;
};
analysis: string;
}
function App() {
const [city, setCity] = useState('');
const [result, setResult] = useState<AnalysisResult | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const handleAnalyze = async () => {
if (!city.trim()) return;
setLoading(true);
setError(null);
try {
const response = await fetch('http://localhost:4000/api/analyze-city', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ city }),
});
if (!response.ok) throw new Error('Request failed');
setResult(await response.json());
} catch {
setError('Failed to analyze city. Check if the backend is running.');
} finally {
setLoading(false);
}
};
return (
<div style={{ maxWidth: 600, margin: '0 auto', padding: 20 }}>
<h2>City Weather Analyzer</h2>
<div style={{ display: 'flex', gap: 8, marginBottom: 12 }}>
<input
value={city}
onChange={e => setCity(e.target.value)}
placeholder="Enter a city name"
onKeyDown={e => e.key === 'Enter' && handleAnalyze()}
style={{ flex: 1, padding: 8 }}
/>
<button onClick={handleAnalyze} disabled={loading}>
{loading ? 'Analyzing...' : 'Analyze'}
</button>
</div>
{error && <p style={{ color: 'red' }}>{error}</p>}
{result && (
<div>
<h3>{result.city}</h3>
<p>{result.analysis}</p>
<pre>{JSON.stringify(result.weather, null, 2)}</pre>
</div>
)}
</div>
);
}
export default App;
为什么这种设置可行
关注点分离
React 应用并不知道 LLM 或外部 API 的细节——它只调用一个端点并渲染返回结果。更换 Gemini 为 GPT‑4 或更改数据源都不需要修改前端。
可测试性
Flows 只是 async 函数。它们可以单元测试、mock,并在不依赖 UI 的情况下独立验证。
开箱即用的可观测性
Genkit 开发 UI 能精准展示每一步的输入和输出,使得生产环境的调试变得可行。
渐进式复杂度
从单步 flow 开始,按需添加步骤(缓存、内容审查等)。架构能够随需求增长而扩展。
接下来可以做什么
这个示例仅触及表面。在生产环境中,你可能还需要:
- 输入校验和速率限制
- 对外部 API 调用进行缓存
- 为提升用户体验实现流式响应
- 身份验证
- 带重试机制的健壮错误处理
Genkit 还支持 检索增强生成(RAG)、评估以及部署到 Firebase 或 Cloud Run。请参阅官方 Genkit 文档获取详细指南。
如果你正在为生产应用构建 AI 功能,并且已经厌倦了临时拼凑的方案,不妨认真考虑一下 Genkit。只要熟悉 TypeScript,上手曲线很低,而它提供的结构化方式在 AI 逻辑日益复杂时会带来丰厚回报。