跳到正文

交付与集成 ​

一句话:交付是入口段把外部身份翻译成平台主体、出口段把对象变成送达回执,协议按“对面是谁”而非部署位置选面;代价是每一跳都要重校验签名、每一次重投都要幂等键兜住,且 Agent 只能表达产物意图,不碰渠道凭据、不决定收件人——越过这条线,提示注入就变成发送事故。

定位:一条消息从外部世界到达 Agent、Agent 产物回到外部世界的完整通路。协议、入口、产出物与执行体接入四件事,本质是同一条链路的四段:入口段负责“外部身份 → 平台主体”的翻译,出口段负责“对象 → 送达回执”的闭环。

本篇由原《协议、渠道与集成地图》《个人助理 Hub》《产出物与 IM 交付》《OpenCode 执行体接入与运行时资源池》四篇合并而成。

为什么需要 ​

没有交付链路时,平台的真实形态是“Agent 只能生成、不能送达”:任务在沙箱里跑完,报告躺在对象存储里没人知道;用户在飞书里发了消息,Agent 回复到了错误的另一个人的会话;编码任务改完了代码,diff 停在容器里,重启即丢。具体会踩的坑每一段都有:

  • 入口段:Cookie 跨不出域名,用户被反复要求登录;内网服务信任 X-User-Id 请求头,任何人能访问内网就能冒充任何人;渠道重推同一条消息没有幂等键,同一个任务执行两次;
  • 协议段:Agent 同时开 A2A 与 Chat Completions,鉴权只配了一边,等于把最弱暴露面复制到所有入口;
  • 出口段:把文件 base64 塞进消息撑爆上下文;把对象存储内网 URL 发给用户打不开;把 Bot Token 注入上下文,提示注入就能让它往任意群发消息;发送后没有回执,任务验收无据;
  • 执行体段:编码工具不经平台直接开放给用户,读库、执行命令、写仓库全部失控;一个编码任务吃光节点资源,热池没有水位与残留清理,上一个用户的凭据残留给下一个用户。

所以这条链路不是“功能清单”,而是每一段的身份翻译、投递语义与失败责任的接缝清单。四段拼起来,才是“外部世界可进、产出物可出”的平台。

账号与命名空间分层见 02,入口网关见 03,沙箱资源模型见 21,任务闭环与完成证据见 22,四通信面的完整推导见 24。


§一 全链路组件图 ​

每一段的协议、关键身份字段与失败语义:

段协议关键身份字段失败语义
外部 → HigressIM 回调(验签)、HTTPS 页面、A2A JSON-RPC/HTTPexternal_user_id、external_message_id、渠道回调签名验签失败直接拒绝,不做“宽容处理”;渠道侧自行重试,平台靠幂等键去重
Higress → Hub Router内部 HTTP(同源代理)session_id → subject 解析结果,网关注入 X-Auth-Subject + HMAC会话查不到 fail-closed 返回 401,不降级为匿名
Hub Router → agentscope-servicemTLS + HMAC 签名头subject、tenant_id、instance_id无实例触发 Provisioner 懒创建返回 202;缓存未命中不“随便找一台”,只能创建或 503
agentscope-service → 内核平台内部任务语义task_id、session_id、attempt、lease_owner/lease_epoch执行失败进入固定等待原因(waiting_tool/failed),账本不丢状态
内核 → 产出物服务流式写入 + 登记接口artifact_id、owner_subject、checksum登记失败进 Outbox 补偿;未过扫描不得进入 deliverable
产出物服务 → 渠道适配器事件投递(至少一次)幂等键 artifact_id + channel + recipient发送失败退避重试,仍失败降级为链接,再失败在会话里留“交付失败”事件
用户 → 下载网关HTTPS + 一次性 ticketticket_id、当前主体ticket 过期或非本人拒绝;可由本人按主体重新签发

三个边界判断先说清楚:流结束不等于任务结束(SSE 断开不取消任务);交付失败不等于任务失败(产物已生成,交付是最后一公里,可重试不回滚任务);能连到端口不等于有权调用(内网不是信任边界)。

代码锚点:渠道适配器对应 agentscope-java/agentscope-extensions/agentscope-extensions-channel 下的 channel-dingtalk、channel-feishu、channel-wecom、channel-weixin、channel-github、channel-gitlab 与 channel-common 模块;协议通道对应 agentscope-extensions-protocol 下的 a2a、agui、agent-protocol、chat-completions-web 模块。模块化是能力,不是治理:装上了不等于配对了,每接一个扩展就多一个数据面、一套凭据、一条删除路径。


§二 协议通道 ​

四通信面:按双方身份选协议,不按部署位置 ​

协议清单越列越长,但选型只需要先回答一个问题:通信双方是谁。按部署位置分类(进程内/进程间/跨组织)会把工具调用、任务协作和内部消息混成一类;按双方身份分四个面,每个面再选语义档(S1 请求响应 / S2 请求域流式 / S3 任务句柄 / S4 持久通道)。

通信面双方首选协议默认语义平台落点常见错配
人机面用户/前端 ↔ AgentAG-UIS2 流式,长任务升 S3渠道/前端 + SSE把流断开当任务取消
能力面Agent ↔ 工具/数据MCPS1,长工具升 S3Toolkit + MCP Gateway把工具调用塞进消息总线
协作面Agent ↔ AgentA2AS3 任务句柄A2A 端点 + 网关用同步 RPC 调长任务
内构面平台服务端组件无标准S4 会话通道,任务认领 S3LiteTopic + 事件流把 LiteTopic 当通用事件总线

选型决策树(只问“对面是谁”,问完再按任务长短挑语义档):

三条落点:同一对双方只选一个面,同时开 A2A 与 Chat Completions 而只配一边鉴权,等于把最弱暴露面复制到所有入口;语义档升级只由时长驱动,不由发起方身份驱动;兼容层是出口不是入口,主入口永远走人机面。

A2A:协作面,预留互操作不作生产依赖 ​

A2A 的价值是跨框架、跨组织协作,Agent Card 通常暴露在 /.well-known/agent-card.json。能被发现不等于能被调用:Agent Card 是描述不是授权,跨组织调用的身份、配额与审计仍在网关做。

成熟度标注:A2A(Google 2025-04 初版)处于规范演进期,多实现互操作需按 S1–S4 档位逐个联调验证,不能按文档字面承诺对接。实现位于 agentscope-extensions-protocol/agentscope-extensions-a2a。平台姿态:预留互操作能力,但不把它放上生产关键路径;协议版本钉死(记录所验证的 schema 版本),升级协议等于回归联调。

AG-UI:展示层事件流,不是状态权威 ​

AG-UI 适合自建前端,事件粒度(文本块、工具调用、状态变更)比自然语言更可控,是任务状态向界面投影视图的通道。两条硬约束:事件流不是状态的权威——服务端会话与账本才是,前端重连后要能从服务端重建视图;每条事件要有续传点(after_seq / Last-Event-ID),断线重放不能造成重复渲染或重复执行。v2 文档中无独立生产实现证据,落地同样以实测为准。

MCP Streamable HTTP:能力面工具调用 ​

能力面走 MCP,默认 S1 请求响应;执行时间长的工具(构建、跑测试)升级为 S3 任务句柄,用句柄轮询而不是把连接挂住。MCP Server 的鉴权与配额挂在网关或 Toolkit 层,不让每个消费方各自直连。

Chat Completions 兼容层:临时兼容,不做主入口 ​

Chat Completions 兼容层适合“把 Agent 临时塞进只认模型接口的系统”。它的代价是隐藏工具循环、会话恢复与审批——这些恰恰是平台要审计的东西。不要用它承载需要审计的长任务;每个协议端点要有独立的鉴权、配额与审计配置。

六契约要素:缺一项不能上生产 ​

选完面与档,每一条通信通道还要写清六个契约要素:

要素必填内容平台字段
标识会话、任务、轮次、消息各自身份与从属session_id/task_id/task_run_id/turn_id/message_id
顺序哪个作用域内保序ordering_scope = session / task / none
投递保证至少一次/至多一次/恰好一次 + 去重键idempotency_key,默认至少一次
续传点断连/换节点从哪里继续after_seq / Last-Event-ID
生命周期通道与任务何时创建、过期、销毁TTL、deadline_at
背压过载时如何减速限流、通道暂停、队列水位、Worker 槽位

渠道接入是这个模型最具体的应用:钉钉、飞书、企微、GitHub、GitLab 适配器带来的是外部身份与外部消息模型,不是平台身份。接入必须做三张映射——external_user_id → platform subject(同一个人在私聊、群聊、网页端落到同一主体)、external_message_id → idempotency_key(渠道重推是常态,没有幂等键就执行两次)、chat/group → session scope(群会话不带个人记忆,个人会话不读群上下文)。回调要验签、限制重放窗口、轮换密钥;“机器人被移出群”要映射成会话停用,否则任务会继续往一个不存在的会话里写。

装配顺序从出口往里:provider → store → memory/toolkit → agent → 协议 → 渠道,每装一层跑一次最小闭环,确认没有偷偷建立第二个权威。验收看四条:同一用户跨渠道是否同一主体、重推是否只执行一次、关掉一个 provider 能否回退、每个能力是否只有一个权威。


§三 个人助理 Hub ​

“每人一个助理、全公司一个域名”的形态下,所有流量必须同源代理。前提判断:单域名等于同源反向代理,不是跳转——一旦改成“登录后 302 到实例域名”,Cookie 出不了域,后面所有身份方案都要重做。用户始终停留在 assistant.example.com,Hub 在服务端把请求代理到个人实例,页面、SSE、WebSocket 都走同一个源;确实要嵌入别的源时,只能用一次性 ticket 换短时令牌,不能把长期 Cookie 放进 URL。

项约定原因
Cookie 名__Host-sid__Host- 前缀强制 Secure、Path=/、无 Domain
属性HttpOnly; Secure; SameSite=Lax防脚本读取、防跨站携带
内容随机会话 ID,不含用户信息用户信息以服务端会话表为准
存储Redis / 数据库会话表可撤销、可轮换、可多设备管理
有效期空闲超时 + 绝对超时降低 Cookie 泄漏后的窗口
登录/登出登录轮换会话 ID;登出服务端删会话 + 清 Cookie防会话固定;只清浏览器端等于没登出

状态变更请求(POST/PUT/DELETE)配 CSRF Token 或双提交 Cookie;SSE/GET 不需要但要来源校验。不把 JWT 放 localStorage:它无法被服务端撤销,也不解决 CSRF。Cookie 只证明“浏览器登录过 Hub”,不证明“这个请求属于这个人”——身份必须在每一跳重新校验。

路由:subject → instance 三种形态 ​

Hub 拿到会话后先解析 session → subject,再用 subject 查实例:

形态路由键隔离成本适用
共享池(tenant, subject) 作为状态命名空间逻辑隔离低大多数个人助理
专属实例subject → instance_id → endpoint进程/文件隔离高强隔离、个人镜像
混合默认共享,白名单专属分级中高管、敏感岗位

关键认知:“每人一个助理”不等于“每人一个 Pod”。“个人”体现在工作区、记忆、凭据与配额四个命名空间,不体现在计算单元;共享池用 IsolationScope.USER 加按用户切分的存储前缀即可满足绝大多数场景。路由表 Redis 做缓存、数据库或 Nacos 做权威,字段至少含 subject、instance_id、endpoint、version、status、updated_at;缓存未命中只能触发创建或返回 503。

身份透传:剥离、签名、mTLS、每跳重校验 ​

四件事缺一不可:

  1. 剥离:入口处删除客户端自带的 X-User-Id、X-Tenant、X-Instance 等头,只保留网关注入的版本;
  2. 签名:注入的身份头带 HMAC(subject|tenant|timestamp|nonce),实例验签并拒绝过期请求,密钥轮换、带时间戳与 nonce、拒绝重放;
  3. mTLS:Router 到实例、实例到模型网关都校验客户端证书,端口被内网扫到、没有证书也调不通;
  4. 重校验:实例不因为“来自内网”就信任请求,每次检查 owner 与租户是否匹配。

Provisioner:懒创建与生命周期 ​

首次登录时创建用户记录、工作区前缀、Vault 路径与记忆作用域;需要专属实例的再异步创建,期间返回 202“正在准备”。闲置回收:专属实例超过阈值无请求挂起、下次请求唤醒;共享池按并发扩缩容,不按人扩容。离职:IdP 停用后网关立即拒绝,吊销凭据、导出并清理工作区与记忆,审计单独保留。迁移:状态在外部存储,实例漂移不影响用户,Router 只认 subject 不认 IP。会话存储是高可用单点,Redis 故障时网关 fail-closed。

与数字分身产品线的边界 ​

数字分身产品线(多实例路由与网关访问控制、实例生命周期、个人助手集群产品方案)已独立成栏(08 数字分身线)。本篇只保留它与 agentscope-service 平台层的两处接缝:

  • 身份透传契约:网关完成企业身份 → 实例访问钥匙的转换后,平台侧调用实例走签名头 + mTLS,与上文四件事同构;
  • 执行体注册接口:个人实例可注册为 agentscope-service 的被管执行体(External/Hosted),诉求经平台转发到本人实例——调度、审批与审计的权威在平台控制面,不在实例。

实例内部怎么认证、怎么拉起、卷怎么挂,属数字分身线事实,本栏不复述。

判断方案成立看五条:跨账号不串数据、改头换不了身份、直连实例被拒、登出立即失效、升级不用重新登录。


§四 产出物与交付 ​

原则:Agent 产出的是对象,不是消息。交付是“对象落库 + 短时凭证 + 渠道适配 + 回执”的独立可审计服务,不在 Agent 循环里。

生命周期:created → scanned → deliverable → delivered ​

产出物登记接口是唯一的写入口,所有工具产出都走它:

字段作用缺失的后果
artifact_id全局唯一、幂等键重试会重复发送
owner_subject归属主体无法做 ACL 与审计
session_id / task_id回溯来源用户追问时找不到上下文
mime / size / checksum选交付方式、校验完整性大文件直传失败、内容被替换
storage_uri对象存储位置(tenant/subject 前缀)把二进制塞进数据库
acl可见范围(个人/会话/租户)群聊误发
retention过期时间永久链接泄漏
statuscreated / scanned / deliverable / delivered / expired无法重试与对账

生命周期单向:创建 → 安全扫描 → 可交付 → 已交付 → 过期或删除,任何一步失败都不能跳过去直接发,尤其是扫描。扫描是交付前的最后一道闸:DLP(密钥、个人信息、敏感数据命中策略转审批,不直接发)、恶意软件扫描、内容合规三类,全部过了才置 scanned → deliverable。状态迁移事件走 Outbox 表与业务写库同事务落盘,再异步投递给渠道适配器——保证“登记成功但事件丢失”不会发生,消费侧至少一次 + 去重键收敛为有效一次。

交付形态:三选一,按约束选 ​

形态做法适合限制
媒体直传先传 IM 媒体接口拿 media_id,再发文件/图片消息小文件、图片、聊天内预览各 IM 有大小与类型限制,上传与发送是两步
短时链接一次性 ticket,发含链接的消息大文件、需权限校验、需审计必须由下载网关鉴权,不能直连对象存储
交互卡片卡片展示摘要 + 按钮(查看/下载/确认)报告、审批、需后续动作要维护卡片状态与更新接口

选择顺序:能预览的小文件直传,大文件走链接,需要动作的走卡片。不为统一全走链接(用户多点一次转化就掉一截),不为体验全直传(大文件失败且无法撤销)。异步规则:收到请求先回“处理中”卡片,完成后更新卡片或补发结果消息并带会话深链(/s/{session}?artifact={id});IM 发送失败退避重试 → 降级链接 → 仍失败在会话里留“交付失败”事件。

渠道适配器:统一四件事,差异进能力表 ​

每个 IM 一个适配器,内部统一 upload、send、update(卡片)、receipt 四个动作;飞书、企微、钉钉的差异用能力表表达,不用 if-else 糊在业务里:

能力说明
最大文件超过就走链接
支持类型图片/文件/语音/视频分别处理
卡片更新是否支持完成后原地更新
频率限制每 Bot、每群、每用户的 RPM
回执粒度是否返回已读/下载事件

凭据按租户与 Bot 隔离存 Vault,适配器启动时取,不进入 Agent 上下文。发送幂等键固定为 artifact_id + channel + recipient,IM 超时后先查回执再重试,同一产物重试只发一条。回执回写任务账本,作为 completion_evidence 的一部分:artifact_id + checksum + message_id + recipient + status;交付完成不等于任务验收。

下载网关:一次性 ticket + TTL + 二次鉴权 ​

ticket 用随机 ID + 服务端记录(artifact、subject、过期时间、一次性标记),TTL 五到十五分钟;比把签名直接放 URL 里更容易撤销与审计。三条硬规则:

  1. 收件人来自会话主体:session → subject → IM 绑定;模型输出的“发给张三”只能作为意图,不能作为地址;
  2. 群聊需要显式确认:默认只发个人会话;发群必须用户确认,并检查 artifact 的 ACL 是否允许该群;
  3. 下载要二次鉴权:ticket 只证明“这条链接是给这个产物的”,网关还要校验当前主体是否有权读,不能只验签名就放行。

对象存储预签名 URL 只用于服务端内部回源,用户拿到的永远不是对象存储直链。Bot 被移出群、用户离职、应用被停用,渠道标记不可用并撤销未过期 ticket。保留期分级:临时产物七天、交付物三十天、审计类单独保留,到期前可续期。文档类产物加接收人水印,记录谁在什么时候下载了哪个版本。

Agent 的交付能力边界 ​

Agent 只负责产出与登记意图,不碰 IM 凭据、不决定收件人、不选择交付形态的最终执行。渠道适配器只执行“把 artifact X 发给 subject Y”这一件事。这条边界同时是提示注入的最后防线:即使模型被诱导“发给财务群”,收件人仍由服务端会话决定,群发还要人工确认。

判断交付成立看四条:重试不重复发送、非本人打不开、过期链接被拒、每次下载都能追到人和版本。


§五 编码执行体接入 ​

OpenCode 不是“再写一个编码 Agent”的替代品,而是编码执行引擎:opencode run(headless 非交互,--format json 机器可读)、opencode acp(ACP 协议子进程,JSON-RPC over stdio)、opencode serve(服务态)三个可程序化接入面,分别对应平台的三种编排者。企业缺的不是执行引擎,是把现成编码 Harness 的接入、管控、资源治理做好。

两条管控路线:同一执行体,不同控制主体 ​

路线 A:QwenPaw ACP 拉起路线 B:agentscope-service 代理管控
控制主体人(用户直接对话/指导 opencode)平台编码智能体(代替人做控制与编排)
承载形态QwenPaw 个人实例经 delegate_external_agent 拉起 ACP 子进程agentscope-service Hosted/BYO 接入模式,编码 Harness 注册为被管执行体
适用场景个人助理、人工引导式编码、交互调试业务迭代需求下发、多 Agent 协作、无人值守交付
资源隔离个人实例即隔离单元(用户级)平台沙箱:执行按 session、数据按 user、配额按 tenant_id + user_id
状态实机闭环已验证(2026-10-25:QwenPaw 容器经 ACP 拉起 opencode 完成编码交付)设计定稿,待 Hosted/BYO 实证

两条路线不是二选一:编码智能体上线后路线 B 成为主形态,路线 A 退化为“人直接介入编码会话”的旁路。QwenPaw 是独立的执行体产品线,本篇只保留平台层接缝——ACP 会话边界、执行体注册接口与管控语义;实例内部实现细节见数字分身线。

运行时资源池:与 21 篇沙箱模型共用一套机制 ​

资源池复用沙箱的“四把钥匙”,不另造隔离模型:执行隔离(每任务/会话一个 opencode 执行单元,IsolationScope.SESSION)、数据隔离(工作区按 user_id 挂用户卷,任务产物按 task_id 落对象存储)、配额归属(tenant_id + user_id:并发任务数、CPU/内存、出网、月度时长)、网络与凭据(git 用临时 deploy key、模型 key 走 Vault 短时注入、出网默认拒绝)。

池化机制五件事:

  • 分池:按镜像/规格/运行时类别分(opencode runner / test runner / deploy runner),不按用户预分配;用户凭据 claim 之后注入;
  • WarmPool:低于水位补货、高于水位回收,命中率与 P95 冷启动延迟一起看——命中率低时热池比冷启动更贵,热池闲置也是成本;
  • Claim 原子性:CAS + fencing token(claimed_by + lease_epoch 租约机制,每次接管 epoch 加一),避免两请求领同一执行体、迟到清理误杀已复用实例;
  • 生命周期:PENDING → 配额原子预留/领取热池 → READY(镜像 + AGENTS.md + 工作区挂载)→ RUNNING → IDLE/SNAPSHOTTING → STOPPED(快照 + 产物登记)→ 回收(杀进程、清环境变量、删临时目录、卸载卷、验证无凭据残留);
  • 开发测试分容器:code-runner(opencode 写码 + 轻自检)与 test-runner(干净镜像、只读 repo、收窄网络,全量验收)分离,防止开发环境依赖污染测试可复现性。

编码任务专属控制点:验收前置(任务携带可执行验收,结束必须产出测试报告,不能只回“大概完成”);审批收敛两点(变更入库 PR 合并、上线部署走人工审批 HITL,内部执行全自动);失败回炉(测试失败带明细回炉 code-runner,重试设上限如 3 轮,超限上报主管 Agent 决策)。

认证矩阵:两层钥匙 ​

执行体实例有两层钥匙,对应两个调用面:

层钥匙谁持有用途
运行时边界层runtime token(每实例独立随机,平台 Vault 保管),HTTP 头精确匹配仅平台平台 ↔ 实例服务间通道:网关/agentscope-service/定时任务转发时注入,直接授权、无需模拟登录
用户身份层实例主人 JWT(Bearer)实例主人经平台分发 / 网关代持实例侧确认“调用主体 = 绑定该实例的员工”

矩阵结论由实机验证支撑:无 token 一律 401;仅用户 JWT 无法越过 runtime 边界——实例只认平台代理,用户不直连实例;runtime token + JWT 叠加是网关代理的标准动作。由此,“统一账号密码伪装登录”不是本方案的形态:钥匙每实例独立随机、用户不接触实例钥匙、可轮换可吊销、全程审计。浏览器直连实例页面必然 401(地址栏导航不带自定义头),生产形态是浏览器只碰统一入口网关域名,网关注入钥匙后代理页面/API/SSE,实例端口永不暴露给用户网络。

与产出物服务的接缝 ​

编码执行体的产物——diff、分支、测试报告、部署产物四类——在执行单元 STOPPED 之前写入对象存储并调用产出物服务登记,产生 ArtifactCreated 事件进入 §四 的交付链:审批(PR 合并/上线)走 HITL,通过后由渠道适配器投递(评审人收 diff 卡片、负责人收测试报告链接)。这条接缝保证三件事:回收前产物可审计、交付幂等复用同一套 artifact_id + channel + recipient 规则、执行体失败回炉与交付重试互不阻塞。


§六 已支持场景走线 ​

场景一:IM 消息到达 → Hub → Agent 执行 → 产出物 → 回 IM ​

失败语义:重推同一条消息命中幂等键不重复执行;扫描未过停在 scanned 之前转审批;IM 发送失败退避重试后降级为链接;任务执行不受交付失败影响。

场景二:编码任务派发 → OpenCode 执行 → diff 产出 → 审批 → 交付 ​

失败语义:执行崩溃进 FAILED 可重试(attempt 加一);租约到期被接管时 lease_epoch 栅栏拒绝旧执行者写回;回收前验证无凭据残留;审批未过产物停在 deliverable 不外发。

场景三:下载链接——产物生成 → ticket 签发 → 点击 → 网关鉴权 → 流式回源 ​

失败语义:非本人拒绝(即使持有有效 ticket);链接泄漏在 TTL 内也可由服务端吊销;每次下载可追溯到人和版本。

场景四:外部 Agent 接入——A2A 端点注册 → 能力发现 → 消息投递 → 响应回传 ​

失败语义:鉴权在网关,A2A 端点自身不建立第二套身份权威;平台侧的租户与审批语义不出现在 Card 里,跨组织调用结果只带任务级状态。再次强调姿态:A2A 是早期规范,此场景是预留的互操作路径,按钉死的 schema 版本联调验证,不承载生产关键链路——生产交付仍以场景一至三的平台自有渠道链为准。


适用:AgentScope Java 2.x(agentscope-extensions-channel / agentscope-extensions-protocol)+ Higress + agentscope-service 2.1.0-BETA1 + OpenCode 2.x | 由原 13/17/18/34 四篇合并 | 初稿 2026-10

把 AI 工程讲到既懂也会用