跳到主要内容

Genkit — 架构与原理

30 秒导读: Genkit 是 Google(Firebase 团队)开源的、用来搭 AI 应用的全栈框架。它最核心的一招是:把模型、工具、提示、流程、检索器……所有能被调用的东西,都做成同一个原语 Action——一个自描述、会校验输入输出、能被追踪、能流式返回、并由一个注册表按 "/类型/名字" 寻址的函数。你写的 ai.generate(...)ai.defineFlow(...)、插件里的每个模型,底下都是 Action。理解了 Action + 注册表,就理解了整个 Genkit。

1. 这是什么(零基础也能懂)

  • 一句话定义: Genkit 是一套"AI 应用脚手架"——用统一的 API 接入各家大模型,帮你把提示模板、结构化输出、工具调用、多轮会话、可观测性这些活儿都标准化。

  • 解决什么问题 / 给谁用: 假设你要写一个服务端功能:"用户问一句话,让 Gemini 回答,过程中模型可以自己去查天气、查数据库,最后按一个固定 JSON 结构返回"。裸调各家模型 SDK 你得自己处理:提示拼装、工具往返、把模型输出解析成类型、失败重试、还要能在本地调试看每一步。Genkit 把这些统统封好,给后端工程师用。

  • 它能做什么(功能):

    • 用统一接口接入 Google / OpenAI / Anthropic / Ollama 等模型(靠插件)。
    • 类型安全的结构化输出(给个 Zod schema,拿回校验过的对象)。
    • 工具调用 / agentic 循环:模型自己决定调哪个工具,框架自动跑完再问模型。
    • Dotprompt 提示模板、多轮 Session/Chat、RAG 检索、评估器。
    • 本地 Dev UI:可视化每一次执行的 trace,单独跑某个 flow / prompt。
  • 用起来什么样: 最小示例(来自 README.md:15-25):

    import { genkit } from 'genkit';
    import { googleAI } from '@genkit-ai/google-genai';

    const ai = genkit({ plugins: [googleAI()] }); // 建实例、装插件

    const { text } = await ai.generate({ // 一次生成
    model: googleAI.model('gemini-flash-latest'),
    prompt: 'What is the meaning of life?',
    });
  • 一句话直觉 / 类比: 把 Genkit 想成 AI 版的依赖注入容器 + 中间件框架。所有"能力"(模型、工具、提示)都注册进一个中央注册表,用一个字符串 key 就能取用;所有调用都走同一条"包了追踪和校验"的管道。

2. 顶层全景(它大概怎么转)

Genkit 的骨架就三样:门面 genkit() 负责装配,注册表 Registry 负责存取,Action 是被存取的那唯一一种东西。一次 ai.generate 调用,就是从注册表里按 key 取出模型 Action 和工具 Action,喂进"工具循环"跑完。

怎么读下面这张图: 从上到下是一次调用的生命周期;记住——每个方框里流动、被查找、被执行的,都是同一种 Action

┌───────────────────────────────────────────────────────────┐
│ genkit({ plugins:[googleAI(), ...] }) —— 门面/装配 │
│ 建注册表、把每个插件的能力注册进去、暴露 defineXxx/generate │
└───────────────────────────┬───────────────────────────────┘
│ 注册 register

┌───────────────────────────────────────────────────────────┐
│ Registry 注册表 —— 按 "/类型/名字" 寻址 │
│ 存的全是同一种东西:Action │
│ /model/googleai/gemini-... /tool/getWeather │
│ /prompt/myPrompt /flow/myFlow /util/generate │
└───────┬───────────────────────────────────────────┬───────┘
ai.generate │ lookupAction("/type/name") │ listActions
▼ ▼
┌───────────────────────────────┐ ┌────────────────────┐
│ /util/generate 工具循环 │ │ Reflection Server │
│ 模型 →(要调工具)→ 跑工具 │ │ → 本地 Dev UI │
│ → 回填结果 → 再问模型(≤5 轮) │ │ (看 trace / 试跑) │
└───────────────────────────────┘ └────────────────────┘

部件一句话职责:

部件干什么在哪个文件
Action框架唯一原语:自描述 + Zod 校验 + 可追踪 + 可流式 + 可远程调用的函数js/core/src/action.ts:238
Registry"/type/name" 存取 Action;懒加载插件;可 overlay 出子注册表js/core/src/registry.ts:152
genkit() 门面建注册表、装插件、暴露 defineFlow/defineTool/generatejs/genkit/src/genkit.ts:171
Plugin把 model / tool / embedder / retriever 等注册进注册表js/core/src/plugin.ts:27
generate 工具循环模型调用 + 自动工具循环(agentic 引擎)js/ai/src/generate/action.ts:337
Tool / Interrupt可被模型自动调用的 Action;interrupt 停机等人工js/ai/src/tool.ts:336
Dotprompt / 格式化器提示模板渲染 + 结构化输出解析(json/array/enum…)js/ai/src/prompt.ts:248js/ai/src/formats/
Session / Agent多轮会话状态与快照持久化js/ai/src/session.ts:152
Reflection Server把注册表暴露成 HTTP,喂给本地 Dev UIjs/core/src/reflection.ts:73

主线走一遍(高层,不进代码):

  1. genkit({ plugins }) 建一个 Registry,把 /util/generate 这个内置 Action、默认输出格式、以及每个插件都注册进去(genkit.ts:configure 650)。插件此时只登记、不初始化
  2. 你调 ai.generate({ model, prompt, tools })。框架先 overlay 出一个子注册表(放本次的临时动态工具),把模型名、工具名解析成注册表里的 Action。
  3. 进入"工具循环":调模型 → 若模型回了 toolRequest 就跑对应工具 Action → 把结果拼回消息 → 再调模型,直到模型不再要工具,或超过 maxTurns(默认 5)。
  4. 最后按 output 里的 schema/格式把模型输出解析成类型化对象返回。
  5. 开发时,Reflection Server 读同一个注册表,本地 Dev UI 就能列出所有 Action、单独试跑、看每一步 trace。

3. 阅读地图(建议顺序,由浅入深)

先读 01 建立"一切皆 Action"的心智模型,再顺着"能力怎么进来(02)→ 怎么被驱动(03/04)→ 输入输出怎么塑形(05)→ 状态与观测(06)"往下走。

  1. Action 原语与注册表:一切皆 Action —— 最该先读的一章。Action 的类型/校验/追踪/流式,以及注册表 "/type/name" 寻址、懒加载、overlay 子注册表。
  2. Plugin 系统与 genkit() 门面:如何把能力装进注册表 —— 插件(v1/v2)如何把模型、工具、检索器注册进来;genkit() 如何装配一切;懒加载 initializer/resolver。
  3. generate:模型调用与自动工具循环(agentic 引擎) —— ai.generate/util/generate 到递归工具循环的全过程;maxTurns 护栏;中间件洋葱模型;流式。
  4. 工具执行、中断与 human-in-the-loop —— 工具如何被解析执行;interrupt 用异常停机;respond/restart 如何续跑一个被打断的工具调用。
  5. Dotprompt 与结构化输出:让模型吐出可校验的类型 —— .prompt 模板渲染;格式化器把模型文本解析成 Zod 类型;把 schema 作为指令注入提示。
  6. 会话状态与可观测性:Chat、Session 与 Dev UI —— 多轮会话状态、快照存储与 Agent;OpenTelemetry span 追踪;Reflection Server 与本地 Dev UI。

4. 巧妙之处(读者要带走的精华)

  • 一切皆 Action —— 一个原语打天下。 模型、工具、提示、流程、检索器都是 Action,于是追踪、输入输出校验、流式、寻址、中间件这一整套只写一遍就人人共享。定义处的注释一句话点破它的野心:"Self-describing, validating, observable, locally and remotely callable function"(js/core/src/action.ts:236)。

  • 字符串 key 即路由。 每个 Action 的 key 是 "/类型/名字"(如 /model/googleai/gemini-2.5-flash),在 action() 里生成(js/core/src/action.ts:467),注册时按同规则入表(js/core/src/registry.ts:301)。好处:一个字符串就能定位任意能力——支持按名字引用工具、Dev UI 列举、乃至远程调用;parseRegistryKey 负责反解(js/core/src/registry.ts:99)。

  • 插件懒加载,冷启动便宜。 注册插件时只登记 provider,真正 initializer() 推迟到 lookupAction 命中该插件、或首次 initializeAllPlugins 时才跑(js/core/src/registry.ts:242434)。用不到的插件不付初始化代价。

  • overlay 子注册表,隔离临时状态。 每次 generateRegistry.withParent(registry) 造一个子注册表(js/ai/src/generate.ts:504js/ai/src/generate/action.ts:91),本次的动态工具/中间件工具注册在子表里,查不到再落到父表(js/core/src/registry.ts:195258)——不污染全局

  • agentic 循环 = 递归 + 护栏。 工具循环不是 while,而是"处理完这一轮工具就递归调 generateHelper 进下一轮",并用 maxTurns ?? 5 做硬上限,超了抛 ABORTED(js/ai/src/generate/action.ts:514562)。

  • 中断用异常实现 human-in-the-loop。 工具里调 interrupt() 抛出 ToolInterruptError(js/ai/src/tool.ts:521),框架捕获后把 toolRequest 原样返回给上游、标 finishReason:'interrupted'(generate/action.ts:531-539);人确认后用 respond/restart 构造续跑请求(js/ai/src/tool.ts:452414)。

  • 洋葱中间件,一套两用。 无论包装普通 Action 还是包装 model/generate,都用同一种"dispatch(index) 沿链下钻"的洋葱模型(js/core/src/action.ts:406js/ai/src/generate/action.ts:260)。

  • Zod schema 双向发力。 同一个 schema 既在运行时校验入参/出参(js/core/src/action.ts:497600),又被转成 JSON schema 喂给模型当工具定义、或当结构化输出的约束指令(js/ai/src/tool.ts:260)。

5. 代码地图(导航索引)

按符号名 grep 比按行号更抗上游漂移。下面每行给出主题、文件、真实符号。

主题文件路径关键符号
Action 类型定义(原语)js/core/src/action.ts:238Action(type)、ActionMetadata
造 Action / 追踪包裹 / 校验js/core/src/action.ts:450action()runInNewSpanparseSchema
造 + 注册 Actionjs/core/src/action.ts:692defineAction
双向流式 Actionjs/core/src/action.ts:748bidiActionstreamBidi
注册表(存取核心)js/core/src/registry.ts:152RegistryactionsById
Action 类型枚举js/core/src/registry.ts:40ACTION_TYPESActionType
key 寻址 / 反解js/core/src/registry.ts:99parseRegistryKey
懒加载查找js/core/src/registry.ts:222lookupActioninitializePlugin
overlay 子注册表js/core/src/registry.ts:195Registry.withParent
内置 Dotprompt 引擎挂载js/core/src/registry.ts:175this.dotprompt = new Dotprompt
插件 provider 接口js/core/src/plugin.ts:27PluginProviderInitializedPlugin
genkit() 门面 / 装配js/genkit/src/genkit.ts:171Genkitgenkit()configure
注册内置 generate actionjs/ai/src/generate/action.ts:80defineGenerateAction(/util/generate)
用户级 generate 入口js/ai/src/generate.ts:490generatetoGenerateActionOptions
工具循环(agentic 引擎)js/ai/src/generate/action.ts:337generateActionTurnmaxTurns
中间件洋葱调度js/ai/src/generate/action.ts:260dispatchGeneratedispatchModel
工具解析 / 转定义js/ai/src/tool.ts:210resolveToolstoToolDefinition
定义工具(注册 tool + tool.v2)js/ai/src/tool.ts:336defineToolbasicTool
中断 / 续跑js/ai/src/tool.ts:486interruptToolInterruptErrorrespondToolrestartTool
工具请求求解 / resumejs/ai/src/generate/resolve-tool-requests.ts:186resolveToolRequestsresolveResumeOption
Dotprompt 渲染js/ai/src/prompt.ts:248definePromptAsyncrenderSystemPrompt
输出格式化器js/ai/src/formats/index.tsconfigureFormatsresolveFormat(json/array/enum/jsonl/text)
会话状态 / 快照存储js/ai/src/session.ts:152SessionSessionStore
Agent(多轮)js/genkit/src/genkit-beta.ts:166defineAgentdefineCustomAgent
反射服务(Dev UI 后端)js/core/src/reflection.ts:73ReflectionServer/api/actions/api/runAction
定义模型 / 流程js/ai/src/model.ts:215js/core/src/flow.ts:107defineModel(actionType:'model')、defineFlow(actionType:'flow')