5.5 会话间通信(Agent-to-Agent)

生成模型:Claude Opus 4.6 (anthropic/claude-opus-4-6) Token 消耗:输入 ~250,000 tokens,输出 ~18,000 tokens(本章合计)


在前面的章节中,我们看到每个会话是一个独立的对话容器。但在实际使用中,Agent 常常需要与其他会话中的 Agent 协作——比如主 Agent 将编码任务委托给一个子代理(Sub-agent),或者查询另一个会话的历史记录。OpenClaw 通过一组会话工具sessions_listsessions_historysessions_send)实现了这种跨会话通信能力。

衍生解释:Agent-to-Agent(A2A)通信是多代理系统中的核心概念。不同于传统的函数调用,A2A 通信中每个 Agent 都是独立的"智能体",有自己的对话上下文和行为模式。它们之间的交互更像是两个人之间的对话,而非简单的 RPC 调用。OpenClaw 的 A2A 通信建立在会话系统之上——每个 Agent 运行在自己的会话中,通过 Gateway 中转消息。

5.5.1 sessions_list / sessions_history / sessions_send 工具

这三个工具构成了 A2A 通信的基础设施,分别负责发现查看发送

sessions_list:会话发现

sessions_list 让 Agent 能够查看当前存在哪些会话,包括自己生成的子代理会话和其他 Agent 的会话。

// src/agents/tools/sessions-list-tool.ts
const SessionsListToolSchema = Type.Object({
  kinds: Type.Optional(Type.Array(Type.String())),   // 过滤类型:main/group/cron/hook/node/other
  limit: Type.Optional(Type.Number({ minimum: 1 })),
  activeMinutes: Type.Optional(Type.Number({ minimum: 1 })),  // 只返回最近 N 分钟活跃的
  messageLimit: Type.Optional(Type.Number({ minimum: 0 })),    // 附带最近 N 条消息
});

export function createSessionsListTool(opts?: {
  agentSessionKey?: string;
  sandboxed?: boolean;
}): AnyAgentTool {
  return {
    name: "sessions_list",
    description: "List sessions with optional filters and last messages.",
    execute: async (_toolCallId, args) => {
      const cfg = loadConfig();
      const a2aPolicy = createAgentToAgentPolicy(cfg);

      // 沙箱 Agent 只能看到自己生成的子代理会话
      const restrictToSpawned = opts?.sandboxed === true
        && visibility === "spawned" && ...;

      const list = await callGateway({
        method: "sessions.list",
        params: { limit, activeMinutes, spawnedBy: restrictToSpawned ? requesterInternalKey : undefined },
      });

      for (const entry of sessions) {
        // 跨 Agent 访问:检查 A2A 策略
        const crossAgent = entryAgentId !== requesterAgentId;
        if (crossAgent && !a2aPolicy.isAllowed(requesterAgentId, entryAgentId)) {
          continue;  // 未授权则跳过
        }

        // 按类型过滤
        const kind = classifySessionKind({ key, gatewayKind, alias, mainKey });
        if (allowedKinds && !allowedKinds.has(kind)) continue;

        // 可选:附带最近消息
        if (messageLimit > 0) {
          const history = await callGateway({
            method: "chat.history",
            params: { sessionKey: resolvedKey, limit: messageLimit },
          });
          row.messages = stripToolMessages(rawMessages);  // 过滤掉工具消息
        }

        rows.push(row);
      }
      return jsonResult({ count: rows.length, sessions: rows });
    },
  };
}

每个返回的会话条目包含:会话键、类型、通道、标签、最后更新时间、模型覆盖、Token 用量等元数据。messageLimit 参数允许 Agent 在列表中直接预览每个会话的最近消息,避免额外调用 sessions_history

sessions_history:历史查看

sessions_history 获取指定会话的对话历史。由于对话历史可能非常大,该工具内置了多层截断保护:

清洗函数 sanitizeHistoryMessage() 会执行以下处理:

处理
说明

文本截断

超过 4000 字符的文本内容截断并添加 …(truncated)… 标记

图片移除

删除 base64 编码的图片数据,替换为 { omitted: true, bytes: N }

签名删除

移除 thinkingSignature 字段(加密签名数据极大且无用)

元数据清理

删除 detailsusagecost 等大型嵌套字段

sessions_send:消息发送

sessions_send 是 A2A 通信的核心——它向目标会话发送一条消息,触发目标 Agent 处理,并可选地等待回复。

该工具支持两种目标定位方式:通过 sessionKey 精确定位,或通过 label(会话标签)模糊查找。两者不可同时使用。

消息发送有两种模式:

即发即忘(Fire-and-Forget)timeoutSeconds = 0

等待回复timeoutSeconds > 0(默认 30 秒)

跨 Agent 访问控制

所有三个工具都遵循统一的 A2A 策略控制。默认情况下,跨 Agent 的访问是禁止的——必须在配置中显式开启:

createAgentToAgentPolicy() 会解析这个配置并生成策略对象,每次跨 Agent 操作都会检查 isAllowed(fromAgentId, toAgentId) 是否返回 true

5.5.2 跨会话消息传递与回复乒乓(Reply Ping-Pong)

sessions_send 不仅仅是发一条消息——它还支持一个完整的多轮对话流程,称为回复乒乓(Reply Ping-Pong)。这个机制允许两个 Agent 在收到消息后自动进行多轮交互,然后将最终结果发布到目标通道。

完整 A2A 流程

以下是一个 sessions_send 触发的完整 A2A 交互流程:

A2A 流程实现

src/agents/tools/sessions-send-tool.a2a.ts 中的 runSessionsSendA2AFlow() 实现了完整的乒乓和公告流程:

乒乓上下文注入

每一轮乒乓都会注入一段额外的系统提示,告知当前 Agent 它正在参与一个 A2A 对话:

这段提示让 Agent 知道:

  1. 自己当前的角色(请求方还是目标方)

  2. 当前是第几轮

  3. 可以通过回复 REPLY_SKIP 主动终止对话

乒乓轮次的上限由配置项 session.agentToAgent.maxPingPongTurns 控制,默认为 5,硬上限也是 5:

公告阶段

乒乓结束后进入公告阶段。目标 Agent 会收到一段包含完整上下文的提示,要求它生成一条面向最终用户的消息:

目标 Agent 可以选择:

  • 回复正常消息:该消息会被通过 send 方法发布到目标通道(如 Telegram 群组、Slack 频道)

  • 回复 ANNOUNCE_SKIP:保持沉默,不向用户发送任何消息


本节小结

OpenClaw 的会话间通信体系提供了完整的多代理协作能力:

  1. 三工具基础设施sessions_list(发现)、sessions_history(查看)、sessions_send(发送)构成了 A2A 通信的完整原语

  2. 细粒度访问控制:跨 Agent 通信默认禁止,需要通过 tools.agentToAgent 配置显式授权,沙箱 Agent 的可见范围进一步受限

  3. 回复乒乓机制:两个 Agent 可以自动进行最多 5 轮的交互式对话,任一方可通过 REPLY_SKIP 提前终止

  4. 公告阶段:乒乓结束后,目标 Agent 可以将协作结果发布到用户可见的通道,或通过 ANNOUNCE_SKIP 保持沉默

  5. 安全保护:80KB 响应上限、历史文本截断、沙箱会话隔离等多层防护确保了 A2A 通信不会导致资源滥用

这套机制使得 OpenClaw 不仅是一个单一的 AI 助手,更是一个可以容纳多个协作 Agent 的平台。在后续章节中,我们将看到子代理(Sub-agent)系统如何利用这些原语实现任务委托和结果汇报。

Last updated