在 Angular 应用程序中使用 Genkit 的完整指南

引言

随着人工智能技术的快速发展,将 AI 能力集成到现代 Web 应用程序中已成为提升用户体验的重要方式。Genkit 作为一个强大的 AI 开发工具包,为开发者提供了便捷的方式来构建和集成 AI 功能。本文将详细介绍如何在 Angular 应用程序中使用 Genkit 流(flows),从项目创建、依赖安装到流定义和前后端交互的全过程。

Angular 作为主流的前端框架之一,结合 Genkit 可以轻松实现 AI 功能的集成。本文将通过一个餐厅菜单建议的具体示例,展示如何在 Angular 应用中实现非流式和流式两种调用方式,帮助开发者快速掌握这一技术组合。

正文

准备工作与环境配置

熟悉 Genkit 流概念

在开始之前,开发者需要熟悉 Genkit 的核心概念------流(flows)。Genkit 流是一种封装 AI 功能的可重用单元,可以接受输入、处理请求并返回输出。理解如何编写和调用 Genkit 流是成功集成的关键。

创建 Angular 项目

为了演示 Genkit 在 Angular 中的使用,我们需要一个支持服务器端渲染(SSR)和服务器路由的 Angular 项目。可以使用 Angular CLI 快速创建这样的项目:

bash 复制代码
ng new --ssr --server-routing

如果已有现有项目,可以通过以下命令添加服务器端路由支持:

bash 复制代码
ng add @angular/ssr --server-routing

这两种方式都能确保我们的应用具备必要的服务器端功能,以便处理 Genkit 流的 API 请求。

安装 Genkit 依赖

在 Angular 项目中安装必要的 Genkit 依赖项是下一步。首先安装核心 Genkit 库:

bash 复制代码
npm install genkit

接着安装至少一个模型插件,例如 Google 的 Gemini 模型:

bash 复制代码
npm install @genkit-ai/googleai

还需要安装 Genkit Express 库,以便在服务器端处理流请求:

bash 复制代码
npm install @genkit-ai/express

虽然可选,但建议全局安装 Genkit CLI 和 tsx 工具,以便开发和测试:

bash 复制代码
npm install -g genkit-cli
npm install --save-dev tsx

这些工具将大大简化开发流程,特别是在测试和调试 Genkit 流时。

定义 Genkit 流

创建流目录结构

在 Angular 项目中创建一个专门的目录来存放 Genkit 流定义是一个好习惯。建议在 src 下创建 genkit 目录:

css 复制代码
src/
  genkit/
    menuSuggestionFlow.ts

这种结构保持了项目的整洁性,便于管理和维护多个流定义。

编写菜单建议流

下面是一个具体的菜单建议流实现示例。这个流接受一个餐厅主题作为输入,返回一个符合该主题的菜单项建议:

typescript 复制代码
import { googleAI } from '@genkit-ai/googleai';
import { genkit, z } from 'genkit';

const ai = genkit({
  plugins: [googleAI()],
});

export const menuSuggestionFlow = ai.defineFlow(
  {
    name: 'menuSuggestionFlow',
    inputSchema: z.object({ theme: z.string() }),
    outputSchema: z.object({ menuItem: z.string() }),
    streamSchema: z.string(),
  },
  async ({ theme }, { sendChunk }) => {
    const { stream, response } = ai.generateStream({
      model: googleAI.model('gemini-2.5-flash'),
      prompt: `Invent a menu item for a ${theme} themed restaurant.`,
    });

    for await (const chunk of stream) {
      sendChunk(chunk.text);
    }

    const { text } = await response;
    return { menuItem: text };
  }
);

这个流定义展示了几个关键点:

  1. 使用 Zod 定义输入输出模式,确保类型安全
  2. 集成 Google 的 Gemini 模型进行文本生成
  3. 支持流式传输,可以实时返回部分结果
  4. 最终返回完整的菜单项建议

这种模式可以轻松扩展到其他类似的 AI 功能实现。

服务器端配置

添加 Express 路由

要在 Angular 应用中暴露 Genkit 流,需要在服务器端配置相应的路由。修改 src/server.ts 文件:

首先添加必要的导入:

typescript 复制代码
import { expressHandler } from '@genkit-ai/express';
import { menuSuggestionFlow } from './genkit/menuSuggestionFlow';

然后初始化 Express 应用并添加 JSON 解析中间件:

typescript 复制代码
app.use(express.json());

最后添加流路由:

typescript 复制代码
app.post('/api/menuSuggestion', expressHandler(menuSuggestionFlow));

这样配置后,前端就可以通过 /api/menuSuggestion 端点调用菜单建议流了。

前端调用实现

非流式调用实现

对于不需要实时更新的场景,可以使用非流式调用方式。修改 src/app/app.component.ts

typescript 复制代码
import { Component, resource, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { runFlow } from 'genkit/beta/client';

@Component({
  selector: 'app-root',
  imports: [FormsModule],
  templateUrl: './app.component.html',
})
export class AppComponent {
  menuInput = '';
  theme = signal('');

  menuResource = resource({
    request: () => this.theme(),
    loader: ({ request }) => runFlow({
      url: 'http://localhost:4200/api/menuSuggestion',
      input: { theme: request }
    }),
  });
}

对应的模板 (app.component.html) 实现:

html 复制代码
<main>
  <h3>Generate a custom menu item</h3>
  <label for="theme">Suggest a menu item for a restaurant with this theme: </label>
  <input type="text" id="theme" [(ngModel)]="menuInput" />
  <button (click)="theme.set(menuInput)">Generate</button>
  <br />
  <br />
  @if (menuResource.isLoading()) {
    <div>Loading...</div>
  } @else if (menuResource.value()) {
    <div>
      <h4>Generated Menu Item:</h4>
      <pre>{{ menuResource.value().menuItem }}</pre>
    </div>
  }
</main>

这种实现简单直接,适合大多数基础场景。

流式调用实现

对于需要实时显示生成过程的场景,流式调用提供了更好的用户体验。扩展组件以支持流式响应:

typescript 复制代码
import { Component, resource, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { runFlow, streamFlow } from 'genkit/beta/client';

@Component({
  selector: 'app-root',
  imports: [FormsModule],
  templateUrl: './app.component.html',
})
export class AppComponent {
  menuInput = '';
  theme = signal('');
  streamedText = signal('');
  isStreaming = signal(false);

  menuResource = resource({
    request: () => this.theme(),
    loader: ({ request }) => runFlow({
      url: 'http://localhost:4200/api/menuSuggestion',
      input: { theme: request }
    }),
  });

  async streamMenuItem() {
    const theme = this.menuInput;
    if (!theme) return;

    this.isStreaming.set(true);
    this.streamedText.set('');

    try {
      const result = streamFlow({
        url: 'http://localhost:4200/api/menuSuggestion',
        input: { theme },
      });

      for await (const chunk of result.stream) {
        this.streamedText.update(prev => prev + chunk);
      }

      const finalOutput = await result.output;
      console.log('Final output:', finalOutput);
    } catch (error) {
      console.error('Error streaming menu item:', error);
    } finally {
      this.isStreaming.set(false);
    }
  }
}

更新模板以支持流式显示:

html 复制代码
<main>
  <h3>Generate a custom menu item</h3>
  <label for="theme">Suggest a menu item for a restaurant with this theme: </label>
  <input type="text" id="theme" [(ngModel)]="menuInput" />
  <br />
  <br />
  <button (click)="theme.set(menuInput)" [disabled]="menuResource.isLoading()">
    Generate
  </button>
  <button (click)="streamMenuItem()" [disabled]="isStreaming()">
    Stream Generation
  </button>
  <br />

  @if (streamedText()) {
    <div>
      <h4>Streaming Output:</h4>
      <pre>{{ streamedText() }}</pre>
    </div>
  }

  @if (menuResource.isLoading()) {
    <div>Loading...</div>
  } @else if (menuResource.value()) {
    <div>
      <h4>Generated Menu Item:</h4>
      <pre>{{ menuResource.value().menuItem }}</pre>
    </div>
  }

  @if (isStreaming()) {
    <div>Streaming...</div>
  }
</main>

流式实现提供了更动态的用户体验,特别适合生成较长内容或需要实时反馈的场景。

高级配置选项

身份验证集成

在实际应用中,通常需要为 API 路由添加身份验证。Genkit 支持通过请求头传递认证令牌:

typescript 复制代码
menuResource = resource({
  request: () => this.theme(),
  loader: ({ request }) => runFlow({
    url: 'http://localhost:4200/api/menuSuggestion',
    headers: {
      Authorization: 'Bearer your-token-here',
    },
    input: { theme: request }
  }),
});

这种机制可以灵活地集成各种认证方案,如 JWT、OAuth 等,确保 API 调用的安全性。

本地测试与调试

在本地开发环境中运行应用时,需要为所使用的模型 API 服务配置凭据。例如,对于 Gemini API:

bash 复制代码
export GEMINI_API_KEY=<your API key>

然后正常启动应用:

bash 复制代码
ng serve

Genkit 的开发工具也可以正常使用,例如启动开发者 UI 来测试流:

bash 复制代码
genkit start -- npx tsx --watch src/genkit/menuSuggestionFlow.ts

这些工具大大简化了开发流程,使得测试和调试 Genkit 流变得更加高效。

结论

本文详细介绍了在 Angular 应用程序中集成 Genkit 的完整流程。从项目创建、依赖安装到流定义和前后端交互,我们通过一个具体的菜单建议示例展示了两种调用方式:传统的非流式调用和更先进的流式调用。

关键要点包括:

  1. Genkit 流提供了一种结构化的方式来封装 AI 功能
  2. Angular 与 Genkit 的集成需要适当的服务器端配置
  3. 流式调用可以实现更动态的用户体验
  4. 身份验证和本地测试工具保证了开发的安全性和便捷性

这种技术组合为开发者提供了强大的工具,可以轻松地将 AI 能力集成到 Angular 应用中,创造更智能、更交互式的用户体验。随着 AI 技术的不断发展,这种集成模式将在现代 Web 开发中扮演越来越重要的角色。

相关推荐
brzhang36 分钟前
颠覆你对代码的认知:当程序和数据只剩下一棵树,能读懂这篇文章的人估计全球也不到 100 个人
前端·后端·架构
斟的是酒中桃1 小时前
基于Transformer的智能对话系统:FastAPI后端与Streamlit前端实现
前端·transformer·fastapi
烛阴1 小时前
Fract - Grid
前端·webgl
JiaLin_Denny1 小时前
React 实现人员列表多选、全选与取消全选功能
前端·react.js·人员列表选择·人员选择·人员多选全选·通讯录人员选择
brzhang2 小时前
我见过了太多做智能音箱做成智障音箱的例子了,今天我就来说说如何做意图识别
前端·后端·架构
为什么名字不能重复呢?2 小时前
Day1||Vue指令学习
前端·vue.js·学习
eternalless2 小时前
【原创】中后台前端架构思路 - 组件库(1)
前端·react.js·架构
Moment2 小时前
基于 Tiptap + Yjs + Hocuspocus 的富文本协同项目,期待你的参与 😍😍😍
前端·javascript·react.js
Krorainas3 小时前
HTML 页面禁止缩放功能
前端·javascript·html
whhhhhhhhhw3 小时前
Vue3.6 无虚拟DOM模式
前端·javascript·vue.js