ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Genkit Dart Agent 人机协同中断机制实战:用 `ctx.interrupt` 实现审批、补全输入与精确恢复

Genkit Dart Agent 人机协同中断机制实战:用 `ctx.interrupt` 实现审批、补全输入与精确恢复 Genkit Dart Agent 人机协同中断机制实战用ctx.interrupt实现审批、补全输入与精确恢复【免费下载链接】skillsAgent Skills for Google products and technologies项目地址: https://gitcode.com/GitHub_Trending/skills29/skills本篇指南基于 skills/cloud/genkit-dart 技能中的 Agent Human-in-the-Loop / Interrupts 参考文档整理而成是 Genkit Dart Agent 系列文档的核心组成部分。建议先阅读 Agent 基础文档 了解defineAgent、chat与状态模型再阅读本文。Interrupt中断是 Genkit Dart Agent 实现人机协同Human-in-the-Loop的关键原语它能让 Agent 在单轮推理中途暂停把控制权交还给你的代码或真人用于审批敏感操作、收集缺失输入、确认执行计划等场景。读完本文你将掌握如何用普通工具建模中断Dart 下没有defineInterrupt、如何在服务端与浏览器客户端检测暂停并精确恢复、如何与toolApproval审批中间件无缝衔接以及中断与会话持久化的关系。中断的本质作为控制流使用的工具调用在 Genkit Dart 中中断interrupt在内部就是一个用作控制流的工具调用中断工具在服务端永远不会执行完成它只负责暂停当前回合。之后你从暂停点精确恢复resume而不是重开一轮。与多数 Genkit 语言 SDK 不同Dart 没有defineInterrupt这样的专用 API。建模中断的标准做法是把一个普通工具的函数体写成ctx.interrupt(...)将其加入 Agent 的tools列表即可。由于中断工具永远不会返回执行结果不要给它声明outputSchema——工具的输出是由调用方在恢复resume时提供的见下文respond。这一点在仓库的 SKILL.md 中被明确记录为 Dart 特有的注意事项interrupts are modeled as tools that callctx.interrupt(...)(there is nodefineInterrupt)。中断与持久化是正交的中断不依赖任何会话存储无论 Agent 使用 session store如InMemorySessionStore、FileSessionStore、FirestoreSessionStore还是 client-managed state无服务端存储中断的行为完全一致。唯一的要求是在同一个chat实例上恢复对于裸的ai.generate调用则需要把返回的 state/snapshot 携带回恢复调用中。标准的端到端流程可概括为chat.send(text: ...) → 响应携带 res.interruptsAgent 已暂停 → 收集人类输入展示中断数据、等待决定 → chat.resume(respond: [...]) 精确恢复定义一个会中断的工具银行转账审批示例下面是一个完整的可运行示例bankingAgent在转账前总是通过userApproval中断请求人类审批审批通过后才执行transferMoney。该示例完整来自 agents-human-in-the-loop.mdimport package:genkit/genkit.dart; import package:schemantic/schemantic.dart; import genkit.dart; part banking_agent.g.dart; Schema() abstract class $UserApprovalInput { Field(description: The action to be approved) String get action; Field(description: Details about the action) String get details; } Schema() abstract class $TransferMoneyInput { double get amount; String get toAccount; } Schema() abstract class $TransferMoneyOutput { bool get success; String get transactionId; } /// Interrupt: always pauses. The caller provides { approved, feedback } on /// resume. No outputSchema — the output comes from the resume call. final userApproval ai.defineTool( name: userApproval, description: Ask the user for approval before a sensitive action., inputSchema: UserApprovalInput.$schema, fn: (input, ctx) async ctx.interrupt(), ); /// Executes the transfer. Only reached after the user approves. final transferMoney ai.defineTool( name: transferMoney, description: Transfer money to a specified account., inputSchema: TransferMoneyInput.$schema, outputSchema: TransferMoneyOutput.$schema, fn: (input, _) async TransferMoneyOutput( success: true, transactionId: txn-${DateTime.now().millisecondsSinceEpoch}, ), ); final bankingAgent ai.defineAgent( name: bankingAgent, system: You are a banking assistant. ALWAYS use the userApproval interrupt to confirm before executing transferMoney., tools: [userApproval, transferMoney], use: [retry()], store: InMemorySessionStore(), );代码要点Schema()注解来自schemantic库package:schemantic/schemantic.dart$UserApprovalInput这样的$前缀抽象类配合生成的.g.dartpart 提供类型安全的输入/输出结构。schemantic 是 Genkit Dart 所有数据模型的基础详见 schemantic.md。ctx.interrupt()的参数是展示给人类的数据。本例中调用时不传参但模型会通过inputSchema填充{ action, details }该数据在响应中以interrupt.input暴露。userApproval刻意没有outputSchema因为它的返回值由恢复调用方在 resume 时通过respond(...)注入。transferMoney是普通工具只有在审批通过、回合被恢复后才会真正执行。Agent 的 system prompt 通过措辞约束模型行为ALWAYS use the userApproval interrupt to confirm before executing transferMoney引导模型先中断、后执行。use: [retry()]是核心包package:genkit/genkit.dart自带的中间件RetryPlugin需在Genkit实例上注册用于模型瞬时错误自动重试store: InMemorySessionStore()提供内存会话存储测试/开发友好重启即失。其他中间件filesystem、skills、toolApproval来自package:genkit_middleware。服务端检测暂停并恢复resume当 Agent 暂停时响应中的res.interrupts非空。其中每个AgentInterrupt暴露三个核心成员成员类型含义.nameString中断工具的名称用于识别是哪个中断触发了暂停.inputMap模型传入的数据即ctx.interrupt(...)展示给人类的内容如{ action, details }.respond(output)builder构造一个恢复条目提供该工具的输出不实际执行工具。不发送需配合chat.resume.restart([payload])builder构造一个恢复条目重新发出原始工具请求重试 / 让工具真正执行。可传入可选Map载荷该载荷嵌套在metadata.resumed下工具侧通过ctx.resumed读回。不发送注意respond与restart都是构造器builder它们只返回恢复条目并不会发起任何网络/进程调用。真正发送动作的是接下来的chat.resume(...)/chat.resumeStream(...)。非流式恢复在同一个chat上调用chat.resume(...)把respond/restart条目直接传入final chat bankingAgent.chat(); var res await chat.send(text: Transfer \$500 to my savings account.); final approval res.interrupts.where((i) i.name userApproval).firstOrNull; if (approval ! null) { print(approval.input); // { action, details } — show this to the human // Collect the human decision, then resume with the interrupts output: res await chat.resume( respond: [ approval.respond({approved: true, feedback: Looks good}), ], ); } print(res.text); // final confirmation恢复时提供的{approved: true, feedback: Looks good}正是userApproval工具的输出——因为在定义中断工具时省略了outputSchema所以这里完全由调用方决定注入的数据形状。流式恢复与sendStream对应恢复也有流式版本chat.resumeStream(...)final turn chat.resumeStream( respond: [approval.respond({approved: true})], ); await for (final chunk in turn.stream) { stdout.write(chunk.text); } final res await turn.response;一次恢复多个中断respond 与 restart 混合Agent 可能在一次响应中触发多个中断例如并行发出了多个工具请求你可以传入多个 builder 一次性全部恢复也可以混合respond提供输出与restart重新执行工具await chat.resume( respond: [a.respond({approved: true})], restart: [b.restart()], );客户端浏览器 / Flutter远程中断同样的模式在 HTTP 场景下完全成立。浏览器或任何 Dart 客户端通过package:genkit/client.dart的remoteAgent与 Agent 通信。客户端会自动跟踪快照snapshot因此在同一个chat上恢复时能精确接续暂停点。从 agents.md 可知remoteAgent可以对接任何 Genkit Agent 端点后端实现语言无关——Dart、JS/TypeScript、Go 均可线上协议一致只需把url指向承载 Agent 的服务器。import package:genkit/client.dart; final agent remoteAgent(url: /api/bankingAgent); final chat agent.chat(); // 1. Send and detect the pause. final res await chat.send(text: Transfer \$500 to savings.); final pending res.interrupts.where((i) i.name userApproval).firstOrNull; if (pending ! null) { // pending.input → { action, details }; render an approval dialog. // 2. After the human approves/denies, resume the SAME chat. final turn chat.resumeStream( respond: [pending.respond({approved: true, feedback: ok})], ); await for (final chunk in turn.stream) { /* render chunk.text */ } final finalRes await turn.response; // If finalRes.interrupts is non-empty, the agent paused again — repeat. }典型前端交互流程chat.send(...)得到带interrupts的响应用pending.input如{ action, details }渲染一个审批对话框而不是渲染模型的普通气泡人类做出决定后对同一个chat调用chat.resumeStream(respond: [...])若恢复后的finalRes.interrupts仍非空说明 Agent再次暂停重复上述循环直到中断列表为空。在 Flutter 应用中客户端同样使用remoteAgentagents.md 中说明客户端与 Flutter 无特殊绑定中断、自定义状态、artifacts 的用法与服务端完全一致。注意remoteAgent的context参数在 HTTP 传输下会被拒绝UnsupportedError远程 Agent 的上下文在服务端从 HTTP 请求中推导。与 toolApproval 审批中间件集成除了手写ctx.interrupt(...)Genkit Dart 还提供了开箱即用的审批方案toolApproval中间件来自package:genkit_middleware详见 genkit_middleware.md。它能把选中的工具自动转成审批中断无需任何自定义中断代码——中间件自己会拦截每次工具调用只有当工具位于approved白名单中或者其请求的resumed载荷携带{ tool-approved: true }时该工具才被放行。在ai.generate裸调用场景下中断后响应携带FinishReason.interruptedresponse.interrupts是ListToolRequestPart恢复时通过interruptRestart: [interrupt.restart({tool-approved: true})]并配合toolChoice: ToolChoice.none防止立即重调完成审批放行。而在 Agent 场景下中断是AgentInterrupt恢复用chat.resume(restart: [...])机制完全对齐。要让审批通过的工具在恢复时真正执行需要restart 那个被暂停的中断并携带审批载荷。.restart(...)builder 会把载荷嵌套到metadata.resumed下——这正是中间件读取的位置// interrupt is the paused AgentInterrupt from res.interrupts. // Pass this restart entry back when resuming the chat: await chat.resume( restart: [interrupt.restart({tool-approved: true})], );如果你选择在工具内部手写ctx.interrupt(...)作为审批门如coding_agent示例的做法则需要自己检查恢复载荷通过工具的ctx.resumedgetter 读取final resumed ctx.resumed; // the payload passed to .restart(...) final isApproved resumed is Map resumed[tool-approved] true;两种方式适用场景不同toolApproval中间件零代码接入适合对一组工具统一要求审批的常见安全场景。在 agents.md 的codingAgent示例中它甚至与filesystem(...)、skills(...)等中间件叠加使用顺序敏感toolApproval需在filesystem之前。注意使用审批中间件时 Agent 必须配置store如InMemorySessionStore()并且要注册ToolApprovalPlugin()插件手写ctx.interrupt完全自定义审批流程、自定义展示数据与恢复载荷适合业务规则复杂的场景如本指南的银行转账示例。中断的低层数据模型原理支撑理解中断在底层如何表达有助于排查问题和设计自定义流程。从 genkit.md 的数据模型可以看到模型发起的工具调用在会话中体现为ToolRequestPartname/ref/input工具执行结果体现为ToolResponsePartname/ref/output。ai.generate的响应通过response.interrupts暴露触发中断的工具请求在 Agent 场景则包装为AgentInterrupt。结合文档描述可以推断其控制流模型发出ToolRequestPart→ 工具函数体内调用ctx.interrupt()→ 该工具请求被标记为已中断不产生ToolResponsePart回合暂停并返回res.interrupts→ 调用方通过respond注入工具输出等价于补充一个ToolResponsePart但不执行工具或通过restart重新发出原始ToolRequestPartmetadata.resumed携带载荷→ 回合从暂停点继续。这也是为什么服务端会校验每个respond/restart条目是否与会话历史匹配恢复条目必须构建自响应中的中断对象而不是手工拼装的片段详见下文注意事项。注意事项与常见坑Notes Gotchas以下要点直接来自 agents-human-in-the-loop.md是实践中最容易踩坑的地方无需存储No store required。中断在 session store 与 client-managed state 两种模式下工作方式相同。使用 session store 时服务端拥有历史每轮产生不可变的 snapshot不使用 store 时服务端完全无状态状态由remoteAgent客户端自动往返携带。respond/restart只是构造器。它们返回恢复条目、并不会发送任何请求你仍必须显式调用chat.resume(...)。恢复校验Resume validation。服务端会针对会话历史校验每个respond/restart条目——务必从响应中的中断对象构建条目approval.respond(...)/interrupt.restart(...)不要手工拼装消息片段否则会被拒绝。再次暂停Re-pausing。恢复后产生的新响应可能再次中断例如 Agent 需要第二轮审批。正确做法是循环while (res.interrupts.isNotEmpty) { 收集输入 → resume }直到res.interrupts为空。UX 提示。对于被中断的回合不要渲染模型消息气泡应基于interrupt.input展示审批 UI等恢复完成后再渲染模型的最终回复。扩展阅读中断是 Genkit Dart Agent 能力矩阵中的一环与下列主题紧密关联Agent 基础与 client-managed statedefineAgent的全部选项tools、use、store、stateSchema、maxTurns等、chat.send/sendStream、remoteAgent客户端、无存储模式会话与持久化InMemorySessionStore/FileSessionStore/FirestoreSessionStore的选择与loadChat(snapshotId: ...)恢复历史中间件toolApproval审批中断、filesystem沙箱文件访问、skills按需注入指令Agent 状态工具内通过ai.currentSessionState()读写类型化自定义状态customPatch流式同步高级自定义 AgentdefineCustomAgent完全接管回合多步编排与自定义进度流HTTP 部署genkit_shelf的shelfHandler挂载agent.action、getSnapshotDataAction、abortAgentActionCORS 需放行X-Genkit-Stream-Id头Genkit Dart 技能总览Agent 之外还有 Flows、Dotprompt、插件生态与 Genkit CLIgenkit start、genkit flow:run、genkit trace:*。【免费下载链接】skillsAgent Skills for Google products and technologies项目地址: https://gitcode.com/GitHub_Trending/skills29/skills创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表