Genkit Dart 多智能体编排:使用 agents() 中间件实现 Orchestrator 与 Sub-Agent 任务委派
2026/9/14 0:42:47 网站建设 项目流程

Genkit Dart 多智能体编排:使用 agents() 中间件实现 Orchestrator 与 Sub-Agent 任务委派

【免费下载链接】skillsAgent Skills for Google products and technologies项目地址: https://gitcode.com/GitHub_Trending/skills29/skills

本指南基于 Genkit Dart(AI SDK for Dart)的 Agent 体系中一个非常实用的模式:编排器(Orchestrator)智能体将任务委派给多个专业子智能体(Sub-Agent)(如researchercoder)。通过package:genkit_middleware提供的agents()中间件,只需一行配置即可获得"按需委派 + 结果回收"的完整能力。读完本文你将掌握:如何定义可被编排器发现的子智能体、如何用agents()挂载委派工具、delegate_to_<name>工具与系统提示词的自动注入机制、maxDelegations/historyLength/toolPrefix等关键选项,以及如何跨智能体共享 Artifact 并规避失控循环。

认识 Orchestrator 与 Sub-Agent 模式

在复杂业务场景中,单个智能体往往难以同时胜任"检索资料"与"编写代码"等多类差异巨大的任务。Genkit Dart 推荐的做法是:

  • 编排器(Orchestrator):一个主智能体,负责理解用户请求、判断该交给谁、串联多个子任务的执行顺序,并最终综合所有子结果给出答复;
  • 子智能体(Sub-Agent):若干职责单一、可被编排器调用的专业智能体,例如负责资料检索的researcher与负责编码的coder

子智能体委派(Sub-agent delegation)的底层实现来自package:genkit_middleware/agents.dart中的agents()中间件。其工作原理(以当前仓库的 agents-multi-agent.md 为骨架)可以概括为三步:

  1. 注入委派工具:中间件为每个子智能体自动生成一个委派工具,命名为delegate_to_<name>(默认前缀delegate_to);
  2. 扩充系统提示词:在编排器的系统提示词末尾追加一个<sub-agents>块,把各子智能体的名称与描述呈现给模型,让模型知道"什么情况该调谁";
  3. 执行委派并回收结果:当模型调用某个委派工具时,中间件实际运行对应的子智能体,并把子智能体的响应作为该工具调用的结果返回给编排器,编排器据此继续综合生成最终答案。

在深入代码之前,建议先阅读 agents.md 掌握defineAgentchat()、会话(Session)等基础概念,本指南默认你已经具备这些前置知识。

注册中间件插件

要让agents()中间件在运行时可用,必须在Genkit实例的plugins列表中注册AgentsPlugin()。按照 agents.md 与 genkit_middleware.md 的约定,retryRetryPlugin随核心包package:genkit/genkit.dart提供,而agentsfilesystemskillstoolApproval等智能体中间件来自package:genkit_middleware

import 'package:genkit/genkit.dart'; import 'package:genkit_middleware/agents.dart'; final ai = Genkit(plugins: [googleAI(), AgentsPlugin(), RetryPlugin()]);

通常你还会把AgentsPluginFilesystemPlugin()SkillsPlugin()ToolApprovalPlugin()RetryPlugin()等一并注册到共享的Genkit实例上,让use: [...]中引用的所有中间件都能被解析(详见 genkit_middleware.md)。

第一步:定义子智能体

子智能体与普通智能体没有本质区别,仍用ai.defineAgent定义。关键点在于:必须为每个子智能体提供description。该描述会从注册表元数据(registry metadata)中被自动发现,并展示给编排器,模型正是依靠它来判断"什么时候应该把任务委派给谁"。

import 'genkit.dart'; final researcher = ai.defineAgent( name: 'researcher', description: 'A thorough research assistant that provides well-sourced answers.', system: 'You are a thorough research assistant. When asked a question, ' 'provide a clear, well-structured, and well-sourced answer.', maxTurns: 10, ); final coder = ai.defineAgent( name: 'coder', description: 'Writes, debugs, and explains code. Use for any programming tasks.', system: 'You are an expert programmer. Provide clean, well-commented code ' 'with explanations. Use Dart by default unless asked otherwise.', maxTurns: 10, );

这里的几个要点值得展开:

  • name是委派工具名的组成部分:默认情况下,researcher会得到delegate_to_researcher工具,coder会得到delegate_to_coder工具;
  • description决定委派质量:描述越精确(例如注明"Use for any programming tasks"),模型越不容易把编码类请求误派给研究型子智能体;
  • maxTurns: 10:限制子智能体内部工具调用循环的轮数上限,避免单个子任务陷入无限循环。这与 agents.md 中defineAgentmaxTurns选项含义一致(该文件示例中使用maxTurns: 30);
  • 子智能体同样可以拥有自己的toolsstoreuse中间件配置——它们本身就是完整的智能体,只是额外承担了"被委派"的职责。

第二步:挂载编排器并配置 agents() 中间件

编排器通过use: [...]数组挂载agents()中间件。这里传入的是子智能体的名称列表agents: ['researcher', 'coder']),它们的描述会自动从注册表发现,无需重复填写。

import 'package:genkit/genkit.dart'; import 'package:genkit_middleware/agents.dart'; import 'genkit.dart'; final orchestratorAgent = ai.defineAgent( name: 'orchestratorAgent', system: ''' You are a helpful project assistant. Analyze the user's request and delegate to the appropriate sub-agent. If the request requires both research AND code, call them sequentially. After receiving sub-agent responses, synthesize a final answer for the user.''', use: [ agents( agents: ['researcher', 'coder'], maxDelegations: 5, // guard rail against runaway delegation loops historyLength: 4, // forward the last N user/model messages as context ), ], store: InMemorySessionStore(), );

这一段包含多个值得细说的设计决策:

  • 系统提示词中的编排指令system明确告诉模型"先分析再委派""需要研究与编码时按顺序调用""收到子结果后综合成最终答复",这与中间件自动注入的<sub-agents>块(含子智能体描述)协同工作,共同塑造编排行为;
  • store: InMemorySessionStore():为编排器提供服务端会话持久化。根据 agents-sessions.md,一旦智能体配置了store,服务端便持有会话历史,每轮产生不可变的快照(snapshot),快照链承载多轮对话状态。虽然多智能体编排不是必须使用 store,但在需要跨轮保持编排上下文、后续支持分支(branching)或后台执行(background)时,它是必要的基础设施;
  • 委派限制与上下文窗口maxDelegations: 5historyLength: 4分别用于防失控循环和控制上下文量,详见下文选项说明。

运行编排器

编排器的运行方式与其他任何智能体完全一致——使用chat()开启会话并发送流式消息:

final chat = orchestratorAgent.chat(); final turn = chat.sendStream( text: 'Research the best sorting algorithms, then write a Dart quicksort.', ); await for (final chunk in turn.stream) { stdout.write(chunk.text); } final res = await turn.response;

上述请求同时涉及"研究"与"编码",正是验证编排器能力的理想用例:编排器应首先委派给researcher获取排序算法资料,再委派给coder编写 Dart 快速排序,最后综合两份子结果输出最终答复。

agents() 选项详解

原文档对agents()中间件的选项给出了精炼说明,下面结合源码语义逐项展开:

选项类型必填默认值作用说明
agentsList<String>子智能体名称列表。每个名称对应的description会从注册表元数据自动发现,并展示给编排器供其决策委派对象
toolPrefixStringdelegate_to生成工具名的前缀,最终工具名为<toolPrefix>_<agent>,即默认的delegate_to_<name>
maxDelegationsint每次generate调用中允许的最大委派次数,防止委派死循环(runaway delegation loops)。示例中设为5作为安全护栏
historyLengthint0/省略转发给子智能体的最近 N 条用户/模型消息数量,作为其上下文。0或省略时只发送任务描述本身,不携带历史
artifactStrategy'inline' \| 'session''inline'控制子智能体产生的 Artifact 如何回传给编排器,详见下文"跨智能体共享 Artifact"

两个容易混淆的维度需要澄清:

  • maxDelegationsmaxTurns:前者限制的是一次generate内的委派次数(防止 A 委派 B、B 又委派 C 的连锁失控);后者限制的是单个智能体(含子智能体)内部工具调用循环的轮数。二者分别从"委派链路"与"单点循环"两个方向约束智能体行为,建议同时配置;
  • historyLength与上下文成本:值越大,子智能体获得的上下文越丰富,但每次委派消耗的 token 也越多。如果你的子任务彼此独立(例如多次查询天气),0(仅任务描述)通常已足够;而需要子智能体理解完整对话脉络时,才适当调大(如示例中的4)。

跨智能体共享 Artifact

子智能体可以产出 Artifacts——命名的、带内容的交付物(文件、报告、代码等),它们存于会话中(按名称去重),并随响应返回。agents()artifactStrategy选项决定这些 Artifact 如何到达编排器:

  • 'inline'(默认):Artifact 内容直接包含在委派工具的结果中,模型可以直接看到内容;同时 Artifact 也会合并进父级会话。适合编排器需要"读内容再综合"的场景,例如把coder生成的代码片段直接展示给模型用于最终整理;
  • 'session':Artifact 只合并进父级会话,工具结果中只列出 Artifact 名称而非内容。合并后的 Artifact 以调用标识(invocation id)命名空间隔离,形如<invocationId>/<name>,避免多个子智能体产出同名 Artifact 时互相覆盖。适合"先产出、后按需读取"的场景。

需要说明的是:Dart 当前版本尚未提供独立的artifacts()中间件(这是 Genkit Dart 与部分其他语言实现的一个差异点)。根据 agents-artifacts.md,你需要直接在会话 Artifact API 之上自定义write_artifact/read_artifact工具(内部通过ai.currentSession().addArtifacts()/getArtifacts()操作)。此外,如果目标是让智能体操作磁盘上的真实文件(沙箱工作区),则应使用filesystem()中间件(filesystem(rootDirectory: ...),由FilesystemPlugin()支撑),它提供list_files/read_file/write_file/search_and_replace四个工具。会话 Artifact 与磁盘文件是互补的两种方案:前者用于对话范围的交付物流转,后者用于持久化的磁盘工作。

其他可搭配的中间件

package:genkit_middleware还导出了filesystemskillstoolApproval等中间件,retry则随核心包package:genkit提供。它们的挂载方式一致——通过智能体的use: [...](或ai.generateuse)——详见 genkit_middleware.md。各中间件能力一览:

中间件来源包提供的工具/能力
filesystem(...)genkit_middlewarelist_files/read_file/write_file/search_and_replace,限制在rootDirectory
skills(...)genkit_middlewareuse_skill:按名称加载指定目录下SKILL.md中的专门指令到系统提示词
toolApproval(...)genkit_middleware拦截指定工具的执行并要求显式审批,返回FinishReason.interrupted
retry()genkit(核心)对瞬时模型错误自动重试

在编排场景中,retry()通常与委派搭配使用:子智能体执行可能偶发模型瞬时错误,自动重试能显著提升整体成功率。而toolApproval的中断(interrupt)机制与多智能体委派有一个重要交互注意点,见下节。

注意事项:子智能体中的中断不会被传播

原文档特别强调了一个易踩的坑:如果子智能体触发了一个中断(interrupt),该中断会作为普通的工具响应(tool response)报告回编排器,而不会作为可恢复的中断(resumable interrupt)向上传播

这意味着:如果子智能体内部依赖人工审批类中断(如 agents-human-in-the-loop.md 中基于ctx.interrupt(...)userApproval工具,或toolApproval中间件拦截的工具),编排器并不会暂停等待人工输入,而是把"请求审批"当作一次普通的结果收下。因此实践中应委派自包含(self-contained)的任务——把需要人工介入的环节放在编排器层自己处理,避免在子智能体深处埋入中断逻辑。

顺带说明:Dart 中没有独立的defineInterrupt,中断是通过普通工具在其函数体内调用ctx.interrupt(...)来实现的(详见 agents-human-in-the-loop.md)。在多智能体场景下请牢记"子智能体的中断不外传"这一边界。

从源码视角理解委派链路

结合 agents.md 与 SKILL.md,可以从体系层面进一步确认这套机制在 Genkit Dart 中的定位:

  • Agent 是"提示词 + 工具 + 会话"的持久化原语ai.defineAgent把提示词配置、工具列表、(可选的)会话存储合并注册为单个 action。agents()中间件正是以"给编排器附加工具"的形式实现委派——每一个delegate_to_<name>在模型视角里就是一个普通工具;
  • 中间件与 Agent 天生配套use: [...]数组就是为这类横切能力设计的挂载点,"子智能体委派、文件系统访问、技能加载、工具审批、自动重试——每个都只要一行"(agents.md)。多智能体编排不必手写"循环调用子智能体并拼接结果"的胶水代码,中间件替你完成了"注入工具 → 执行子智能体 → 回收结果"的完整闭环;
  • 会话快照是编排状态的基础:编排器自身是普通 Agent,其多轮状态、子智能体合并的 Artifact 都体现在会话快照链中(agents-sessions.md),这也是后续支持分支、后台执行、HTTP 服务化的前提。

生产化:从本地验证到 HTTP 服务

多智能体编排器与普通 Agent 一样可以投入生产,仓库文档提供了完整的落地路径:

  • CLI 验证genkit flow:run只运行 flow 而不运行 agent。要快速、非交互式地验证编排器,可以像 agents.md 建议的那样,把一轮对话包进一次性 flow 再通过genkit flow:run触发(genkit flow:run tryOrchestrator '"...?"' -- dart run main.dart);完整开发期调试则应使用genkit start -- dart run main.dart捕获 trace,通过genkit trace:list/genkit trace:get <traceId>检查模型 I/O 与工具调用(详见 SKILL.md);
  • HTTP 服务化:使用genkit_shelfshelfHandler暴露orchestratorAgent.action主轮次端点,并按需暴露getSnapshotDataAction(快照查询/恢复)与abortAgentAction(后台中止)等配套 action,见 agents.md 的"Serve an agent over HTTP"一节;
  • 客户端消费:浏览器 / Dart / Flutter 客户端从package:genkit/client.dart使用remoteAgent(url: ...),其底层 HTTP 协议与语言无关——即使编排器或子智能体用 JS/TypeScript 或 Go 实现,客户端同样可以调用。多轮对话、中断、Artifact 流在客户端与服务端行为一致。

小结

Genkit Dart 的多智能体编排提供了一条极低成本的"编排器 + 子智能体"落地路径:定义子智能体时写好description,在编排器use中挂载agents(agents: [...]),中间件便自动完成委派工具注入、系统提示词扩充与结果回收。实际使用时请重点把握四个决策点:

  1. 子智能体描述质量决定委派准确率;
  2. maxDelegations+maxTurns双重护栏防止失控循环;
  3. artifactStrategy按"编排器是否需要直接读内容"在'inline''session'间选择;
  4. 子任务保持自包含,避免在子智能体内依赖不传播的人工中断。

更多进阶话题可继续阅读仓库中的 agents-custom.md(defineCustomAgent完全接管单轮执行)、agents-branching.md(从快照分叉对话)与 agents-deployment.md(多智能体 HTTP 部署与 CORS)。

【免费下载链接】skillsAgent Skills for Google products and technologies项目地址: https://gitcode.com/GitHub_Trending/skills29/skills

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询