深度定制 Genkit 中间件:用 generateMiddleware 构建可复用、可插拔的生成管线(Genkit JS)
深度定制 Genkit 中间件用 generateMiddleware 构建可复用、可插拔的生成管线Genkit JS【免费下载链接】skillsAgent Skills for Google products and technologies项目地址: https://gitcode.com/GitHub_Trending/skills29/skills导读在 GenkitGoogle 出品的 AI 应用开发框架中中间件是横切关注点cross-cutting concerns的统一承载机制重试、降级、注入工具、改写请求/响应等能力都可以通过中间件挂载到生成流程上。本篇以 skills/cloud/genkit-js/references/middleware-custom.md 为骨架系统讲解如何用generateMiddleware编写命名、可复用、带 Zod 配置校验的自定义中间件并把它通过.plugin()注册为 Genkit 插件让它在ai.generate、可执行 Prompt 和 Agent 上统一生效同时能被 Genkit Dev UI 识别与可视化。中间件机制一览use: [...]数组在深入自定义之前先明确中间件挂载的基本方式。Genkit 中间件通过use: [...]数组附着到生成调用上该数组在ai.generate/ai.generateStream、可执行 PromptdefinePrompt和 AgentdefineAgent上都受支持见 使用中间件import { retry } from genkit-ai/middleware; const res await ai.generate({ model: googleAI.model(gemini-flash-latest), prompt: Say hello, use: [retry({ maxRetries: 2 })], }); // Prompt 与 Agent 上使用同样的数组 const myPrompt ai.definePrompt({ name: p, prompt: ..., use: [retry()] }); const myAgent ai.defineAgent({ name: a, system: ..., use: [retry()] });注意这里的关键形态中间件本身是一个可配置的工厂函数如retry()/artifacts()调用它返回一个供use: [...]使用的引用。genkit-ai/middleware包中的七个现成中间件retry、fallback、artifacts、agents、filesystem、skills、toolApproval都是这种工厂形态而generateMiddleware正是用来构建这一类自定义工厂的底层工具——这正是本篇文章的核心。用 generateMiddleware 定义命名中间件generateMiddleware是自定义中间件的入口它负责两件事声明中间件的元信息name与description后者用于 Dev UI 与调试展示以及可选的configSchema返回一个工厂函数调用后得到一个可放进use: [...]的引用工厂内部可拿到用户传入的config以及 Genkit 的ai实例。原文档给出了一个完整的timing.ts示例——一个记录模型调用耗时的中间件// timing.ts import { generateMiddleware, z } from genkit; const OptionsSchema z.object({ label: z.string().optional() }); export const timing generateMiddleware( { name: timing, description: Logs how long the model call takes., configSchema: OptionsSchema, }, ({ config, ai }) { // Runs once per generate() call. Return any of the hooks below. return { model: async (req, ctx, next) { const start Date.now(); const res await next(req, ctx); console.log([${config?.label ?? timing}] ${Date.now() - start}ms); return res; }, }; } );关键点拆解configSchema与 ZodconfigSchema使用 Zod schema 定义工厂的入参。用户调用timing({ label: gen })时传入的对象会被校验不合法时抛错从而让中间件的配置在编译期和运行期都是类型安全的。原文档用z.object({ label: z.string().optional() })声明了一个可选字符串参数这也呼应了 setup.md 中推荐的“schema 优先”工程实践——Genkit 中无论是 Flow 输入输出、Prompt 输入还是工具参数都统一使用zZod声明。工厂回调的时机注释明确指出工厂回调({ config, ai }) ...在每次generate()调用时执行一次它返回的 hooks 集合定义了这一轮生成中中间件介入的具体位置。ai实例在回调中可用这意味着中间件内部还可以进一步调用ai.generate、读取注册表等实现更复杂的能力编排。与内置中间件的同构性generateMiddleware产出的工厂形态与genkit-ai/middleware包内的中间件完全一致因此自定义中间件与内置中间件在use: [...]中可以无缝混用、按序执行。注册为插件让中间件进入 Dev UI自定义中间件拿到.plugin()方法可以直接注册进genkit({ plugins: [...] })。这是推荐做法——不注册时中间件依然能在use: [...]中工作但无法被 Genkit Dev UI 识别与可视化import { genkit } from genkit; import { googleAI } from genkit-ai/google-genai; import { timing } from ./timing.js; export const ai genkit({ plugins: [googleAI(), timing.plugin()], }); // Then use the factory in use: [...]: await ai.generate({ model: googleAI.model(gemini-flash-latest), prompt: Hi, use: [timing({ label: gen })], });这与内置中间件的注册方式完全一致。以retry为例使用中间件 中的标准做法是export const ai genkit({ plugins: [googleAI(), retry.plugin(), artifacts.plugin()], });注册带来的额外收益Prompt frontmatter 中的use注册后中间件不仅限于代码内使用还可以在 Dotprompt 的 frontmatter 中通过名称引用。在 dotprompt.md 中可以看到.prompt文件的use字段接受中间件名裸字符串或{ name, config }映射名称会解析到已注册在 Genkit 实例上的中间件use: - name: retry # bare string also works: - retry config: maxRetries: 4要让名称可解析前提同样是先注册中间件插件const ai genkit({ plugins: [googleAI(), retry.plugin()], promptDir: ./prompts, });这意味着你的自定义中间件只要通过.plugin()注册就能同样在.prompt文件里以use: - name: timing的方式声明式挂载这大大提升了中间件的复用范围。四个可用 Hook 详解generateMiddleware的实例化回调返回一个GenerateMiddlewareDef可以选择性地包含以下任意子集Hook包裹的对象典型用途入参类型generate(envelope, ctx, next)整个 generate 动作注入请求参数、后处理响应、跨工具循环捕获错误envelope携带{ request, currentTurn, messageIndex }model(req, ctx, next)底层模型调用缓存、重试、请求/响应改写req为GenerateRequesttool(req, ctx, next)单个工具调用校验输入、缓存或覆盖工具输出req为ToolRequestPart返回ToolResponsePart \| undefinedtools: ToolAction[]静态注入工具中间件激活时向模型注入能力artifacts()/filesystem()正是这样注入工具的ToolAction[]一个同时声明全部 hooks 的骨架示例generateMiddleware({ name: example }, ({ ai }) ({ generate: async (envelope, ctx, next) next(envelope, ctx), model: async (req, ctx, next) next(req, ctx), tool: async (req, ctx, next) next(req, ctx), tools: [ /* ToolAction[] */ ], }));Hook 的链式语义每个 hook 都接受next(...)来继续调用链——可以传入被修改后的 request/envelope并在await之后对结果做变换再返回。这种洋葱模型onion model与 Node.js 中间件一脉相承也是genkit-ai/middleware中内置中间件的实现基础。以仓库中agents()中间件多 Agent 编排为例可以直观看到tools静态注入 hook 的实际用途agents()为每个子 Agent 注入一个delegate_to_name工具并在系统提示词后追加sub-agents块模型调用委托工具时中间件运行子 Agent 并把其响应作为工具结果返回。同样artifacts()中间件Artifacts 用法通过静态注入write_artifact/read_artifact两个工具并每轮注入artifacts清单让 Agent 在会话中产出命名交付物。这些能力都是“在tools: ToolAction[]中返回工具定义 在系统提示词层面做注入”的组合拳自定义中间件完全可以按同样的模式实现。如何选择合适的 Hook原文档给出了非常清晰的决策指南需要转换整个回合的 prompt/messages 或最终结果→ 选generate。例如跨整个工具循环的统一错误处理、结果后处理、在请求中注入全局参数需要缓存 / 重试 / 改写单次模型往返→ 选model。这是性能类中间件如内置的retry的主战场需要门控或记忆化工具执行→ 选tool。例如校验工具入参、对高频工具结果做缓存需要为模型提供额外能力→ 选tools。静态注入一组ToolAction让模型在中间件激活期间总是能看到这些工具。值得注意的是内置核心中间件也遵循这一模型。在genkit核心无需额外安装包从genkit/model/middleware导入中simulateConstrainedGeneration、validateSupport、augmentWithContext等中间件都是通过改写模型请求或结果来实现能力的例如import { simulateConstrainedGeneration } from genkit/model/middleware; await ai.generate({ model: someModel, prompt: ..., use: [simulateConstrainedGeneration()], });理解了 hook 分工你就能判断某个横切需求该落到哪个层级而不是一律堆在generate上。最佳实践与注意事项始终通过.plugin()注册Reminder: register custom middleware via.plugin()(see above). It works inuse: [...]without registering, but unregistered middleware is not visible to the Genkit Dev UI.原文档的提醒值得再次强调不注册也能工作但 Dev UI 看不到它。Genkit Dev UI 的中间件可视化、trace 展示和调试体验都依赖于注册信息因此生产级代码中请务必把每个自定义中间件都加入plugins: [...]。与内置中间件协同自定义中间件与genkit-ai/middleware包的内置中间件可以自由组合。例如在多 Agent 编排场景中一个典型组合是use: [ agents({ agents: [researcher, coder], artifactStrategy: session }), artifacts({ readonly: true }), retry(), ];你的自定义中间件可以作为这个链条中的一员与内置中间件一起按use数组的顺序依次执行。关注配置的校验与默认值利用configSchema对工厂参数做 Zod 校验并在工厂回调内部给config提供合理默认值如config?.label ?? timing可以让中间件的调用方只传必要的参数符合 Genkit “Be Minimal”只指定与默认值不同的选项的工程约定见 SKILL.md。小结generateMiddleware把“定义命名中间件 → 注册为插件 → 在use: [...]/ Prompt frontmatter 中使用”这一完整链路收拢成一个清晰的 API用元信息声明身份用configSchema保证配置安全用四个 hooksgenerate/model/tool/tools在正确的层级注入横切逻辑最后通过.plugin()让中间件成为 Genkit 实例的一等公民。掌握这套机制后你不仅能消费内置中间件还能像retry、artifacts、agents那样构建自己的可复用能力让重试、观测、工具注入等逻辑以统一、可组合、可调试的方式贯穿整个生成管线。【免费下载链接】skillsAgent Skills for Google products and technologies项目地址: https://gitcode.com/GitHub_Trending/skills29/skills创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考