使用 Genkit 构建 AI 驱动的 React 应用

发布: (2025年12月7日 GMT+8 14:27)
7 min read
原文: Dev.to

Source: Dev.to

介绍

过去几个月我一直在使用各种 AI 框架,而 Genkit 已经成为我在生产应用中首选的工具。它是 Google 开源的用于构建 AI 工作流的框架,能够解决将大语言模型(LLM)接入真实应用时出现的诸多痛点。

如果你曾尝试在生产应用中加入 AI 功能,可能会遇到相同的问题:

  • Prompt spaghetti – 提示最初是简单的字符串,随后会演变成带有上下文注入的多行模板,最终散落在各个服务中,缺乏明确的所有权。
  • Debugging black holes – AI 流程出现异常。是提示词的问题?模型?还是传入的上下文?没有合适的可观测性,定位问题简直是噩梦。
  • Integration mess – 需要调用外部 API,把数据传给 LLM,可能还要做后处理,最后返回结构化响应。如果没有干净的抽象,这一切会变得非常丑陋。

Genkit 通过提供 flow 原语 来解决这些问题——可以把它想象成一个函数,内部可以包含多个步骤(API 调用、LLM 调用、转换),并自带日志记录和开发者 UI 供检查。

我们将创建一个 城市分析器,它:

  1. 从前端获取城市名称。
  2. 从外部 API 获取真实的天气数据。
  3. 将数据传给 Gemini 进行分析。
  4. 返回原始数据和 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 逻辑日益复杂时会带来丰厚回报。

Back to Blog

相关文章

阅读更多 »