先说明白这篇解决什么。大多数团队接 agent 的第一次翻车,都不是模型不行,而是「它把不该改的东西改了」——本地跑的时候没人管,接了真库就出事。Genkit Dart 1.0 里最值得用的能力,恰好是能治这个病的那个:让流程在关键步骤停下来,等一个明确的确认再继续。

第一步:装依赖,跑起本地开发界面

# 在已有的 Dart / Flutter 工程里加依赖
dart pub add genkit

# 起本地开发界面(可逐次查看每次调用的输入输出与链路)
dart run genkit:start

genkit start 会拉起一个本地 UI,这是这套工具真正的学习加速器:你不需要靠 print 猜模型收到了什么、返回了什么。下面每改一步,都在这个界面里看一眼再往下走。

第二步:把一段 AI 逻辑写成一个「有类型」的 flow

Genkit Dart 的核心是 flow:一段可观察、可测试、输入输出都有类型的处理单元。类型不是装饰,它决定了模型的结构化输出能不能被编译器接住。官方把「在 Dart 里定义一次 schema、让模型按它产出结构化结果」这件事交给了 schemantic。

// 只读流程:给一个订单号,回一段可读的说明,不写任何东西
final explainOrder = defineFlow(
  name: 'explainOrder',
  inputSchema: /* 订单号 */,
  outputSchema: /* 一段文本 */,
  fn: (input, ctx) async {
    final res = await ai.generate(
      prompt: '用三句话说明这个订单现在卡在哪:${input.orderId}',
    );
    return res.text;
  },
);

先写只读流程有两个好处:一是没有副作用,怎么试都不会把线上数据搞坏;二是它能把「模型接得上、类型对得上、链路看得见」这三件事一次验证完。等这段跑顺了,再谈写操作。

第三步:给写操作加一道「停下来问人」

要真正干活,就得让 agent 调工具改数据。Genkit Dart 的做法是在工具执行里返回 .interrupt(...):流程在这里暂停,把「我准备做什么」交出去让调用方决定,确认后再从原处继续。这比在业务代码里到处插 if (needConfirm) 干净得多——暂停是可恢复的,状态不靠你自己在外面维护。

// 写操作:不是直接执行,而是先抛出一个「待确认」
final refundOrder = defineFlow(
  name: 'refundOrder',
  fn: (req, ctx) async {
    if (!req.confirmed) {
      return ctx.interrupt(
        reason: '准备给订单 ${req.orderId} 退款 ${req.amount},是否确认?',
        resumeWith: (answer) => answer.approved,
      );
    }
    return await doRefund(req.orderId, req.amount);
  },
);

这一段是整篇最值得抄的地方:把「要不要问人」的决策放进流程本身,而不是靠外层包一层审批。配套的做法是给每个写操作单独定一个「可放开」的顺序——先放开退款,稳了两周再放开改价,别一次全开。

第四步:把提示词从代码里挪出去

提示词写在代码字符串里,改一句话就要重新发版;Dotprompt 把它拆成独立的 .prompt 文件,提示词、模型选择与输出 schema 放在一起,改文案等于改配置。

---
model: googleai/gemini
input:
  schema:
    orderId: string
output:
  format: text
---
用三句话说明订单 {{orderId}} 目前卡在哪一步,
只陈述事实,不要给建议。

好处不只在「改起来方便」:提示词进版本管理之后,谁在什么时候改了什么、改完分数有没有动,都能和代码一样被回看。配合 genkit_middleware 里的自动重试,可以少写一批样板代码。

第五步:上链路,别再用 print 调试

本地用 genkit start 看,线上用 genkit_otel 把每次调用的链路导出到 OpenTelemetry。agent 出问题往往是「第几步开始歪的」,光看最后那句回复没用,得能逐段回放。

还有两个实验性能力,建议先别上生产

1.0 里放出了实验性的多轮持久 agent(defineAgent / remoteAgent,在 package:genkit/experimental.dart)和生成式 UI(genkit_a2ui)。它们能做的事很吸引人,但接口还在变动期;如果想试,放在分支里、别进主流程。

落地顺序(照着做就行)

  1. 写一个只读 flow,在 genkit start 里跑通,确认模型与类型都接得上
  2. 挑一个「失败了也能人工补救」的写操作,给它加 .interrupt,跑一遍确认能暂停、能恢复
  3. 把提示词搬进 .prompt 文件,纳入版本管理
  4. 接上 genkit_otel,把线上链路跑起来,再按「一次一类动作」的节奏放开权限

最后提醒一句:具体 API 签名以官方文档与 pub.dev 上的包版本为准,1.0 之后仍在快速迭代;文里的代码是结构示意,接进去之前先对着当前版本的文档核一遍参数名。