引言

随着人工智能技术的快速发展,将 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 快速创建这样的项目:

ng new --ssr --server-routing

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

ng add @angular/ssr --server-routing

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

安装 Genkit 依赖

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

npm install genkit

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

npm install @genkit-ai/googleai

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

npm install @genkit-ai/express

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

npm install -g genkit-cli
npm install --save-dev tsx

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

定义 Genkit 流

创建流目录结构

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

src/
  genkit/
    menuSuggestionFlow.ts

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

编写菜单建议流

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

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 文件:

首先添加必要的导入:

import { expressHandler } from '@genkit-ai/express';
import { menuSuggestionFlow } from './genkit/menuSuggestionFlow';

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

app.use(express.json());

最后添加流路由:

app.post('/api/menuSuggestion', expressHandler(menuSuggestionFlow));

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

前端调用实现

非流式调用实现

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

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) 实现:

<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>

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

流式调用实现

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

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);
    }
  }
}

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

<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 支持通过请求头传递认证令牌:

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:

export GEMINI_API_KEY=<your API key>

然后正常启动应用:

ng serve

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

genkit start -- npx tsx --watch src/genkit/menuSuggestionFlow.ts

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

结论

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

关键要点包括:

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

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

Logo

全面兼容主流 AI 模型,支持本地及云端双模式

更多推荐