Genkit × Angular:用流式 Flow 在 Angular 应用中构建 Gemini 驱动的角色生成器与聊天机器人
【免费下载链接】genkitOpen-source framework for building agentic apps in JavaScript, Go, Dart, and Python, built and used in production by Google项目地址: https://gitcode.com/GitHub_Trending/ge/genkit
本文基于 Genkit 仓库中samples/js-angular/示例及其 README 编写,讲解如何在 Angular 前端中通过 Genkit 流式 Flow 消费 Gemini 的逐块输出:既能实现"RPG 角色 JSON 逐字段流式渲染",也能实现带工具调用(tool request/response)的会话式聊天机器人。读完本文,你将掌握该示例的完整安装运行流程、服务端 Flow 定义方式(defineFlow、defineAgent、generateStream+partial-json),以及 Angular 侧streamFlow客户端消费流式数据的完整链路。
一、示例概览
samples/js-angular/是一个演示"在 Angular 应用中实现流式 AI 响应"的完整前后端示例,核心能力包括两部分:
- RPG 角色生成器(
streamCharactersFlow):按指定数量生成角色档案,以 JSON 格式流式返回,前端逐块渲染; - 交互式聊天机器人(
chatbotFlow):基于 Gemini 的会话式 Agent,支持系统提示词、工具调用与内存级会话历史。
技术栈组合为:Angular(前端框架)+Genkit(AI 编排层)+Gemini(模型,代码中通过googleAI.model('gemini-flash-latest')调用,README 标注为 Gemini 2.5 Flash)。
整体架构是一个经典的前后端分离双端口结构:
server/:基于 Express 的 Genkit Flow 服务,通过@genkit-ai/express的startFlowServer暴露 HTTP 端点,监听127.0.0.1:3400;genkit-app/:Angular 18 单页应用,开发服务器监听http://localhost:4200/,通过 Genkit JS 客户端的streamFlow直接调用 Flow 端点。
二、前置条件与安装
按 README 的要求:
- 安装Node.js v20+和 npm;
- 准备一个Google Developer Gemini API Key(可从 Google AI Studio 创建),并设置环境变量:
export GEMINI_API_KEY=your_api_key_here然后在samples/js-angular/目录下安装依赖:
npm i npm run setup从根目录 package.json 的脚本定义可以看到,setup会在根目录、server/、genkit-app/三个子项目下分别执行npm i:
"setup": "npm i && cd server && npm i && cd ../genkit-app && npm i"其中 server/package.json 的关键依赖包括:
| 依赖 | 作用 |
|---|---|
genkit(^1.0.4) | 核心 SDK,提供defineFlow、defineAgent、ai.generateStream等 |
@genkit-ai/google-genai(^1.17.0) | Google Gemini 模型插件 |
@genkit-ai/express(^1.17.0) | 将 Flow 挂载到 Express 的 Flow Server |
partial-json(^0.1.7) | 解析"尚未生成完"的残缺 JSON,支撑 JSON 流式渲染 |
tsx/genkit-cli | 开发时以tsx --watch方式热启动 TypeScript 服务 |
三、启动方式与端口
启动开发服务器:
npm run genkit:dev该脚本通过concurrently并行拉起两个进程(见根 package.json):
"genkit:dev": "concurrently npm:start:server npm:start:ng", "start:server": "cd server && npm run genkit:dev", "start:ng": "cd genkit-app && npm run start"start:server执行的是genkit start -- tsx --watch src/index.ts(见 server/package.json),即通过 Genkit CLI 启动 Flow Server,默认监听3400 端口,并附带 Genkit Dev UI 与热重载;start:ng执行 Angular CLI 的ng serve,监听4200 端口。
启动后访问http://localhost:4200/即可进入应用。前端路由定义在 app.routes.ts 中,包含三个页面:/home、/samples/streaming-json(角色生成器)、/samples/chatbot(聊天机器人),未匹配路径重定向到 home。
四、服务端:Flow Server 与两个核心 Flow
4.1 Genkit 实例与插件注册
服务端入口 server/src/index.ts 只有两件事:
import { startFlowServer } from '@genkit-ai/express'; import { chatbotFlow } from './chatbot.js'; import { streamCharacters } from './jsonStreaming.js'; startFlowServer({ flows: [chatbotFlow, streamCharacters], });startFlowServer来自@genkit-ai/express,会把传入的 flows 挂载为 HTTP 端点(即前端调用的/chatbotFlow、/streamCharacters)。Genkit 实例在 server/src/genkit.ts 中创建并注册 Gemini 插件:
import { googleAI } from '@genkit-ai/google-genai'; import { genkit } from 'genkit'; export const ai = genkit({ plugins: [googleAI()], });4.2 流式 JSON 生成器:streamCharacters
samples/js-angular/server/src/jsonStreaming.ts 演示了"结构化输出的流式增量渲染",是全文技术含量最高的部分。它定义了一个带streamSchema的 Flow:
const GameCharactersSchema = z.object({ characters: z .array( z.object({ name: z.string().describe('Name of a character'), abilities: z .array(z.string()) .describe('Various abilities (strength, magic, archery, etc.)'), }).describe('Game character') ).describe('Characters'), }); export const streamCharacters = ai.defineFlow( { name: 'streamCharacters', inputSchema: z.number(), // 输入:要生成的角色数量 outputSchema: z.string(), // 最终输出:完整 JSON 文本 streamSchema: GameCharactersSchema, // 中间流块遵循的结构 }, async (count, { sendChunk }) => { const { response, stream } = await ai.generateStream({ model: googleAI.model('gemini-flash-latest'), output: { format: 'json', schema: GameCharactersSchema }, config: { temperature: 1 }, prompt: `Respond as JSON only. Generate ${count} different RPG game characters.`, }); let buffer = ''; for await (const chunk of stream) { buffer += chunk.content[0].text!; if (buffer.length > 10) { sendChunk(parse(maybeStripMarkdown(buffer), Allow.ALL)); } } return (await response).text; } );实现要点:
streamSchema是关键机制:它声明 Flow 的中间流块(chunk)符合GameCharactersSchema结构,Genkit 会据此向客户端逐块发送部分结构;ai.generateStream以output: { format: 'json', schema: ... }让 Gemini 按约束输出 JSON,temperature: 1保证角色创意多样性;partial-json库的parse(buffer, Allow.ALL)负责把"还没生成完"的残缺 JSON(如{"characters":[{"name":"Ar)解析为部分对象,从而实现前端"角色字段逐个长出来"的视觉效果;sendChunk把每个部分对象推给客户端;- 还有一个防御性细节:
maybeStripMarkdown用正则剥离模型可能包裹的```json代码围栏,保证 partial 解析不被 Markdown 标记干扰。
4.3 带工具与历史的聊天 Agent:chatbotFlow
samples/js-angular/server/src/chatbot.ts 基于通用工厂defineAgent组装出一个具备人设、工具与会话记忆的 Agent Flow:
export const chatbotFlow = defineAgent(ai, { name: 'chatbotFlow', model: googleAI.model('gemini-flash-latest'), tools: [weatherTool, datePicker], returnToolRequests: true, systemPrompt: 'You are a helpful agent. You have the personality of Agent Smith from Matrix. ...', historyStore: inMemoryStore(), });它注册了两个"UI 型"工具:
weatherTool:入参为date与location,实现体是空函数(async () => undefined)——真正的"天气卡片"由前端渲染,工具仅作为模型与 UI 之间的信号;datePicker:一个 UI 工具,模型可请求打开日期选择器,前端用 Angular Material 的mat-datepicker采集日期后回传。
defineAgent的通用逻辑在 server/src/agent.ts 中,流程为:
- 定义统一的
AgentInput(conversationId+prompt,prompt 可为字符串或 Part 数组,兼容工具响应); - Flow 执行时先用
ai.run('retrieve-history', ...)从HistoryStore按conversationId加载历史;若无历史且配置了systemPrompt,则以系统消息初始化上下文; - 调用
ai.generate({ prompt, messages: history, model, tools, returnToolRequests, streamingCallback })发起流式生成,streamingCallback来自defineFlow的第二参数,是服务端流式输出的直接管道; - 再用
ai.run('save-history', ...)把resp.messages写回存储,完成多轮记忆的闭环。
本示例的historyStore是一个进程内Record<string, MessageData[]>字典(inMemoryStore()),重启即清空;接口HistoryStore { load(id); save(id, history) }是明确的抽象,可以推断替换为 Firestore、数据库等持久实现只需实现这两个方法。
五、Angular 前端:用streamFlow消费流式响应
两个页面组件都来自genkit/beta/client的streamFlow发起调用,URL 分别指向 Flow Server 的/streamCharacters与/chatbotFlow(硬编码http://127.0.0.1:3400/...,见组件源码)。
5.1 流式 JSON 页面(StreamingJSONComponent)
samples/js-angular/genkit-app/src/app/samples/streaming-json/streaming-json.component.ts 的逻辑非常直接:
const response = streamFlow({ url, // http://127.0.0.1:3400/streamCharacters input: Number.parseInt(this.count), }); for await (const chunk of response.stream) { this.characters = chunk; // 每个部分 JSON 对象直接驱动模板渲染 } console.log('streamConsumer done', await response.output);for await...of response.stream持续消费服务端sendChunk推来的部分对象,每次更新都会触发 Angular 变更检测,实现字段级的渐进渲染;流结束后response.output才是服务端最终返回的完整 JSON 文本。
5.2 聊天机器人页面(ChatbotComponent)
samples/js-angular/genkit-app/src/app/samples/chatbot/chatbot.component.ts 演示了带工具交互的完整循环:
- 发起提问:
ask()把用户消息压入history,调用callFlow({ role: 'user', text }); - 流式消费:
streamFlow({ url, input: { prompt, conversationId: this.id } })逐块消费chunk.content——content.text增量拼接到同一条 model 气泡上(首块创建气泡,后续追加);content.toolRequest则作为独立条目插入历史,触发工具 UI; - 工具 UI 渲染:模板 chatbot.component.html 用
@switch (entry.toolRequest.name)按工具名分发——weatherTool渲染一个固定的天气卡片(展示请求中的location),datePicker渲染 Angular Material 日期选择器; - 工具响应回传:用户在日期选择器中确认后,
datePicked()以{ role: 'user', toolResponse: { name, ref, output } }再次调用callFlow,把用户选择作为工具输出送回服务端,模型在既有会话上下文中继续回答。
这一"请求 → 流式文本/工具请求 → UI 收集 → 工具响应"的闭环,正是returnToolRequests: true(服务端)与前端toolRequest/toolResponse消息结构(组件内定义的InputSchema/OutputSchema接口)协同工作的结果:服务端把工具调用"交还"给前端渲染与应答,而不是在服务端自行执行(示例中两个工具的实现体也是空函数,从代码结构看其职责纯粹是信号传递)。
六、工程结构与延伸阅读
samples/js-angular/ ├── package.json # 根脚本:setup / genkit:dev(concurrently 双进程) ├── server/ │ ├── package.json # genkit + @genkit-ai/express + partial-json │ └── src/ │ ├── index.ts # startFlowServer 入口(端口 3400) │ ├── genkit.ts # genkit 实例 + googleAI 插件 │ ├── agent.ts # defineAgent 通用工厂(历史加载/保存 + 流式 generate) │ ├── chatbot.ts # chatbotFlow:工具 + 人设 + 内存历史 │ └── jsonStreaming.ts # streamCharacters:JSON 约束输出 + partial-json 增量流 └── genkit-app/ ├── angular.json # Angular CLI 工程配置 └── src/app/ ├── app.routes.ts # /home、/samples/streaming-json、/samples/chatbot └── samples/ ├── streaming-json/ # 流式 JSON 角色生成器组件 └── chatbot/ # 聊天 + 工具 UI 组件需要注意的运行前提与限制:
- 前端调用地址硬编码为
http://127.0.0.1:3400,因此要求 Flow Server 与浏览器处于可直连的本机环境,跨机器部署时需修改组件内的url常量; - 聊天历史为进程内存级(
inMemoryStore),服务端重启会丢失全部会话; - 示例依赖
genkit/beta/client的streamFlowAPI,属于示例锁定版本的客户端能力,升级 SDK 时应以仓库中package.json声明的版本(genkit^1.0.4)为准验证兼容性。
该示例完整覆盖了 Genkit JS 侧"服务端定义流式 Flow → Express 挂载 → 浏览器streamFlow消费"的链路,是研究 Genkit 流式协议(streamSchema、sendChunk、streamingCallback、tool request/response 消息结构)的一个高信息密度参照实现。
【免费下载链接】genkitOpen-source framework for building agentic apps in JavaScript, Go, Dart, and Python, built and used in production by Google项目地址: https://gitcode.com/GitHub_Trending/ge/genkit
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考