跳转到内容

多 Agent 路由与委托

Gemini CLI — 多 Agent 路由与委托

学习目标

  • 理解 Gemini CLI 的 Agent 定义系统和 YAML 配置格式
  • 掌握本地/远程子 Agent 的通信协议差异
  • 分析 Agent 工具封装模式和活动追踪机制

前置知识

本章涉及多 Agent 路由的通用原理,建议先阅读:

下文假设你已理解 Agent 隔离和消息路由的通用概念,直接聚焦 Gemini CLI 的具体实现。


项目实践

Agent 定义系统

Gemini CLI 使用 YAML 配置文件声明 Agent,而非硬编码。每个 Agent 定义包含:

.agents/example-agent.yaml
name: code-reviewer
description: 专门负责代码审查的 Agent
model: gemini-2.5-pro
max_turns: 30
max_time_minutes: 10
tools:
- read_file
- grep
- glob
- shell # 受限的 shell 命令

加载机制AgentLoader 从以下目录扫描 YAML 文件:

  • 内置 Agent 目录
  • 用户 .agents 目录
  • 工作区 .agents 目录(需文件夹信任)

配置校验packages/core/src/agents/agentLoader.ts):

  • 名称必须符合 [a-z0-9-_]+ slug 格式
  • 工具名称必须通过 isValidToolName 验证
  • MCP 服务器配置支持完整的 OAuth 和 Google Credentials 认证

Agent 作为工具(AgentTool 模式)

在 Gemini CLI 中,子 Agent 被封装为可调用工具。当主 Agent 的 LLM 响应请求调用某个 Agent 时:

主 Agent LLM: functionCall("agent_code-reviewer", { query: "审查 auth.ts" })
ToolRegistry: 识别为 Agent 调用(AGENT_TOOL_NAME)
AgentTool.execute():
├── 查找已注册的 Agent 定义
├── 创建 AgentSession(本地或远程)
├── 发送 query 到子 Agent
└── 返回子 Agent 的输出结果

关键优势:主 Agent 的 LLM 无需知道子 Agent 的内部实现,只需像调用工具一样使用它。这使得 Agent 的添加和替换对主 Agent 透明。

本地 vs 远程子 Agent 协议

维度本地子 Agent远程子 Agent
通信方式同一进程内,直接调用A2A 协议,跨进程/网络
实现LocalSessionInvocationRemoteSessionInvocation
隔离级别独立的 GeminiClient 实例,共享进程独立进程,完全隔离
延迟较高(网络往返)
资源共享 Node.js 事件循环独立资源

本地子 Agent 协议local-subagent-protocol.ts):

  • 通过 LocalAgentExecutor 在同一进程中创建独立的 GeminiClient 实例
  • 每个子 Agent 有独立的 SchedulerToolRegistryMessageBus
  • 子 Agent 的工具调用受父 Agent 的 Policy 约束

远程子 Agent 协议remote-subagent-protocol.ts):

  • 基于 A2A(Agent-to-Agent)协议
  • 通过 A2AClientManager 管理远程 Agent 连接
  • 支持 HTTP/SSE 和 WebSocket 传输
  • A2A 认证:支持 google-credentialsoauth 两种认证类型

SubagentActivity 事件追踪

子 Agent 执行过程中会向主 Agent 推送结构化事件:

interface SubagentActivityEvent {
isSubagentActivityEvent: true;
agentName: string;
type: 'TOOL_CALL_START' | 'TOOL_CALL_END' | 'THOUGHT_CHUNK' | 'ERROR';
data: Record<string, unknown>;
}

主 Agent 侧展示

  • TUI 中显示子 Agent 的实时进度(运行中/完成/错误/取消)
  • 子 Agent 的工具调用作为活动日志记录
  • 错误类型区分:REJECTED(用户拒绝)、CANCELLED(用户取消)、GENERIC(执行错误)

终止模式AgentTerminateMode):

模式触发条件
GOAL子 Agent 调用了 complete_task 工具
ERROR执行中抛出未捕获异常
TIMEOUT超过 max_time_minutes 限制
MAX_TURNS超过 max_turns 限制
ABORTED用户或父 Agent 主动取消
ERROR_NO_COMPLETE_TASK_CALL未调用 complete_task 即退出

A2A 服务器

Gemini CLI 还包含一个 a2a-server 包(packages/a2a-server/),允许将本地 Gemini CLI 实例作为远程 Agent 暴露给其他 Agent 调用:

  • 实现了 A2A 协议的服务器端
  • 支持通过 HTTP/SSE 接收 Agent 任务
  • 与 CLI 共享相同的 Agent 定义和配置

这使得 Gemini CLI 可以作为多 Agent 网络中的节点,既可以是调用者,也可以是被调用者。


问题与规避

子 Agent 的 Token 消耗

问题:每个子 Agent 有独立的 LLM 会话,多个子 Agent 并行时 Token 消耗叠加。

对策

  • 每个 Agent 定义可设置 max_turns(默认 30)和 max_time_minutes(默认 10)
  • 超时/超限自动终止子 Agent
  • max_turns 限制防止子 Agent 陷入自己的循环

远程 Agent 的网络故障

问题:A2A 协议依赖网络,连接中断导致子 Agent 调用失败。

对策

  • A2A 客户端内置重试逻辑
  • 超时配置(Agent 定义中的 timeout 字段)
  • 失败后返回 SubagentActivityErrorType.GENERIC 错误,主 Agent 可选择重试或降级

Agent 工具的参数传递

问题:Agent 作为工具调用时,如何将查询参数模板化为子 Agent 的系统提示。

对策:AgentLoader 支持 YAML 中的模板变量,调用时将 params 注入到子 Agent 的系统提示中。参数通过 Zod schema 验证,确保类型正确。


设计取舍

Agent 作为工具 vs 直接委托

方案优势代价
Agent 作为工具对主 Agent 透明、统一调用接口、易注册到工具表Agent 输出受工具的返回值格式限制,灵活性降低
直接委托子 Agent 可以完全控制交互流程主 Agent 需要了解子 Agent 的协议,耦合度高

Gemini CLI 的选择:将 Agent 封装为工具,保持了调用接口的一致性,同时通过 SubagentActivityEvent 提供了足够的可观测性。

本地进程内隔离 vs 独立进程

方案优势代价
进程内(独立实例)低延迟、共享内存、资源效率高崩溃可能影响主进程
独立进程完全隔离、独立资源管理通信延迟高、需要序列化

Gemini CLI 的选择:默认使用进程内隔离(适合大多数场景),同时通过 A2A 协议支持独立进程模式(适合需要强隔离或分布式部署的场景)。


参考来源