E2A(Everything-to-Agent)协议说明

实现jiuwenswarm/common/e2a/models.pyadapters.pyconstants.py__init__.py)。版本E2A_PROTOCOL_VERSION(默认 1.0)。冲突时:以 models.py 中 dataclass 字段为最终实现源,并回头修正本文。请求:E2AEnvelope;响应:E2AResponse
English../en/E2A-protocol.md


0. 文档位置与单一真源

位置 角色
docs/zh/E2A-protocol.md(本文,中文) 规范说明、易混点、示例
docs/en/E2A-protocol.md 同上,英文版
jiuwenswarm/common/e2a/models.py 请求 E2AEnvelope、响应 E2AResponse 与子结构;from_dict / to_dict
jiuwenswarm/common/e2a/constants.py E2A_SOURCE_PROTOCOL_*E2A_RESPONSE_KINDSE2A_RESPONSE_STATUS_*、ACP 方法名与 SessionUpdate 字符串(运行时以元组为准)
jiuwenswarm/common/e2a/adapters.py ACP / A2A → E2A;E2A → ACP JSON-RPC;E2AResponse → ACP / A2A 投影(见 §8)
jiuwenswarm/common/e2a/__init__.py 对外导出

1. 定位与边界

  • 经 Gateway 规范化之后发往 AgentServer 的载荷,用 E2A 表达;通道原始字段在 Gateway 入口映射到规范化字段(user_idmessage_idparams 等),不要求把整包 channel JSON 当作 E2A 主路径。
  • channel_context:可选;仅过渡或无法落入规范化字段的残余键;from_dict 可将旧 metadata 迁入以兼容历史。
  • E2A 不规定传输层(WebSocket、HTTP 等)。

2. 易混点:method 的两种含义(必读)

同一字段 method 在不同来源下含义不同,不是两套字段,也不要request_id / message_id 混用。

来源 method 典型取值 说明
网关 → AgentServer chat.sendhistory.getchat.interrupt、… 项目内 RPC 名;不在 constants.ACP_CLIENT_TO_AGENT_METHODS 中枚举
ACP 经 envelope_from_acp_jsonrpc 转入 session/promptinitialize、… 与 JSON-RPC method 一致;完整列表以 constants.ACP_CLIENT_TO_AGENT_METHODS 为准
内部特殊 null 例如心跳等无 RPC 枚举的场景

出口转 ACPenvelope_to_acp_jsonrpc_call 仅在 method == "session/prompt" 时经 merge_params_to_acp_promptparams 补全 prompt;若当前 methodchat.send,需另行在业务适配层映射为 ACP 的 session/promptparams,否则生成的 JSON-RPC 对纯 ACP 端无效。


3. 与 ACP、A2A 的关系(摘要)

维度 ACP A2A E2A(对内)
角色 JSON-RPC 会话 Task / Message / Card 统一信封;外部经适配器转入
method JSON-RPC method 抽象操作(绑定层映射) §2
扩展 _meta metadata 网关用规范化字段;a2a_metadata / acp_meta 仅互操作

4. E2AEnvelope 字段表

4.1 基础与关联

字段 类型 说明
protocol_version string 默认 1.0
request_id string / null 网关↔AgentServer 主 id(流式 chunk 对齐)
jsonrpc_id string / number / null ACP 转入时的 JSON-RPC id
correlation_id string / null 追踪链,与 request_id 分工不同
task_id / context_id string / null A2A 对齐
session_id string / null 会话
message_id string / null 平台或 A2A 消息 id;语义勿与 request_id
is_stream boolean 是否流式响应

4.2 provenanceE2AProvenance

字段 说明
source_protocol e2a / acp / a2a(常量见 E2A_SOURCE_PROTOCOL_*
converter / converted_at / details 转换组件、时间、线索(如 jsonrpc_method

手工构造且未改默认时 source_protocole2a。旧字段 binding 会迁入 provenance.details.migrated_from_binding(兼容)。

4.3 时间、身份、入口

字段 说明
timestamp 规范:RFC 3339 UTC 字符串;from_dict 可接受 float/int 纪元秒并规范化
identity_origin system / user / agent / service
channel 入口名;只写 channelfrom_dict 可读 channel_id
user_id / source_agent_id 用户或上游 Agent

timestamp(业务时刻)≠ provenance.converted_at(转入 E2A 时刻)。

4.4 methodparams、桥接

字段 说明
method §2from_dict 可读 req_method
params 唯一业务参数字典(与 JSON-RPC params 对齐):RPC 选项、用户正文、多模态块、附件等全部放在此对象内,不再使用顶层 payload(见下表约定)
ext_method method == "ext" 时的真实名
session_update_kind 桥接 ACP session/update 时,与 constants.ACP_SESSION_UPDATE_KINDS 一致
expected_output_modes A2A acceptedOutputModes

params 常用键约定(可按业务扩展,以下为与 ACP 桥接协作的推荐名):

用途
prompt 已是 ACP ContentBlock 数组时直接使用(merge_params_to_acp_prompt 不覆盖)
content_blocks 与 ACP Vec<ContentBlock> 同构的 JSON 数组;在 method==session/prompt 且无 prompt 时可映射为 prompt
text / content / query 纯文本用户输入;无 prompt/content_blocks 时取第一个非空字符串生成单条 text block
files / attachments 附件列表,元素形状见 E2AFileRefuri 必填)
其他 modepage_idx、心跳文案等,随网关 RPC 自由扩展

4.5 auth、扩展槽

字段 说明
auth E2AAuth:凭据引用等
channel_context 可选溢出 / 旧 metadata 兼容,见 §1
a2a_metadata A2A 互操作(与适配器参数名 metadata 对应)
acp_meta merge_params_to_acp_prompt 中合并入 params._meta(见 §7)

5. 子结构(摘要)

  • E2AFileRef:附件元素推荐形状(uri 必填;namemime_typesize_meta 可选);通常出现在 params.filesparams.attachments 数组中。
  • E2AAuthmethod_idbearer_tokenapi_key_refcredential_refextra_headers_meta

6. constants.py 中的 ACP 字符串(桥接参考)

以下用于 ACP 桥接或文档对照,运行时jiuwenswarm.common.e2a.constants 中元组为准:

  • ACP_CLIENT_TO_AGENT_METHODS:客户端 → Agent 的 JSON-RPC method 名
  • ACP_AGENT_TO_CLIENT_METHODSACP_NOTIFICATION_NAMES:下行 / 通知
  • ACP_SESSION_UPDATE_KINDSsession_update_kind 取值
  • E2A_RESPONSE_KINDSE2A_RESPONSE_STATUS_*E2A_A2A_STREAM_BRANCHES:响应 response_kind / status / a2a.stream_event.branch(见 §12

网关 RPCchat.send 等)由项目自行约定,放在上述元组中。


7. merge_params_to_acp_prompt

Python:jiuwenswarm.common.e2a.merge_params_to_acp_prompt(envelope)

当且仅当 method == "session/prompt" 时:

  1. params 已有 prompt,不覆盖。
  2. 否则若 params.content_blocks 为非空数组,用作 prompt
  3. 否则若 params.textparams.contentparams.query 中有非空字符串,生成单条 { "type": "text", "text": "..." } 作为 prompt
  4. 若信封有 session_idparamssession_id,则写入。
  5. envelope.acp_meta 合并进 params._meta

其他 method 直接返回 params 的浅拷贝。


8. 适配器(adapters.py

函数 作用
envelope_from_acp_jsonrpc ACP JSON-RPC → E2A,provenance.source_protocol=acpmethod 为 ACP method
envelope_from_a2a_send_message A2A SendMessage 语义 → E2A;参数 metadata 写入 a2a_metadata(A2A 专用,不是网关 metadata
envelope_to_acp_jsonrpc_call E2A → JSON-RPC 描述;不修改 provenance
e2a_response_to_acp_jsonrpc_response E2AResponse → 单条 JSON-RPC 响应对象(resulterror);不适用时返回 None
e2a_response_to_a2a_stream_payload E2AResponsea2a.stream_event)→ A2A StreamResponse 形 JSON(四选一);不适用时返回 None

9. 序列化与兼容键

  • E2AEnvelope.to_dict() / from_dict(data)
  • 只读兼容channel_idchannelreq_methodmethodmetadatachannel_context(仅当 channel_context 为空),bindingprovenance(见上)
  • 可选:若 JSON 顶层仍存在旧键 payload(对象),其键会合并进 params(不覆盖 params 已有键),便于极少数遗留载荷;新协议不应再发顶层 payload

10. 替代 AgentRequest 的字段对应

AgentRequest E2AEnvelope
request_id request_id
channel_id channel
session_id session_id
req_method method
params params
is_stream is_stream
timestamp(float) timestamp(字符串,自动规范化)
metadata 目标态:映射进规范化字段;兼容:from_dictchannel_context

11. 示例(JSON 形状)

11.1 网关规范化后 → AgentServer(聊天,流式)

{
  "protocol_version": "1.0",
  "request_id": "req_abc_01",
  "session_id": "sess_xyz",
  "channel": "web",
  "method": "chat.send",
  "is_stream": true,
  "timestamp": "2026-03-28T12:00:00+00:00",
  "identity_origin": "user",
  "user_id": "u_001",
  "params": {
    "content": "你好",
    "mode": "plan",
    "query": "你好"
  },
  "provenance": {
    "source_protocol": "e2a"
  }
}

channel_context 可省略(空对象序列化时可不出现或 {})。

11.2 旧 AgentRequest 形状(from_dict 可读)

{
  "request_id": "req_legacy_1",
  "channel_id": "feishu",
  "session_id": "sess_feishu_1",
  "req_method": "chat.send",
  "is_stream": true,
  "timestamp": 1774524781.15,
  "params": {
    "content": "查桌面文件",
    "mode": "plan",
    "query": "查桌面文件"
  },
  "metadata": {
    "feishu_open_id": "ou_xxx",
    "message_id": "om_xxx"
  }
}

解析后:channel=feishumethod=chat.sendtimestamp 变为 RFC 3339 字符串;若未单独映射,metadata 会进入 channel_context(迁移期)。目标态应在 Gateway 把 feishu_open_id 等写入 user_id / message_id 等,并尽量清空 channel_context

11.3 ACP 转入(逻辑示例,由 envelope_from_acp_jsonrpc 构造)

  • method = "session/prompt"
  • jsonrpc_id = 42
  • params = { "session_id": "s1", "prompt": [...] }
  • provenance.source_protocol = "acp"detailsjsonrpc_method

11.4 导出 ACP JSON-RPC(envelope_to_acp_jsonrpc_call 输出形状)

{
  "jsonrpc": "2.0",
  "id": 42,
  "method": "session/prompt",
  "params": {
    "session_id": "s1",
    "prompt": [{"type": "text", "text": "hi"}]
  }
}

仅当信封 method == "session/prompt"params 中尚无 prompt 时,merge_params_to_acp_prompt 才会根据 content_blockstext/content/query 补全 prompt

11.5 session/prompt + 多模态(全在 params

{
  "method": "session/prompt",
  "session_id": "s1",
  "jsonrpc_id": 1,
  "params": {
    "content_blocks": [
      {"type": "text", "text": "请描述这张图"},
      {"type": "image", "mime_type": "image/png", "data": "<base64 omitted>"}
    ]
  }
}

merge_params_to_acp_prompt 后,params 会多出与 content_blocks 等价的 prompt(若原本无 prompt)。


12. E2A 响应协议(E2AResponse

Agent → 网关 → 客户端的每一条出站记录(含流式多帧)使用 E2AResponse 表达,与 E2AEnvelope 字段命名对称(snake_case、RFC 3339 timestamp、复用 E2AProvenance)。

12.1 三层结构

层级 名称 说明
L1 响应信封 关联 id、顺序、流结束、状态、出处
L2 判别载荷 response_kind + body(由 kind 约束)
L3 projections(可选) acp / a2a 键下存放已组装的对外协议 JSON,仅辅助;业务解析不得依赖 L3 作为唯一真源

12.2 is_finalstatus

  • is_finaltrue 表示同一 request_id不再会有后续 E2AResponse 记录。仅表示传输序列结束,表示业务一定成功(失败帧也可 is_final: true)。
  • status:取 constants.E2A_RESPONSE_STATUS_*succeeded / failed / in_progress)。当 status == failed 时,response_kind 应为 e2a.error(若同时使用 acp.jsonrpc_error 作投影,以本文 §12.6 为准)。

12.3 不变量(规范)

  • 同一 request_idsequence0 起严格递增(0, 1, 2, …)。
  • 正常结束:恰好一条记录的 is_finaltrue
  • 异常(对端断连、进程崩溃):可能无法发送 is_final: true;实现上应通过超时与连接状态补偿,本文不强制「必须补发结束帧」。

12.4 L1:信封字段表

字段 类型 必填 说明
protocol_version string 与请求一致;默认 1.0
response_id string 本记录唯一 id(建议 UUID)
request_id string 回显请求;流式 chunk 对齐
sequence integer request_id 下从 0 递增
is_final boolean 最后一帧为 true
status string succeeded / failed / in_progress
response_kind string §12.5;运行时以 constants.E2A_RESPONSE_KINDS 为准
timestamp string RFC 3339 UTC;本记录生成时刻
provenance object E2AProvenance(与请求相同结构)
body object L2 载荷
jsonrpc_id string / number / null 回显 ACP JSON-RPC id
correlation_id string 分布式追踪
task_id / context_id / session_id / message_id string 与请求信封同义
is_stream boolean 是否对应流式请求(回显或推断)
identity_origin string 默认倾向 agent(响应来自 Agent)
channel / user_id / source_agent_id 同请求 回显
method string / null 回显请求的 method(含心跳 null
metadata object 通道/业务自定义键值字典;与旧版 AgentResponse.metadata 对齐;协议转换失败时可临时写入兜底。AgentServer→Gateway WebSocket 整包旧 JSON 使用 constants.E2A_WIRE_LEGACY_AGENT_RESPONSE_KEY / E2A_WIRE_LEGACY_AGENT_CHUNK_KEY_e2a_wire_legacy_*),与业务自定义键建议用通道前缀区分
projections object L3:acpa2a 可选键,值为任意 JSON
channel_context / a2a_metadata / acp_meta object 与请求对称的溢出槽;注意:与 metadata 分工不同——a2a_metadata / acp_meta 仅 ACP/A2A 互操作,metadata 为通用业务与兜底

12.5 L2:response_kindbody 要点

运行时以 constants.E2A_RESPONSE_KINDS 为准;bodydict,下列为规范性说明。

response_kind body 要点
e2a.complete result:业务结果对象(与请求的 params 哲学对称,避免无关键散落 L1)
e2a.chunk delta_kindtext / reasoning / tool / customdelta:字符串或结构化对象;可选 mime_type
e2a.error codemessagedetails;可选 external{ "protocol": "acp"|"a2a", "type": string, "payload": object }
acp.session_update 贴近 ACP session/update 通知负载;sessionUpdate 等字段与 ACP_SESSION_UPDATE_KINDS 对齐
acp.prompt_result 对应 session/prompt 的 JSON-RPC result(如 stop_reason、可选 usage_meta
acp.jsonrpc_error 等价 JSON-RPC error 语义:codemessage、可选 data(与 e2a.error 并存时:内核e2a.error 表达失败;acp.jsonrpc_error 可作 L3 或专用 kind 仅供 ACP 边界)
a2a.task A2A Task 形对象;JSON 键名与官方 proto 的 JSON 映射一致(snake_case,与 spec/a2a.proto 字段对应)
a2a.message A2A Message 形对象(同上)
a2a.stream_event branchtask / message / status_update / artifact_update(常量 E2A_A2A_STREAM_BRANCHES);payload:对应分支对象
ext ext_method + params(与请求 ext 对称)

推荐策略:网关 / AgentServer 内部优先使用 e2a.complete / e2a.chunk / e2a.error;在出口适配层生成 acp.* / a2a.* 或写入 projections

12.6 与 ACP / A2A 的映射(摘要)

  • ACP:JSON-RPC 响应由 jsonrpc_id + acp.prompt_resultresult)或 e2a.error / acp.jsonrpc_errorerror)映射;acp.session_update 对应 session/update 通知的 params。详见 e2a_response_to_acp_jsonrpc_response(§8)。
  • A2Aa2a.stream_eventStreamResponse 四分支一一对应;e2a_response_to_a2a_stream_payload 产出单事件 JSON。 a2a.task / a2a.message 对应 SendMessage 两种返回。

12.7 日志建议(如 E:\logs

  • 一行一条完整 E2AResponse JSON,便于检索与按 sequence 重放。
  • 每条至少含 request_idresponse_idsequenceis_finalresponse_kindtimestamp
  • 勿记录 bearer_token、完整模型输出中的敏感内容;大 body 可将来扩展 body_ref(本版未定义)。

12.8 示例:流式两帧 + 结束帧

{"protocol_version":"1.0","response_id":"r1","request_id":"req_1","sequence":0,"is_final":false,"status":"in_progress","response_kind":"e2a.chunk","timestamp":"2026-03-29T12:00:00+00:00","provenance":{"source_protocol":"e2a"},"body":{"delta_kind":"text","delta":"你好"}}
{"protocol_version":"1.0","response_id":"r2","request_id":"req_1","sequence":1,"is_final":false,"status":"in_progress","response_kind":"e2a.chunk","timestamp":"2026-03-29T12:00:01+00:00","provenance":{"source_protocol":"e2a"},"body":{"delta_kind":"text","delta":"世界"}}
{"protocol_version":"1.0","response_id":"r3","request_id":"req_1","sequence":2,"is_final":true,"status":"succeeded","response_kind":"e2a.complete","timestamp":"2026-03-29T12:00:02+00:00","provenance":{"source_protocol":"e2a"},"body":{"result":{"content":"你好世界"}}}

E2A-AgentRequest-log-migration.md 中的请求日志收编可对照:响应侧将来可同样约定统一前缀与单行 JSON。


13. 安全建议

  • 优先 credential_ref / api_key_ref,由网关换票;日志勿完整打印 bearer_token
  • 跨域使用 TLS 与最小权限。

本文与 jiuwenswarm/common/e2a/models.pyE2AEnvelopeE2AResponse)同步维护。