欢迎光临
我们一直在努力

OpenClaw ACP模式:代理通信协议深度解析

🦞 摘要:OpenClaw ACP(Agent Communication Protocol)是支撑多代理协作的核心通信协议,定义了代理之间、代理与网关之间的消息交换规范。本文从协议基础出发,系统讲解ACP的四种消息类型(请求/响应/通知/心跳)、JSON消息格式与字段定义、可靠性保证机制(确认/重试/幂等)、安全性设计(认证与加密)、性能优化策略(压缩/批量/流式),并通过三个实战案例——自定义ACP消息、ACP网关部署、协议扩展开发——帮助开发者深入掌握ACP协议,构建高效可靠的多代理系统。


目录

    • 一、ACP协议基础
      • 1.1 什么是ACP
      • 1.2 ACP的设计目标
      • 1.3 ACP在OpenClaw架构中的位置
      • 1.4 ACP与Binding路由的关系
    • 二、消息类型详解
      • 2.1 请求消息(Request)
      • 2.2 响应消息(Response)
      • 2.3 通知消息(Notification)
      • 2.4 心跳消息(Heartbeat)
      • 2.5 消息类型对比
    • 三、消息格式规范
      • 3.1 消息信封结构
      • 3.2 Header字段详解
      • 3.3 Payload字段详解
      • 3.4 会话键(SessionKey)路由机制
      • 3.5 流式响应格式
    • 四、可靠性机制
      • 4.1 ACK确认机制
      • 4.2 重试策略
      • 4.3 幂等性保证
      • 4.4 消息持久化与恢复
    • 五、安全性设计
      • 5.1 认证体系
      • 5.2 通道级安全
      • 5.3 上下文可见性模型
      • 5.4 安全审计
    • 六、性能优化
      • 6.1 消息压缩
      • 6.2 批量处理
      • 6.3 流式传输优化
      • 6.4 连接池与复用
      • 6.5 健康监控与自动恢复
    • 七、实战案例1:自定义ACP消息
      • 7.1 场景描述
      • 7.2 定义消息Schema
      • 7.3 发送自定义消息
      • 7.4 注册消息处理器
    • 八、实战案例2:ACP网关部署
      • 8.1 场景描述
      • 8.2 完整配置
      • 8.3 部署验证步骤
      • 8.4 监控与告警
    • 九、实战案例3:协议扩展开发
      • 9.1 场景描述
      • 9.2 扩展架构
      • 9.3 实现扩展
      • 9.4 注册扩展
      • 9.5 使用扩展
    • 十、最佳实践与总结
      • 10.1 ACP协议最佳实践
      • 10.2 常见陷阱
      • 10.3 协议演进方向
      • 10.4 参考资源

一、ACP协议基础

1.1 什么是ACP

ACP(Agent Communication Protocol)是OpenClaw多代理架构中的核心通信协议。在OpenClaw的设计哲学中,网关(Gateway) 是所有通信的枢纽——它连接着聊天应用、代理运行时、CLI工具、Web控制界面和移动节点。而ACP正是定义这些组件之间如何交换信息的一套规范化协议。

与传统的HTTP REST API不同,ACP采用了双向流式通信模型,更适合代理场景下的实时交互需求。ACP不仅承载了消息的传输,还内置了会话管理、路由分发、安全认证等关键能力。

💡 核心概念:在OpenClaw中,一个"代理"(Agent)是完整的作用域单元——包含工作区文件、身份配置(AGENTS.md/SOUL.md/USER.md)、认证档案、模型注册表和会话存储。ACP协议的使命就是在这些代理之间,以及代理与外部系统之间建立可靠的通信通道。

1.2 ACP的设计目标

ACP协议在设计之初就明确了以下核心目标:

设计目标说明实现方式
可靠性 确保消息不丢失、不重复处理 ACK确认机制 + 幂等设计
实时性 支持流式响应和低延迟交互 WebSocket双向流 + 分块传输
安全性 认证与加密保护通信安全 Token认证 + TLS加密
可扩展性 支持自定义消息类型和协议扩展 插件式消息处理器
互操作性 跨平台、跨语言兼容 JSON标准格式 + Schema验证

1.3 ACP在OpenClaw架构中的位置

#mermaid-svg-wCPdpe6oGmi63nEX{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-wCPdpe6oGmi63nEX .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-wCPdpe6oGmi63nEX .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-wCPdpe6oGmi63nEX .error-icon{fill:#552222;}#mermaid-svg-wCPdpe6oGmi63nEX .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-wCPdpe6oGmi63nEX .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-wCPdpe6oGmi63nEX .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-wCPdpe6oGmi63nEX .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-wCPdpe6oGmi63nEX .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-wCPdpe6oGmi63nEX .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-wCPdpe6oGmi63nEX .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-wCPdpe6oGmi63nEX .marker{fill:#333333;stroke:#333333;}#mermaid-svg-wCPdpe6oGmi63nEX .marker.cross{stroke:#333333;}#mermaid-svg-wCPdpe6oGmi63nEX svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-wCPdpe6oGmi63nEX p{margin:0;}#mermaid-svg-wCPdpe6oGmi63nEX .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-wCPdpe6oGmi63nEX .cluster-label text{fill:#333;}#mermaid-svg-wCPdpe6oGmi63nEX .cluster-label span{color:#333;}#mermaid-svg-wCPdpe6oGmi63nEX .cluster-label span p{background-color:transparent;}#mermaid-svg-wCPdpe6oGmi63nEX .label text,#mermaid-svg-wCPdpe6oGmi63nEX span{fill:#333;color:#333;}#mermaid-svg-wCPdpe6oGmi63nEX .node rect,#mermaid-svg-wCPdpe6oGmi63nEX .node circle,#mermaid-svg-wCPdpe6oGmi63nEX .node ellipse,#mermaid-svg-wCPdpe6oGmi63nEX .node polygon,#mermaid-svg-wCPdpe6oGmi63nEX .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-wCPdpe6oGmi63nEX .rough-node .label text,#mermaid-svg-wCPdpe6oGmi63nEX .node .label text,#mermaid-svg-wCPdpe6oGmi63nEX .image-shape .label,#mermaid-svg-wCPdpe6oGmi63nEX .icon-shape .label{text-anchor:middle;}#mermaid-svg-wCPdpe6oGmi63nEX .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-wCPdpe6oGmi63nEX .rough-node .label,#mermaid-svg-wCPdpe6oGmi63nEX .node .label,#mermaid-svg-wCPdpe6oGmi63nEX .image-shape .label,#mermaid-svg-wCPdpe6oGmi63nEX .icon-shape .label{text-align:center;}#mermaid-svg-wCPdpe6oGmi63nEX .node.clickable{cursor:pointer;}#mermaid-svg-wCPdpe6oGmi63nEX .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-wCPdpe6oGmi63nEX .arrowheadPath{fill:#333333;}#mermaid-svg-wCPdpe6oGmi63nEX .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-wCPdpe6oGmi63nEX .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-wCPdpe6oGmi63nEX .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wCPdpe6oGmi63nEX .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-wCPdpe6oGmi63nEX .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wCPdpe6oGmi63nEX .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-wCPdpe6oGmi63nEX .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-wCPdpe6oGmi63nEX .cluster text{fill:#333;}#mermaid-svg-wCPdpe6oGmi63nEX .cluster span{color:#333;}#mermaid-svg-wCPdpe6oGmi63nEX div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-wCPdpe6oGmi63nEX .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-wCPdpe6oGmi63nEX rect.text{fill:none;stroke-width:0;}#mermaid-svg-wCPdpe6oGmi63nEX .icon-shape,#mermaid-svg-wCPdpe6oGmi63nEX .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wCPdpe6oGmi63nEX .icon-shape p,#mermaid-svg-wCPdpe6oGmi63nEX .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-wCPdpe6oGmi63nEX .icon-shape .label rect,#mermaid-svg-wCPdpe6oGmi63nEX .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wCPdpe6oGmi63nEX .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-wCPdpe6oGmi63nEX .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-wCPdpe6oGmi63nEX :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

工具层

代理层

网关层 – ACP核心

外部通道

Discord

Telegram

WhatsApp

Slack

飞书 Feishu

ACP路由引擎

会话管理器

安全认证层

消息序列化

Agent: main

Agent: coding

Agent: social

exec

browser

skills

memory

如图所示,ACP协议运行在网关层,是连接外部通道与内部代理的桥梁。所有来自Discord、Telegram、WhatsApp等通道的消息,都经过ACP路由引擎处理后分发到对应的代理实例。

1.4 ACP与Binding路由的关系

在OpenClaw的多代理模型中,Binding(绑定)是ACP路由的核心机制。每条Binding定义了一条从通道账户到代理实例的路由规则:

{
bindings: [
{
agentId: "alex",
match: { channel: "whatsapp", peer: { kind: "direct", id: "+15551230001" } }
},
{
agentId: "mia",
match: { channel: "whatsapp", peer: { kind: "direct", id: "+15551230002" } }
}
]
}

当ACP收到一条消息时,路由引擎会按照以下优先级进行匹配:

  • peer match:精确的DM/群组/通道ID匹配
  • parentPeer match:线程继承匹配
  • guildId + roles:Discord角色路由
  • guildId:Discord服务器级别
  • teamId:Slack团队级别
  • accountId match:通道账户级别
  • Channel-level match:accountId: "*" 通配
  • Default agent:回退到默认代理
  • 这种确定性、最高特异性优先的路由策略,确保了消息总是被路由到最合适的代理实例。


    二、消息类型详解

    ACP定义了四种核心消息类型,每种类型服务于不同的通信场景。

    2.1 请求消息(Request)

    请求消息是最基础的ACP消息类型,用于代理向其他代理或网关发起同步调用。请求消息要求接收方返回一个响应。

    典型场景:

    • 用户通过通道发送消息,触发代理处理
    • 代理调用工具(tool use)
    • 代理之间的协作请求

    请求消息特征:

    • 必须携带唯一messageId用于关联响应
    • 包含发送方agentId和目标路由信息
    • 可选的timeout字段控制超时行为
    • 支持priority字段标记紧急程度

    2.2 响应消息(Response)

    响应消息是对请求消息的回复,携带处理结果或错误信息。ACP的响应模型支持流式响应——代理可以分多次发送响应片段,最终以完成标记结束。

    典型场景:

    • 代理返回生成的文本内容
    • 工具调用返回执行结果
    • 多代理协作中的中间结果传递

    响应消息特征:

    • 携带correlationId关联原始请求
    • 支持streaming: true标记流式响应
    • 包含chunkIndex和isFinal控制流式分块
    • 可携带error字段表示处理失败

    2.3 通知消息(Notification)

    通知消息是一种即发即忘(fire-and-forget)的消息类型,发送方不期望接收方返回响应。通知消息用于单向的状态推送和事件广播。

    典型场景:

    • 代理状态变更通知(上线/下线/忙碌)
    • 通道连接状态变化
    • 定时任务触发(Cron/Heartbeat)
    • 系统事件广播

    通知消息特征:

    • 不需要correlationId
    • 携带event字段标识事件类型
    • 支持broadcast: true进行广播
    • 不触发ACK确认机制(可选)

    2.4 心跳消息(Heartbeat)

    心跳消息是ACP的保活机制,用于维持代理与网关之间的连接活跃度,并支持轻量级的状态同步。

    典型场景:

    • WebSocket连接保活
    • 代理健康状态上报
    • 通道连接状态检测
    • 网关触发周期性检查任务

    心跳消息特征:

    • 固定间隔发送(默认30秒)
    • 携带status字段反映代理当前状态
    • 网关可通过心跳响应下发配置变更
    • 心跳超时触发自动重连

    🔑 心跳与定时任务的分工:在OpenClaw中,心跳适合批量检查(如收件箱+日历+通知合并处理),允许时间略有偏差。而定时任务(Cron)适合精确定时的场景(如"每周一早上9点整")。两者互补而非替代。

    Agent

    Gateway

    通道客户端

    Agent

    Gateway

    通道客户端

    #mermaid-svg-7UWMfGphsOHly9N4{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-7UWMfGphsOHly9N4 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7UWMfGphsOHly9N4 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7UWMfGphsOHly9N4 .error-icon{fill:#552222;}#mermaid-svg-7UWMfGphsOHly9N4 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7UWMfGphsOHly9N4 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7UWMfGphsOHly9N4 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7UWMfGphsOHly9N4 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7UWMfGphsOHly9N4 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7UWMfGphsOHly9N4 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7UWMfGphsOHly9N4 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7UWMfGphsOHly9N4 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7UWMfGphsOHly9N4 .marker.cross{stroke:#333333;}#mermaid-svg-7UWMfGphsOHly9N4 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7UWMfGphsOHly9N4 p{margin:0;}#mermaid-svg-7UWMfGphsOHly9N4 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-7UWMfGphsOHly9N4 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-7UWMfGphsOHly9N4 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-7UWMfGphsOHly9N4 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-7UWMfGphsOHly9N4 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-7UWMfGphsOHly9N4 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-7UWMfGphsOHly9N4 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-7UWMfGphsOHly9N4 .sequenceNumber{fill:white;}#mermaid-svg-7UWMfGphsOHly9N4 #sequencenumber{fill:#333;}#mermaid-svg-7UWMfGphsOHly9N4 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-7UWMfGphsOHly9N4 .messageText{fill:#333;stroke:none;}#mermaid-svg-7UWMfGphsOHly9N4 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-7UWMfGphsOHly9N4 .labelText,#mermaid-svg-7UWMfGphsOHly9N4 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-7UWMfGphsOHly9N4 .loopText,#mermaid-svg-7UWMfGphsOHly9N4 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-7UWMfGphsOHly9N4 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-7UWMfGphsOHly9N4 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-7UWMfGphsOHly9N4 .noteText,#mermaid-svg-7UWMfGphsOHly9N4 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-7UWMfGphsOHly9N4 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-7UWMfGphsOHly9N4 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-7UWMfGphsOHly9N4 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-7UWMfGphsOHly9N4 .actorPopupMenu{position:absolute;}#mermaid-svg-7UWMfGphsOHly9N4 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-7UWMfGphsOHly9N4 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-7UWMfGphsOHly9N4 .actor-man circle,#mermaid-svg-7UWMfGphsOHly9N4 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-7UWMfGphsOHly9N4 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    请求-响应模式

    通知模式

    心跳模式

    Request (messageId: msg-001)

    Forward Request

    Response (correlationId: msg-001)

    Forward Response

    Notification (event: status_change)

    Forward Notification

    Heartbeat (status: active)

    Heartbeat ACK + config_update

    2.5 消息类型对比

    特征RequestResponseNotificationHeartbeat
    方向 双向(需要响应) 单向(回应请求) 单向(无需响应) 双向(保活)
    可靠性 ACK + 重试 关联请求可靠性 可选ACK 自动重连
    流式支持
    典型负载 用户消息/工具调用 生成内容/执行结果 状态变更/事件 连接状态
    优先级 可配置 继承请求 最高

    三、消息格式规范

    3.1 消息信封结构

    所有ACP消息都遵循统一的信封(Envelope)格式,包含消息头(Header)和消息体(Payload)两部分:

    {
    "header": {
    "messageId": "msg-7f8a9b2c-4e5d",
    "correlationId": null,
    "timestamp": 1718764800000,
    "version": "1.0",
    "type": "request",
    "source": {
    "agentId": "main",
    "channel": "telegram",
    "accountId": "default",
    "peerId": "tg:123456789",
    "sessionKey": "agent:main:tg:123456789"
    },
    "destination": {
    "agentId": "coding",
    "target": "agent"
    },
    "metadata": {
    "priority": "normal",
    "ttl": 30000,
    "idempotencyKey": "idem-7f8a9b2c",
    "traceId": "trace-a1b2c3d4"
    }
    },
    "payload": {
    "contentType": "text/plain",
    "content": "帮我重构这段代码",
    "attachments": []
    }
    }

    3.2 Header字段详解

    字段类型必填说明
    messageId string 消息唯一标识,UUID v4格式
    correlationId string 关联的请求消息ID,响应消息必填
    timestamp number 消息发送时间戳(毫秒)
    version string 协议版本号,当前为"1.0"
    type enum 消息类型:request/response/notification/heartbeat
    source object 消息来源信息
    source.agentId string 发送方代理ID
    source.channel string 来源通道标识
    source.accountId string 通道账户ID
    source.peerId string 发送者对等ID
    source.sessionKey string 会话路由键
    destination object 消息目标信息
    destination.agentId string 目标代理ID
    destination.target enum 目标类型:agent/gateway/broadcast
    metadata object 扩展元数据
    metadata.priority enum 优先级:low/normal/high/critical
    metadata.ttl number 消息存活时间(毫秒)
    metadata.idempotencyKey string 幂等键,防止重复处理
    metadata.traceId string 分布式追踪ID

    3.3 Payload字段详解

    Payload部分承载消息的实际内容,支持多种内容类型:

    字段类型必填说明
    contentType string 内容MIME类型
    content string 消息正文内容
    attachments array 附件列表
    attachments[].type enum 附件类型:image/audio/video/document
    attachments[].url string 附件URL
    attachments[].data string Base64编码的附件数据
    attachments[].mimeType string 附件MIME类型
    attachments[].size number 附件大小(字节)
    toolCalls array 工具调用请求列表
    toolResults array 工具调用结果列表
    streaming object 流式传输控制信息

    3.4 会话键(SessionKey)路由机制

    会话键是ACP路由的核心标识符,格式为:

    agent:<agentId>:<mainKey>

    其中mainKey的生成规则由session.dmScope配置决定:

    dmScope值mainKey格式适用场景
    main main 单用户,所有会话共享
    per-peer <peerId> 多用户按发送者隔离
    per-channel-peer <channel>:<peerId> 多通道多用户隔离
    per-account-channel-peer <account>:<channel>:<peerId> 多账户多通道隔离

    ⚠️ 安全提示:sessionKey是路由选择器,不是授权令牌。拥有某个sessionKey并不意味着获得了对应用户的权限。详见OpenClaw安全文档。

    3.5 流式响应格式

    ACP支持代理以流式方式返回长文本响应,这在代码生成、文章撰写等场景中尤为重要:

    // 第一个分块
    {
    "header": {
    "messageId": "resp-chunk-1",
    "correlationId": "msg-7f8a9b2c-4e5d",
    "type": "response"
    },
    "payload": {
    "contentType": "text/markdown",
    "content": "这是一段流式",
    "streaming": {
    "chunkIndex": 0,
    "isFinal": false,
    "totalChunks": null
    }
    }
    }

    // 最后一个分块
    {
    "header": {
    "messageId": "resp-chunk-final",
    "correlationId": "msg-7f8a9b2c-4e5d",
    "type": "response"
    },
    "payload": {
    "contentType": "text/markdown",
    "content": "响应的结尾部分。",
    "streaming": {
    "chunkIndex": 5,
    "isFinal": true,
    "totalChunks": 6
    }
    }
    }


    四、可靠性机制

    ACP协议在消息传输的各个环节都设计了可靠性保障,确保在不可靠的网络环境中依然能够正确交付消息。

    4.1 ACK确认机制

    ACP采用应用层ACK确认机制,与传输层的TCP ACK互补。每条请求消息和重要的通知消息都要求接收方在处理完成后返回ACK:

    接收方

    发送方

    接收方

    发送方

    #mermaid-svg-d6kZXsvbLecHmzl3{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-d6kZXsvbLecHmzl3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-d6kZXsvbLecHmzl3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-d6kZXsvbLecHmzl3 .error-icon{fill:#552222;}#mermaid-svg-d6kZXsvbLecHmzl3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-d6kZXsvbLecHmzl3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-d6kZXsvbLecHmzl3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-d6kZXsvbLecHmzl3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-d6kZXsvbLecHmzl3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-d6kZXsvbLecHmzl3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-d6kZXsvbLecHmzl3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-d6kZXsvbLecHmzl3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-d6kZXsvbLecHmzl3 .marker.cross{stroke:#333333;}#mermaid-svg-d6kZXsvbLecHmzl3 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-d6kZXsvbLecHmzl3 p{margin:0;}#mermaid-svg-d6kZXsvbLecHmzl3 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-d6kZXsvbLecHmzl3 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-d6kZXsvbLecHmzl3 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-d6kZXsvbLecHmzl3 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-d6kZXsvbLecHmzl3 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-d6kZXsvbLecHmzl3 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-d6kZXsvbLecHmzl3 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-d6kZXsvbLecHmzl3 .sequenceNumber{fill:white;}#mermaid-svg-d6kZXsvbLecHmzl3 #sequencenumber{fill:#333;}#mermaid-svg-d6kZXsvbLecHmzl3 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-d6kZXsvbLecHmzl3 .messageText{fill:#333;stroke:none;}#mermaid-svg-d6kZXsvbLecHmzl3 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-d6kZXsvbLecHmzl3 .labelText,#mermaid-svg-d6kZXsvbLecHmzl3 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-d6kZXsvbLecHmzl3 .loopText,#mermaid-svg-d6kZXsvbLecHmzl3 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-d6kZXsvbLecHmzl3 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-d6kZXsvbLecHmzl3 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-d6kZXsvbLecHmzl3 .noteText,#mermaid-svg-d6kZXsvbLecHmzl3 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-d6kZXsvbLecHmzl3 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-d6kZXsvbLecHmzl3 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-d6kZXsvbLecHmzl3 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-d6kZXsvbLecHmzl3 .actorPopupMenu{position:absolute;}#mermaid-svg-d6kZXsvbLecHmzl3 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-d6kZXsvbLecHmzl3 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-d6kZXsvbLecHmzl3 .actor-man circle,#mermaid-svg-d6kZXsvbLecHmzl3 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-d6kZXsvbLecHmzl3 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    处理消息中…

    Request (messageId: msg-001)

    ACK (correlationId: msg-001, status: received)

    Response (correlationId: msg-001)

    ACK (correlationId: resp-001, status: received)

    ACK消息包含以下关键字段:

    字段说明
    status 确认状态:received/processing/completed/failed
    timestamp 确认时间戳
    error 失败时的错误信息

    4.2 重试策略

    当发送方在指定超时时间内未收到ACK,ACP将按照指数退避策略进行重试:

    # ACP重试策略伪代码
    class ACPRetryPolicy:
    def __init__(self):
    self.max_retries = 3
    self.base_delay_ms = 1000 # 初始延迟1秒
    self.max_delay_ms = 30000 # 最大延迟30秒
    self.backoff_multiplier = 2.0 # 退避倍数
    self.jitter_factor = 0.1 # 抖动因子

    def next_delay(self, attempt: int) > int:
    """计算下一次重试的延迟时间"""
    if attempt > self.max_retries:
    raise MaxRetriesExceededError(
    f"Exceeded max retries ({self.max_retries})"
    )

    # 指数退避 + 随机抖动
    delay = self.base_delay_ms * (self.backoff_multiplier ** (attempt 1))
    delay = min(delay, self.max_delay_ms)

    # 添加随机抖动防止惊群效应
    jitter = delay * self.jitter_factor * (2 * random() 1)
    return int(delay + jitter)

    def should_retry(self, error: Exception) > bool:
    """判断错误是否可重试"""
    retryable_types = {
    TimeoutError,
    ConnectionError,
    ServiceUnavailableError
    }
    return isinstance(error, tuple(retryable_types))

    上面的代码展示了ACP重试策略的核心逻辑:指数退避保证在服务端压力过大时不会雪崩,随机抖动防止多个客户端同时重试造成惊群效应,最大延迟上限则避免了过长的等待时间。

    4.3 幂等性保证

    ACP通过idempotencyKey字段实现消息级别的幂等性保证。相同的idempotencyKey只会被处理一次,即使消息因网络问题被重复投递:

    幂等性实现原理:

  • 发送方为每条消息生成唯一的idempotencyKey(通常为UUID)
  • 接收方维护最近处理的键值缓存(TTL通常为5分钟)
  • 收到消息时先检查缓存,若键已存在则直接返回之前的结果
  • 处理完成后将键和结果写入缓存
  • 幂等性适用场景:

    场景幂等要求实现方式
    工具调用 idempotencyKey + 结果缓存
    状态变更 乐观锁 + 版本号
    消息发送 去重窗口 + fingerprint
    心跳检测 自然幂等(覆盖即可)

    4.4 消息持久化与恢复

    ACP网关在消息处理的关键节点进行持久化,确保进程崩溃后可以恢复:

    #mermaid-svg-1xMfKQ5MXIYyw0so{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-1xMfKQ5MXIYyw0so .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-1xMfKQ5MXIYyw0so .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-1xMfKQ5MXIYyw0so .error-icon{fill:#552222;}#mermaid-svg-1xMfKQ5MXIYyw0so .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-1xMfKQ5MXIYyw0so .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-1xMfKQ5MXIYyw0so .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-1xMfKQ5MXIYyw0so .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-1xMfKQ5MXIYyw0so .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-1xMfKQ5MXIYyw0so .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-1xMfKQ5MXIYyw0so .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-1xMfKQ5MXIYyw0so .marker{fill:#333333;stroke:#333333;}#mermaid-svg-1xMfKQ5MXIYyw0so .marker.cross{stroke:#333333;}#mermaid-svg-1xMfKQ5MXIYyw0so svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-1xMfKQ5MXIYyw0so p{margin:0;}#mermaid-svg-1xMfKQ5MXIYyw0so .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-1xMfKQ5MXIYyw0so .cluster-label text{fill:#333;}#mermaid-svg-1xMfKQ5MXIYyw0so .cluster-label span{color:#333;}#mermaid-svg-1xMfKQ5MXIYyw0so .cluster-label span p{background-color:transparent;}#mermaid-svg-1xMfKQ5MXIYyw0so .label text,#mermaid-svg-1xMfKQ5MXIYyw0so span{fill:#333;color:#333;}#mermaid-svg-1xMfKQ5MXIYyw0so .node rect,#mermaid-svg-1xMfKQ5MXIYyw0so .node circle,#mermaid-svg-1xMfKQ5MXIYyw0so .node ellipse,#mermaid-svg-1xMfKQ5MXIYyw0so .node polygon,#mermaid-svg-1xMfKQ5MXIYyw0so .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-1xMfKQ5MXIYyw0so .rough-node .label text,#mermaid-svg-1xMfKQ5MXIYyw0so .node .label text,#mermaid-svg-1xMfKQ5MXIYyw0so .image-shape .label,#mermaid-svg-1xMfKQ5MXIYyw0so .icon-shape .label{text-anchor:middle;}#mermaid-svg-1xMfKQ5MXIYyw0so .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-1xMfKQ5MXIYyw0so .rough-node .label,#mermaid-svg-1xMfKQ5MXIYyw0so .node .label,#mermaid-svg-1xMfKQ5MXIYyw0so .image-shape .label,#mermaid-svg-1xMfKQ5MXIYyw0so .icon-shape .label{text-align:center;}#mermaid-svg-1xMfKQ5MXIYyw0so .node.clickable{cursor:pointer;}#mermaid-svg-1xMfKQ5MXIYyw0so .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-1xMfKQ5MXIYyw0so .arrowheadPath{fill:#333333;}#mermaid-svg-1xMfKQ5MXIYyw0so .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-1xMfKQ5MXIYyw0so .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-1xMfKQ5MXIYyw0so .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1xMfKQ5MXIYyw0so .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-1xMfKQ5MXIYyw0so .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1xMfKQ5MXIYyw0so .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-1xMfKQ5MXIYyw0so .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-1xMfKQ5MXIYyw0so .cluster text{fill:#333;}#mermaid-svg-1xMfKQ5MXIYyw0so .cluster span{color:#333;}#mermaid-svg-1xMfKQ5MXIYyw0so div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-1xMfKQ5MXIYyw0so .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-1xMfKQ5MXIYyw0so rect.text{fill:none;stroke-width:0;}#mermaid-svg-1xMfKQ5MXIYyw0so .icon-shape,#mermaid-svg-1xMfKQ5MXIYyw0so .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1xMfKQ5MXIYyw0so .icon-shape p,#mermaid-svg-1xMfKQ5MXIYyw0so .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-1xMfKQ5MXIYyw0so .icon-shape .label rect,#mermaid-svg-1xMfKQ5MXIYyw0so .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1xMfKQ5MXIYyw0so .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-1xMfKQ5MXIYyw0so .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-1xMfKQ5MXIYyw0so :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    持久化

    持久化

    持久化

    读取

    读取

    读取

    消息接收

    消息日志

    路由分发

    分发记录

    代理处理

    处理结果

    响应发送

    崩溃恢复

    持久化策略采用WAL(Write-Ahead Log)模式:消息在处理前先写入日志,处理完成后标记为已完成。崩溃恢复时,网关重放未完成的消息,保证at-least-once投递语义。配合幂等机制,最终实现exactly-once处理语义。


    五、安全性设计

    ACP协议的安全性设计遵循OpenClaw的个人助手信任模型——一个网关实例对应一个可信操作者边界。在这个模型下,ACP实现了多层次的认证与加密机制。

    5.1 认证体系

    ACP支持以下认证模式:

    认证模式说明适用场景
    token 共享密钥认证 单操作者部署
    password 密码认证 简单场景
    trusted-proxy 代理信任头认证 反向代理部署
    device 设备令牌认证 移动节点
    mTLS 双向TLS证书认证 企业级部署

    Token认证流程:

    // openclaw.json 配置
    {
    gateway: {
    auth: {
    mode: "token",
    token: "replace-with-long-random-token" // 至少32字符
    }
    }
    }

  • 客户端连接时在WebSocket握手或HTTP请求头中携带Authorization: Bearer <token>
  • 网关验证token有效性
  • 认证通过后建立会话,后续帧不再需要重复认证
  • token变更时所有已建立连接失效,需要重新认证
  • 5.2 通道级安全

    每个通道都有独立的安全策略配置:

    {
    channels: {
    telegram: {
    dmPolicy: "pairing", // 配对模式:新发送者需配对码
    allowFrom: ["tg:123456789"],
    groupPolicy: "allowlist",
    healthMonitor: { enabled: true }
    },
    whatsapp: {
    dmPolicy: "allowlist", // 白名单模式
    allowFrom: ["+15555550123"],
    groups: {
    "*": { requireMention: true } // 群聊需要@提及
    }
    },
    discord: {
    dmPolicy: "pairing",
    groupPolicy: "allowlist"
    }
    }
    }

    DM策略对比:

    策略行为安全等级
    pairing 新发送者需配对码审批 ⭐⭐⭐⭐
    allowlist 仅白名单内发送者 ⭐⭐⭐⭐⭐
    open 允许所有DM(需allowFrom: ["*"])
    disabled 拒绝所有DM ⭐⭐⭐⭐⭐⭐

    5.3 上下文可见性模型

    ACP区分两个独立的安全概念:

    • 触发授权:谁可以触发代理(dmPolicy、groupPolicy、白名单、提及门控)
    • 上下文可见性:什么补充上下文可以注入到模型输入中

    contextVisibility设置控制补充上下文的过滤:

    设置值行为
    all(默认) 保留所有收到的补充上下文
    allowlist 仅保留白名单内发送者的补充上下文
    allowlist_quote 类似allowlist,但保留一条显式引用回复

    5.4 安全审计

    OpenClaw内置安全审计工具,建议定期运行:

    # 基础安全审计
    openclaw security audit

    # 深度审计(包含更多检查项)
    openclaw security audit –deep

    # 自动修复常见安全问题
    openclaw security audit –fix

    # JSON格式输出(便于CI/CD集成)
    openclaw security audit –json

    审计覆盖范围包括:入站访问策略、工具爆炸半径、文件系统权限漂移、执行审批配置、浏览器控制暴露、以及开放通道的工具暴露等。

    🔒 安全原则:OpenClaw的安全模型基于可信操作者边界。sessionKey是路由选择器而非授权令牌;多个用户共享一个带工具权限的代理时,每个用户都能驱动该代理的全部权限集。详见安全文档。


    六、性能优化

    ACP协议在性能方面做了多层优化,从消息压缩到批量处理再到流式传输,确保在高负载场景下依然保持低延迟和高吞吐。

    6.1 消息压缩

    ACP支持在传输层对消息进行压缩,减少网络带宽消耗:

    压缩算法压缩率CPU开销适用场景
    gzip 大消息体(>1KB)
    deflate 中等消息体
    none 小消息体(<256B)

    压缩决策基于消息体大小自动选择:小于256字节的消息不压缩,超过1KB的消息使用gzip,介于两者之间的使用deflate。

    6.2 批量处理

    ACP支持将多条消息合并为一次传输,减少网络往返次数:

    {
    "header": {
    "messageId": "batch-7f8a9b2c",
    "type": "batch",
    "timestamp": 1718764800000,
    "version": "1.0",
    "source": { "agentId": "main" },
    "destination": { "target": "gateway" }
    },
    "payload": {
    "contentType": "application/x-acp-batch",
    "content": null,
    "batchItems": [
    {
    "messageId": "msg-001",
    "type": "notification",
    "event": "status_change",
    "content": { "status": "processing" }
    },
    {
    "messageId": "msg-002",
    "type": "notification",
    "event": "progress_update",
    "content": { "percent": 45 }
    },
    {
    "messageId": "msg-003",
    "type": "response",
    "correlationId": "req-previous",
    "content": { "chunk": "中间结果" }
    }
    ]
    }
    }

    批量处理特别适用于以下场景:

    • 心跳与状态更新合并发送
    • 多个工具调用结果批量返回
    • 日志/遥测数据周期性上报
    • 多代理协作中的进度汇报

    6.3 流式传输优化

    ACP的流式传输针对长文本和代码生成场景进行了专门优化:

    分块策略:

    参数默认值说明
    chunkSize 512字符 每个分块的最大字符数
    flushInterval 100ms 定时刷新间隔
    maxBufferTime 5000ms 最大缓冲等待时间
    enableCompression true 是否压缩流式分块

    背压控制:当客户端处理速度跟不上发送速度时,ACP实现了自动背压机制——发送方在接收方的缓冲区达到阈值时自动暂停发送,等待接收方消费后再继续。

    6.4 连接池与复用

    ACP在网关层维护连接池,避免频繁创建和销毁连接的开销:

    # ACP连接池管理伪代码
    class ACPConnectionPool:
    """管理到各代理实例的ACP连接池"""

    def __init__(self, config):
    self.max_connections_per_agent = config.get("maxPerAgent", 10)
    self.idle_timeout_ms = config.get("idleTimeoutMs", 60000)
    self.health_check_interval_ms = config.get("healthCheckMs", 30000)
    self.pools = {} # agentId -> ConnectionPool

    async def acquire(self, agent_id: str) > Connection:
    """获取一个到指定代理的连接"""
    pool = self.pools.get(agent_id)
    if pool is None:
    pool = self._create_pool(agent_id)
    self.pools[agent_id] = pool

    conn = await pool.acquire()
    if not conn.is_healthy():
    await pool.release(conn)
    conn = await self._create_new_connection(agent_id)

    return conn

    async def release(self, agent_id: str, conn: Connection):
    """释放连接回连接池"""
    pool = self.pools.get(agent_id)
    if pool and conn.is_healthy():
    await pool.release(conn)
    else:
    await conn.close()

    async def _create_pool(self, agent_id: str):
    """为指定代理创建连接池"""
    return ConnectionPool(
    max_size=self.max_connections_per_agent,
    idle_timeout=self.idle_timeout_ms,
    health_check=self.health_check_interval_ms
    )

    连接池管理确保了高并发场景下的资源复用,同时通过健康检查机制自动剔除故障连接。max_connections_per_agent限制每个代理的最大连接数,防止单个代理占用过多资源。

    6.5 健康监控与自动恢复

    OpenClaw网关内置通道健康监控,可以自动检测和重启僵死的通道连接:

    {
    gateway: {
    channelHealthCheckMinutes: 5, // 每5分钟检查一次
    channelStaleEventThresholdMinutes: 30, // 30分钟无事件视为僵死
    channelMaxRestartsPerHour: 10 // 每小时最多重启10次
    },
    channels: {
    telegram: {
    healthMonitor: { enabled: true }, // 全局启用
    accounts: {
    alerts: {
    healthMonitor: { enabled: false } // 特定账户禁用
    }
    }
    }
    }
    }


    七、实战案例1:自定义ACP消息

    7.1 场景描述

    假设我们需要实现一个代码审查代理,当主代理完成代码生成后,自动将代码发送给审查代理进行质量检查。这就需要定义自定义的ACP消息类型。

    7.2 定义消息Schema

    首先,我们为代码审查请求定义专用的消息格式:

    {
    "$schema": "https://openclaw.ai/schemas/acp/v1",
    "type": "object",
    "title": "CodeReviewRequest",
    "properties": {
    "header": {
    "type": "object",
    "properties": {
    "messageId": { "type": "string", "format": "uuid" },
    "type": { "const": "request" },
    "source": {
    "type": "object",
    "properties": {
    "agentId": { "type": "string" },
    "sessionKey": { "type": "string" }
    }
    },
    "destination": {
    "type": "object",
    "properties": {
    "agentId": { "const": "code-reviewer" },
    "target": { "const": "agent" }
    }
    },
    "metadata": {
    "type": "object",
    "properties": {
    "idempotencyKey": { "type": "string" },
    "priority": { "enum": ["low", "normal", "high"] },
    "customType": { "const": "code-review" }
    }
    }
    }
    },
    "payload": {
    "type": "object",
    "properties": {
    "contentType": { "const": "application/x-acp-code-review" },
    "code": { "type": "string" },
    "language": { "type": "string" },
    "context": { "type": "string" },
    "rules": {
    "type": "array",
    "items": { "type": "string" }
    }
    }
    }
    }
    }

    这个Schema定义了完整的代码审查请求消息结构。customType: "code-review"在metadata中标识了这是一个自定义消息类型,contentType: "application/x-acp-code-review"则使用自定义MIME类型来区分消息负载。

    7.3 发送自定义消息

    // 通过ACP发送代码审查请求
    async function sendCodeReviewRequest(gateway, code, language, context) {
    const message = {
    header: {
    messageId: crypto.randomUUID(),
    timestamp: Date.now(),
    version: "1.0",
    type: "request",
    source: {
    agentId: "main",
    sessionKey: "agent:main:telegram:123456"
    },
    destination: {
    agentId: "code-reviewer",
    target: "agent"
    },
    metadata: {
    priority: "normal",
    idempotencyKey: `review-${Date.now()}`,
    customType: "code-review",
    ttl: 60000 // 60秒超时
    }
    },
    payload: {
    contentType: "application/x-acp-code-review",
    code: code,
    language: language,
    context: context,
    rules: [
    "security-vulnerability-check",
    "performance-anti-pattern",
    "code-style-consistency",
    "error-handling-completeness"
    ]
    }
    };

    const response = await gateway.acpSend(message);

    if (response.header.metadata.customType === "code-review") {
    return {
    approved: response.payload.approved,
    issues: response.payload.issues,
    suggestions: response.payload.suggestions,
    score: response.payload.score
    };
    }

    throw new Error("Unexpected response type");
    }

    这段代码展示了自定义ACP消息的完整发送流程:构建消息信封、填充自定义payload、通过网关发送、解析自定义响应格式。idempotencyKey确保了重试安全,ttl控制了超时行为。

    7.4 注册消息处理器

    在审查代理端注册对应的处理器:

    // 在code-reviewer代理中注册ACP消息处理器
    class CodeReviewHandler implements ACPMessageHandler {
    readonly customType = "code-review";
    readonly supportedContentTypes = [
    "application/x-acp-code-review"
    ];

    async handle(message: ACPMessage): Promise<ACPMessage> {
    const { code, language, context, rules } = message.payload;

    // 执行代码审查逻辑
    const reviewResult = await this.performReview(
    code, language, context, rules
    );

    return {
    header: {
    messageId: crypto.randomUUID(),
    correlationId: message.header.messageId,
    timestamp: Date.now(),
    version: "1.0",
    type: "response",
    source: { agentId: "code-reviewer" },
    destination: { agentId: message.header.source.agentId },
    metadata: { customType: "code-review" }
    },
    payload: {
    contentType: "application/x-acp-code-review-result",
    approved: reviewResult.approved,
    issues: reviewResult.issues,
    suggestions: reviewResult.suggestions,
    score: reviewResult.score,
    reviewedAt: new Date().toISOString()
    }
    };
    }

    private async performReview(code, lang, ctx, rules) {
    // 实际审查逻辑…
    return { approved: true, issues: [], suggestions: [], score: 95 };
    }
    }

    // 注册到ACP路由引擎
    gateway.acpRouter.register("code-review", new CodeReviewHandler());


    八、实战案例2:ACP网关部署

    8.1 场景描述

    在生产环境中部署ACP网关,需要考虑多代理路由、安全策略、健康监控和性能调优等多个维度。本案例展示一个完整的多代理ACP网关部署配置。

    8.2 完整配置

    // ~/.openclaw/openclaw.json – 生产环境ACP网关配置
    {
    // ===== 网关基础配置 =====
    gateway: {
    mode: "local",
    bind: "loopback",
    auth: {
    mode: "token",
    token: "${GATEWAY_AUTH_TOKEN}" // 从环境变量读取
    },
    channelHealthCheckMinutes: 5,
    channelStaleEventThresholdMinutes: 30,
    channelMaxRestartsPerHour: 10,
    handshakeTimeoutMs: 15000,
    push: {
    apns: {
    relay: { baseUrl: "https://ios-push-relay.openclaw.ai" }
    }
    }
    },

    // ===== 多代理配置 =====
    agents: {
    defaults: {
    workspace: "~/.openclaw/workspace",
    model: {
    primary: "anthropic/claude-sonnet-4-6",
    fallbacks: ["openai/gpt-5.4"]
    },
    skills: ["github", "weather", "search"],
    sandbox: {
    mode: "non-main",
    scope: "agent"
    }
    },
    list: [
    {
    id: "main",
    workspace: "~/.openclaw/workspace-main"
    },
    {
    id: "coding",
    workspace: "~/.openclaw/workspace-coding",
    skills: ["github", "code-review", "testing"]
    },
    {
    id: "social",
    workspace: "~/.openclaw/workspace-social",
    skills: ["social-media", "content-gen"]
    }
    ]
    },

    // ===== 路由绑定 =====
    bindings: [
    {
    agentId: "main",
    match: { channel: "whatsapp", accountId: "default" }
    },
    {
    agentId: "coding",
    match: { channel: "discord", accountId: "coding" }
    },
    {
    agentId: "social",
    match: { channel: "telegram", accountId: "social" }
    },
    {
    agentId: "main",
    match: { channel: "slack", peer: { kind: "direct", id: "U_MAIN_USER" } }
    }
    ],

    // ===== 通道配置 =====
    channels: {
    whatsapp: {
    dmPolicy: "pairing",
    allowFrom: ["+15555550123"],
    groups: { "*": { requireMention: true } },
    healthMonitor: { enabled: true }
    },
    discord: {
    dmPolicy: "pairing",
    groupPolicy: "allowlist",
    accounts: {
    default: {
    token: "${DISCORD_BOT_TOKEN_MAIN}",
    guilds: {
    "123456789012345678": {
    channels: {
    "222222222222222222": { allow: true, requireMention: false }
    }
    }
    }
    },
    coding: {
    token: "${DISCORD_BOT_TOKEN_CODING}",
    guilds: {
    "123456789012345678": {
    channels: {
    "333333333333333333": { allow: true, requireMention: false }
    }
    }
    }
    }
    }
    },
    telegram: {
    accounts: {
    default: {
    botToken: "${TELEGRAM_BOT_TOKEN_MAIN}",
    dmPolicy: "pairing"
    },
    social: {
    botToken: "${TELEGRAM_BOT_TOKEN_SOCIAL}",
    dmPolicy: "allowlist",
    allowFrom: ["tg:123456789"]
    }
    }
    }
    },

    // ===== 会话配置 =====
    session: {
    dmScope: "per-channel-peer",
    threadBindings: {
    enabled: true,
    idleHours: 24,
    maxAgeHours: 0
    },
    reset: {
    mode: "daily",
    atHour: 4,
    idleMinutes: 120
    }
    },

    // ===== 安全加固 =====
    tools: {
    profile: "messaging",
    deny: ["group:automation", "group:runtime"],
    fs: { workspaceOnly: true },
    exec: { security: "allowlist", ask: "on-miss" },
    elevated: { enabled: false }
    },

    // ===== 消息与群聊配置 =====
    messages: {
    visibleReplies: "automatic",
    groupChat: {
    visibleReplies: "message_tool",
    unmentionedInbound: "room_event"
    }
    }
    }

    这份配置展示了一个完整的生产级ACP网关部署方案。关键设计决策包括:使用环境变量存储敏感token、多代理隔离通过binding路由、每个通道独立的安全策略、以及工具权限的最小化原则。

    8.3 部署验证步骤

    # 步骤1:验证配置有效性
    openclaw doctor
    openclaw doctor –deep

    # 步骤2:检查代理绑定
    openclaw agents list –bindings

    # 步骤3:验证通道连接
    openclaw channels status –probe

    # 步骤4:运行安全审计
    openclaw security audit

    # 步骤5:启动网关
    openclaw gateway restart

    # 步骤6:验证ACP消息路由
    openclaw gateway status

    8.4 监控与告警

    #mermaid-svg-F9Dq0NVO1NROVbR4{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-F9Dq0NVO1NROVbR4 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-F9Dq0NVO1NROVbR4 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-F9Dq0NVO1NROVbR4 .error-icon{fill:#552222;}#mermaid-svg-F9Dq0NVO1NROVbR4 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-F9Dq0NVO1NROVbR4 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-F9Dq0NVO1NROVbR4 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-F9Dq0NVO1NROVbR4 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-F9Dq0NVO1NROVbR4 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-F9Dq0NVO1NROVbR4 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-F9Dq0NVO1NROVbR4 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-F9Dq0NVO1NROVbR4 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-F9Dq0NVO1NROVbR4 .marker.cross{stroke:#333333;}#mermaid-svg-F9Dq0NVO1NROVbR4 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-F9Dq0NVO1NROVbR4 p{margin:0;}#mermaid-svg-F9Dq0NVO1NROVbR4 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-F9Dq0NVO1NROVbR4 .cluster-label text{fill:#333;}#mermaid-svg-F9Dq0NVO1NROVbR4 .cluster-label span{color:#333;}#mermaid-svg-F9Dq0NVO1NROVbR4 .cluster-label span p{background-color:transparent;}#mermaid-svg-F9Dq0NVO1NROVbR4 .label text,#mermaid-svg-F9Dq0NVO1NROVbR4 span{fill:#333;color:#333;}#mermaid-svg-F9Dq0NVO1NROVbR4 .node rect,#mermaid-svg-F9Dq0NVO1NROVbR4 .node circle,#mermaid-svg-F9Dq0NVO1NROVbR4 .node ellipse,#mermaid-svg-F9Dq0NVO1NROVbR4 .node polygon,#mermaid-svg-F9Dq0NVO1NROVbR4 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-F9Dq0NVO1NROVbR4 .rough-node .label text,#mermaid-svg-F9Dq0NVO1NROVbR4 .node .label text,#mermaid-svg-F9Dq0NVO1NROVbR4 .image-shape .label,#mermaid-svg-F9Dq0NVO1NROVbR4 .icon-shape .label{text-anchor:middle;}#mermaid-svg-F9Dq0NVO1NROVbR4 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-F9Dq0NVO1NROVbR4 .rough-node .label,#mermaid-svg-F9Dq0NVO1NROVbR4 .node .label,#mermaid-svg-F9Dq0NVO1NROVbR4 .image-shape .label,#mermaid-svg-F9Dq0NVO1NROVbR4 .icon-shape .label{text-align:center;}#mermaid-svg-F9Dq0NVO1NROVbR4 .node.clickable{cursor:pointer;}#mermaid-svg-F9Dq0NVO1NROVbR4 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-F9Dq0NVO1NROVbR4 .arrowheadPath{fill:#333333;}#mermaid-svg-F9Dq0NVO1NROVbR4 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-F9Dq0NVO1NROVbR4 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-F9Dq0NVO1NROVbR4 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-F9Dq0NVO1NROVbR4 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-F9Dq0NVO1NROVbR4 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-F9Dq0NVO1NROVbR4 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-F9Dq0NVO1NROVbR4 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-F9Dq0NVO1NROVbR4 .cluster text{fill:#333;}#mermaid-svg-F9Dq0NVO1NROVbR4 .cluster span{color:#333;}#mermaid-svg-F9Dq0NVO1NROVbR4 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-F9Dq0NVO1NROVbR4 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-F9Dq0NVO1NROVbR4 rect.text{fill:none;stroke-width:0;}#mermaid-svg-F9Dq0NVO1NROVbR4 .icon-shape,#mermaid-svg-F9Dq0NVO1NROVbR4 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-F9Dq0NVO1NROVbR4 .icon-shape p,#mermaid-svg-F9Dq0NVO1NROVbR4 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-F9Dq0NVO1NROVbR4 .icon-shape .label rect,#mermaid-svg-F9Dq0NVO1NROVbR4 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-F9Dq0NVO1NROVbR4 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-F9Dq0NVO1NROVbR4 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-F9Dq0NVO1NROVbR4 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    告警规则

    监控指标

    消息吞吐量

    响应延迟P99

    ACK超时率

    重试次数

    连接池使用率

    通道健康状态

    吞吐量下降 >50%

    P99延迟 >5s

    ACK超时率 >5%

    重试率 >10%

    连接池 >80%

    通道僵死

    通知操作者


    九、实战案例3:协议扩展开发

    9.1 场景描述

    当ACP的标准消息类型无法满足业务需求时,开发者可以通过协议扩展机制添加自定义的消息类型、编解码器和中间件。本案例实现一个"工作流编排"扩展,支持多代理之间的结构化协作。

    9.2 扩展架构

    #mermaid-svg-vuO7qsL4yoJCjXU2{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-vuO7qsL4yoJCjXU2 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-vuO7qsL4yoJCjXU2 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-vuO7qsL4yoJCjXU2 .error-icon{fill:#552222;}#mermaid-svg-vuO7qsL4yoJCjXU2 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-vuO7qsL4yoJCjXU2 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-vuO7qsL4yoJCjXU2 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-vuO7qsL4yoJCjXU2 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-vuO7qsL4yoJCjXU2 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-vuO7qsL4yoJCjXU2 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-vuO7qsL4yoJCjXU2 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-vuO7qsL4yoJCjXU2 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-vuO7qsL4yoJCjXU2 .marker.cross{stroke:#333333;}#mermaid-svg-vuO7qsL4yoJCjXU2 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-vuO7qsL4yoJCjXU2 p{margin:0;}#mermaid-svg-vuO7qsL4yoJCjXU2 g.classGroup text{fill:#9370DB;stroke:none;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-vuO7qsL4yoJCjXU2 g.classGroup text .title{font-weight:bolder;}#mermaid-svg-vuO7qsL4yoJCjXU2 .cluster-label text{fill:#333;}#mermaid-svg-vuO7qsL4yoJCjXU2 .cluster-label span{color:#333;}#mermaid-svg-vuO7qsL4yoJCjXU2 .cluster-label span p{background-color:transparent;}#mermaid-svg-vuO7qsL4yoJCjXU2 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-vuO7qsL4yoJCjXU2 .cluster text{fill:#333;}#mermaid-svg-vuO7qsL4yoJCjXU2 .cluster span{color:#333;}#mermaid-svg-vuO7qsL4yoJCjXU2 .nodeLabel,#mermaid-svg-vuO7qsL4yoJCjXU2 .edgeLabel{color:#131300;}#mermaid-svg-vuO7qsL4yoJCjXU2 .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-vuO7qsL4yoJCjXU2 .label text{fill:#131300;}#mermaid-svg-vuO7qsL4yoJCjXU2 .labelBkg{background:#ECECFF;}#mermaid-svg-vuO7qsL4yoJCjXU2 .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-vuO7qsL4yoJCjXU2 .classTitle{font-weight:bolder;}#mermaid-svg-vuO7qsL4yoJCjXU2 .node rect,#mermaid-svg-vuO7qsL4yoJCjXU2 .node circle,#mermaid-svg-vuO7qsL4yoJCjXU2 .node ellipse,#mermaid-svg-vuO7qsL4yoJCjXU2 .node polygon,#mermaid-svg-vuO7qsL4yoJCjXU2 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-vuO7qsL4yoJCjXU2 .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-vuO7qsL4yoJCjXU2 g.clickable{cursor:pointer;}#mermaid-svg-vuO7qsL4yoJCjXU2 g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-vuO7qsL4yoJCjXU2 g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-vuO7qsL4yoJCjXU2 .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-vuO7qsL4yoJCjXU2 .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-vuO7qsL4yoJCjXU2 .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-vuO7qsL4yoJCjXU2 .dashed-line{stroke-dasharray:3;}#mermaid-svg-vuO7qsL4yoJCjXU2 .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-vuO7qsL4yoJCjXU2 #compositionStart,#mermaid-svg-vuO7qsL4yoJCjXU2 .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-vuO7qsL4yoJCjXU2 #compositionEnd,#mermaid-svg-vuO7qsL4yoJCjXU2 .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-vuO7qsL4yoJCjXU2 #dependencyStart,#mermaid-svg-vuO7qsL4yoJCjXU2 .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-vuO7qsL4yoJCjXU2 #dependencyStart,#mermaid-svg-vuO7qsL4yoJCjXU2 .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-vuO7qsL4yoJCjXU2 #extensionStart,#mermaid-svg-vuO7qsL4yoJCjXU2 .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-vuO7qsL4yoJCjXU2 #extensionEnd,#mermaid-svg-vuO7qsL4yoJCjXU2 .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-vuO7qsL4yoJCjXU2 #aggregationStart,#mermaid-svg-vuO7qsL4yoJCjXU2 .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-vuO7qsL4yoJCjXU2 #aggregationEnd,#mermaid-svg-vuO7qsL4yoJCjXU2 .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-vuO7qsL4yoJCjXU2 #lollipopStart,#mermaid-svg-vuO7qsL4yoJCjXU2 .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-vuO7qsL4yoJCjXU2 #lollipopEnd,#mermaid-svg-vuO7qsL4yoJCjXU2 .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-vuO7qsL4yoJCjXU2 .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-vuO7qsL4yoJCjXU2 .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-vuO7qsL4yoJCjXU2 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-vuO7qsL4yoJCjXU2 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-vuO7qsL4yoJCjXU2 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    ACPExtension

    +name: string

    +version: string

    +initialize(config)

    +register(router)

    +shutdown()

    WorkflowExtension

    +name = "workflow"

    +version = "1.0.0"

    -workflowEngine: WorkflowEngine

    -stepRegistry: Map

    +initialize(config)

    +register(router)

    +handleWorkflowRequest(message)

    +handleStepComplete(message)

    +handleWorkflowCancel(message)

    WorkflowEngine

    -activeWorkflows: Map

    -persistenceLayer: Persistence

    +createWorkflow(definition)

    +executeStep(workflowId, stepId)

    +completeStep(workflowId, stepId, result)

    +cancelWorkflow(workflowId)

    +getWorkflowStatus(workflowId)

    ACPRouter

    +registerHandler(type, handler)

    +send(message)

    +broadcast(message)

    9.3 实现扩展

    // workflow-extension.ts – 工作流编排ACP扩展
    import type { ACPExtension, ACPMessage, ACPHandler } from '@openclaw/acp';

    interface WorkflowStep {
    stepId: string;
    agentId: string;
    action: string;
    input: Record<string, unknown>;
    timeout?: number;
    retryPolicy?: { maxRetries: number; backoffMs: number };
    }

    interface WorkflowDefinition {
    workflowId: string;
    name: string;
    steps: WorkflowStep[];
    onFailure: 'stop' | 'skip' | 'retry';
    onComplete?: string; // 通知目标
    }

    interface WorkflowState {
    workflowId: string;
    status: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled';
    currentStepIndex: number;
    results: Map<string, unknown>;
    startedAt: number;
    updatedAt: number;
    error?: string;
    }

    export class WorkflowExtension implements ACPExtension {
    readonly name = 'workflow';
    readonly version = '1.0.0';

    private workflowEngine: WorkflowEngine;
    private router: ACPRouter | null = null;

    async initialize(config: Record<string, unknown>): Promise<void> {
    this.workflowEngine = new WorkflowEngine({
    maxConcurrentSteps: config.maxConcurrentSteps ?? 3,
    defaultTimeout: config.defaultTimeout ?? 60000,
    persistenceEnabled: config.persistenceEnabled ?? true,
    });
    console.log('[WorkflowExtension] Initialized with config:', config);
    }

    async register(router: ACPRouter): Promise<void> {
    this.router = router;

    // 注册自定义消息类型处理器
    router.registerHandler('workflow-start', this.handleStart.bind(this));
    router.registerHandler('workflow-step-complete', this.handleStepComplete.bind(this));
    router.registerHandler('workflow-step-failed', this.handleStepFailed.bind(this));
    router.registerHandler('workflow-cancel', this.handleCancel.bind(this));
    router.registerHandler('workflow-status', this.handleStatusQuery.bind(this));

    console.log('[WorkflowExtension] Registered 5 message handlers');
    }

    private async handleStart(message: ACPMessage): Promise<ACPMessage> {
    const definition: WorkflowDefinition = message.payload.content;

    // 验证工作流定义
    if (!definition.steps || definition.steps.length === 0) {
    return this.createErrorResponse(
    message, 'Workflow must have at least one step'
    );
    }

    // 创建并启动工作流
    const state = await this.workflowEngine.createWorkflow(definition);
    await this.executeNextStep(state);

    return {
    header: {
    messageId: crypto.randomUUID(),
    correlationId: message.header.messageId,
    timestamp: Date.now(),
    version: '1.0',
    type: 'response',
    source: { agentId: 'gateway' },
    destination: { agentId: message.header.source.agentId },
    metadata: { customType: 'workflow-started' }
    },
    payload: {
    contentType: 'application/x-acp-workflow-state',
    content: {
    workflowId: state.workflowId,
    status: state.status,
    currentStepIndex: state.currentStepIndex,
    startedAt: state.startedAt
    }
    }
    };
    }

    private async executeNextStep(state: WorkflowState): Promise<void> {
    if (!this.router) throw new Error('Router not initialized');

    const definition = await this.workflowEngine.getDefinition(state.workflowId);
    if (state.currentStepIndex >= definition.steps.length) {
    // 工作流完成
    state.status = 'completed';
    await this.workflowEngine.updateState(state);

    // 发送完成通知
    if (definition.onComplete) {
    await this.router.send({
    header: {
    messageId: crypto.randomUUID(),
    timestamp: Date.now(),
    version: '1.0',
    type: 'notification',
    source: { agentId: 'gateway' },
    destination: { target: definition.onComplete },
    metadata: { customType: 'workflow-completed' }
    },
    payload: {
    contentType: 'application/x-acp-workflow-state',
    content: {
    workflowId: state.workflowId,
    status: 'completed',
    results: Object.fromEntries(state.results)
    }
    }
    });
    }
    return;
    }

    const step = definition.steps[state.currentStepIndex];

    // 发送步骤执行请求到目标代理
    await this.router.send({
    header: {
    messageId: crypto.randomUUID(),
    timestamp: Date.now(),
    version: '1.0',
    type: 'request',
    source: { agentId: 'gateway' },
    destination: { agentId: step.agentId, target: 'agent' },
    metadata: {
    customType: 'workflow-step-execute',
    idempotencyKey: `step-${state.workflowId}${step.stepId}`,
    ttl: step.timeout ?? 60000
    }
    },
    payload: {
    contentType: 'application/x-acp-workflow-step',
    content: {
    workflowId: state.workflowId,
    stepId: step.stepId,
    action: step.action,
    input: {
    step.input,
    previousResults: Object.fromEntries(state.results)
    }
    }
    }
    });
    }

    private async handleStepComplete(message: ACPMessage): Promise<ACPMessage> {
    const { workflowId, stepId, result } = message.payload.content;
    const state = await this.workflowEngine.getState(workflowId);

    if (!state || state.status !== 'running') {
    return this.createErrorResponse(message, 'Invalid workflow state');
    }

    // 记录步骤结果
    state.results.set(stepId, result);
    state.currentStepIndex++;
    state.updatedAt = Date.now();
    await this.workflowEngine.updateState(state);

    // 继续执行下一步
    await this.executeNextStep(state);

    return { /* ACK响应 */ } as ACPMessage;
    }

    private async handleStepFailed(message: ACPMessage): Promise<ACPMessage> {
    const { workflowId, stepId, error } = message.payload.content;
    const definition = await this.workflowEngine.getDefinition(workflowId);

    switch (definition.onFailure) {
    case 'stop':
    await this.workflowEngine.updateState({
    workflowId,
    status: 'failed',
    error: `Step ${stepId} failed: ${error}`
    });
    break;
    case 'skip':
    const state = await this.workflowEngine.getState(workflowId);
    if (state) {
    state.currentStepIndex++;
    await this.workflowEngine.updateState(state);
    await this.executeNextStep(state);
    }
    break;
    case 'retry':
    // 实现重试逻辑
    await this.executeNextStep(
    await this.workflowEngine.getState(workflowId)
    );
    break;
    }

    return { /* ACK响应 */ } as ACPMessage;
    }

    private async handleCancel(message: ACPMessage): Promise<ACPMessage> {
    const { workflowId } = message.payload.content;
    await this.workflowEngine.updateState({
    workflowId,
    status: 'cancelled',
    updatedAt: Date.now()
    });
    return { /* ACK响应 */ } as ACPMessage;
    }

    private async handleStatusQuery(message: ACPMessage): Promise<ACPMessage> {
    const { workflowId } = message.payload.content;
    const state = await this.workflowEngine.getState(workflowId);
    return {
    header: {
    messageId: crypto.randomUUID(),
    correlationId: message.header.messageId,
    timestamp: Date.now(),
    version: '1.0',
    type: 'response',
    source: { agentId: 'gateway' },
    destination: { agentId: message.header.source.agentId }
    },
    payload: {
    contentType: 'application/x-acp-workflow-state',
    content: state
    }
    };
    }

    private createErrorResponse(original: ACPMessage, error: string): ACPMessage {
    return {
    header: {
    messageId: crypto.randomUUID(),
    correlationId: original.header.messageId,
    timestamp: Date.now(),
    version: '1.0',
    type: 'response',
    source: { agentId: 'gateway' },
    destination: { agentId: original.header.source.agentId }
    },
    payload: {
    contentType: 'application/json',
    content: { error }
    }
    };
    }

    async shutdown(): Promise<void> {
    // 清理活跃工作流
    await this.workflowEngine?.shutdown();
    console.log('[WorkflowExtension] Shutdown complete');
    }
    }

    这个WorkflowExtension实现了一个完整的工作流编排扩展,支持:顺序步骤执行、步骤完成/失败处理、工作流取消、状态查询、失败策略(停止/跳过/重试)、以及完成通知。通过register()方法向ACP路由引擎注册5个自定义消息处理器,实现了多代理之间的结构化协作。

    9.4 注册扩展

    在openclaw.json中注册协议扩展:

    {
    plugins: {
    entries: {
    "acp-workflow": {
    enabled: true,
    config: {
    maxConcurrentSteps: 3,
    defaultTimeout: 60000,
    persistenceEnabled: true
    }
    }
    }
    }
    }

    9.5 使用扩展

    代理之间通过工作流消息进行协作:

    // 主代理发起代码审查+测试工作流
    const workflowMessage = {
    header: {
    messageId: crypto.randomUUID(),
    timestamp: Date.now(),
    version: "1.0",
    type: "request",
    source: { agentId: "main" },
    destination: { target: "gateway" },
    metadata: { customType: "workflow-start" }
    },
    payload: {
    contentType: "application/x-acp-workflow-definition",
    content: {
    workflowId: `wf-${Date.now()}`,
    name: "code-review-and-test",
    steps: [
    {
    stepId: "review",
    agentId: "code-reviewer",
    action: "review-code",
    input: { filePath: "src/main.ts" },
    timeout: 30000
    },
    {
    stepId: "test",
    agentId: "coding",
    action: "run-tests",
    input: { testSuite: "unit" },
    timeout: 60000
    },
    {
    stepId: "deploy-check",
    agentId: "coding",
    action: "deployment-readiness",
    input: { environment: "staging" },
    timeout: 15000
    }
    ],
    onFailure: "stop",
    onComplete: "agent:main"
    }
    }
    };


    十、最佳实践与总结

    10.1 ACP协议最佳实践

    基于以上内容,总结ACP协议在实践中的关键最佳实践:

    🔧 可靠性方面:

    • 为所有关键消息设置idempotencyKey,确保重试安全
    • 合理配置ttl字段,避免消息长时间挂起
    • 使用指数退避重试策略,避免雪崩效应
    • 在关键节点进行消息持久化,支持崩溃恢复

    🔒 安全方面:

    • 使用token认证模式而非open策略
    • 设置dmPolicy: "pairing"作为默认DM策略
    • 群聊始终启用requireMention: true
    • 定期运行openclaw security audit
    • 将敏感配置(token等)存储在环境变量中,不要硬编码

    ⚡ 性能方面:

    • 大消息体启用压缩
    • 合并高频通知为批量消息
    • 长文本响应使用流式传输
    • 配置合理的连接池大小
    • 启用通道健康监控

    📐 架构方面:

    • 遵循单一信任边界原则——一个网关一个操作者
    • 使用session.dmScope: "per-channel-peer"实现多用户隔离
    • 代理间通过自定义消息类型协作,而非共享状态
    • 协议扩展优先于修改核心协议

    10.2 常见陷阱

    陷阱后果正确做法
    将sessionKey当作授权令牌 安全误判 sessionKey仅用于路由
    多个不信任用户共享一个代理 权限泄露 分离信任边界
    未设置idempotencyKey 重复处理 关键消息必设幂等键
    心跳和定时任务混淆 架构混乱 心跳=批量检查,定时=精确调度
    忽略contextVisibility 上下文泄露 按需配置上下文过滤
    直接修改核心协议 升级冲突 使用协议扩展机制

    10.3 协议演进方向

    ACP协议仍在持续演进,以下是一些值得关注的方向:

  • 多代理协商协议:代理之间自动协商任务分配和结果合并
  • 端到端加密:代理间消息的端到端加密支持
  • Schema Registry:自定义消息类型的注册中心,支持发现和验证
  • 可观测性增强:OpenTelemetry集成,分布式追踪标准化
  • 自适应流控:基于网络状况和负载动态调整传输参数
  • 10.4 参考资源

    • 📚 OpenClaw官方文档
    • 🔧 多代理路由
    • 🔒 安全指南
    • ⚙️ 配置参考
    • 🛠️ 技能系统
    • 🏗️ 功能列表

    🦞 总结:ACP协议是OpenClaw多代理架构的通信基石。通过四种消息类型(请求/响应/通知/心跳)覆盖了从实时交互到保活检测的全部场景;JSON信封格式提供了统一且可扩展的消息规范;ACK确认、指数退避重试和幂等设计构成了三层可靠性保障;Token认证、通道策略和上下文可见性控制形成了纵深安全防御;压缩、批量、流式和连接池优化确保了高性能传输。掌握ACP协议,是构建可靠、安全、高效的多代理系统的关键一步。


    赞(0)
    未经允许不得转载:171主机测评 » OpenClaw ACP模式:代理通信协议深度解析
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址