docs: establish MQ-only and Dispatcher OSS token baseline

This commit is contained in:
2026-09-21 12:27:46 +08:00
parent cff0be1cce
commit 704652bd0d
11 changed files with 534 additions and 373 deletions
+7 -3
View File
@@ -150,7 +150,7 @@
## AI配置与参数(用户已确认)
- P1采用百炼/火山ASR、OpenAI兼容LLM、火山TTS;SDK首选及未通过门禁见组件清单§1.3/§4.3。基础栈方向确定不等于精确版本、许可证或参数能力已验收;不为补字段改为自写协议。
- Dispatcher按MQ任务agent_version_id调用SaaS已有AI版本GET校验租户/源Schema/不可变摘要/能力后向Agent交付执行快照。Agent不直连SaaS,不从CLI/env/源码常量或SDK默认覆盖AI业务值,不新增task-config猜测路径、MQ模式字段或调参后台。
- Dispatcher按MQ任务agent_version_id,经RabbitMQ专用Topic向SaaS取得不可变AI配置/授权,响应回原Dispatcher校验租户/源Schema/不可变摘要/能力并持久绑定后向Agent交付执行快照。旧SaaS AI版本GET方案已废弃,不再开发HTTP client。Agent不直连SaaS,不从CLI/env/源码常量或SDK默认覆盖AI业务值,不新增task-config猜测路径、任务MQ模式字段或调参后台。
- 已有model/prompt/voice/speed/ASR输入与识别/temperature/max_tokens/timeout及对话控制必须实际传入SDK或控制器;热词/VAD/top_p/音量/阶段时限等所需扩展先在上游补GAP-09,再生成校验。严格additionalProperties不放宽,不借metadata/raw_request透传。
- SaaS新版本供新任务引用,无需改代码/重启D/A;在途/原排队任务固定快照,同版本异内容拒绝。缓存按租户+版本隔离,断SaaS无有效授权缓存拒新准入;显式0/false与未提供保真,并发通话不得共享可变SDK参数。
- 凭据/供应商端点来自受控引用且有授权/出口校验,不能因可调参数绕过安全硬限额或启用不安全重试。OpenAI默认自动重试显式关闭;日志只留脱敏版本/摘要/有效参数,不打印prompt/变量/密钥。
@@ -160,10 +160,14 @@
- 本项目设计/运行/验收文档只在自身 `docs/` 维护。上游共享接口有唯一权威来源;导入带版本、来源和哈希的不可变契约包,再生成类型/校验,不维护重复手写 Schema。
- 新内部消息/许可/fencing 协议需先获批;不擅自改变 SaaS 路径、字段、状态、路由或控制语义。
- SaaS外呼命令/业务结果只经Dispatcher走RabbitMQ;内部Unary是已批准的执行通道,不新增对外HTTP拨号/业务回调。OSS配置源SaaSD承接recording-uploads/upload_id/completeAgent从Dispatcher取得受限目标/凭证/headers等配置后**直连OSS上传文件**,D不接收或转发录音内容、不替Agent上传。Agent经R13提交上传元信息,D协调SaaS verified后通过MQ返回OSS IDAgent不直连SaaS不禁止其直连OSS
- **SaaS↔Dispatcher的全部交互唯一经RabbitMQ专用Topic订阅完成,禁止双方任何HTTP请求/回调/兼容通道或故障回退**,包括执行、控制、查询、整体补传、AI配置/授权、录音业务会话/complete/verified及响应。OSS配置/TOKEN不来自SaaSAgent领取及显式重申请TOKEN只经D↔A Unary;本规则不禁止Agent→OSS、ARI、AI供应商HTTP(S)或gRPC的HTTP/2
- **每个Dispatcher必须有独立、全局唯一且不重复的ID及独立接收Topic/队列**;指定D的任务/配置/上传结果不能由其它D抢收,也不能广播后仅靠正文过滤。身份与tenant/Agent/Cell ID、dispatcher_epoch分开;保留租户独立队列及原值tenant_key,完整新路由长度预算须重验。具体ID生成/持久化、Topic/绑定、消息字段/关联/错误/期限须随W01新版本冻结,不凭本文给旧严格Schema添加字段。
- **OSS相关配置存于Dispatcher配置文件,Agent向Dispatcher领取临时上传TOKEN后直传OSS,不保存长期凭据;SaaS不再下发OSS配置/TOKEN。** D复用官方SDK提供受限TOKEN/目标信息,配置缺失/无效明确失败;不在样例、源码、日志或证据中保存实际密钥/完整TOKEN。过期只允许A显式向D重新申请,不自动续期或向SaaS申请TOKEN;精确配置格式/TOKEN形态/UploadGrant映射另行核验,不猜字段。
- D不接收/转发文件;SaaS业务会话/upload_id及complete/verified仍经MQ**上传完成后的SaaS独立校验职责不变**。A经R13报元信息,D仅在持久校验SaaS verified后发recording.ready/OSS ID。保留D现有签发能力,不误删为旧路径;但D本地HEAD不能替代SaaS verified。业务会话/complete的异步MQ与R12/R13衔接仍须核验,不无限阻塞或伪造完成。
- 本次MQ-only纠正只修改计划、契约及相关设计文档;旧不可变包、生成索引和历史证据原样保留,不代表新合同已发布。受影响W01/W02/W04/W05/W07/W08/W11/W12/W13/W14按plan§8.2重新验证。全局唯一D身份/专用Topic及本地D1/D2隔离fixture为当前合同要求,不授权双D业务运行、HA或共享额度。
- 文本实时事件准确名为transcript.updated,不新增call.transcript别名;OSS文本归档不能替代实时文字/opt-out,缺少专用资产授权接口时明确未启用,不能伪装recording.ready。
- 现有MQ信封command_type/command_id、event_type/aggregate_*与正文已对齐;事件payload专属约束尚需补齐,不把通用object校验当完整验收。字段索引只读生成,不手改成第二套Schema。
- `tenant_key` 原值一对一绑定,不清洗、编码或截断;超出 224 个 UTF-8 字节的既有路由预算时停止发布并保留源任务。
- `tenant_key` 原值一对一绑定,不清洗、编码或截断;旧布局224个UTF-8字节预算不能在加入D身份后直接照搬,W01须冻结并验证完整routing key/queue预算及分隔符/通配符边界;超限停止发布并保留源任务。
- 持久 inbox 后 ACK;状态与 outbox 同事务;confirm 不等于 SaaS 应用收讫。重复投递、未知执行和恢复不能触发重复拨号。
- 配额覆盖所有 Cell/实例及未知占用;租约过期不自动释放不明通话。控制CAS为 expected_task_revisionpause与stop的drain/hangup区分;paused可按新授权恢复,stopped不可恢复。整体补传仅call_id/source_command_id,禁止新增task/execution补传。最后发起许可、权限和屏障须故障注入。
- management是SIP配置唯一编辑/审批面。P1通过批准的版本化静态制品和受控部署入口交付,D核验目标/准入屏障,Agent加载并报告;不要求在线发布控制面。静态交接合同须批准,旧直接写Agent面不能同时启用;成功必须证明精确快照已被Asterisk加载。
+19 -13
View File
@@ -1,8 +1,8 @@
# go-sip
面向生产的Go SIP调度与执行项目:同一module/二进制通过Cobra提供 `dispatcher``agent` 两个业务子命令,分阶段替换Python,保留现有SaaS契约
面向生产的Go SIP调度与执行项目:同一module/二进制通过Cobra提供 `dispatcher``agent` 两个业务子命令,分阶段替换Python,保留既有业务语义,SaaS交互统一为MQ-only
> **当前状态:已完成本阶段项目内 W01W14单节点/单 Cell/单租户契约、实现与本地/隔离验收;真实 SaaS/MQ、真实供应商/ECS、生产切换及双节点/第二 Cell/双租户延期第二阶段。**
> **当前状态:W01W14单节点本地验收证据保留;本轮SaaS↔Dispatcher MQ-only修订的文档已纠正,受影响契约、实现与验收重新列为待验证,见计划§8.2。** 真实SaaS/MQ、供应商/ECS、生产切换及第二Cell/第二租户仍属第二阶段。
> 本项目已独立拆仓运营;源码、配置、依赖、迁移、测试、部署和文档均在此目录内维护。远程仓库为 `git.ipao.vip/rogee/go-sip`,本地 Git 默认分支为 `main`;真实外呼仍受逐次授权、capture-first、白名单和时间门禁约束。
## 已确认的范围
@@ -17,25 +17,28 @@
- 重写完整 Agent:调度控制面、Cell 外呼执行、ARI/RTP/录音、AI 流式适配、Cell 配置接收。
- 分阶段迁移,最终构建、测试和运行不依赖 Python、父仓库目录或其它业务项目内部代码。
- Asterisk 继续负责 SIP;独立 SIP 管理平台继续拥有配置管理权,均不纳入重写。
- SaaS指令/业务结果仍走RabbitMQOSS配置源SaaSDispatcher统一授权/验证与回传,Agent直传OSS。实时文字事件为 `transcript.updated`,OSS文本用于归档,不能只回文件ID替代文字事件
- **SaaS↔Dispatcher全部请求、响应和事件只走RabbitMQ,双方禁止任何HTTP。每个D都有全局唯一ID及独立接收Topic/队列,不能共享队列抢收或广播后过滤。** 控制/查询/补传、AI配置/授权、上传会话/complete/verified均在内;精确新消息/拓扑待冻结,P1不扩为多D协调/HA
- **OSS配置存于Dispatcher配置文件,Agent向Dispatcher领取临时上传TOKEN后直传OSS,不保存长期凭据。** SaaS不下发OSS配置/TOKEND保留SDK签发能力,不转发文件。SaaS业务会话及最终verified仍经MQ,D本地对象验证不能替代SaaS校验。实时文字仍为 `transcript.updated`,归档不能替代实时事件。
- 本次必须支持 **ASR-only****ASR + LLM + TTS** 两种模式;本阶段已完成本地/协议隔离双模式验收。百炼/火山ASR、OpenAI兼容LLM、火山TTS的真实供应商能力和生产参数联调仍标第二阶段/未启用,不能将 Mock 写成真实供应商通过;禁止复用旧LLM/TTS。
- **AI业务配置由Dispatcher按任务agent_version_id调用SaaS已有AI配置GET获取,校验后传给Agent。** 模型、提示词、音色/语速、识别、超时/打断等参数不写死;新版本用于新任务,无需重启,在途通话固定快照。静态SIP发布不代表AI配置静态硬编码。
- **AI业务配置/授权由D按任务agent_version_id经MQ向SaaS取得,经本D专用Topic收响应、校验并持久绑定后交付Agent;旧AI GET已废弃。** 模型、提示词、音色/语速、识别、超时/打断等参数不写死;新版本用于新任务,无需重启,在途通话固定快照。静态SIP发布不代表AI配置静态硬编码。
- 本轮验收范围收敛为单节点、单 Agent、单 Cell、单租户;保留 tenant_key 原值、独立队列、复合幂等和有界窗口。双节点、第二 Cell、双租户公平/背压/恢复不在本轮开发或验收范围,作为后续阶段。
- `upload-session/complete/verified`、RabbitMQ ACL/TLSapplication receipt 本阶段按版本化契约、Schema、fixture 和本地隔离状态机验收;真实 SaaS/MQ 联调延期第二阶段
- `upload-session/complete/verified`的全MQ版本及R12/R13异步衔接须重新冻结/实现/本地验证;旧契约/fixture不覆盖本次修订。真实SaaS/MQ及既有ACL/TLS/application receipt联调仍第二阶段,不新增生产授权
## 当前本地实现
以下保留修订前实现事实,本轮未修改代码;旧租户拓扑、HTTP控制、启动AI注入和D本地验证直接发ready不符合新接入目标;D签发TOKEN的职责保留,配置文件接线和授权约束仍需核验,须按计划§8.2纠正,不能当作MQ-only已完成。
- `contracts/upstream/` 嵌入项目内自包含 W01 基线,记录父源 commit、dirty 继承和 SHA-256;运行时代码读取该包,不读取父目录。
- `internal/store/` 提供 SQLite inbox、任务、租户/跨 Cell 配额、控制 CAS、replay 和 outbox 事务。
- `internal/agent/` 只使用文件保存执行状态、transcript、录音/资产和崩溃恢复信息;boot 不释放未知占用;录音通过 Dispatcher 授权的短期 Alibaba OSS presigned PUT 直传,源文件在 verified handoff 前保留。
- `internal/ai/` 对固定 AI Schema 做不可变快照校验,支持项目内显式 `full_ai`/`asr_only` 分支;`mock_pipeline` 只用于隔离协议/取消/参数测试,不宣称真实供应商已启用。
- `internal/control/` 提供契约定义的内部查询/控制/replay HTTP 面;外呼命令仍只从 RabbitMQ 进入
- `internal/control/` 仍有旧查询/控制/replay HTTP实现;已列为删除项,不再作为SaaS接入合同或并行兼容通道
- `internal/rpc/` 提供 `agent.v1` Unary handlers、mTLS TLS1.3 配置、会话世代/fencing、CAS、permit/fact/upload metadata 边界;Agent CLI 可选启动受证书保护的 gRPC listener,并校验静态 Cell 制品与 AI 授权边界。`internal/calllog/` 提供按手机号 HMAC 关联、掩码和 allow-list JSONL 外呼业务日志,不写原始号码、凭据、音频、转写或 prompt。
- `internal/health/` 使用 gopsutil 采样主机/进程资源;媒体端口和 AI 配额未接入时明确报告 unknown。`internal/mq/` 默认 bounded prefetch=1、per-tenant DLQ 和 publisher confirm`make mq-integration-local` 只启动 disposable RabbitMQ。
- `make acceptance-local` 会校验固定契约、跑 race/vet/build、执行 mock Agent/Dispatcher smoke,并确认 real mode 无凭据时拒绝;Alibaba OSS 实际授权测试由 `AGENT_CALL_OSS_INTEGRATION=1` 门控,使用受控环境变量,不把凭据写入仓库;`make release` 生成带 dirty-source/哈希的 local-development manifest;证据见 [`docs/evidence/20260918-local-development.json`](docs/evidence/20260918-local-development.json)。
- 直接依赖版本/本地许可证清单见 [`docs/evidence/20260918-dependencies.md`](docs/evidence/20260918-dependencies.md);门禁结论见 [`docs/evidence/20260918-acceptance-matrix.md`](docs/evidence/20260918-acceptance-matrix.md)SIP 注册/认证/From/PAI/前缀/选路对比见 [`docs/evidence/20260919-sip-routing-implementation-comparison.md`](docs/evidence/20260919-sip-routing-implementation-comparison.md),手机号业务日志边界见 [`docs/evidence/20260919-phone-call-business-log.md`](docs/evidence/20260919-phone-call-business-log.md)。
这些证据签收本阶段单节点/单 Cell/单租户 P1 适用范围不等于真实供应商、生产 SaaS/MQ receipt、生产切换或第二阶段拓扑通过。范围和延期项见 [`docs/evidence/20260920-scope-amendment.md`](docs/evidence/20260920-scope-amendment.md)。
这些证据签收旧基线的单节点/单Cell/单租户P1适用范围,不覆盖本次MQ-only修订,也不等于真实供应商、生产SaaS/MQ receipt、生产切换或第二阶段拓扑通过。范围和延期项见 [`docs/evidence/20260920-scope-amendment.md`](docs/evidence/20260920-scope-amendment.md)。
## 文档
@@ -43,14 +46,17 @@
| 文档 | 内容 |
| --- | --- |
| [plan-0918:开发计划与需求阅读索引](docs/plan-0918.md) | 首读入口:W00–W16及并行子任务、I/M/G关口、R0–R7索引、认领/单写/隔离/合并规则;部署候选前置到W13-a |
| [plan-0918:开发计划与需求阅读索引](docs/plan-0918.md) | 首读入口:W00–W16及并行子任务、I/M/G关口、R0–R10索引、认领/单写/隔离/合并规则;部署候选前置到W13-a |
| [Go重写方案 v0.3](docs/Go重写方案_v0.3.md) | 本次单节点/单 Cell/单租户目标、至少3 SIP fixture、双AI模式、P0/P1/第二阶段分期 |
| [通信与事件数据交互 v0.1](docs/通信与事件数据交互_v0.1.md) | 首发Unary、静态SIP与双模式、§6.16.3 SaaS配置来源/参数矩阵/安全边界 |
| [OpenAPI与MQ字段索引 v0.1](docs/OpenAPI与MQ字段索引_v0.1.md) | 从上游自动提取42个HTTP操作/115个命名组件、Schema与源哈希 |
| [SaaS↔Dispatcher契约](docs/contracts/saas-dispatcher.md) | MQ-only目标、D唯一身份/独立Topic、待冻结消息及旧实现差异 |
| [Dispatcher↔Agent契约](docs/contracts/dispatcher-agent.md) | 当前Unary/Proto事实及MQ上传结果的衔接待办 |
| [SaaS/MQ/D/A/OSS泳道图](docs/contracts/saas-rabbitmq-oss-dispatcher-agent-timeline.md) | 全MQ目标时序,Agent直传OSS,无SaaS↔D HTTP |
| [OpenAPI与MQ字段索引 v0.1](docs/OpenAPI与MQ字段索引_v0.1.md) | 旧源只读提取42个HTTP操作/115个组件及哈希;不代表新MQ-only入口,不手改生成物 |
| [deploys:物理机 systemd 发布包](deploys/README.md) | 锁定 Debian 13/Go 1.27.1/发布版本,构建并上传不依赖 Docker 的 Agent/Dispatcher 安装包 |
| [验证与切换验收 v0.3](docs/验证与切换验收_v0.3.md) | 10个首发汇总门禁、88项基线按阶段适用、P2公平性及独立容量验收 |
| [开源组件选型与复用清单 v0.2](docs/开源组件选型与复用清单_v0.2.md) | §1.3首发基础库/AI SDK选择,§4.3参数能力及锁版/许可/PoC门禁 |
| [G0开发准备与契约冻结方案 v0.1](docs/G0开发准备与契约冻结提案_v0.1.md) | D01D10方已确认;本阶段项目内契约/Proto/PoC按单节点范围签收,真实外部发布生产联调延期第二阶段 |
| [G0开发准备与契约冻结方案 v0.1](docs/G0开发准备与契约冻结提案_v0.1.md) | D01D10方已确认;MQ-only新增GAP-10及受影响G0须重新验证,真实外部发布/生产联调另行安排 |
| [AGENTS.md](AGENTS.md) | 本独立子项目的开发与安全约束 |
## 独立项目原则
@@ -58,14 +64,14 @@
- 当前设计文档以本目录 `docs/` 为唯一维护位置,不再向父项目另存一份。
- 对接协议的现有权威来源仍属于上游;后续在本项目导入带来源、版本和 SHA-256 的不可变契约发布包,不能另写一套同名 Schema。
- 后续发行包、CI、数据库和运行配置独立。不能靠 `../agent_call``../management``../sip_mock_server` 或父项目环境文件运行。
- 业务代码范围仅包括 SIP Agent/Dispatcher 与 AsteriskRabbitMQ、OSS、AI 供应商及 SaaS API 是 SaaS 提供的基础设施,不在本项目生产包中部署。独立集成测试使用自有隔离数据库、RabbitMQ 和契约 fixtureOSS 数据面使用 Alibaba 官方 Go SDKDispatcher 仅签发短期 grantAgent 直传且不持有 AK/SK;外部 SIP Mock 只能以固定镜像及版本化协议接入,不导入其源码。
- 业务代码范围仅包括 SIP Agent/Dispatcher 与 AsteriskRabbitMQ、OSS、AI 供应商及 SaaS API 是 SaaS 提供的基础设施,不在本项目生产包中部署。独立集成测试使用自有隔离数据库、RabbitMQ 和契约 fixtureOSS数据面复用官方SDK/标准HTTP,D按自身配置文件提供临时TOKEN,A直传且不持有长期AK/SKSaaS业务会话/最终verified仍经MQ,不作为OSS配置或TOKEN来源;外部 SIP Mock 只能以固定镜像及版本化协议接入,不导入其源码。
- 真实外呼、云创建、供应商调用和消费授权均是独立门禁,不能由测试成功或本文档自动授权。
## 阶段与下一步
1. **P0首发契约与依赖门禁。** 冻结8种事件payload、双AI模式(GAP-08)、SaaS任务AI配置读取/调参(GAP-09)、首发Unary职责、单 Cell 静态快照来源/加载回执、SaaS录音授权和至少3家SIP trunk的配置/协议 fixture。只核验实际采用的SDK;火山TTS参数覆盖、精确版本/许可证未核验前不宣布锁库,不等待未来动态发布/文本OSS归档合同。
1. **P0先完成MQ-only新合同(GAP-10)。** 冻结D唯一ID/生命周期、独立Topic/队列/绑定、全部请求响应/关联/错误/期限及Unary异步衔接;旧包原样保留。其余已有基线按受影响范围重新验证,包括8种事件payload、双AI模式(GAP-08)、SaaS任务AI配置读取/调参(GAP-09)、首发Unary职责、单 Cell 静态快照来源/加载回执、D配置文件/临时上传TOKEN及SaaS最终verified和至少3家SIP trunk的配置/协议 fixture。只核验实际采用的SDK;火山TTS参数覆盖、精确版本/许可证未核验前不宣布锁库,不等待未来动态发布/文本OSS归档合同。
2. **P1:本次单节点内测上线。** 完成单 Cell、单租户、双模式、幂等/控制/配额/恢复/录音安全的本地/隔离闭环;真实 ECS、真实外呼、生产 SaaS/MQ 联调不作为本阶段前置。
3. **第二阶段:** 真实 SaaS/MQ upload-session/complete/verified、RabbitMQ ACL/TLS/application receipt、真实供应商/ECS 联调,以及双节点、第二 Cell、第二租户公平调度。
4. **后续另立项:** 在线动态发布、自动跨供应商FALLBACK、多Dispatcher HA/分布式配额、权重借用、文本OSS归档、1000路完整AI/N+1。既有call/command整体补传不是通用回放平台,当前单节点首发仍保留。
阶段目标详见主方案§1/§10,首发验收见验收方案§1.1–§1.2;文件名保持不变。本阶段单节点/单 Cell/单租户本地 P1 已签收;真实依赖、供应商、云/拨号、生产 receipt、容量和切换属第二阶段,证据见 `docs/evidence/20260920-local-p1-acceptance.md`
阶段目标详见主方案§1/§10,首发验收见验收方案§1.1–§1.2;文件名保持不变。单节点/单Cell/单租户本地P1曾签收,本次MQ-only受影响门禁以计划§8.2待验证为准;真实依赖、供应商、云/拨号、生产receipt、容量和切换属第二阶段,证据见 `docs/evidence/20260920-local-p1-acceptance.md`
@@ -2,7 +2,7 @@
## 1. 状态、权限与使用方式
**状态:D01D10 方案已由用户确认;项目内 W01 契约基线和 W02 Proto 已建立,但外部权威发布、D10 PoC/运行验收和真实预算仍待完成,G0 尚未通过。** 确认依据为用户本轮指令:“除 OSS 上传Agent会直连上传,从Dispatcher拿相关配置;其它按建议更新文档。” OSS 文件由 Agent 从 Dispatcher 获取受限配置后直接上传,Dispatcher 不中转文件内容;其余按已列建议执行
**状态:D01D10原方向已确认,旧W01契约W02 Proto已有项目内证据;本轮用户确认SaaS↔Dispatcher全MQ,受影响G0重新验证,见[计划§1.2/§8.2](plan-0918.md)。** SaaS与D之间所有请求、响应和事件禁止HTTP,每个D具备全局唯一ID和独立接收Topic/队列;新Schema/拓扑/关联待发布。OSS补充确认:配置存于D配置文件,A向D领取临时上传TOKEN后直传;SaaS不再提供OSS配置/TOKEN,最终verified校验职责不变,D不转发文件。原HTTP方向被本修订替代,不把旧证据覆盖到新设计
本项目已创建并验证自己的 Go module、W01 bundle、W02 Proto/stubs、RPC/mTLS 和本地 Mock 测试;未修改父项目权威来源、字段索引或生成产物,未访问真实供应商或创建云资源。文件名保留“提案”以保持链接稳定,不代表还需重复审批已确认方向。
@@ -11,7 +11,7 @@
- 外部字段/路径/状态仍以带来源哈希的上游主契约为准。本文件记录已确认的变更方向和验收要求,**不能被代码当 Schema 使用**。后续由上游维护者修改唯一来源并发布,再导入生成;本文件保留为决策历史,不继续手工维护字段副本。
- 内部机制、字段方向及兼容策略已确认,正式 Proto 的类型/编号/错误映射等仍需定稿与校验。本轮不生成 `.proto`,也不以临时 JSON 绕过契约;未提供的字段细节、实际预算和技术验证不能视为一并通过。
- 角色简称:S=SaaS/上游契约维护方,M=管理平台维护方,D/ADispatcher/Agent 实现方,O=部署/安全/测试负责人。角色不代表已指定人员或已签收;实际姓名、版本、日期和证据须在批准记录补全。
- 外部交互仍保留现有 7 条 SaaS 业务路径及 8 种 MQ 事件;已有 AI 版本 GET 的部署归属由 §4 确认,不新增 HTTP 拨号/业务结果回调、MQ 模式字段或内部 MQ。
- 保留既有业务语义和8类业务事件,但原7条SaaS HTTP路径与AI GET全部废弃为接入方式;控制/查询/补传/配置授权/上传协调都改为专用MQ请求响应。D↔A仍Unary,A→OSS仍直传,不新增任务mode字段或内部MQ。
- 依据:[重写方案](Go重写方案_v0.3.md)、[通信设计](通信与事件数据交互_v0.1.md)、[验收方案](验证与切换验收_v0.3.md)、[只读字段索引](OpenAPI与MQ字段索引_v0.1.md)、[组件清单](开源组件选型与复用清单_v0.2.md)。索引不是独立契约包。
## 2. 方案确认记录与开发边界
@@ -22,13 +22,13 @@ D01D10 的共同状态为 **用户已确认方案/源发布与验证未完
| --- | --- | --- | --- | --- |
| D01 | §3 八种事件的 payload、条件约束与同域版本 | S 提交,D/A 联合验证 | 上游发布版本、Schema、每种事件正反例、跨语言校验/摘要适用规则 | 业务结果序列化与消费闭环冻结;不阻塞只读工具 PoC |
| D02 | §4.1 双 AI 模式及旧版本兼容(GAP-08) | S 提交,D/A 签收 | 两分支 Schema、旧完整模式兼容样本、模式资源/行为断言 | 两模式执行配置实现与 P1 双模式签收 |
| D03 | §4.2 SaaS 配置授权、参数、受控引用(GAP-09) | S/O 提交,D/A 签收 | 部署归属、租户授权/撤销合同、摘要金样、参数到 SDK 映射 PoC | 配置缓存/下发和对应 AI 参数能力承诺 |
| D03 | §4.2 SaaS MQ配置/授权、参数、受控引用(GAP-09/10 | S/O 提交,D/A 签收 | 请求响应/原D关联、租户授权/撤销合同、摘要金样、参数到SDK映射PoC | 配置缓存/下发和对应 AI 参数能力承诺 |
| D04 | §5 Unary/最后许可/控制屏障(GAP-04) | D/A 提交,S 确认业务控制语义,O 确认恢复边界 | 已批准协议、状态转移及崩溃矩阵;随后生成 Proto 并做隔离 PoC | 跨 Cell 发起、控制、事实提交和恢复实现冻结 |
| D05 | §5.2 共用证书会话与撤销(GAP-05 | O 提交,D/A 签收 | Endpoint/SAN 清单、角色隔离、重放负例、全组轮换方案及风险签收 | 节点准入和敏感配置交付 |
| D06 | §6.1 P1 静态制品交接(GAP-03 P1 | M/O 提交,D/A 签收 | 制品来源、授权矩阵、唯一写入口、精确加载证据格式 | SIP 静态集成与真实发布 |
| D07 | §6.2 Agent 取配置后直连 OSS 上传(GAP-02 P1 | S/O 提交,D/A 签收 | 受限配置、Agent→OSS 直传及 D 不转发文件证据;既有接口作用域、15分钟token、显式重新申请/完成幂等与 verified 证据 | 录音交接闭环;文本归档仍延后 |
| D07 | §6.2 D配置文件/临时TOKEN与A直传(GAP-02 P1 | D/O提交配置与TOKEN合同,S提交业务会话/verifiedD/A联合签收 | D配置来源及失败反例、TOKEN/UploadGrant映射、15分钟/显式向D重申请、A直传/D不转发;SaaS MQ会话/verified与R12/R13衔接 | 录音交接闭环;文本归档仍延后 |
| D08 | §6.3 profile、单活恢复、保留(GAP-06/07 P1 | O 提交,D/A 签收 | 明确数值/来源/预算、备份与恢复演练设计、唯一所有权方案 | 对应运行参数冻结与真实验收,不阻塞离线原理 PoC |
| D09 | §7 独立契约包和重建链 | S/M 提供版本,D/A 负责导入 | 只读包、清单、可重复生成/校验、项目外隔离构建记录 | 独立可交付构建,不得临时读取父目录运行 |
| D09 | §7 独立契约包和重建链,补GAP-10全MQ版本 | S/M 提供版本,D/A 负责导入 | 新版D身份/Topic/消息/错误/期限及正反例、只读包/哈希、可重复生成校验;不覆盖旧包 | 独立可交付构建,不得临时读取父目录运行 |
| D10 | §8 依赖和关键 SDK PoC | D/A 提交,O 审核许可/安全 | 精确版本、许可/NOTICE、漏洞处置及对应 PoC 原始记录 | 未验证组件进入正式实现/发布依赖 |
### 2.1 允许先做、不能先做
@@ -45,6 +45,12 @@ P1 必须实施当前真实租户的限额,并在单活 D 的同一 SQLite 事
P2 增加多个同时活跃租户的等权轮询、额度不足跳过、公平恢复、全链路背压和公平性验收;不是首次实现原子配额。P1 可用两个模拟租户测隔离,但不因此开放第二真实租户。
### 2.3 本轮MQ-only补充(已确认方向,消息细节待冻结)
本轮补充纳入D03/D04/D07/D09/D10及通信设计GAP-10,不新增运行验收编号。完整约束见[SaaS↔D契约](contracts/saas-dispatcher.md):每个D全局唯一ID、独立Topic/接收队列、请求/响应固定原D与租户、持久inbox/outbox、错目标/重复身份/重投/乱序/超时/重启恢复;新路由长度须重算,不能照搬旧224字节租户预算。ID生命周期、Topic/绑定、消息枚举/字段/关联、错误/期限未冻结前不实现猜测协议。
P1仍为单节点/单Agent/单Cell/单租户/单活D,下文沿用的早期两Cell/两Agent全量矩阵仅为后续目录,不是本轮门禁。新增D1/D2本地消息fixture只验证定向隔离,不授权多D业务调度、HA或共享配额。旧OpenAPI/只读索引和历史证据原样保存;新MQ生产链不得保留SaaS↔D HTTP;D提供上传TOKEN的职责保留,但D本地对象验证不能替代SaaS verified。
## 3. D01:外部事件 Schema 补齐方案(已确认方向)
### 3.1 上游修改方法
@@ -92,12 +98,12 @@ ASR-only 静音以实际输入活动及获批 VAD 策略判定,最长时限从
### 4.2 读取、授权与快照(方向已确认,待 S/O 补齐部署与合同细节)
1. 明确已有 `GET /internal/v1/ai/agent-versions/{agent_version_id}`**SaaS 部署入口**、服务身份与可信租户绑定;租户不从响应任意覆盖任务归属。无权、找不到、不可用分别处理,不猜测第二个 task-config 路径。
1. D按原任务租户/agent_version_id经MQ请求SaaS不可变配置和授权,SaaS经原D专用Topic响应;校验请求关联、来源/目标与可信租户,不能用响应覆盖归属。无权、找不到、不可用分别表达,精确MQ消息及错误待GAP-10冻结;旧AI GET已废弃,不开发HTTP client或第二个task-config路径。
2. 在首次成功准备执行配置时,将 `(原值 tenant_key, agent_version_id, 源 Schema 版本, 原内容摘要)` 与执行绑定并持久化。尚未取得有效配置的排队项不能准入;已有有效绑定的排队/在途项不因“最新版本”改变配置。新任务须显式引用新 agent_version_id。
3. 摘要采用上游已有算法及其精确输入边界,导入跨语言金样:键顺序、Unicode、数值、缺省/null/0/false 均须验证;本项目不另选 canonical 算法。若上游未发布明确算法,保持该项阻塞,不把普通 json.Marshal 字节当权威摘要。
4. 模式/参数归一化结果作为独立有效快照,关联原摘要和适配器版本,不修改原不可变内容。日志仅留版本、摘要及允许的脱敏参数,不留 prompt/variables/密钥。
5. P1 **不凭离线缓存授予新的发起许可**:D 可缓存不可变内容,但最终许可前须有本次执行的有效授权检查;SaaS 不可达且无上游已批准有效期的授权记录则拒新准入。已确认撤销阻止新许可,已发许可按 §5 有界收敛,在途终止仍走批准控制,不擅自挂断;撤销传播时效等源细节仍需冻结。
6. 如需要离线准入,上游须另外发布租户绑定、有效期、撤销传播和缓存可用性规则;未批准前不从本地 TTL 或 ETag 推导业务授权。每次权威检查使用已有读取合同,不新增自造授权接口。
6. 如需要离线准入,上游须另外发布租户绑定、有效期、撤销传播和缓存可用性规则;未批准前不从本地 TTL 或 ETag 推导业务授权。每次权威检查使用新版获批MQ请求响应,不走HTTP或自造授权接口。
7. provider_ref/credential_ref 由 O 管理的受控注册表解析,包含供应商、允许 Endpoint、API/资源版本、凭据种类、租户作用域及出口/能力限制;AI 业务参数不写入该注册表。Agent 不直连 SaaS,也不接收全局凭据库。
8. D 向被绑定 Agent 交付本执行最小权限配置/凭据;会话元数据、令牌和敏感值不进日志。配置哈希与原 JSON 校验、有效参数映射和权限检查都成功才准入。并发 SDK 客户端不得共享可变的 per-call 参数。
@@ -134,7 +140,7 @@ ASR-only 静音以实际输入活动及获批 VAD 策略判定,最长时限从
### 5.2 共用证书下的激活、代次和撤销
- D 从预配置 Endpoint 列表发 R01 受限探测,校验 TLS SAN/SNI、Agent 群组证书及部署期预期目标,再发 R02 绑定 agent/cell/boot。A 只接受独立 Dispatcher 身份;不能执行 A 自报 URL。
- D 每次取得唯一运行所有权后建立新的 dispatcher_epochA 每次启动产生新的 boot_id,旧 boot 的执行许可一律无效,但旧执行/资产占用不清零。generation 在所属 epoch 内单调;epoch 不能按 UUID 大小排序。
- D的全局唯一逻辑ID及专用Topic不等于运行代次,身份保持/恢复及重复拒绝随GAP-10冻结;不能以新epoch清除原消息归属。D 每次取得唯一运行所有权后建立新的 dispatcher_epochA 每次启动产生新的 boot_id,旧 boot 的执行许可一律无效,但旧执行/资产占用不清零。generation 在所属 epoch 内单调;epoch 不能按 UUID 大小排序。
- R02 采用持久 activation operation_idD 先保存 pending 绑定,再由 A 持久接受,D 收到/查询到相同绑定后标 active;回复丢失重查原操作。active 前 A 不获得执行/敏感配置权限。会话令牌用成熟安全随机源,D 仅保存必要校验材料,禁止自研密码协议。
- 激活冲突、旧 epoch、重放、错误 boot、未完成绑定都 fail closed。新 epoch 激活必须在旧 D 已停用/网络隔离/身份撤销可证明后进行;备份恢复不能仅修改一个 generation 数字声称隔离旧进程。
- 共用私钥泄露可能冒充整组 Agent,Endpoint 和会话只能降低误绑定/重放风险,不形成节点级密码学隔离。撤销/轮换针对全组;先关准入、收敛许可并处理在途,再替换证书和会话。不能关闭主机名验证或以风险声明替代演练。
@@ -177,7 +183,7 @@ D 先 CAS 持久化控制及关闭新许可,再向所有相关 Cell 下发同
以下为**用户已确认、尚未实测的内部隔离 PoC 初始值**,不覆盖验收 §9 的上游基线或真实供应商预算:最终许可 TTL 上限 1000ms、R08 单次 deadline 500ms;一般查询/事实提交单次 deadline 3s;接收控制单次 deadline 1s(不是 applied 完成时限)。重试不得越原业务/许可截止,退避继承已批准 profile;不能证明 TTL/网络/时钟余量时拒绝发起。
每条内部 Unary 编码后上限 512KiB,许可/控制请求上限 64KiB;不传音频/录音字节,不无限批量事实。外部 MQ 256KiB、HTTP JSON 64KiB 等源限制仍独立有效,内部较大上限不能绕过外部上限;超限拒绝/告警而非截断。协议初版仅支持同一批准版本和显式能力集,未知枚举/关键字段拒绝准入;后续兼容范围须有升级测试再扩展,不承诺自动滚动升级。
每条内部 Unary 编码后上限 512KiB,许可/控制请求上限 64KiB;不传音频/录音字节,不无限批量事实。外部MQ仍按256KiB源上限及新版分消息约束验证,旧HTTP JSON 64KiB仅保留为历史来源,不用于恢复SaaS↔D HTTP;内部较大上限不能绕过外部上限;超限拒绝/告警而非截断。协议初版仅支持同一批准版本和显式能力集,未知枚举/关键字段拒绝准入;后续兼容范围须有升级测试再扩展,不承诺自动滚动升级。
## 6. D06–D08:静态交接、录音与运行恢复
@@ -191,14 +197,14 @@ M 是唯一编辑/审批面;制品外层记录 source_release、source_digest
### 6.2 P1 录音配置与 Agent 直连 OSS 上传(用户已确认)
**上传数据面固定为 AgentOSS配置/授权控制面为 SaaS → Dispatcher → Agent。Dispatcher 不接收缓存或代理转发录音文件内容,也不替 Agent 执行 OSS 文件上传。** Agent 从 Dispatcher 取得相关受限配置后,使用官方 OSS SDK/成熟上传客户端直接连接获准 OSS 目标;“Agent 不直连 SaaS”不禁止其直连 OSS
**上传数据面固定为AgentOSSOSS配置唯一来源为D配置文件,临时上传TOKEN由D通过Unary交给A。** SaaS不下发OSS配置/TOKEN;其业务会话/complete/verified仍经MQ,上传完成后的独立校验职责不变。D不接收/缓存/转发文件,不替A上传,A不持长期凭据
OSS 配置源仍是 SaaSD 通过原 recording-uploads/upload_id/complete 流程管理每个录音的上传会话。下发配置包含契约允许的 HTTPS 上传目标、必要 headers/受限上传凭证、有效期及对象/内容绑定;不下发全局长期 AK,不维护 Agent/CLI 的第二套 bucket/AK 配置。S 仍须补齐作用域、size/checksum、续期是否保持原会话及 complete 幂等返回的源细节
D/O须核验配置文件读取、服务地址/bucket/对象规则、受控凭据配置或引用、SDK签发及TOKEN与现有UploadGrant映射;未给定的精确字段不能猜。配置缺失/无效明确失败,不向SaaS取配置、不用A本地bucket/长期AK兜底;样例/日志不存实际密钥或完整TOKEN。D仅交付原执行/对象所需TOKEN、目标/方法、headers与期限。S仍须补齐既有业务会话、对象定位/校验、size/checksumcomplete幂等合同;MQ中不传D配置文件/长期凭据/TOKEN,不能假定D配好OSS便等于SaaS可独立校验
1. A 封口文件并持久元信息,通过 R12 向 D 取得该原录音身份的受限上传配置;D 负责与 SaaS 申请/续期
2. A 直接把文件内容上传至指定 OSS,不把文件发给 D,也不通过内部 gRPC 传录音字节。D 失联时已有仍有效授权可继续上传;授权过期或缺失则保留原文件,等待 D 恢复后续原会话,不改用本地长期密钥。
3. A 通过 R13 向 D 报告原资产/会话及上传元信息;D 调用 SaaS completeSaaS 独立校验对象。上传成功但 D/complete 不可达时保留待完成状态,不能发 ready。
4. verified 后 D 事务记录资产状态和 recording.ready outbox,再通过 MQ 回传 OSS ID。PUT/complete 超时分别按原对象/会话幂等对账,恢复不能新建资产或重拨。未定义的续期/查询语义仍阻塞相应恢复分支,不猜测接口。
1. A封口并持久元信息,经R12向D领取临时TOKEN,D按自身配置复用SDK提供。原SaaS业务会话/资产登记仍MQ,但不是TOKEN来源。业务MQ响应的pending/有界等待/重取仍由W02/W11冻结,不无限阻塞RPC
2. A 直接把文件内容上传至指定 OSS,不把文件发给 D,也不通过内部 gRPC 传录音字节。D 失联时已有仍有效授权可继续上传;TOKEN过期或缺失则保留原文件,D恢复后由A显式向D重新申请,不自动续期,不向SaaS申请TOKEN,不改用A本地长期密钥。
3. A通过R13报告原资产/会话及元信息;D经MQ提交completeSaaS独立校验后经原D专用Topic返回结果。上传成功但D/MQ/complete响应不可达时保留待完成状态,不ready,不用D本地HEAD替代
4. D持久校验SaaS MQ verified后,事务记录资产状态和recording.ready outbox,再MQ回传OSS ID;A取得最终Unary结果的衔接同样须冻结。PUT/complete 超时分别按原对象/会话幂等对账,恢复不能新建资产或重拨。未定义的续期/查询语义仍阻塞相应恢复分支,不猜测接口。
本地删除条件继承验收 profileverified、ready 已得到要求的发布确认、无恢复任务且满足保留;MQ confirm 不等于 SaaS 应用收讫,不额外等待不存在的应用 ACK。文本 OSS 归档继续受 GAP-02 延后约束,实时 transcript.updated/opt-out 不等待 OSS。
@@ -235,7 +241,7 @@ OSS 配置源仍是 SaaSD 通过原 recording-uploads/upload_id/complete 流
| 3Unary/身份/许可 | 成熟 gRPC/mTLS、SAN 错配/重放、R02 丢包、§5 崩溃矩阵、RPC 超时不重拨、同机第二 D 拒绝启动 | D04/D05方案已确认;后续开发时定稿/生成Proto并验证,未通过不得签收 |
| 4ARI/RTP/录音 | 本地固定 digest Asterisk、确定通道关联、ExternalMedia、PCMA/PCM、事件断连与取消清理;故障下注入 originate 响应丢失不得二次提交 | 先换成熟 SDK/修上游;不能手写 ARI/SIP/RTP/G.711 替代 |
| 5ASR/LLM/TTS 参数 | 百炼/火山 ASR、OpenAI 兼容 LLM、火山 TTS 的实际锁定 SDK 对照 §4 记录传参、0/false、取消和重试;Mock 检查边界行为 | SDK 缺能力不走 metadata/raw_request;报阻塞或批准替代 SDK |
| 6MQ/录音交接 | 隔离真实 broker 的 confirm/重投/限流、SQLite outbox、受限上传及 complete 超时恢复;供应商 API 用协议 Mock | 本地通过不替代真实 MQ/OSS/云身份验证 |
| 6MQ/录音交接 | 隔离broker验证D1/D2定向Topic与租户隔离、错目标/重复ID、不可路由/confirm丢失、请求响应乱序/重启恢复;D从配置文件提供临时TOKEN并覆盖配置缺失/无效反例;SaaS协议Mock经MQ提供AI/业务会话/verifiedA直传OSS;证明无SaaS↔D HTTP或SaaS下发OSS配置/TOKEN | 本地通过不替代真实 MQ/OSS/云身份验证 |
每份证据记录环境/mock-mixed-real、版本、输入边界、预期/实际、脱敏日志、失败注入点和残余风险;失败/blocked 不删样本。SDK 本地 Mock 只验证客户端映射,不证明供应商服务端支持、费用或双模式真实可用。真实 SIP/AI/OSS 必须另行授权并分别验收。
+23 -20
View File
@@ -18,7 +18,7 @@
7. 不接入PostgreSQL;独立Dispatcher统一全局任务/配额,使用本地持久SQLite。Agent不设SQLite业务库,文字/录音及必要执行/上传恢复信息流式落文件。
8. 内部采用 **Unary gRPC**Dispatcher预配置Agent Endpoint列表,Agent业务启动参数尽量只有Dispatcher Endpoint,不增加内部MQ或双向流。
9. 所有Agent共用一套mTLS证书;这只认证Agent群组,单节点授权另由受控Endpoint/自动会话绑定落实。Dispatcher身份独立,不能伪称具备独立节点证书隔离。
10. OSS配置来源于SaaSDispatcher向Agent提供受限上传信息并统一回传SaaS。文本保留实时MQ事件,OSS用于归档;录音按OSS ID查看。
10. OSS配置存于Dispatcher配置文件,Agent向Dispatcher领取临时上传TOKEN后直传;SaaS不下发OSS配置/TOKEN,仍负责最终独立校验并经MQ返回verified。文本保留实时MQ事件,OSS用于归档;录音按OSS ID查看。
11. Dispatcher感知Agent健康、必要资源、软件/协议能力、获授权SIP供应商和已加载配置版本;在静态授权候选内按固定、可解释策略分配新任务,不做智能负载评分或自动跨供应商重拨。
12. 管理平台仍为SIP配置唯一编辑面;P1以批准的静态快照经受控部署入口加载,Dispatcher控制准入并核验Agent加载事实,暂不建设在线动态发布/回滚编排。
13. 本次固定1个Agent、1套Asterisk、1个单活Dispatcher、单 Cell、单租户;至少3家独立SIP供应商保留为 trunk 配置/路由/协议 fixture 覆盖。双节点、第二 Cell、双租户不在本轮开发或验收范围。
@@ -27,7 +27,7 @@
以上范围不再列为待定。G0只冻结P1实际使用的Proto、事件payload、双AI模式及SaaS配置/调参、静态配置交接、录音授权、共享证书授权和运行阈值;后续功能有明确阶段,不再要求所有未来缺口同时关闭。本轮不创建Go骨架、数据库、broker或真实呼叫。[G0开发准备与契约冻结方案](G0开发准备与契约冻结提案_v0.1.md) D01–D10的方向、模式/许可/恢复机制及内部PoC初始profile已获用户确认;权威源发布、正式Proto及验证仍未完成,不能视为G0通过。本轮只同步文档,不修改上游Schema或创建代码。
OSS数据面已明确为**Agent→OSS直连上传**Agent从Dispatcher获取本录音的受限目标/凭证/headers等配置后自行上传文件;Dispatcher不接收或转发录音字节,负责SaaS上传会话/complete协调及verified后的MQ结果回传。每次下发token有效15分钟;过期或失败保留源文件,由调用方显式重新申请,不自动续期或重试。此直传不改变Agent不直连SaaS的边界。
OSS数据面已明确为**Agent→OSS直连上传**D读取自身配置文件,经SDK提供本录音的临时TOKEN/受限目标与headersA通过Unary领取后自行上传。D不接收或转发录音字节;既有SaaS业务会话/complete及verified仍经MQ,最终校验职责不变。每次TOKEN有效15分钟;过期或失败保留源文件,由调用方显式向D重新申请,不向SaaS取TOKEN,不自动续期或重试。此直传不改变Agent不直连SaaS的边界。
### 1.1 本次P1目标与完成标准
@@ -111,6 +111,8 @@ Dispatcher配置包含MQ/SaaS受控引用、SQLite路径、两个Agent Endpoint
### 3.2 权威来源与独立拆仓
**本轮用户已确认SaaS↔Dispatcher全MQ:双方不再有任何HTTP请求/回调;每个D有全局唯一ID和独立接收Topic/队列。** 执行、控制、查询、补传、AI配置/授权、上传会话及complete/verified全部经MQ,详见[SaaS↔D契约](contracts/saas-dispatcher.md)与[计划§1.2/§8.2](plan-0918.md)。旧HTTP和旧租户路由是被替代的设计;新Schema/拓扑/身份生命周期/关联待冻结,旧源包及哈希不改,受影响实现/验收重新验证。
现有上游权威是《SaaS交互_OpenAPI与MQ契约规划_v0.1.md》(正文 v1.0)及经核验的发布产物。现有产物包括 `mq.schema.json``executor.openapi.yaml``cell-agent.openapi.yaml`、AI 配置及 SIP 管理相关契约。文件存在不代表完整覆盖:P0 必须逐条核对正文、Schema、状态语义和实现差异。
- 将经批准的协议作为**版本化、只读发布包**导入本项目 `contracts/`,记录源仓库、源版本/提交、文件 SHA-256 和生成器版本;工作区未提交修改不能冒充某个提交的发布产物。
@@ -119,9 +121,9 @@ Dispatcher配置包含MQ/SaaS受控引用、SQLite路径、两个Agent Endpoint
- 新增调度器—Cell 的消息、fencing、回执或对账字段,先形成契约变更提案并获批;本方案不擅自定字段或 MQ 路由。
- 本项目自己的设计、运行、验收文档只在此处维护;上游共享契约仍只有一个编辑源。
- 本项目已独立初始化 Git,并按 `git.ipao.vip/rogee/go-sip` 维护;不创建 submodule 或改变父仓库跟踪关系。项目许可证和第三方 NOTICE 在对外发布前确认。
- [通信与事件数据交互](通信与事件数据交互_v0.1.md)覆盖1类外部执行命令、8类业务事件、现有HTTP归属及13个内部Unary职责草案;[字段索引](OpenAPI与MQ字段索引_v0.1.md)完整提取42个HTTP操作/115个命名组件。
- [通信与事件数据交互](通信与事件数据交互_v0.1.md)覆盖执行命令、8类业务事件、全MQ请求响应目录及13个内部Unary职责草案;[字段索引](OpenAPI与MQ字段索引_v0.1.md)完整提取42个HTTP操作/115个命名组件。
- 已逐字段/哈希确认MQ信封command_type/command_id、event_type/aggregate_*及task_revision与正文一致;但事件payload目前仅通用object,专属约束仍需上游补齐(GAP-01)。不能以旧测试或通用object校验冒充完整验收。文字事件准确名称为transcript.updated。
- 42操作/115组件是上游目录,不是本项目首发工作量。P1保留既有7条SaaS交互路径/8种事件,消费实际所需AI配置和静态制品;不实现管理平台30个API或复制其业务后端。生成/校验仅覆盖使用入口及其引用闭包,来源包与只读字段索引保持完整。
- 42操作/115组件是上游目录,不是本项目首发工作量。P1保留既有业务语义/8种业务事件,但7条HTTP交互及AI GET改为获批MQ请求响应;消费实际所需AI配置和静态制品;不实现管理平台30个API或复制其业务后端。生成/校验仅覆盖使用入口及其引用闭包,来源包与只读字段索引保持完整。
- 当前AI Schema强制要求llm/prompt/tts/asr/conversation,缺少明确的ASR-only表达。GAP-08须先在上游批准模式/缺省/文字与错误语义;不在MQ添加临时mode字段或绕过required校验。
## 4. Go 1.27.1 的采用策略
@@ -155,18 +157,17 @@ P0 必须以锁定 tag/commit 的 PoC 验证 Go 1.27.1、Asterisk 镜像、API/
## 5. 目标运行架构
```text
SaaS ── RabbitMQ命令 ──> dispatcher(单活SQLite
SaaS <── RabbitMQ事件 ── dispatcher outbox
SaaS <── 既有HTTP控制/查询/录音握手 ──> dispatcher
SaaS ── RabbitMQD专用Topic/队列 ──> dispatcher全局唯一ID单活SQLite
SaaS <── RabbitMQSaaS专用订阅 ───── dispatcher inbox/outbox
执行/控制/查询/补传/AI配置授权/上传协调全部经MQ;无双方HTTP
管理平台 ── 批准静态快照 ──> 受控部署入口(维护窗口)
dispatcher ── Unary gRPC ─────── agent-1 ↔ Asterisk-1 ↔ 获授权的SIP trunk
mTLS └─ agent-2 ↔ Asterisk-2 ↔ 获授权的SIP trunk
├─ 两种AI模式、文字/录音/恢复文件
└─ Dispatcher授权 → OSS直传
dispatcher ── Unary gRPC ─────── agent-1 ↔ Asterisk-1 ↔ 获授权的SIP trunk
mTLS ├─ 两种AI模式、文字/录音/恢复文件
└─ D配置文件 → D临时TOKEN → Agent直传OSS
```
内部不增加MQ或双向流。Agent不拿broker/SaaS管理凭据,业务事件统一由Dispatcher发布;内部RPC不是新的SaaS拨号HTTP入口。两个Cell分别使用获授权固定出口,多EIP直连;至少3家供应商按矩阵预配置独立trunk,不承诺每家已同时授权两个出口。Dispatcher可与一个Cell同机但权限/目录/资源隔离,该节点故障会同时影响中央调度,不能宣传两节点即自动HA
内部不增加MQ或双向流MQ-only不禁止Unary的HTTP/2、ARI/供应商HTTP(S)或Agent→OSS PUT。Agent不拿broker/SaaS管理凭据,业务事件统一由D发布。P1只运行一个D/Agent/Cell/Asterisk,至少3家独立trunk保留;D1/D2路由隔离仅作本地消息fixture,不扩为多D业务协调、第二Cell或HA。D与Cell同机仍分进程/目录/资源,该节点故障会同时影响中央调度。
### 5.1 Dispatcher:唯一业务状态与调度权威
@@ -231,14 +232,14 @@ P1允许维护窗口暂停新任务:关闭受影响资源准入、排空/对
### 6.1 消息接入与 ACK
以下MQ消费/发布和业务数据库事务由Dispatcher承担Agent事实经gRPC进入中央事务。沿用 `agent-call` 资源命名空间,不因项目名变化而重命名协议资源:命令 exchange 为 directrouting key 为 `agent-call.tenant.{tenant_key}.call.execute`,事件 routing key 为 `agent-call.{event_type}`;不把 SaaS 业务结果队列改成每租户一条
以下MQ消费/发布和业务数据库事务由D承担Agent事实经Unary进入中央事务。每个D有全局唯一逻辑ID、独立接收Topic/队列/绑定,不能共队列竞争消费指定D的消息或广播后过滤;身份不是dispatcher_epoch。原仅按tenant路由的direct拓扑不再用于新接入。精确命名、SaaS订阅、ID生命周期与请求/响应/来源关联随W01新版本冻结,不能直接给旧Schema加字段
1. 以可信队列绑定校验 `tenant_key`、正文身份和版本;原值一对一,不清洗、编码或截断。超过 224 个 UTF-8 字节的既有路由预算时保留任务停发,不能换一个 key
1. 以可信绑定校验目标D、原租户/请求和版本;tenant_key仍原值保留、租户队列仍独立。加入D身份后须重算完整routing key/queue长度并测分隔符/通配符边界,不能套用旧224字节预算;超限保留任务停发,不清洗、编码或截断
2. 先验证认证、租户归属和基本 Schema,再识别已持久化的历史幂等事实,最后才对新执行检查当前控制状态、不可变 agent 版本和资源准入。重投不能因当前配置/任务状态变化而改写原决定;权限检查不能因命中历史而绕过。外部命令不能注入 SIP 地址、主叫、密钥或任意 AI URL。
3. inbox 与可恢复待执行状态在同一事务持久化后,才 ACK broker;无效消息按既有拒绝/死信契约可靠处理,不能先 ACK 后记原因。临时额度不足进入受限持久等待,不丢弃合法任务。
4. 传输消息 ID 与业务标识的映射遵守契约;命令、执行、事件的唯一键分别为 `(tenant_id, command_id)``(tenant_id, execution_id)``(tenant_id, event_id)`,禁止只按 execution ID 跨租户去重。同键异载荷按契约冲突处理;换 command ID 不能绕过同一执行去重。
5. 业务 outbox 与产生它的状态变更同事务提交;MQ confirm 后只标记运输层发布成功。confirm 丢失允许以原 `event_id` 重发,不创造第二个业务结果。
6. broker 持久化、publisher confirm、mandatory/不可路由处理、手动 ACK 和重试退避均必须真实验证。事件 confirm 不是 SaaS 业务应用收讫,不增加应用 ACK
6. broker 持久化、publisher confirm、mandatory/不可路由处理、手动 ACK 和重试退避均必须真实验证。事件confirm不是SaaS业务应用收讫,不自行增加application receipt协议。全MQ控制/查询/配置/上传仍需明确业务响应,精确合同须冻结;重复/迟到/超时沿原关联恢复,不回退HTTP或换D重拨
启动须核验 broker 的 exchange 类型、精确绑定、持久化、ACL、队列上限和拒绝发布策略;配置漂移即不 ready。处理 mandatory return、confirm 丢失、blocked 和全局水位;满队列拒绝新发布,不丢队头,SaaS 保留原任务。死信/重试恢复回原租户配额,租户停用和清理先完成未决任务/消息/资产对账。现成 AMQP 库不等于已实现这些业务保证;连接恢复封装也需过 S04/S05/S20S22。
@@ -279,14 +280,14 @@ SIP 外呼时间窗口为 Asia/Shanghai 每日 `[09:00, 20:00)`;窗口外在
沿用主契约的租户作用域 `command_id` 幂等、`expected_task_revision` CAS;区分 `requested_task_revision``applied_task_revision`,同 command 重试不能再次递增版本。
- HTTP 202 仅表示 accepted;相关 Cell、在途许可和要求的挂断均确认后,才能通过 MQ 报 applied。失联/结果未知继续 applying/reconciling 并告警,不伪造成功。
- 控制请求经目标D专用Topic接收,D持久后经MQ返回accepted;相关Cell、在途许可和要求的挂断均确认后,才能经MQ报applied。失联/结果未知继续 applying/reconciling 并告警,不伪造成功。
- `pause` 停止新发起,已拨出(包括拨号/振铃)和已接通电话继续原生命周期;收敛的是尚可发起的在途许可,不以暂停为由取消已拨出通道。`stop` 必须显式指定 `active_call_policy=drain|hangup`:drain 让已有电话自然结束;hangup 还需 `outbound.hangup` 权限并等待挂断确认。
- 撤销号码先由 SaaS 禁发并对相关旧任务建立整体 pause 屏障,不能用临时删 broker 消息代替。SaaS 过滤撤销对象、对账旧执行后,paused 任务可以按当前已生效 revision 恢复;剩余对象用新 command/execution 授权,旧命令重投不能复活。
- 只有 stopped 任务不可 resume。是否改用新任务由 SaaS 决定,Agent 不能强制把 pause 改成不可恢复的 stop。
### 6.5 整体补传
HTTP 仅按一个 `call_id``source_command_id` 发起整体补传,不增加 task/execution 范围或事件/turn/资产筛选。受理时固定截止点,使用原 event ID/内容/版本,分批、限速并优先实时结果;source command 尚无 call 也应能恢复其结果。区分不存在与保留过期(404/410,补传自身 command.result 不得循环纳入;completed 只表示该固定集合发布完成,不代表 SaaS 已应用。
SaaS经目标D专用Topic仅按一个 `call_id``source_command_id` 发起整体结果补传,不增加 task/execution 范围或事件/turn/资产筛选。受理时固定截止点,使用原 event ID/内容/版本,分批、限速并优先实时结果;source command 尚无 call 也应能恢复其结果。区分不存在与保留过期(精确MQ错误码待冻结),响应同样经MQ;不得重发call.execute作为补传,补传自身command.result不得循环纳入;completed 只表示该固定集合发布完成,不代表 SaaS 已应用。
### 6.6 最终文字、事件与 DNC
@@ -327,7 +328,7 @@ Dispatcher对Agent稳定事实去重后,按 command/call/transcript_segment/re
### 7.2.1 AI配置来源与调参边界(本次必需)
- 配置流固定为:MQ任务引用agent_version_id → **Dispatcher调用SaaS已有AI版本GET** → 校验租户/源Schema/摘要及供应商能力 → R07执行快照/获批缓存引用 → Agent会话局部SDK请求与控制器。Agent不直接查询SaaS,不从CLI/env另填业务参数,不新增HTTP拨号或结果回调
- 配置流固定为:MQ任务引用agent_version_id → **D经MQ请求SaaS不可变配置/授权,原D专用Topic收响应** → 校验租户/关联/源Schema/摘要/能力并持久绑定 → R07执行快照/获批缓存引用 → Agent会话局部SDK请求与控制器。旧AI GET已废弃;Agent不查询SaaS,不从CLI/env另填参数
- 初选组件为百炼devinyf/dashscopego、火山ASR/TTS共用GizClaw/doubao-speech-go、LLM官方openai-go;均需实际协议与参数PoC,尤其火山TTS不能忽略语速/音量等字段。基础库及未完成的锁版门禁见 [组件清单§1.3/§4.3](开源组件选型与复用清单_v0.2.md)。
- 模型、提示词/变量、语言/中间稿、音色/语速、音频格式、temperature/Token上限、超时、开场白、打断、静音/轮数/时长、分句/缓存等已有参数必须从SaaS贯穿到执行;热词/VAD/top_p/音量/阶段超时等实际所需扩展先由上游补GAP-09。精确字段及缺省/单位/能力规则仅在源契约维护,需求索引见 [交互§6.1–§6.3](通信与事件数据交互_v0.1.md)。
- 在已支持并获授权的能力范围内,SaaS发布新不可变版本,新任务显式引用即可调参,无需改代码/重建/重启;在途与原排队任务固定原快照,未知执行不因版本变化重拨。SIP静态发布不限制这条AI配置读取链路,也不需要建设R04通用热更新平台。
@@ -336,11 +337,13 @@ Dispatcher对Agent稳定事实去重后,按 command/call/transcript_segment/re
### 7.3 录音交接
OSS配置来源于SaaS,不在Agent启动参数中维护bucket/AK/SaaS地址。Dispatcher统一解析获授权的配置/版本,承接现有上传申请和complete;Agent通过gRPC获取仅本执行/对象所需的HTTPS目标、headers和有效期,直传OSS并回报元信息。verified后由Dispatcher写outbox发布recording.ready,不把PUT200/ETag当验证成功。SaaS全局配置拉取接口未在现有OpenAPI定义,列为GAP-02而非虚构已可调用路径
**OSS配置存于D配置文件;A经R12向D领取临时上传TOKEN后直传OSS,不保存长期凭据。** D依据自身配置复用官方SDK提供原执行/对象所需TOKEN、目标、headers与期限;配置缺失/无效明确失败,不向SaaS获取配置/TOKEN,不用A本地bucket/长期AK兜底。TOKEN过期只接受A显式向D重新申请,配置格式及TOKEN/UploadGrant映射须核验,不猜字段,样例/日志不写真实凭据
既有SaaS业务会话/资产登记、R13之后的complete及verified仍经MQSaaS最终独立校验职责不变,MQ不传D配置文件/长期凭据/TOKEN。D仅在持久校验SaaS verified及oss_id后写recording.ready outboxPUT200/ETag或D本地HEAD不能替代。保留D签发能力,不将其误删为旧路径。对象定位/校验所需业务信息及R12/R13有界等待/恢复衔接待W01/W02/W11核验,不虚构HTTP或无限等待Unary。
spool 按继承的测试 profile 在 70% 告警、80% 停止新接单,降至 60% 且依赖恢复后才恢复;为活动通话预留剩余录音空间。已 verified、ready 持久并确认发布、无已知恢复任务的本地已交接录音,测试至少保留 24h;未确认/失败文件不自动删。生产保留另行批准,不能在 confirm 后无条件删原始资产。
`CALL_NOT_REGISTERED` 保留文件并等待;签名失效续用原 recording/upload 会话,不制造新资产。仅接受受控 HTTPS 目标与约定 headers,禁止任意重定向/跨对象写入;verified 对象须防止旧签名覆盖。SaaS/对象侧独立读真实字节验证,不能信自报摘要或把 ETag 当 SHA-256。
`CALL_NOT_REGISTERED` 保留文件并等待;TOKEN失效只允许A显式向D重新申请,保持原recording/upload语义,不自动续期或制造新资产。仅接受受控 HTTPS 目标与约定 headers,禁止任意重定向/跨对象写入;verified 对象须防止旧签名覆盖。SaaS/对象侧独立读真实字节验证,不能信自报摘要或把 ETag 当 SHA-256。
Agent对已签名PUT使用标准库HTTP/约定headers,需要OSS API才用官方SDK,不自写签名或拿不必要长期凭据。文字也流式落文件,但实时transcript.updated/contact.opt_out仍经gRPC→Dispatcher→MQ及时回SaaS。文本OSS归档的授权/完成/引用尚缺契约,未冻结前明确未启用,不伪装录音;保留文字MQ链路不受此影响。补传按§6.5整体call/command范围。
+14 -6
View File
@@ -14,6 +14,8 @@
精确字段号和枚举以 `proto/agent/v1/agent.proto` 为唯一源;本文不另造 protobuf。
**SaaS↔Dispatcher 边界已修订为 MQ-only**,详见 [SaaS↔Dispatcher 契约](./saas-dispatcher.md)。D 有全局唯一身份和独立接收 Topic;这不改变内部 Unary 或 Agent→OSS 直传。**OSS 配置由 D 配置文件维护,Agent 向 D 领取临时上传 TOKENSaaS 不再提供 OSS 配置/TOKEN。** D 的签发职责保留;本文“当前实现”仍不能证明配置文件/TOKEN 全部约束已接通,尤其 D 本地对象验证不能代替 SaaS MQ verified。本轮不改变 SaaS 最终校验归属,未改 Proto/代码。
## 2. Service 方法与方向
| RPC | 方向 | 当前代码状态 | 接收端 |
@@ -39,7 +41,7 @@
1. Dispatcher 从受控 Agent endpoint 文件读取 `agent_id``cell_id`、地址和 `server_name`
2. `rpc.DialFromFiles` 使用 CA、客户端证书、私钥和 server name 建立 TLS gRPC 连接。
3. Dispatcher 对每个 endpoint 先 `GetAgentStatus`,再以返回的 `boot_id``ActivateAgent`
4. Dispatcher 生成本次 `dispatcher_epoch`Agent 用 `session_generation` 持久化 fencing 水位。
4. Dispatcher 生成本次 `dispatcher_epoch`Agent 用 `session_generation` 持久化 fencing 水位。`dispatcher_epoch` 是运行代次,不是全局唯一的 Dispatcher 逻辑 ID;后者的 MQ 关联及与内部会话的绑定待新契约冻结,当前 Proto 未因此自动增加字段。
5. Agent 新会话会 fence 旧的 `agent_id + cell_id + boot_id + epoch + generation` 组合;旧请求返回 `ABORTED`,不会自动释放未知执行。
### 3.2 mTLS 与身份
@@ -172,7 +174,7 @@ Dispatcher 从已校验的 `call.execute` 构造 bindingAgent 回报不能改
请求字段:`meta``target``state``OPEN/CLOSED/DRAINING/QUARANTINED`)、`barrier_id``expected_admission_generation``reason`
响应:`OperationReceipt` + `applied_admission_generation`。Agent 侧按目标 Agent 做 generation CAS;代次不匹配返回 `CONFLICT`。当前代码没有 `AgentCoordinator` 的调用封装,也没有把此状态操作接到 SaaS HTTP 控制入口。
响应:`OperationReceipt` + `applied_admission_generation`。Agent 侧按目标 Agent 做 generation CAS;代次不匹配返回 `CONFLICT`。当前代码没有 `AgentCoordinator` 的调用封装。该方法属于 D→A 内部准入职责,不把它直接等同 SaaS 任务控制;SaaS 业务控制只能经 MQ 进入 D,旧 HTTP 控制入口应移除
### 5.5 `GetExecutionPermit`
@@ -274,16 +276,20 @@ Dispatcher 侧额外要求:
当前 Dispatcher 验证:Agent/Cell/operation/idempotency 元数据、完整 execution/tenant binding、合法 `tenant_key`、asset ID、upload ID、正数文件大小和 SHA-256;超过 OSS 最大文件大小返回 `RESOURCE_EXHAUSTED`
成功响应:`OperationReceipt``UploadGrant``state`当前生产 Dispatcher handler
成功响应:`OperationReceipt``UploadGrant``state`目标是 D 读取自身 OSS 配置文件并向 A 提供临时 TOKEN/受限上传信息;以下记录当前 handler,不代表配置入口、TOKEN 形态和新版业务会话约束已全部验收
-`tenant_key + "\\0" + execution_id + "\\0" + asset_id` 的 SHA-256 hex 生成 object key,并加配置的 key prefix
- 将 binding、asset、grant、object key、state 持久到 Dispatcher SQLite
- grant 的过期时间由 OSS client 配置提供;
- 同 upload ID 同 binding/asset 返回原 grant;绑定不同返回冲突;
- 过期 grant 只有在显式再次 `RequestUpload` 时才替换,不自动续期。
- 过期 grant 只有在显式再次 `RequestUpload` 时才替换,不自动续期。目标流程同样由 Agent 显式向 D 重新领取 TOKEN;D 使用自身配置,不向 SaaS 申请 TOKEN,不改变原资产/会话。
新请求当前返回 `UPLOAD_STATE_REQUESTED`;持久层状态为 `granted``UPLOADING` 枚举存在,但当前 Dispatcher handler 不把 Agent 的 PUT 过程映射为该状态。
目标流程为 **D 依据自身配置文件向 A 提供临时上传 TOKEN,不向 SaaS 申请 OSS 配置/TOKEN**。D 侧配置缺失/无效时明确失败,不切换配置源;长期凭据不交给 A,不写入示例、日志或证据。TOKEN 的精确形态、SDK 能力及与 `UploadGrant` 的映射须核验冻结,不能只把现有字段改称 TOKEN 就宣称完成。
既有 SaaS 业务会话/资产登记仍经 MQ,D 关联原租户/执行/资产后才交付相应上传信息;这不是由 SaaS 签 TOKEN。业务响应可能晚于 RPC deadlineW02/W11 仍须冻结 pending、有界等待、原操作重取及最终结果,不无限阻塞 Unary、不因超时另造 upload ID。当前 D 的本地签发能力保留复用,禁止删除后改为等待 SaaS 下发配置。本段不新增 RPC/Proto 字段。
### 6.3 Agent → OSS 直接上传
Agent 获得 grant 后使用 `internal/agent.UploadClient.UploadFile`
@@ -312,6 +318,8 @@ Dispatcher 当前执行:
响应为 `OperationReceipt``state=COMPLETED``oss_id`。同 upload ID 同 OSS ID 的重复 complete 返回 `ACCEPTED`;未知 upload 返回 `NOT_FOUND`;对象校验失败返回 `FAILED_PRECONDITION` 且标记可重试。
以上是旧本地验证事实。**目标流程必须由 D 经 MQ 提交 complete,SaaS 独立验证对象后经本 D 专用 Topic 返回 verified/oss_id;D 校验原请求、租户、资产和会话并持久化后,才可记完成及写 recording.ready outbox。** D 本地 HEAD、PUT 2xx 或 broker confirm 不能替代 SaaS verified。等待中/超时/重复响应及 A 获取最终结果的 Unary 衔接由 W02/W11 冻结,尚未完成;不凭此说明宣称当前 handler 已符合目标。
## 7. 错误、幂等与未知结果
### 7.1 gRPC FailureCode
@@ -339,8 +347,8 @@ Agent 侧写操作的内存 operation key 为:
1. `GetBootstrap``SetAdmissionState` 虽有 handler,但当前 Dispatcher 启动流程没有调用完整 bootstrap/admission 编排。
2. `Execute` 的当前 RPC 实现只证明准备/幂等/配置校验,不证明 ARI/RTP/SIP 已由该 RPC 直接完成。
3. Dispatcher → SaaS 的 `recording-uploads` HTTP handshake 未在当前 Go client 中接入;当前完成路径是 Dispatcher 直接验证 OSS
4. Dispatcher → SaaS AI version GET 未接;当前 AI snapshot/authorization 由启动输入提供并在 Agent 侧校验
3. D 配置文件→临时 TOKEN→A 直传的完整接线/约束,以及 SaaS 业务会话/complete/verified 的 MQ 协调与 R12/R13 有界衔接仍待核验;D 已有签发能力保留复用,但本地对象验证不能替代 SaaS verified。旧 `recording-uploads` HTTP 方案继续废弃,不开发 client
4. SaaS AI 配置/授权的 MQ 请求响应与持久绑定尚未接;当前快照/授权由启动输入提供。旧 AI version GET 已废弃,不能作为后续实现方向
5. 不能把 generated service 中的全量方法数当作每个 listener 都可调用;实际 listener 能力以第 2 节和 `DispatcherServer` 代码为准。
## 9. 依据文件
+131 -222
View File
@@ -1,64 +1,90 @@
# SaaS ↔ Dispatcher 对接契约(实现事实
# SaaS ↔ Dispatcher 对接契约(MQ-only 设计与实现差异
## 1. 适用范围与事实等级
本文只描述当前 Go 实现能够证明的字段、方向、接口和状态;不把上游 OpenAPI 中尚未接入的 HTTP 客户端写成“已实现”
**用户已确认:RabbitMQ 是 SaaS 与 Dispatcher 的唯一交互通道,双方之间禁止任何 HTTP 请求或回调。每个 Dispatcher 都有独立、全局唯一的 ID,并通过各自独立的专用 Topic 接收事件。** 本规则覆盖执行、控制、查询、补传、AI 配置与授权、录音上传会话及完成验证,不保留 HTTP 特例或回退
| 内容 | 当前状态 | 事实来源 |
**OSS 补充确认:OSS 相关配置存于 Dispatcher 配置文件;Agent 经 Unary 向 Dispatcher 领取临时上传 TOKEN 后直传 OSS,不持有长期凭据。SaaS 不再下发 OSS 配置或上传 TOKEN。此次只调整配置/TOKEN 来源,上传完成后的 SaaS 独立校验和 MQ verified 职责保持不变。**
本文区分三种事实:
| 层级 | 本次状态 | 使用边界 |
| --- | --- | --- |
| SaaS → Dispatcher`call.execute` RabbitMQ 命令 | 已实现 | `internal/dispatcher/consumer.go``internal/store/store.go` |
| Dispatcher → SaaSRabbitMQ 业务事件及 outbox | 已实现 | `internal/contract/contract.go``internal/store/facts.go``internal/dispatcher/dispatcher.go` |
| SaaS → Dispatcher:控制、命令查询、命令补传 HTTP | 已实现,但只有当前代码列出的行为 | `internal/control/http.go` |
| SaaS → Dispatcher:通话查询、通话补传 HTTP | 路由存在,当前固定返回 `404` | `internal/control/http.go` |
| Dispatcher → SaaS:录音 upload-session/complete HTTP | 上游契约已定义,当前代码没有 SaaS HTTP client | `contracts/upstream/2026-09-19-p1-v1/saas.openapi.yaml` |
| Dispatcher → SaaSAI version GET | 上游契约已定义,当前代码没有 SaaS HTTP client;当前 Agent 启动时读取本地快照 | `contracts/upstream/2026-09-19-p1-v1/ai-config.openapi.yaml``internal/ai/*.go` |
| 已确认设计 | MQ-only、Dispatcher 唯一身份、独立 Topic | 后续设计与实现必须遵守 |
| 待冻结的消息契约 | 身份分配/持久化、Topic 命名、消息类型、关联字段、错误与超时规则 | 见 §2、§5、§6;不能据中文语义自行拼 JSON 或给旧 Schema 加字段 |
| 现有实现/旧包 | 下列旧字段、路由及代码事实 | 仅用于识别差异,不代表新设计已实现或通过验收 |
契约包固定来源为 `contracts.SourceCommit = 2026-09-19-p1-v1`。MQ 与事件正文必须以该目录中的 JSON Schema 为准,不维护第二套手写 Schema
当前固定包为 `contracts/upstream/2026-09-19-p1-v1/``contracts.SourceCommit = 2026-09-19-p1-v1`)。其中的 HTTP OpenAPI 与仅按租户路由的 MQ 拓扑**不再是目标方案**。旧包及其哈希保持不变;W01 须发布新版本、严格 Schema、拓扑及正反例,不能手改旧包、生成字段索引或通过放宽 `additionalProperties` 绕过冻结
## 2. 通信拓扑与方向
本轮只纠正文档,不修改代码、Proto 或 Schema,也不宣称新 MQ 闭环已通过。现有实现事实沿用此前核验记录,受影响部分必须按新基线重新验证。
| 方向 | 接口 | 传输 | 交付语义 |
| --- | --- | --- | --- |
| SaaS → Dispatcher | `call.execute` | RabbitMQ command exchange | Dispatcher 完成 Schema、租户路由、截止时间和 SQLite 持久化后才 ACK |
| Dispatcher → SaaS | `command.result` 等事件 | RabbitMQ event exchange + SQLite outbox | publisher confirm 只代表 broker 收到;不代表 SaaS 业务事务已应用 |
| SaaS → Dispatcher | 控制/查询/补传 | 内部 HTTP | 控制和补传的 `202` 只代表已接受/持久化,不代表 Agent 已执行 |
| Dispatcher → SaaS | 录音会话申请/完成 | 上游定义的内部 HTTP | 当前项目尚未接入;不能用本地 OSS 结果代替 SaaS `verified` |
| Dispatcher → SaaS | `GET /internal/v1/ai/agent-versions/{agent_version_id}` | 上游定义的内部 HTTP | 当前项目尚未接入;不能读取 `latest` 或用默认配置替代 |
## 2. 通信拓扑、Dispatcher 身份与交付语义
`tenant_key` 原值复制到消息体、队列、binding 和 routing key;必须是有效 UTF-8,最大 `224` 字节,不清洗、编码或截断。
### 2.1 唯一通道与边界
## 3. RabbitMQ 命令:SaaS → Dispatcher
| 交互 | 唯一允许的路径 | 语义 |
| --- | --- | --- |
| SaaS 下发执行、控制、查询、补传 | SaaS → RabbitMQ → 指定 Dispatcher 专用 Topic/队列 | 持久受理不等于执行完成;响应仍经 MQ |
| Dispatcher 回传结果、查询响应和业务事件 | Dispatcher → RabbitMQ → SaaS 专用订阅 | 能识别来源 Dispatcher、租户、原请求及业务对象 |
| Dispatcher 获取 AI 配置/授权 | Dispatcher → RabbitMQ → SaaSSaaS → RabbitMQ → 原 Dispatcher 专用订阅 | 固定租户和不可变版本,响应不能被其它 Dispatcher 消费 |
| 业务上传会话、complete/verified | Dispatcher ↔ RabbitMQ ↔ SaaS | 只传业务会话/对象元信息与验证结果;不下发 OSS 配置或 TOKEN,不传录音字节 |
| 临时上传 TOKEN 领取/显式重新申请 | Agent ↔ Unary ↔ Dispatcher | D 依据自身配置文件提供受限 TOKEN/上传目标;不向 SaaS 申请 TOKEN |
| Dispatcher ↔ Agent | 既有 Unary gRPC | 不改为内部 MQ,也不让 Agent 直连 SaaS |
| Agent → OSS | 受限目标上的直接 PUT | 保留 HTTP(S) 对象上传;禁止的是 SaaS↔Dispatcher HTTP,不是 OSS/ARI/供应商协议或 gRPC 的 HTTP/2 |
### 3.1 拓扑
### 2.2 全局唯一身份与独立 Topic
| 元素 | 值 |
| --- | --- |
| command exchange | `agent-call.commands.v1`durable `direct` |
| tenant queue | `agent-call.executor.{tenant_key}.v1`durable、单一精确 binding |
| routing key | `agent-call.tenant.{tenant_key}.call.execute` |
| event exchange | `agent-call.events.v1`durable `topic` |
| Dispatcher dead-letter exchange | `agent-call.dead-letter.v1`durable `topic` |
| 默认 prefetch | `1` |
- `dispatcher_id` 在本文中是**逻辑身份名称,尚不是旧 MQ Schema 或 Proto 已有字段**。每个 Dispatcher 的 ID 必须独立、全局不重复;不能拿租户 ID、Agent ID、Cell ID、地址或启动代次代替。
- Dispatcher 身份与 `dispatcher_epoch` 分开:前者识别 Dispatcher,后者用于一次运行所有权/会话的 fencing。正常重启、恢复时如何保持身份及拒绝重复身份,须在 W01 冻结并由 W05 验证,不因 epoch 改变就丢弃原消息、执行或资产归属。
- 每个 Dispatcher 有独立的接收 Topic 及对应队列/绑定;多个 Dispatcher **不能共用一条接收队列竞争消费指定目标的消息,也不能全部订阅同一广播 Topic 后仅靠正文过滤**
- RabbitMQ 的 Topic 订阅由 exchange、routing key、queue 和 binding 表达;具体名称、类型、绑定格式及 ID 在信封/属性中的位置随新版本冻结。本轮不另造一套可直接部署的命名格式。
- SaaS 发给 D1 的命令、配置、授权和上传结果,只能进入 D1 的专用接收路径;D2 的路径与之独立。D1 发出的响应/事件须能回溯 D1 与原请求。SaaS 订阅布局亦由同一版契约定义,不假定现有共享结果队列已满足新约束。
- 独立 Dispatcher 路由不替代租户隔离:保留租户独立队列、有界窗口、原值 `tenant_key` 和复合幂等语义;新拓扑必须同时区分 Dispatcher 与租户,不能退化为 Dispatcher 内所有租户共享无界队列。
- `tenant_key` 不清洗、编码或截断。旧布局的 224 UTF-8 字节预算不能在加上 Dispatcher 身份后直接照搬;W01 须校验完整 routing key/queue 名长度及分隔符、通配符边界,超限拒绝发布并保留源任务,不改变既有租户标识。
Dispatcher 为每个消费租户声明 command queue 和 `.dlq.v1` 队列。SaaS 结果队列由 SaaS 管理,拓扑基线为 `agent-call.saas.events.v1`binding `agent-call.#`
P1 仍只运行一个单活 Dispatcher。现在必须在合同及本地路由测试中区分两个 Dispatcher 身份;这不授权多节点上线、多 Dispatcher 共享配额、自动选主、HA 或自动迁移任务。未知执行不得因目标离线而改投另一个 Dispatcher
### 3.2 `call.execute` 外壳
### 2.3 请求、响应、持久化和恢复
消息为 JSON`additionalProperties: false`
1. 所有请求和响应都走 MQ;异步响应必须关联原请求、目标/来源 Dispatcher、原租户及业务对象。精确键名、关联方式、消息枚举、错误与期限在 W01 冻结;`trace_id` 不能代替业务幂等身份。
2. 发送意图/业务变更与 outbox 同事务;接收方持久 inbox 和处理状态后才 ACK。相同业务请求的重投返回原决定,同身份异内容冲突,不能生成第二次拨号或上传资产。
3. publisher confirm、消费者 ACK、业务 accepted、控制 applied 和 SaaS verified 各自独立。confirm 只说明 broker 接收,不等于对端已应用;接收 ACK 不能代替业务响应。
4. 响应重复、乱序、迟到、丢失和重启后恢复必须按原关联处理;响应等待有界,不跨网络持有 SQLite 写事务。超时表示未获确定结果,不等于业务失败,不允许 HTTP 查询兜底、换 ID 重拨或静默换 Dispatcher。
5. 队列满、无绑定/不可路由、broker 断连必须可见并保留原消息。不能通过 confirm 单独认定路由成功;须覆盖 mandatory/return 和实际目标消费证据。
6. MQ 往返响应不意味着新增一套任意 application receipt 协议。已有业务结果和 verified 语义保留;需要补齐的响应消息必须进入版本化契约,不能借现有八类业务事件自由透传。
## 3. RabbitMQ 执行命令:旧基线与待改项
### 3.1 旧拓扑(仅作实现差异记录,禁止用于新接入)
| 元素 | 旧值 | 新设计差异 |
| --- | --- | --- |
| command exchange | `agent-call.commands.v1`durable `direct` | 须按 §2 冻结面向指定 Dispatcher 的 Topic 拓扑 |
| tenant queue | `agent-call.executor.{tenant_key}.v1` | 没有 Dispatcher 身份,不能让多个 D 共用 |
| routing key | `agent-call.tenant.{tenant_key}.call.execute` | 仅区分租户/操作,不能唯一指定 Dispatcher |
| event exchange | `agent-call.events.v1`durable `topic` | 新发布路径须可识别来源 Dispatcher |
| dead-letter exchange | `agent-call.dead-letter.v1`durable `topic` | 恢复必须保留原 Dispatcher、租户和消息身份 |
| 默认 prefetch | `1` | 保持有界消费;不是多 Dispatcher 隔离证明 |
旧实现按消费租户声明 command queue 和 `.dlq.v1`SaaS 结果队列基线为 `agent-call.saas.events.v1`、binding `agent-call.#`。这些名称记录旧包事实,不构成新拓扑批准。
### 3.2 旧 `call.execute` 外壳
以下是旧 Schema 的精确字段记录,`additionalProperties: false`;新 Dispatcher 路由与关联尚未进入该外壳,不能直接追加字段并宣称兼容。
| 字段 | 类型 | 必填/约束 |
| --- | --- | --- |
| `schema_version` | string | 必须为 `1.0` |
| `command_type` | string | 必须为 `call.execute` |
| `schema_version` | string | 旧版固定 `1.0` |
| `command_type` | string | 固定 `call.execute` |
| `command_id` | string | 1128 字节;不可含空白、`/``\\` |
| `tenant_id` | string | 同上 |
| `tenant_key` | string | 非空有效 UTF-8;实现额外限制 224 字节 |
| `tenant_key` | string | 非空有效 UTF-8实现额外限制 224 字节 |
| `trace_id` | string | 同 `id` 约束 |
| `issued_at` | RFC3339 时间 | 必填 |
| `not_after` | RFC3339 时间 | 必填;Dispatcher 接收时已到期则拒绝 |
| `payload` | object | 必须符合 `executePayload` |
| `not_after` | RFC3339 时间 | 必填;不能因重投延期 |
| `payload` | object | 符合 `executePayload` |
### 3.3 `payload` 数据结构
### 3.3 `payload` 数据结构
| 字段 | 类型 | 约束 |
| --- | --- | --- |
@@ -66,26 +92,21 @@ Dispatcher 为每个消费租户声明 command queue 和 `.dlq.v1` 队列。SaaS
| `task_id` | string | 必填 ID |
| `task_item_id` | string | 必填 ID |
| `task_revision` | integer | `>= 1` |
| `callee` | string | 1256 字符;保留业务原始被叫号码 |
| `callee` | string | 1256 字符;保留原始被叫号码 |
| `route_policy_id` | string | 必填 ID |
| `caller_profile_id` | string | 必填 ID |
| `agent_version_id` | string | 必填 ID |
| `variables` | object | 必填;当前 Schema 允许任意附加属性,业务白名单仍由上游约束 |
| `variables` | object | 必填; Schema 允许附加属性,业务白名单仍受源约束 |
| `ring_timeout_ms` | integer | `>= 1` |
| `max_call_duration_ms` | integer | `>= 1` |
Go 实现对应 `internal/contract.ExecutePayload``payload``json.RawMessage` 保留原始 JSONDispatcher 不把变量转成另一套协议
对应 `internal/contract.ExecutePayload``payload``json.RawMessage` 保留原始 JSON。改传输不授权改变业务号码、版本、摘要或新增 MQ mode 字段
### 3.4 接收、幂等与 ACK
### 3.4 接收事实与新验收要求
1. `ConsumeTenant` 校验 `tenant_key`,声明租户队列,再以 `prefetch=1` 消费
2. `AcceptCommand` 调用 `Store.IngestCommand`:校验源 Schema、routing key、`not_after`,计算原始 body 的 SHA-256。
3.`command_id` 查 inbox:同 ID 同 body 为重复;同 ID 不同 body 为冲突并拒绝。
4. 新命令在一个 SQLite 事务内写入 inbox、task 和 `command.result(accepted)` outbox。
5. handler 成功后才 ACK。Schema、JSON、租户路由等永久错误 `Reject(false)`,经死信队列处理;其它错误 `Nack(requeue=true)`
6. `command_id``execution_id``task_id` 等业务标识不因重投而更换;`not_after` 不因重投延期。
`ConsumeTenant` 校验租户、声明队列,以 `prefetch=1` 消费;`AcceptCommand`/`Store.IngestCommand` 校验源 Schema、routing key、`not_after` 和原始 body SHA-256。当前以 `command_id` 查 inbox,同 ID 同 body 为重复、异 body 为冲突;新命令同事务写 inbox、task、`command.result(accepted)` outbox。成功后 ACK;永久错误 `Reject(false)`,其余错误 `Nack(requeue=true)`
当前 `command.result` 初始 payload
旧初始结果 payload
```json
{
@@ -98,11 +119,13 @@ Go 实现对应 `internal/contract.ExecutePayload``payload` 以 `json.RawMess
}
```
## 4. RabbitMQ 事件:Dispatcher → SaaS
新验收须补充 §2 的 Dispatcher 定向/来源校验、所有 MQ 交互的关联和持久恢复,以及租户复合幂等要求。不能把当前仅按 `command_id` 查重的事实写成这些要求已满足。
### 4.1 通用外壳
## 4. RabbitMQ 业务事件:旧字段语义与新路由要求
事件 routing key 固定为 `agent-call.{event_type}`。外壳字段全部必填:
### 4.1 旧通用外壳
旧 routing key 为 `agent-call.{event_type}`;新来源 Dispatcher 的表达待 W01 冻结。以下字段在旧版全部必填:
```json
{
@@ -120,30 +143,27 @@ Go 实现对应 `internal/contract.ExecutePayload``payload` 以 `json.RawMess
}
```
`event_type` 的源 Schema 枚举为:
源枚举为 `command.result``call.status``transcript.updated``call.finished``recording.ready``recording.failed``transcript.failed``contact.opt_out`。它们不自动覆盖新增的配置/查询/上传响应消息。
`command.result``call.status``transcript.updated``call.finished``recording.ready``recording.failed``transcript.failed``contact.opt_out`
事件 payload 必须同时通过 `mq.schema.json``event-payloads.schema.json``EventBuilder` 不接受未知字段;`aggregate_version` 由 Dispatcher SQLite 按 `aggregate_type + aggregate_id` 递增,Agent 不能指定它。
事件同时通过 `mq.schema.json``event-payloads.schema.json``EventBuilder` 拒绝未知字段。旧实现由 SQLite 按 `aggregate_type + aggregate_id` 递增版本,Agent 不能指定版本;新基线仍须验证租户/Dispatcher 归属,不将旧实现等同完整隔离
### 4.2 当前代码实际生成的事件
| 事件 | 事实来源 | 当前代码行为 |
| --- | --- | --- |
| `command.result` | 命令持久受理、执行接受事实 | 命令接收路径直接生成;Agent `EXECUTION_ACCEPTED` fact 也映射到该类型 |
| `call.status` | Agent `CALL_STATUS` fact | Dispatcher 校验 fact 后生成 |
| `call.finished` | Agent `CALL_FINISHED` fact | Dispatcher 校验 fact 后生成 |
| `transcript.updated` | Agent `TRANSCRIPT_UPDATED` fact | 保留实时文字事件名,不使用 `call.transcript` |
| `transcript.failed` | Agent `TRANSCRIPT_FAILED` fact | 当前代码尝试映射,但使用 `aggregate_type=transcript``mq.schema.json` 只允许 `transcript_segment`,因此该路径当前无法通过双 Schema 校验 |
| `contact.opt_out` | Agent `CONTACT_OPT_OUT` fact | Dispatcher 校验 fact 后生成 |
| `recording.ready` | 已验证 OSS 上传完成 | `CompleteUpload` 将完成状态和 outbox 放在同一 SQLite 事务 |
| `recording.failed` | 上游 Schema | 当前 Go 代码没有对应 `FactKind` 或事件生成路径 |
| 事件 | 当前代码行为 |
| --- | --- |
| `command.result` | 命令接收及 `EXECUTION_ACCEPTED` fact 生成 |
| `call.status` / `call.finished` | 对应 Agent fact Dispatcher 校验后生成 |
| `transcript.updated` | 实时文字;不得改名 `call.transcript` |
| `transcript.failed` | 当前映射为 `aggregate_type=transcript`,但 Schema 要求 `transcript_segment`,该路径阻塞 |
| `contact.opt_out` | 对应 Agent fact 经 Dispatcher 校验后生成 |
| `recording.ready` | 旧 `CompleteUpload` 本地对象验证后同事务写完成状态/outbox;不等于 SaaS MQ verified |
| `recording.failed` | Schema 已定义,当前无对应 FactKind/生成路径 |
`RECORDING_PROGRESS` fact 只写入 Dispatcher 事实表,不生成 SaaS MQ 事件。`TRANSCRIPT_FAILED` 虽在两个事件 payload Schema 中声明,但当前 `internal/rpc/dispatcher_events.go` 将其聚合类型写成 `transcript`,随后被 `mq.schema.json` 拒绝;不能把该路径记为已交付
`RECORDING_PROGRESS` 只保存 fact,不发布 MQ 事件。上述已知实现差异不因本次文档改写而消失
### 4.3 已冻结的专属 payload 关键字段
### 4.3 专属 payload 关键字段
完整约束 `event-payloads.schema.json`;下面列出对接方必须使用的字段,不是新的 Schema
完整约束仍在固定包 `event-payloads.schema.json`;下表不是第二套 Schema
| 事件 | 必填字段 |
| --- | --- |
@@ -156,189 +176,78 @@ Go 实现对应 `internal/contract.ExecutePayload``payload` 以 `json.RawMess
| `transcript.failed` | `call_id`, `reason_code`, `retryable` |
| `contact.opt_out` | `call_id`, `task_id`, `task_item_id`, `requested_at` |
`recording.ready` 只能表示对象已验证;不能用 PUT 成功、ETag本地路径替代 `oss_id` 和 SHA-256
新设计中 `recording.ready` 只能在 D 收到并持久校验 SaaS 的 MQ verified 结果及 `oss_id` 后发布;PUT 2xx、ETag本地路径或 D 单独 HEAD 成功都不替代该结果
### 4.4 Outbox 交付
`Store.RecordExecutionFact`、命令接收和上传完成均可在状态事务中写 outbox。`Dispatcher.FlushOutbox`
`Dispatcher.FlushOutbox` claim `pending/retry``dispatching`,发布 persistent JSON 并等 confirm;成功记 `published`,失败记 `retry`,重启把 `dispatching` 恢复为 `retry`。重复 fact 同 `fact_id + content_sha256` 不生成第二条事件,异摘要冲突。SaaS 按租户/事件身份幂等应用。
- claim `pending/retry` 记录并标记 `dispatching`
- 通过 RabbitMQ persistent JSON message 发布并等待 publisher confirm
- 成功标记 `published`;失败标记 `retry`
- 重启时将 `dispatching` 恢复为 `retry`
新设计将这一持久交付原则覆盖请求与响应,补齐不可路由、来源/目标、相关状态恢复验证。业务事件重投保留原身份、内容和域版本,broker confirm 不替代 SaaS 应用收讫。
重复 fact 使用相同 `fact_id + content_sha256` 时不创建第二条事件;同 fact ID 不同摘要返回冲突。SaaS 必须以 `event_id` 做 inbox 幂等,broker confirm 不能当作 SaaS 应用收讫。
## 5. SaaS → Dispatcher:控制、查询和补传全部经 MQ
## 5. Dispatcher 提供的 HTTP 接口:SaaS → Dispatcher
### 5.1 待冻结内容
### 5.1 公共请求头
当前 `internal/control.Handler` 要求:
- `Authorization: Bearer <token>`;配置了 token 时必须精确匹配;
- `X-Tenant-ID`
- `X-Request-ID`
当前 Handler 未强制检查权限 scope。源 `executor.openapi.yaml` 另外要求 `Idempotency-Key`;控制接口当前代码只把该值传入存储层,没有把缺失值直接拒绝,这属于实现与源 OpenAPI 的已知差异。
错误响应为 JSON
```json
{
"type": "about:blank",
"title": "<HTTP status>",
"status": 400,
"code": "<code>",
"detail": "<detail>",
"request_id": "<X-Request-ID>",
"retryable": false
}
```
以下只定义已确认业务语义,**消息类型名、信封字段、响应/错误枚举尚未发布**。旧 HTTP header、URL 和状态码不能直接作为 MQ 合同,也不能塞进旧 `call.execute` 或宽松 metadata 中。
### 5.2 控制任务
**方向:SaaS → Dispatcher**
SaaS 将 pause/resume/stop 控制发到目标 Dispatcher 专用 Topic。保留原 `command_id`、租户/任务归属、`expected_task_revision` CAS、`active_call_policy=drain|hangup` 与原因语义。D 持久受理后经 MQ 回报 accepted;经 Agent 屏障/挂断事实确认后才能回报 applied。重复控制不重复增加 revision,冲突不能伪装成功,stopped 不可恢复。
`POST /internal/v1/outbound/tasks/{task_id}/controls`
### 5.3 查询命令与通话
请求体:
请求和响应都经 MQ;查询固定原 `command_id``call_id`,返回可证明的命令、执行、控制、通话及独立资产状态。响应关联原查询和目标 Dispatcher;不存在、保留过期与暂时不可用分开表达,精确错误码待冻结。超时不走 HTTP 补查,也不证明原执行未发生。
| 字段 | 类型/约束 |
| --- | --- |
| `command_id` | 必填 ID;当前代码用于响应,不单独生成控制 outbox |
| `action` | `pause``resume``stop` |
| `expected_task_revision` | integer`>=1`CAS 版本 |
| `active_call_policy` | 可选:`drain``hangup` |
| `reason` | 非空,最大 512 字符 |
### 5.4 整体补传
当前成功响应为 `202`
仅允许以 `call_id``source_command_id` 请求整体业务结果补传,不增加 task/execution 范围或局部筛选。固定受理截止点,重发原事件 ID/内容/版本,实时优先、分批有界;补传自身结果不能递归进入集合。
```json
{
"command_id": "<id>",
"tenant_id": "<tenant>",
"tenant_key": "<tenant_key>",
"task_id": "<task_id>",
"status": "accepted",
"requested_task_revision": 1,
"accepted_at": "2026-09-19T00:00:00Z"
}
```
补传不是把 `call.execute` 重新发布来重新执行。旧 HTTP source-command replay 当前重发原命令的行为不满足目标整体结果补传,须列入 W12 修正;不能因改为 MQ 就保留这一错误语义。
实现先按 `X-Tenant-ID + task_id` 查询任务,再以任务的 `execution_id` 调用 `ApplyControlDetailed`。版本冲突为 `409`;任务不存在为 `404`;输入错误为 `400``202` 不表示 Agent 已应用控制。
### 5.5 已废弃 HTTP 入口的处理
### 5.3 查询命令
`internal/control/http.go` 当前存在控制、命令查询/补传 handler,通话查询/补传路由固定返回 `404`。这些只是旧实现事实,**不再是可接入或待扩展的 SaaS 接口**。W05/W12 应移除这些 SaaS HTTP 业务入口及相关部署说明,不保留兼容层、并行双通道或 HTTP 兜底。本轮未改代码,不能宣称入口已移除。
**方向:SaaS → Dispatcher**
## 6. Dispatcher → SaaSAI 配置与上传业务协调经 MQ
`GET /internal/v1/outbound/commands/{command_id}`
### 6.1 Dispatcher 配置、临时 TOKEN 与 complete/verified
当前成功响应字段:
1. **OSS 配置唯一来源是 D 的配置文件**,包括所需服务地址、bucket、对象路径规则及签发授权所需的受控凭据配置/引用。配置文件格式及具体字段沿现有能力核验后冻结,本轮不新增猜测的配置键,也不在文档/样例/源码/日志中写实际密钥或完整 TOKEN。配置缺失或无效须明确失败,不改向 SaaS 取配置,不用 Agent 本地配置兜底。
2. Agent 经 R12 向 D 领取临时上传 TOKEN。D 校验并持久关联原租户/执行/资产,依据自身配置复用官方 SDK 提供仅本对象可用、有有效期/方法/大小约束的 TOKEN 及必要上传目标信息,经 Unary 返回 Agent。Agent 不取得 D 的长期凭据或完整配置文件。精确 TOKEN 形态及与现有 `UploadGrant` 的映射待核验,不假定某个 SDK/Proto 已满足全部约束。
3. **业务上传会话与 TOKEN 签发分开**:既有 SaaS 业务会话/资产登记语义仍经 MQ,响应回原 D 专用 Topic;但该响应不再承担 OSS 配置或 TOKEN 的来源。`upload_id`、对象引用及会话/资产关联按新版合同冻结,不因 TOKEN 过期另造资产,也不新加一套未批准的登记协议。
4. Agent 直接 PUT 文件到 OSS,经 R13 只提交原资产/会话、对象引用、大小和 SHA-256 等完成元信息。D 经 MQ 提交 completeSaaS 仍独立验证对象后经 MQ 返回 verified、`oss_id` 或明确失败;D 持久校验 verified 后同事务写资产状态和 `recording.ready` outbox。
5. TOKEN 过期/失效由 Agent 显式向 D 重新申请,D 仍按自身配置提供,不向 SaaS 申请 TOKEN,不自动续期/重试。MQ 响应丢失/重复沿原资产、请求和 `upload_id` 恢复;未获 SaaS verified 保留待完成状态和文件,不提前 ready。
```json
{
"command_id": "<id>",
"command_type": "call.execute",
"tenant_id": "<tenant>",
"tenant_key": "<tenant_key>",
"task_id": "<task_id>",
"execution_id": "<execution_id>",
"call_id": null,
"status": "persisted",
"reason_code": null,
"wait_reason_code": null,
"accepted_at": "<inbox.persisted_at>",
"waiting_since": null,
"admission_deadline": "<envelope.not_after>",
"requested_task_revision": 1,
"applied_task_revision": null,
"task_state": null,
"aggregate_version": 1,
"updated_at": "<inbox.received_at>"
}
```
复用的业务数据包括 `recording_id``call_id``content_type``size_bytes`、SHA-256、声道/采样率/时长。D→A 的临时授权含原会话、TOKEN/上传目标、方法、必要 headers、约束和有效期;D↔SaaS 的 MQ 只承担业务元信息/会话及最终验证,不传 D 的配置文件、长期凭据或临时 TOKEN。SaaS 为独立校验取得必要对象定位及读取能力的既有业务要求仍须满足,精确合同在 W01 冻结,不能假定“D 持有配置”即代表 SaaS 已能验证。
查询按租户隔离;不存在返回 `404`。当前实现不返回 call snapshot、控制应用版本或完整状态机
业务会话/complete 的 MQ 异步结果与 R12/R13 Unary 的衔接须由 W02/W11 冻结 pending、超时、原操作重取及最终结果;TOKEN 本身来自 D,不等待 SaaS 下发配置/TOKEN。不能无限阻塞 RPC,也不能收到 broker confirm 就返回已完成。旧 `saas.openapi.yaml` 仅作语义对照,不是新 MQ Schema;本轮不修改 Proto/配置格式或新增字段
### 5.4 按 source command 补传
当前 D 用 `internal/oss` client 签发 grant 的职责与新确认方向一致,**不应再把 D 签发能力列为待删除或“仅故障回退”**;但配置文件读取、TOKEN 约束及完整接线仍须核验。当前 D 本地验证对象即发 ready 的旧行为仍不能替代 SaaS MQ verified。旧 SaaS HTTP upload-session/complete 方案继续废弃,不开发 HTTP client。
**方向:SaaS → Dispatcher**
### 6.2 AI 不可变配置与授权
`POST /internal/v1/outbound/commands/{source_command_id}/replays`
D 根据 MQ 任务中的原 `tenant_id/tenant_key + agent_version_id`,通过 MQ 向 SaaS 获取不可变配置及有效授权,SaaS 通过原 D 专用 Topic 返回;授权/撤销等交互同样不能走 HTTP。
请求体:
保留既有版本、`immutable``content_sha256``config` 及租户授权语义;源 Schema、不可变摘要、有效期、撤销、能力和供应商受控引用均校验后持久绑定到原执行,再交付 Agent。缓存按租户/版本隔离,在途/原排队任务不漂移,同版本异内容拒绝,0/false 与未提供保真;无有效授权时拒绝新准入,不用 latest、CLI/env 或 SDK 默认值兜底。
```json
{"command_id":"<new-request-id>","reason":"<1..512 chars>"}
```
`ai-config.openapi.yaml` 的 AI GET 已废弃为 D↔SaaS 接入方式,不再开发该 HTTP client。当前 `AISnapshotRaw`/`AIAuthorizationRaw` 启动注入和 Agent 本地校验只证明旧路径;MQ 配置/授权、关联与持久恢复仍待 W01/W07 实现验证。消息细节不能由旧 OpenAPI 自动推定。
必须提供 `Idempotency-Key`。成功响应为 `202`
## 7. 待完成门禁与禁止误读
```json
{"command_id":"<new-request-id>","status":"accepted","snapshot_cutoff":"<now>"}
```
当前实现读取原 inbox body,保留原 `command_id` 和原消息内容,按原租户 routing key 写入 outbox`replay-<Idempotency-Key>` 只是 outbox event ID,不是新的业务 command ID。不存在 source command 返回 `404`。原消息的 `not_after` 不会被改写,重发后仍可能因过期被拒绝。
### 5.5 当前不可用的通话接口
以下路由已匹配,但当前固定返回 `404`
- `GET /internal/v1/outbound/calls/{call_id}`
- `POST /internal/v1/outbound/calls/{call_id}/replays`
不得依据上游 OpenAPI 的 `Call` 结构宣称当前代码已经提供通话查询或通话补传。
## 6. Dispatcher → SaaS 的已声明、未接入 HTTP
### 6.1 录音 upload session / complete
权威源为 `contracts/upstream/2026-09-19-p1-v1/saas.openapi.yaml`
| 方向 | 方法 | 路径 |
| 门禁 | 完成证据 | 当前状态 |
| --- | --- | --- |
| Dispatcher → SaaS | `POST` | `/internal/v1/outbound/recording-uploads` |
| Dispatcher → SaaS | `POST` | `/internal/v1/outbound/recording-uploads/{upload_id}/complete` |
| W01 身份/Topic/消息冻结 | 新版本/来源/哈希、完整消息 Schema、路由、关联/错误/期限及正反例 | 待冻结 |
| W05/W12 MQ 控制面 | 全部交互持久接收/响应、移除旧 HTTP、补传语义正确 | 待实现/验证 |
| W07 MQ AI | 不可变配置/授权、迟到/撤销/重复与缓存隔离 | 待实现/验证 |
| W02/W11 上传授权与业务协调 | D 配置文件→临时 TOKEN→A 直传;R12/R13 与 MQ 业务结果有界衔接、SaaS verified 后才 ready | 待核验/实现/验证 |
| W13/W14 本地联合回归 | D1/D2 Topic 隔离 fixture、身份冲突、broker 故障、全流程无 SaaS↔D HTTP | 待验证 |
公共要求:Bearer、`X-Tenant-ID``X-Request-ID``Idempotency-Key`
路由 fixture 只验证不同 Dispatcher 互不抢收,不把多 Dispatcher 调度或真实 SaaS 联调引入本轮。旧包、旧 HTTP handler、单租户 broker 测试和本地 OSS 成功,均不代表以上门禁通过
申请请求的实际源字段为 `recording_id``call_id``content_type=audio/wav``size_bytes``checksum_algorithm=SHA-256``checksum``channels=1``sample_rate_hz``duration_ms`。返回 `upload_id``recording_id``expires_at``upload_method=PUT``upload_url``required_headers``constraints` 和可空 `oss_id`
## 8. 依据与相关文档
complete 请求为 `recording_id``size_bytes``checksum_algorithm=SHA-256``checksum`、可空 `etag`;成功返回 `status=verified``oss_id``verified_at`
当前 Go 代码没有调用这两个 SaaS HTTP 路径。当前 Dispatcher upload handler 直接使用本地 `internal/oss` client 签发和验证 OSS grant;这条路径属于 Dispatcher ↔ Agent 的当前实现,不能写成 SaaS 已联调
### 6.2 AI immutable version GET
权威源为 `contracts/upstream/2026-09-19-p1-v1/ai-config.openapi.yaml`
`GET /internal/v1/ai/agent-versions/{agent_version_id}`
请求要求租户和 request header,返回不可变版本的 `tenant_id``agent_version_id``status``immutable=true``content_sha256``config``config` 顶层由 `agent_version_id``immutable=true``asr``conversation` 以及 full-AI 模式所需的 `llm``prompt``tts` 组成;ASR-only 不得带后三者。
当前实现只在 `internal/rpc.ServerOptions` 接收本地 `AISnapshotRaw`/`AIAuthorizationRaw`,由 `internal/ai/snapshot.go``internal/ai/authorization.go` 校验版本、摘要、租户、模式、有效期和 egress;没有 SaaS GET client,也没有“断 SaaS 后使用任意默认配置”的回退。
## 7. 禁止误读
1. RabbitMQ publisher confirm ≠ SaaS 已应用。
2. HTTP `202 accepted` ≠ Agent 已执行或控制已生效。
3. 当前本地 OSS grant/verify ≠ SaaS recording session/verified 已完成。
4. Schema 支持 `recording.failed`、通话查询或 AI GET ≠ 当前 Go 代码已经生成或提供这些接口。
5. 任何重试都必须保留原 `command_id``execution_id``event_id``upload_id` 的业务语义;未知执行不能换 ID 重拨。
## 8. 依据文件
- `internal/contract/contract.go`
- `internal/mq/amqp.go`
- `internal/tenant/routing.go`
- `internal/dispatcher/consumer.go`
- `internal/dispatcher/dispatcher.go`
- `internal/store/store.go`
- `internal/store/facts.go`
- `internal/control/http.go`
- `contracts/upstream/2026-09-19-p1-v1/mq.schema.json`
- `contracts/upstream/2026-09-19-p1-v1/event-payloads.schema.json`
- `contracts/upstream/2026-09-19-p1-v1/mq-topology.md`
- `contracts/upstream/2026-09-19-p1-v1/executor.openapi.yaml`
- `contracts/upstream/2026-09-19-p1-v1/saas.openapi.yaml`
- `contracts/upstream/2026-09-19-p1-v1/ai-config.openapi.yaml`
- [计划与需求阅读索引](../plan-0918.md):§1.2、§8.2 的 MQ-only 修订与状态
- [时间泳道图](./saas-rabbitmq-oss-dispatcher-agent-timeline.md):目标流程,不冒充当前实现。
- [Dispatcher ↔ Agent 契约](./dispatcher-agent.md):现有 Proto/handler 事实与上传协调待改项
- 旧实现事实:`internal/contract/contract.go``internal/mq/amqp.go``internal/tenant/routing.go``internal/dispatcher/consumer.go``internal/dispatcher/dispatcher.go``internal/store/store.go``internal/store/facts.go``internal/control/http.go`
- 旧固定包:`contracts/upstream/2026-09-19-p1-v1/``mq.schema.json``event-payloads.schema.json``mq-topology.md``executor.openapi.yaml``saas.openapi.yaml``ai-config.openapi.yaml`;保留原样,不代表 MQ-only 新契约已发布。
@@ -0,0 +1,175 @@
# SaaS ↔ RabbitMQ ↔ Dispatcher ↔ Agent ↔ OSS 时间泳道图
## 1. 用途与事实等级
**本图描述用户已确认的目标设计:SaaS 与 Dispatcher 的所有交互只经 RabbitMQ,不存在双方直连 HTTP。每个 Dispatcher 具有独立、全局唯一的 ID 和独立接收 Topic。** 不再把 AI GET 或录音 HTTP 握手列为待实现目标。
P1 运行范围仍为单节点、单 Cell、单租户、单活 Dispatcher;用 D1/D2 说明消息隔离,并不扩大为多 Dispatcher 调度或 HA。Dispatcher↔Agent 保持 Unary gRPCAgent→OSS 保持直接上传,音频字节不经过 Dispatcher 或 MQ。**OSS 配置存于 D 的配置文件,Agent 向 D 领取临时上传 TOKENSaaS 不下发 OSS 配置/TOKEN。上传完成仍由 SaaS 独立校验并经 MQ 返回 verified,此职责不变。**
- **已确认**MQ-only、全局唯一 Dispatcher 身份、专用 Topic、原业务与幂等边界。
- **待冻结**:精确 Topic/队列/绑定、身份生命周期、MQ 消息类型/字段、请求响应关联/错误,以及异步结果与现有 Unary 的衔接。
- **现有实现不等于目标完成**:旧租户 MQ、HTTP 控制、启动注入 AI 和上传待改项见 §5;D 签发 TOKEN 的职责保留,但 D 本地对象校验不能替代 SaaS verified。
图中“配置请求”“控制请求”“verified 响应”等为中文语义标签,**不是已发布的 command_type/event_type**。精确结构须在新版契约包冻结;旧 `contracts/upstream/2026-09-19-p1-v1/` 和 Proto 不因文档修改而自动支持这些交互。
## 2. 独立 Dispatcher 的订阅关系
```mermaid
flowchart LR
S[SaaS] --> Q[RabbitMQ]
Q --> T1[D1 专用接收 Topic / 队列]
Q --> T2[D2 专用接收 Topic / 队列]
T1 --> D1[Dispatcher D1 / 全局唯一 ID]
T2 --> D2[Dispatcher D2 / 另一全局唯一 ID]
D1 --> Q
D2 --> Q
Q --> ST[SaaS 专用订阅]
ST --> S
```
D1/D2 不竞争同一条接收队列,不靠全量广播后过滤模拟隔离。发往 D1 的任务、控制、AI 配置/授权、业务上传会话/验证结果只能由 D1 接收;D 发出的消息须能识别来源及原请求。Dispatcher 身份不替代租户身份,也不等于 `dispatcher_epoch`。具体命名/关联及完整路由长度约束见 [SaaS↔Dispatcher §2](./saas-dispatcher.md#2-通信拓扑dispatcher-身份与交付语义)。
## 3. 目标时间泳道图
时间自上而下;所有 S↔D 路径均经过 MQ。MQ 中的 D 接收订阅和 SaaS 接收订阅是独立方向,不是两套业务流程。为避免伪造字段,图只索引现有业务数据与待冻结语义。
```mermaid
sequenceDiagram
autonumber
participant S as SaaS
participant SQ as RabbitMQ / SaaS 专用订阅
participant DQ as RabbitMQ / D1 专用接收 Topic 与队列
participant D as Dispatcher D1 / 全局唯一 ID
participant A as Agent
participant O as OSS
Note over S,O: MQ-only 目标流程;新消息 Schema 待冻结,不代表已经实现。
Note over DQ,D: D1 与 D2 的接收路径互相独立;本图仅展开 D1。
S->>DQ: call.execute(明确目标 D1;旧 payload 语义见 saas-dispatcher §3
DQ->>D: 按 D1 专用绑定投递
Note over D: 校验目标、租户、版本、幂等及期限;持久 inbox/task/outbox。
D-->>DQ: 持久成功后 ACK
D->>SQ: command.result accepted(来源 D1、原命令关联)
SQ->>S: 业务受理结果
Note over S,SQ: SaaS 持久接收后 ACKpublisher confirm 不等于业务已应用。
opt 当前执行尚无合法绑定的配置与有效授权
D->>SQ: AI 配置/授权请求(原租户、agent_version_id、请求关联;§6.2
SQ->>S: 配置/授权请求
S->>DQ: 不可变配置、摘要、授权或明确拒绝(目标 D1、原请求关联)
DQ->>D: 配置/授权响应
Note over D: 校验并持久绑定到原执行;无有效授权不得继续发起。
D-->>DQ: 持久成功后 ACK
end
D->>A: GetAgentStatusdispatcher-agent §4.1、§5.1
A-->>D: AgentStatus、boot、能力与资源
D->>A: ActivateAgent(§5.2
A-->>D: ACTIVE + Session
Note over D,A: 原执行配置交付、bootstrap/admission 完整编排仍须按契约核验,不由本图宣称完成。
D->>A: GetExecutionPermit(原 binding、配置摘要、预留;§5.5)
A-->>D: OperationReceipt + ExecutionPermit
D->>A: Execute(原 call.execute、binding、配置摘要、permit;§5.6
A-->>D: OperationReceipt
Note over D,A: ACCEPTED 不等于 SIP 已发起;拨号仍受许可、控制和时间窗口约束。
opt 执行期间控制任务
S->>DQ: 控制请求(原任务、CAS、pause/resume/stopsaas-dispatcher §5.2
DQ->>D: 投递给 D1
Note over D: 持久控制与 outbox;关闭相关新发起权限。
D-->>DQ: 持久成功后 ACK
D->>SQ: 控制 accepted
SQ->>S: 已受理,不是 applied
D->>A: ApplyTaskControldispatcher-agent §5.7
A-->>D: receipt / 控制事实
Note over D,A: 所有必需屏障和挂断事实核验完成后才可 applied。
D->>SQ: 控制进度或 applied 结果
SQ->>S: 按原控制关联更新状态
end
Note over A: 通话、媒体、AI 和录音产生真实 ExecutionFact。
A->>D: ReportExecutionEventdispatcher-agent §6.1
D-->>A: 事实持久接收结果
D->>SQ: call.status / transcript.updated / call.finished / contact.opt_out
SQ->>S: 持久应用业务事件(saas-dispatcher §4
A->>D: RequestUpload(原 binding、AssetDescriptor、upload_id;§6.2
Note over D: OSS 配置来自 D 配置文件;缺失/无效明确失败,不向 SaaS 获取配置或 TOKEN。
Note over D,A: 业务会话 MQ 结果可能晚于 Unary deadline;有界等待/重取待冻结,不是等 SaaS 签 TOKEN。
D->>SQ: 原资产业务上传会话请求(仅元信息,不申请 TOKENsaas-dispatcher §6.1
SQ->>S: 既有业务会话/资产登记语义
S->>DQ: 业务会话结果或拒绝(目标 D1、原请求/资产关联;无 OSS 配置/TOKEN
DQ->>D: 原业务会话响应
Note over D: 校验/持久会话关联,依据自身配置通过 SDK 提供临时 TOKEN。
D-->>DQ: 持久成功后 ACK
D-->>A: Unary 返回 D 提供的临时 TOKEN 及受限 UploadGrant(精确映射待核验)
A->>O: PUT recording bytesdispatcher-agent §6.3
O-->>A: PUT 结果 / ETag
Note over A,O: A 本地记录实际大小与 SHA-256PUT 或 ETag 不等于 verified。
A->>D: CompleteUpload(原资产/会话、大小、SHA-256;§6.4
D->>SQ: complete 请求(原会话及元信息)
SQ->>S: 完成请求
S->>O: 独立验证原对象(具体校验方式由 SaaS 合同定义)
O-->>S: 对象验证依据
S->>DQ: verified + oss_id 或明确失败(原关联)
DQ->>D: 完成验证结果
Note over D: 只有合法 verified 才同事务记录完成状态及 recording.ready outbox。
D-->>DQ: 持久成功后 ACK
D-->>A: 通过获批 Unary 衔接返回最终上传结果
D->>SQ: recording.readysaas-dispatcher §4.3
SQ->>S: OSS ID 与原录音元信息
opt 查询或整体补传
S->>DQ: 原 command/call 查询,或 call_id/source_command_id 整体补传请求
DQ->>D: 按目标 D1 接收(saas-dispatcher §5.3–§5.4
D->>SQ: 关联查询结果,或原事件身份/内容/版本的补传
SQ->>S: 查询响应或补传结果
Note over S,D: 不重新投 call.execute 执行;响应超时不走 HTTP 兜底。
end
```
图中后续步骤均以所需校验和前置条件成功为前提;拒绝、超时和失败保留原关联及待恢复状态,不继续执行成功分支。各类 MQ 请求/响应都适用持久后 ACK,图未重复画出所有 broker confirm/消费者 ACK;它们不能代替业务状态。
## 4. 交互与结构索引
| 交互 | 数据/语义来源 | 新设计状态 |
| --- | --- | --- |
| 身份、目标与专用 Topic | `saas-dispatcher.md §2` | 原则已确认;精确命名/消息关联待冻结 |
| 执行请求/受理结果 | `saas-dispatcher.md §3–§4` | 旧业务字段可对照;Dispatcher 路由待改 |
| AI 配置/授权请求响应 | `saas-dispatcher.md §6.2` | 全部 MQ;旧 HTTP GET 不再是目标 |
| 状态/激活/许可/执行 | `dispatcher-agent.md §4–§5` | 既有 Unary;完整配置/执行接线以代码证据为准 |
| 控制/查询/补传 | `saas-dispatcher.md §5``dispatcher-agent.md §5.7` | MQ 请求与响应待冻结/实现,旧 HTTP 废弃 |
| Agent 事实与业务事件 | `dispatcher-agent.md §6.1``saas-dispatcher.md §4` | 旧 fact/outbox 已有;来源路由与已知映射差异待验证 |
| 临时上传 TOKEN | `saas-dispatcher.md §6.1``dispatcher-agent.md §6.2` | OSS 配置在 D 文件;D 提供 TOKENA 经 Unary 领取,SaaS 不签发或下发配置 |
| 业务上传会话 | `saas-dispatcher.md §6.1` | 保留既有 SaaS 业务语义,经 MQ 关联原资产/会话;不作为 TOKEN 来源,异步衔接待冻结 |
| 文件上传 | `dispatcher-agent.md §6.3` | Agent→OSS,不经 D/MQ,不改变对象上传协议 |
| complete/verified/ready | `saas-dispatcher.md §6.1``dispatcher-agent.md §6.4` | SaaS MQ verified 才触发 D 的 ready;待实现/验证 |
## 5. 当前实现与目标设计差异
| 能力 | 既有实现事实 | 必须完成的纠正 |
| --- | --- | --- |
| Dispatcher 身份/订阅 | MQ 命令仅按 tenant key 路由 | 全局唯一 ID、独立 Topic/队列、原请求定向响应及来源校验 |
| 控制/查询/补传 | 旧 HTTP handler;部分通话路由固定 404source-command replay 重发原命令 | 移除 SaaS HTTP 业务入口;全部 MQ,补传只恢复原业务结果,不重拨 |
| AI 配置来源 | 启动注入 `AISnapshotRaw` / `AIAuthorizationRaw` | SaaS MQ 配置/授权及持久绑定,不接旧 AI GET |
| 上传 TOKEN 与验证 | D 已有 OSS client 签发 grant、直接验证对象并发 ready | 保留 D 签发职责,核验配置文件/TOKEN 约束;业务会话与 verified 仍经 SaaS MQ,本地对象验证不能替代 verified |
| R12/R13 | 当前同步本地 grant/verify | 核验 R12 依据 D 配置提供 TOKEN,以及业务会话/complete 的 MQ 持久关联、有界等待、原操作恢复与最终 Unary 交付 |
| Agent Execute | handler 证明准备/校验/幂等接收 | 不能据此宣称实际 ARI/RTP/SIP/AI 生命周期已完成 |
| TRANSCRIPT_FAILED | 当前 aggregate 类型映射不通过 Schema | 已知差异保留,另行实现修正,不因文档变更标通过 |
## 6. 幂等、失败与验收边界
1. 重投沿原请求、Dispatcher、租户和业务对象关联;同 ID 异内容冲突,不能换 execution/attempt/asset 绕过。
2. D1 离线不把未决请求/响应改投 D2;MQ 中断不回退 HTTP,执行未知不自动重拨。
3. 配置迟到、重复或已撤销时不能覆盖在途绑定;无有效授权拒新准入。
4. 上传回包丢失复用原 `upload_id` 和资产摘要;TOKEN 过期只接受 Agent 显式向 D 重新申请,配置不来自 SaaS,也不把 D 配置/TOKEN 经 MQ 传给 SaaS。未取得 SaaS verified 时不得发 ready 或提前删文件。
5. 新 boot/epoch 不清旧未知执行和占用;RPC 超时按原 binding 查询/对账。
6. 交付检查覆盖无 SaaS↔D HTTP、D1/D2 路由隔离、错目标/重复身份、请求响应乱序/超时/重启恢复、不可路由与 confirm 丢失;不以文档图、单租户 Mock 或本地 OSS 验证冒充通过。
## 7. 相关文档
- [SaaS ↔ Dispatcher 对接契约](./saas-dispatcher.md)
- [Dispatcher ↔ Agent 对接契约](./dispatcher-agent.md)
- [计划与需求阅读索引](../plan-0918.md)
+65 -29
View File
@@ -4,9 +4,9 @@
**本文件是后续 Agent 的开发任务入口:先确认需求及来源,再按依赖开发,最后用证据更新状态。** `0918` 是文件标识,不代表工期或上线日期承诺。本文件不替代权威契约、详细设计或验收用例,不重复维护 Schema。
当前基线:总体架构、P1/P2 边界、D01–D10 方案方向及内部隔离 PoC 初始参数已获用户确认;项目已按用户本次授权自行建立并嵌入 W01 项目内契约基线和 W02 Unary Proto/stubs。**本阶段项目内 G0/W04、单节点/单 Cell/单租户本地验收按范围签收;真实供应商/云/拨号/生产 receipt/容量及切换属于第二阶段**;本地通过不等于外部权威发布或生产放行。
当前基线:总体架构、P1/P2 边界、D01–D10 方案方向及内部隔离 PoC 初始参数已获用户确认;项目已按用户本次授权自行建立并嵌入 W01 项目内契约基线和 W02 Unary Proto/stubs。**旧基线的项目内 G0/W04、单节点/单 Cell/单租户本地验收按范围签收;本轮 MQ-only 修订重新打开受影响的契约、实现和验收项,见 §1.2、§8.2**。真实供应商/云/拨号/生产 receipt/容量及切换属于第二阶段;本地通过不等于新设计通过、外部权威发布或生产放行。
已确认的 OSS 分工:**Agent Dispatcher 获取受限配置后直 OSS 上传文件Dispatcher 不接收/缓存/转发录音内容,不替 Agent 上传**。Dispatcher 负责 SaaS 上传会话/续期/complete 协调及 verified 后的 MQ 结果回传
已确认的 OSS 分工:**OSS 配置存于 Dispatcher 配置文件;Agent Dispatcher 领取临时上传 TOKEN 后直 OSS,不保存长期凭据Dispatcher 不接收/缓存/转发文件**。TOKEN 过期由 Agent 显式向 D 重新申请,不向 SaaS 取配置/TOKEN。SaaS 业务上传会话complete/verified 仍经 MQ,上传完成后的校验归属不变,取得 SaaS verified 后才回传 recording.ready/OSS ID
### 1.1 2026-09-20 本轮范围修订
@@ -26,11 +26,22 @@
3. [G0确认方案](G0开发准备与契约冻结提案_v0.1.md)记录最新确认和待交付门禁;总体、通信、组件和验收文档补充实施细则。
4. 本计划只编排任务和阅读入口。若发现来源冲突,列明两个出处并暂停受影响部分;不得用计划、旧实现或 SDK 默认值覆盖权威合同。
### 1.2 本轮 MQ-only 架构纠正(用户已确认)
1. **SaaS↔Dispatcher 的所有交互只走 RabbitMQ**:执行、控制、查询、整体补传、AI 配置/授权、上传会话、complete/verified 及其响应都通过专用 Topic 订阅;禁止双方任何 HTTP 请求、回调、兼容入口或故障回退。
2. **每个 Dispatcher 有全局唯一、不重复的 ID 和独立接收 Topic/队列**。消息必须定向到指定 D,响应回原 D,来源可追溯;不能多个 D 共用一条队列竞争消费或广播后过滤。Dispatcher 身份不等于 tenant/Agent/Cell ID 或 `dispatcher_epoch`,租户独立队列与原值 `tenant_key` 仍保留。
3. 精确 ID 生成/持久化/重复身份处理、Topic/队列/绑定命名、信封关联/错误/超时以及新 routing key 的完整字节预算须由 W01 冻结;方向已经确认,但不能自行往严格旧 Schema 加字段。P1仍是单活 D,D1/D2 路由隔离只作本地契约/消息 fixture,不新增多 D 调度、HA、共享额度或生产资源。
4. **D↔A 仍为 UnaryA→OSS 仍直传**。MQ-only 不禁止 OSS/ARI/供应商 HTTP(S) 或 gRPC HTTP/2。OSS 配置由 D 配置文件提供,临时 TOKEN 由 D 向 A 提供,不再由 SaaS 下发;保留 D 签发能力,SaaS 最终 verified 校验职责不变。R12/R13 如何衔接业务会话/complete 的异步 MQ 结果,由 W02/W11 核验 pending、原操作恢复与有界等待,不伪造当前 Proto 已支持。
5. 旧 AI GET、控制/查询/补传 HTTP 和上传 HTTP 方案被本修订替代;`contracts/upstream/2026-09-19-p1-v1/`、旧版本、生成索引及历史证据保留原样,不能当作 MQ-only 新合同或重写哈希。相关新版本和正反例发布前,受影响 I 不就绪;禁止先实现猜测消息再补合同。
6. 当前只授权纠正计划/契约及相关设计文档,不修改 Schema/Proto/代码,不访问真实 SaaS、broker、云或拨号。文档完成不关闭 §8.2 的开发/验收待办。
目标语义见 R8,当前内部实现事实与差异见 R9,完整 MQ 目标时序见 R10。R1–R6 或历史证据中与本修订冲突的旧传输、路由及通过结论,不得继续作为新接入依据;只读来源包的变更必须走新版本发布。
## 2. 后续 Agent 的开工与收尾流程
### 2.1 每次任务的必做顺序
1. 读取 `AGENTS.md`、本计划 §1–§4 和 §8 当前状态;检查实际文件、Git 工作区及既有改动,不覆盖别人的工作。项目当前不独立 Git,不自行 init/submodule/移动历史。
1. 读取 `AGENTS.md`、本计划 §1–§4 和 §8 当前状态;检查实际文件、Git 工作区及既有改动,不覆盖别人的工作。项目已有独立 Git;先同步远程,不重新 init/submodule/移动历史。
2. 将本次请求映射到 §5 的 W 编号和 §4 的需求行;按 §3 找到对应详细章节,**读取正文后再决定修改点**。索引、记忆或先前摘要不能替代本次涉及的合同/代码正文。
3. 核验前置证据及当前合同/工具版本:区分“方向已确认”“源已发布”“实现完成”“测试通过”。前置缺失就标 blocked,不猜 Schema、不用临时协议偷跑。
4. 给出本次范围、完成条件和明确不做项,再在当前授权范围内执行。用户明确要求开始本地开发后不重复询问已确认设计;修改上游唯一来源、真实业务网络/付费/拨号/切换等依旧按权限边界处理。
@@ -63,17 +74,20 @@
| R5 | [开源组件选型与复用清单 v0.2](开源组件选型与复用清单_v0.2.md) | §1.3首发基础栈/SDK、§4.3参数能力;全文复用禁区、许可证/锁版/PoC门禁;采用依赖前必读 |
| R6 | [OpenAPI与MQ字段索引 v0.1](OpenAPI与MQ字段索引_v0.1.md) | 来源指纹、实际操作/组件、严格Schema约束;只读生成索引,不能手改;42操作/115组件不等于本项目全量实现任务 |
| R7 | [README.md](../README.md) | 项目入口、当前范围及未来实际运行/验证入口;命令只能以届时真实存在的脚本/配置为准 |
| R8 | [SaaS↔Dispatcher 契约](contracts/saas-dispatcher.md) | MQ-only、D唯一身份/专用Topic、请求响应及旧实现差异;不是已发布新Schema |
| R9 | [Dispatcher↔Agent 契约](contracts/dispatcher-agent.md) | 当前Proto/handler事实;R12/R13与MQ异步协调的待改项;不能把本地OSS验证等同SaaS verified |
| R10 | [SaaS/MQ/D/A/OSS时间泳道图](contracts/saas-rabbitmq-oss-dispatcher-agent-timeline.md) | 目标全MQ时序、独立D订阅和新旧差异;所有中文新消息名仅为语义标签 |
### 3.1 按任务定位必读章节
| 当前要做什么 | 最小详细阅读范围 | 要取得的关键依据 |
| --- | --- | --- |
| 改外部消息/HTTP字段或升级源包 | R3 §3/§4/§6.2/§7R2相应业务路径/事件;R6对应操作/组件及实际发布源 | 原字段、分支条件、租户归属、版本/哈希正反例;不得从索引猜字段 |
| 改外部MQ消息/拓扑或升级源包 | 本计划§1.2R8全文/R10R3 §3/§4/§6.2/§7R2及R6实际源 | D身份/专用Topic、租户/请求关联、错误/期限、版本/哈希正反例;旧HTTP只作语义对照,不继续实现 |
| 配置两种AI模式、缓存或传参 | R3 §4R1 AI章节;R5 §1.3/§4.3R4 §5.1 | mode兼容、原摘要、有效授权、参数到SDK映射、0/false、取消/自动重试 |
| 写SQLite、inbox/outbox或MQ接入 | R1持久化/调度章节;R2可靠性/事件;R3 §3/§5/§6.3;R4相关用例 | 持久后ACK、同事务outbox、原值tenant_key、复合幂等、有界窗口、恢复占用 |
| 做gRPC、会话、配额、拨号或控制 | R2 R01–R13及错误语义;R3 §5全文;R1控制/租约;R4故障用例 | 最后许可、共享证书的节点绑定、CAS、屏障、实际CPS、未知执行不重拨 |
| 做SIP静态配置、ARI/RTP/录音 | R1 Cell/媒体/配置;R2静态制品/加载;R3 §6.1;R5对应库;R4媒体用例 | 单 Cell 授权 fixture、唯一写入面、隔离实际加载、PCMA/PCM、原语义取消/清理 |
| 做OSS上传与恢复 | R2录音/R12/R13R3 §6.2/§6.3R4 E12/E19 | A取D配置后直传;D不传文件;续期/complete幂等、verified文件清理 |
| 做OSS上传与恢复 | R2录音/R12/R13R3 §6.2/§6.3R4 E12/E19 | D配置文件→临时TOKEN→A直传;不向SaaS取OSS配置/TOKEN;显式重申请/complete幂等、SaaS verified文件清理 |
| 调参数、上线或迁移/回退 | R3 §5.5/§6.3R4 §1.2/§9/§9.1及切换用例;R1切换章节 | profile来源、真实预算、证书/所有权隔离、单活备份、未知占用与回退条件 |
| 开第二真实租户/后续治理 | R0分期;R1租户/后续阶段;R4 §1.2及公平性用例 | P2门禁,不能仅把tenant数量从1改成2 |
@@ -88,11 +102,11 @@
| Q01 独立工程 | Go 1.27.1、单module/二进制、Cobra显式agent/dispatcher;无Python运行依赖、不合并management | W00/W03R0/R1/R5 |
| Q02 P1拓扑 | 1个单活D/SQLite、1个Agent/1套Asterisk、至少3家独立SIP trunk 的配置/协议 fixture、1个启用租户;双节点/第二 Cell/第二租户不在本轮 | W05/W06/W09/W14R1/R4 |
| Q03 持久化 | D持有任务/配额/inbox/outbox;A无业务DB,执行/文字/录音/上传恢复落文件;不接PG/NFS双活 | W05/W06/W08/W13R1/R2/R3 §5 |
| Q04 MQ/租户 | SaaS命令和业务结果经D走RabbitMQ租户独立队列原值key复合幂等有界窗口;超过224字节路由预算停发保留,不清洗/截断 | W01/W05/W08/W12R0/R2/R6 |
| Q04 MQ/身份/租户 | SaaS↔D全部交互只走RabbitMQ,禁双方HTTP;每个D全局唯一ID及独立接收Topic/队列,响应回原D;保留租户独立队列/原值key/复合幂等/有界窗口,完整新路由预算须重算,超限停发保留 | W01/W05/W08/W12§1.2、R8/R10 |
| Q05 内部可靠性 | Unary gRPC、D预配Endpoint、共用A证书但独立会话;单 Cell 最后许可、控制屏障和未知占用按本地故障注入验收;跨 Cell 协调延期 | W02/W06/W08/W13R3 §5 |
| Q06 双AI模式 | ASR-only和完整AI均为P1AI版本源于SaaS,D授权后给A,不加MQ模式字段不靠CLI/env覆盖不复用旧LLM/TTS | W01/W07/W10R3 §4/R5 |
| Q06 双AI模式 | ASR-only和完整AI均为P1D经MQ取得SaaS不可变AI配置/授权后给A,不再调用AI GET;不加任务mode字段不靠CLI/env覆盖不复用旧LLM/TTS | W01/W07/W10R8 §6.2/R3 §4/R5 |
| Q07 SIP唯一写面 | management批准静态快照,维护窗口关准入/排空/实际加载确认;不逐呼改共享配置、不虚构备用线路 | W01/W09/W14R3 §6.1 |
| Q08 OSS直传 | SaaS→D→A供受限配置,A→OSS传文件;D协调verifiedMQ回传OSS ID;文字归档不替代实时transcript.updated | W01/W11/W12R3 §6.2 |
| Q08 OSS直传 | D配置文件保存OSS配置,A经Unary向D领临时TOKEN后直传;SaaS不下发OSS配置/TOKEN,业务会话/complete/verifiedMQ,SaaS最终校验不变;D本地验证不替代verified | W01/W02/W11/W12R8 §6.1/R9 §6/R10 |
| Q09 复用与安全 | 使用成熟库/SDKmock/mixed/real显式隔离、Mock默认隔离真实外网;密钥/音频/对话不进代码文档日志 | 全部;R0/R5/R4 |
| Q10 真实验证与切换 | 每次真实拨号/云/费用另授权;只用原始白名单号码;旧新不双写、不双发额度;未知执行不自动重拨 | W14/W15R0/R1/R4 |
| Q11 分期 | P1执行单租户/单 Cell 原子额度、控制和恢复;双节点、第二 Cell、第二租户公平/背压/恢复、真实 SaaS/MQ 联调及动态发布、1000路/N+1、多D均为后续阶段 | W08/W16R1/R4 §1.2 |
@@ -108,8 +122,8 @@ W编号是本计划的工作分解,不是新增协议、需求或验收编号
| 步骤 | 开发内容与预期产物 | 前置/可并行关系 | 完成条件与阅读依据 |
| --- | --- | --- | --- |
| W00 开工核对 | 按§2映射请求,核对实际工具链/文件/工作区;登记范围、源版本、责任角色、外部阻塞 | 后续明确安排开发时开始;本轮只建计划 | 有开工记录和可验证前置清单;无误认已实现内容。读R0/R1/R3 §12/R4 §1.2 |
| W01 项目合同与P1交接冻结 | 由项目契约负责人建立 D01/D02/D03/D06/D07/D08 的项目内开发闭包:事件payload、两模式/参数/授权、静态制品、录音会话、P1 mock profile;保留外部来源和非权威状态 | W00;与W02及不依赖业务字段的W03 PoC并行;不得借此修改/冒充上游权威发布 | `contracts/upstream/2026-09-18-p1-baseline` 自包含、哈希、正反例和严格校验通过;真实预算/授权/外部签收仍明确 blocked。读R2/R3 §34/§6/R6 |
| W02 内部协议定稿 | 把已确认R01R03/R05/R07R13机制定稿为项目内 `agent.v1.AgentControlService` Proto、生成 stubs、错误/幂等/fencing 合同;列清激活、许可、控制、事实/资产恢复状态转移 | W00;与W01并行;后续业务字段必须引用 W01 版本 | `buf lint/build/generate`、生成包测试、错误/幂等/状态合同可检验;不建R04/R06在线发布空壳。读R2 Unary章节/R3 §5 |
| W01 项目合同与P1交接冻结 | 在旧闭包之上发布MQ-only新版本:D唯一身份/生命周期、专用Topic/队列/绑定、所有请求/响应/来源/关联/错误/期限、完整路由长度;复用既有事件/AI/上传语义,不改旧不可变包 | W00;与W02及不依赖业务字段的W03 PoC并行;不得借此修改/冒充上游权威发布 | MQ-only新包自包含、来源/版本/哈希及严格Schema/拓扑正反例通过;旧2026-09-18/19包不能代签,真实预算/授权/外部签收分开。读§1.2/R8/R10及R2/R3/R6 |
| W02 内部协议定稿 | 保留已建立的 `agent.v1.AgentControlService`;核验MQ异步配置/上传结果与Unary的衔接、D逻辑身份与epoch边界;冻结R12/R13 pending/恢复/最终响应,确需变更才更新Proto/stubs,不编造当前能力 | W00;与W01并行;后续业务字段必须引用 W01 版本 | `buf lint/build/generate`、生成包测试、错误/幂等/状态合同可检验;不建R04/R06在线发布空壳。读R2 Unary章节/R3 §5 |
| W03 最小工程、契约包与复用PoC | 按§5.4拆W03-ae:先建最小单module/Cobra/构建基线,再独立验证生成工具、存储/消息、gRPC/媒体和AI/上传SDK | W03-a基线合入后,其余按各自接口依赖并行;源生成须等W01/W02对应产物,原理PoC不猜业务字段;共享依赖由集成负责人统一修改 | 精确版本/许可/漏洞及各子任务PoC证据,生成确定性与隔离构建通过;各子任务分别验收,边界和依赖清晰。W13-a另负责后续可部署完整候选制品。读R3 §7–8/R5 |
| W04 G0证据汇总 | 将D01D10逐项关联项目内契约、Proto、关键本地PoC及未决项,给出本阶段单节点 P1 放行范围 | W01/W02/W03的P1适用产物 | 项目内契约和必需本地PoC有证据即可标本阶段 G0;真实供应商/容量/生产验收列第二阶段,不用Mock代签生产。读R3 §2/§9、R4门禁 |
@@ -121,14 +135,14 @@ W04整体或已记录的对应模块放行是本节共同前置;I/M并行只
| 步骤 | 开发内容与预期产物 | 前置/可并行关系 | 完成条件与阅读依据 |
| --- | --- | --- | --- |
| W05 Dispatcher持久控制面 | SQLite迁移/单活入口、租户复合键、MQ inbox、持久后ACK、状态/outbox同事务;受控查询、任务控制接收及恢复基础 | W04放行 | 重复命令/commit前后故障不丢任务、不造第二执行;队列与持久待发起窗口有界;控制接收不伪装applied。读R1持久化/R2消息/R3 §3/§5 |
| W05 Dispatcher持久控制面 | D唯一身份、专用Topic与租户隔离;所有SaaS请求/响应经MQ inbox/outbox持久接收/关联,控制/查询也经MQ;移除旧SaaS HTTP入口,不留兼容或回退 | W04放行 | 重复命令/commit前后故障不丢任务、不造第二执行;队列与持久待发起窗口有界;控制接收不伪装applied。读R1持久化/R2消息/R3 §3/§5 |
| W06 Agent运行与文件恢复 | 无业务DBgRPC/mTLS、受控激活/boot/session、健康/版本报告、原子文件与崩溃恢复;只接获授权任务 | W04放行;与W05并行 | 错SAN、错节点、重放、旧会话拒绝;新boot保留旧未知占用/资产;文件损坏隔离、敏感信息不泄露。读R2内部协议/文件/R3 §5.15.2 |
| W07 AI配置快照与授权 | W07-dD读取/授权/持久绑定)和W07-aA校验/执行快照);两模式资源声明 | 两端接口I及AI源已发布后可各自实现;联合G需W05/W06的相关模块M,不等对端全部功能完工才写适配 | 旧完整模式兼容、新模式明确;跨租户/同版本异内容/无授权拒绝;在途不漂移;0/false保真。W10继续验证SDK实际行为。读R3 §4/R5 §4.3/R4 §5.1 |
| W07 AI配置快照与授权 | W07-d经MQ获取SaaS不可变版本/授权,响应回原D并持久绑定W07-a校验/执行快照;禁止开发旧AI GET client,覆盖重复/迟到/撤销/重启恢复 | 两端接口I及AI源已发布后可各自实现;联合G需W05/W06的相关模块M,不等对端全部功能完工才写适配 | 旧完整模式兼容、新模式明确;跨租户/同版本异内容/无授权拒绝;在途不漂移;0/false保真。W10继续验证SDK实际行为。读R3 §4/R5 §4.3/R4 §5.1 |
| W08 调度、单 Cell 额度和控制屏障 | W08-d负责D单 Cell 原子额度/许可/CAS,W08-a负责A发起串行区/控制/未知恢复;共享合同单写 | I就绪后两端及W09适配可并行;联合G需要W05–W07相关模块M和W09媒体模块M,一起验证后合入;不是先要求W08.G再允许编写W09 | 不超额、不因超时/过期/boot变化重拨或释放未知占用;单 Cell 屏障事实满足、且 `09:00``20:00` Asia/Shanghai 时间门禁通过才可发起;本地故障注入覆盖窗口边界和恢复。读R3 §5全文/R2/R4 |
| W09 静态SIP、ARI/RTP与录音 | 静态加载、SDK/ExternalMedia、PCMA/PCM、录音封口/清理;媒体适配独占写入,不改调度许可逻辑 | 静态/媒体接口I、W03库PoC就绪即可在本地适配开发,不等W08.G;接入业务发起必须等W08控制模块M,W08/W09联合G前禁止业务准入 | 隔离 Asterisk 22.10.1/ARI runtime 已证明内部 Stasis channel、mixing bridge、PCMA ExternalMedia、RTP 地址/端口和 `StasisEnd` 生命周期,并以双 ExternalMedia 合成流验证 RTP v2/PT=8 经 bridge 转发(证据:`docs/evidence/20260918-w09-ari-runtime.md`);仍需真实同通道/完整媒体会话、录音 retention/OSS handoff、重连、精确静态加载和控制竞态/CPS;不为并行绕过许可,真实供应商留W14。读R1/R3 §6.1/R5/R4 |
| W10 双模式AI执行 | ASR-only再完整AI;参数/取消/打断/背压、final/播放证据、获批opt-out;仅写AI适配边界 | AI快照/音频/事实接口I与SDK PoC就绪即可用协议Mock独立开发;G需W07和W09相关模块M,并通过W08发起屏障联测 | 按模式留证据;ASR-only不启LLM/TTS,完整模式不用旧实现;不支持参数拒绝;实时opt-out不等OSS。真实供应商属于第二阶段。读R3 §4/R5 §4.3/R4 §5.1 |
| W11 Agent→OSS直传与恢复 | W11-d由D负责人写R12/R13与SaaS complete/outboxW11-a仅写A直传/显式重新申请/恢复模块,文件生命周期接口归A核心负责人 | 上传合同、封口文件元信息/生命周期接口I即可用合法测试文件开发,不等整套W09;G需W05/W06相关模块M并接W09实际封口产物联测 | D/gRPC不传文件;有效授权在15分钟内完成一次PUT;过期/失败保留文件并等待显式重新申请;PUT不早发ready;沿原资产恢复,verified后MQ回OSS ID。读R3 §6.2/R2/R4 E12/E19 |
| W12 事件、整体补传及运行可观测性 | D负责人维护事件/outbox/整体补传;A核心及各适配负责人维护其事实/指标公共Schema与指标定义单写 | 按事件接口I和所属模块推进;最终G需W10/W11联合证据;禁止另起Agent同时重写前序模块的事件代码 | confirm不等于应用收讫;无task/execution补传;域版本不互盖;水位/错误阻止不安全准入。日志/指标随模块实现。读R2/R3 §3/§6.3/R4 |
| W11 Agent→OSS直传与恢复 | W11-d核验D配置文件→临时TOKEN及显式重申请,保留SDK签发能力;实现SaaS MQ业务会话/complete/verified与R12/R13持久关联,W11-a保留直传/恢复;仅禁止D本地校验替代SaaS verified,不删除签发职责 | 上传合同、封口文件元信息/生命周期接口I即可用合法测试文件开发,不等整套W09;G需W05/W06相关模块M并接W09实际封口产物联测 | D/gRPC不传文件;有效授权在15分钟内完成一次PUT;过期/失败保留文件并等待显式重新申请;PUT不早发ready;沿原资产恢复,verified后MQ回OSS ID。读R3 §6.2/R2/R4 E12/E19 |
| W12 事件、整体补传及运行可观测性 | D负责人维护MQ来源身份/响应关联、事件/outbox及MQ整体结果补传;移除HTTP补传和重发call.execute当补传的旧行为;A事实/指标公共Schema保持单写 | 按事件接口I和所属模块推进;最终G需W10/W11联合证据;禁止另起Agent同时重写前序模块的事件代码 | confirm不等于应用收讫;无task/execution补传;域版本不互盖;水位/错误阻止不安全准入。日志/指标随模块实现。读R2/R3 §3/§6.3/R4 |
### 5.3 P1验收、切换与P2入口
@@ -170,7 +184,7 @@ W04整体或已记录的对应模块放行是本节共同前置;I/M并行只
| 关口 | 放行什么 | 不能据此宣称什么 |
| --- | --- | --- |
| 方案确认(已完成) | 按既定方向准备合同与PoC,不重复架构审批 | 不等于Schema已发布、Proto已生成、库兼容或实现完成 |
| G0/对应模块前置(本阶段完成) | 按项目内契约和本地/隔离证据放行当前单节点 P1 实现 | 不等于第二 Cell、真实供应商生产或容量验收 |
| G0/对应模块前置(受影响部分重开) | 旧基线曾完成;MQ-only新版本须按§8.2重新取得I与本地证据放行 | 旧通过不等于新MQ契约通过,也不等于第二 Cell、真实供应商生产验收 |
| 本地P1验证(本轮) | 完成单节点/单 Cell/单租户契约、协议 fixture、故障注入和本地回归 | 不等于真实供应商、生产 SaaS/MQ receipt 或生产切换 |
| 真实P1与切换(第二阶段) | 获授权后另行受控联调/首发运行 | 不属于本轮验收;双节点、第二 Cell、第二租户及1000路/N+1仍另立项 |
@@ -178,7 +192,7 @@ W04整体或已记录的对应模块放行是本节共同前置;I/M并行只
### 6.2 不纳入本轮及P1的扩展
P1不预建在线发布/回滚编排、自动SIP FALLBACK、多Dispatcher协调/HA、权重借用、1000路/N+1实现或文本OSS专属归档;这些后续能力另行安排,不阻塞P1的合法开发,也不能将当前安全屏障延期。
P1不预建在线发布/回滚编排、自动SIP FALLBACK、多Dispatcher协调/HA、权重借用、1000路/N+1实现或文本OSS专属归档;这些后续能力另行安排,不阻塞P1的合法开发,也不能将当前安全屏障延期。**D全局唯一ID、专用Topic及D1/D2隔离消息fixture属于本次MQ契约修正,不得借“多D延期”省略;不要求部署第二业务Dispatcher。**
**单EIP+NAT与第二套管理后台不是延期功能,而是明确排除的架构**;后续也不得自行设计、实现或验收。持续采用多机器/多EIP直连,management保持SIP配置唯一编辑/审批面。
@@ -210,29 +224,51 @@ go build ./...
状态词固定使用:`未开始``进行中``blocked``待验证``完成`;“完成”须有对应证据,不代表后续真实验收完成。本节由集成负责人维护,任务负责人提交证据和状态建议。每个父任务按工作包记录I/M/G;代码父任务需所属工作包的合并回归通过才完成,只有局部M时保持待验证。本轮文档完成不等于W00开发开工已执行。
当前已完成 W00/W03-a,并由本项目自行交付 W01 项目合同基线、W02 Proto/stubs;G0、I/M/G 联合门禁和真实验收仍未通过。下表只记录可验证事实,详细证据见 `docs/evidence/20260918-local-development.json` 与 W01/W02 证据,不把局部代码标为整体完成
项目已交付W01基线、W02 Proto/stubs和旧本地P1证据。**本轮MQ-only修订后,受影响父任务改为待验证,当前下一动作及解除条件见§8.2;表内已有证据保留作为修订前历史,不能据旧HTTP、单租户路由或本地OSS结果关闭新门禁。** 未受影响模块的旧通过事实不撤销;本轮没有重新运行代码或真实验收
| 步骤 | 当前状态 | 已有证据/尚缺什么 | 下一动作 |
| 步骤 | 当前状态 | 历史证据/原基线结论(不代表新修订通过) | 当前下一动作 |
| --- | --- | --- | --- |
| W00 | 完成 | 已读 AGENTS、计划与 R0/R1/R3/R4/R5;工具链为 Go 1.27.1;已记录父工作区既有改动和契约源 commit。证据:`docs/evidence/20260918-local-development.json` | 维护本地基线并按 W04 证据门禁推进 |
| W01 | 完成 | 项目内 `contracts/upstream/2026-09-18-p1-baseline` 已形成自包含版本、严格事件/AI/授权/OSS/静态制品/profile Schema、8种事件正例和负例、README/SNAPSHOT/release-manifest/父清单哈希;明确继承 source dirty 且非外部权威。证据:W01 bundle、`internal/contract``internal/ai` 测试 | 外部权威发布、真实预算/授权和生产静态加载仍由 W04/W14 阻塞,不把本地基线冒充外部签收 |
| W02 | 完成 | `proto/agent/v1/agent.proto``gen/agent/v1/*``proto/ERRORS.md`、manifest 已交付;`buf lint/build/generate` 和生成包测试通过,覆盖 R01R03/R05/R07R13,不建 R04/R06 空壳 | W06 继续接入 mTLS/session/runtime handlers;运行集成尚未等同 Proto I 之外的 M/G |
| W01 | 待验证 | 项目内 `contracts/upstream/2026-09-18-p1-baseline` 已形成自包含版本、严格事件/AI/授权/OSS/静态制品/profile Schema、8种事件正例和负例、README/SNAPSHOT/release-manifest/父清单哈希;明确继承 source dirty 且非外部权威。证据:W01 bundle、`internal/contract``internal/ai` 测试 | 发布MQ-only新契约及正反例,见§8.2;外部签收与本地冻结分开 |
| W02 | 待验证 | `proto/agent/v1/agent.proto``gen/agent/v1/*``proto/ERRORS.md`、manifest 已交付;`buf lint/build/generate` 和生成包测试通过,覆盖 R01R03/R05/R07R13,不建 R04/R06 空壳 | 核验D身份/epoch和R12/R13的MQ异步衔接,按需冻结Proto变更,见§8.2 |
| W03 | 完成 | W03-a 单 module/Cobra/构建基线、W03-c 存储/MQ 本地实现、W03-e AI Schema/contract SaaS Mock PoC、W03-d Pion RTP thin adapter、gopsutil 资源采样已有测试;已完成 Go module 许可证清单、`go mod verify``govulncheck@v1.7.0`(Go 1.27.1 构建)无漏洞扫描;临时 module 的 `ari/v5.3.1``openai-go/v3.62.0``dashscopego/v0.1.2``doubao-speech-go` API/协议 Mock PoC 通过但均未锁入项目;固定 Asterisk 22.10.1 + 临时 ARI client runtime PoC 已通过内部 Stasis/bridge/ExternalMedia lifecyclePJSIP/PJSUA2 full mock leg 已通过双向 PCMA、U1/U2/U3 播放、端点 WAV 封口和 ARI cleanup(证据:`docs/evidence/20260918-w09-sip-rtp-ari.md`);一次 disposable RabbitMQ 4.1.8 broker confirm/ACK/DLQ 集成通过,并新增 Dispatcher tenant consume→SQLite inbox/task/outbox→event publish integration test;另以 `TestOutboxProcessCrashRecovery` 覆盖一次真实测试子进程在 outbox claim 后退出、父进程恢复并发布的本地故障窗口(证据:`docs/evidence/20260918-w05-restart.md`);2026-09-19 另锁定物理 Asterisk 22.10.1 source/native-stage、Jansson 2.15.0、PJPROJECT 2.17 和 Debian 13 systemd 构建/安装输入,依赖补充证据见 `docs/evidence/20260918-dependencies.md``make check`、脚本语法和 deployment lock JSON 检查通过 | 本阶段项目内 Go/SDK/契约/隔离验证已签收;生产 ARI module/tag/许可证、供应商真实 SIP/媒体、录音 retention/OSS、批准 broker 版本/ACL、真实 broker/commit 故障注入属于第二阶段;隔离 SIPp-to-PJSIP/ARI signaling 证据见 `docs/evidence/20260918-w09-sip-ari.md`,不把本地或隔离 PoC 当生产通过 |
| W04 | 完成 | W01/W02 项目内产物和 D10 隔离 PoC 已具备;本阶段项目内 G0 契约、Schema、fixture、隔离 PoC 和范围修订已签收;外部权威、真实预算/角色签收、生产依赖及生产运行证据属于第二阶段,不计入本轮门禁;汇总:`docs/evidence/20260918-g0-status.md` | 第二阶段另行补齐外部签收、生产依赖和真实联调;本轮按项目内契约范围继续本地验收 |
| W05 | 完成 | 已有 SQLite inbox/tasks/quotas/controls/replays/outbox、control HTTP、本地 reservation-to-Agent seam 和 RabbitMQ adapter 的 publisher confirm、prefetch=1、per-tenant DLQ 拓扑测试;新增 contract-backed local flow 将租户命令、配额、Agent 执行和 event 校验串联;`make mq-integration-local` 已用 disposable RabbitMQ 4.1.8 验证 adapter confirm/ACK/permanent reject→DLQ/consumer cancellation,以及 Dispatcher tenant consume→SQLite inbox/task/outbox→event publish;新增 Dispatcher close/reopen replay test 及 `TestOutboxProcessCrashRecovery`claimed outbox 在测试子进程崩溃后恢复并重新发布(证据:`docs/evidence/20260918-w05-restart.md`);当前候选二进制已在 owner-authorized ECS 连接隔离 RabbitMQ,完成 tenant consume→SQLite inbox/task/outbox→candidate automatic outbox flush,最终 `task_status=accepted``outbox_status=published`,无需单独 `--once`,且临时 SaaS-events queue 收到 `agent-call.command.result`(证据:`docs/evidence/20260918-rabbitmq-integration.md`);本阶段契约拓扑、隔离 broker、confirm/ACK/DLQ、单租户窗口和本地崩溃恢复已签收;批准生产 broker/ACL、SaaS application receipt 和真实 broker 故障注入延期第二阶段 | 第二阶段另行进行生产 broker/SaaS 联调和多 Cell 联测;本轮保持单 Cell 隔离证据 |
| W04 | 待验证 | W01/W02 项目内产物和 D10 隔离 PoC 已具备;本阶段项目内 G0 契约、Schema、fixture、隔离 PoC 和范围修订已签收;外部权威、真实预算/角色签收、生产依赖及生产运行证据属于第二阶段,不计入本轮门禁;汇总:`docs/evidence/20260918-g0-status.md` | 汇总新MQ合同及本地PoC,重新签收受影响G0,见§8.2 |
| W05 | 待验证 | 已有 SQLite inbox/tasks/quotas/controls/replays/outbox、control HTTP、本地 reservation-to-Agent seam 和 RabbitMQ adapter 的 publisher confirm、prefetch=1、per-tenant DLQ 拓扑测试;新增 contract-backed local flow 将租户命令、配额、Agent 执行和 event 校验串联;`make mq-integration-local` 已用 disposable RabbitMQ 4.1.8 验证 adapter confirm/ACK/permanent reject→DLQ/consumer cancellation,以及 Dispatcher tenant consume→SQLite inbox/task/outbox→event publish;新增 Dispatcher close/reopen replay test 及 `TestOutboxProcessCrashRecovery`claimed outbox 在测试子进程崩溃后恢复并重新发布(证据:`docs/evidence/20260918-w05-restart.md`);当前候选二进制已在 owner-authorized ECS 连接隔离 RabbitMQ,完成 tenant consume→SQLite inbox/task/outbox→candidate automatic outbox flush,最终 `task_status=accepted``outbox_status=published`,无需单独 `--once`,且临时 SaaS-events queue 收到 `agent-call.command.result`(证据:`docs/evidence/20260918-rabbitmq-integration.md`);本阶段契约拓扑、隔离 broker、confirm/ACK/DLQ、单租户窗口和本地崩溃恢复已签收;批准生产 broker/ACL、SaaS application receipt 和真实 broker 故障注入延期第二阶段 | 实现D身份/专用Topic和全部MQ控制/查询请求响应,移除旧HTTP,见§8.2 |
| W06 | 完成 | 已有文件状态、transcript/assets、unknown 恢复和损坏隔离测试;`agent.v1` runtime handlers、TLS1.3 mTLS config、持久 session-generation journal、session/fencing/CAS/permit/fact/upload boundary、静态 Cell 制品启动路径/激活校验、gopsutil 主机/进程采样(媒体/AI维度显式 unknown)、R01 pre-activation status、Dispatcher AgentCoordinator/no-retry reconciliation 与可选 CLI listener 已通过本地测试;mTLS smoke、session generation、fingerprint allowlist 和错误 endpoint 拒绝已有证据;跨主机/第二 Cell/fleet-wide rotation 属后续阶段,不阻塞本轮单 Cell 验收 | 本阶段单 Cell 健康/版本报告与隔离故障矩阵已纳入本地 P1 证据;后续阶段再做跨主机 fleet 管理 |
| W07 | 完成 | W01 项目内 AI 双模式/授权/digest Schema 和样例已闭合;RPC Agent 可按部署注入的不可变快照/授权/egress 对 permit 与 Execute 做租户、版本/digest/mode、有效期、egress、撤销校验,DispatcherCoordinator 继续绑定 permit;本地 SaaS contract/fixture Mock 已由 `scripts/check-contracts.sh``internal/contract``internal/ai``make mq-integration-local` 覆盖(证据:`docs/evidence/20260919-local-saas-contract-mock.md`);真实 SaaS 读取/持久授权源延期第二阶段;本阶段契约 fixture/隔离授权已签收 | 第二阶段接入正式 D→A 生产授权消息和快照持久绑定;真实供应商另验 |
| W08 | 完成 | 已有租户/单 Cell scope 原子配额和 control CAS 本地测试;`Dispatcher.ExecuteReserved` 已把 SQLite reservation 与 fenced Agent permit/Execute 联通,提交前失败原子回队列,未知结果转 unknown 并保留占用;mTLS mock Agent accepted receipt 已有证据;单 Cell 最后发起屏障、控制竞态、本地故障注入和真实 MQ receipt 的本阶段边界已按契约/隔离证据签收;生产 receipt 延期第二阶段 | 本阶段本地门禁已通过;生产联测和多 Cell 依赖第二阶段 |
| W07 | 待验证 | W01 项目内 AI 双模式/授权/digest Schema 和样例已闭合;RPC Agent 可按部署注入的不可变快照/授权/egress 对 permit 与 Execute 做租户、版本/digest/mode、有效期、egress、撤销校验,DispatcherCoordinator 继续绑定 permit;本地 SaaS contract/fixture Mock 已由 `scripts/check-contracts.sh``internal/contract``internal/ai``make mq-integration-local` 覆盖(证据:`docs/evidence/20260919-local-saas-contract-mock.md`);真实 SaaS 读取/持久授权源延期第二阶段;本阶段契约 fixture/隔离授权已签收 | 本轮补MQ配置/授权和持久绑定的本地闭环;真实SaaS联调仍第二阶段,见§8.2 |
| W08 | 待验证 | 已有租户/单 Cell scope 原子配额和 control CAS 本地测试;`Dispatcher.ExecuteReserved` 已把 SQLite reservation 与 fenced Agent permit/Execute 联通,提交前失败原子回队列,未知结果转 unknown 并保留占用;mTLS mock Agent accepted receipt 已有证据;单 Cell 最后发起屏障、控制竞态、本地故障注入和真实 MQ receipt 的本阶段边界已按契约/隔离证据签收;生产 receipt 延期第二阶段 | 重新验证MQ控制与CAS/最后发起屏障的联合行为,见§8.2 |
| W09 | 完成 | 已采用 Pion RTP v1.10.5 的 bounded `PacketGuard`,使用库解析并覆盖 payload/SSRC/包长边界;项目内 `contract.ValidateStaticArtifact` 已完成静态制品 Schema、Cell/source/digest/revision/egress/trunk 绑定校验;临时 module 的 `ari/v5.3.1` 已在固定 Asterisk 22.10.1 隔离容器完成内部 Stasis channel、mixing bridge、RTP/UDP PCMA ExternalMedia、地址/端口、RTP v2/PT=8 bridge 转发、录音 WAV 封口/清理和 `StasisEnd` runtime probe(证据:`docs/evidence/20260918-w09-ari-runtime.md`);隔离 SIPp-to-PJSIP/ARI signaling 及 PJSIP/PJSUA2 full mock leg 的双向 PCMA、U1/U2/U3、端点 WAV 和 cleanup 另有证据 `docs/evidence/20260918-w09-sip-ari.md``docs/evidence/20260918-w09-sip-rtp-ari.md`;新增本地 `TestPacketGuardPreservesPCMAPayloadByteForByte` 通过 160-byte PT=8 fixture 的精确 payload/header 保真;owner-authorized ECS 又按用户选择对三条登记线路各发一条真实 INVITE(目标 `15003164745`):数企返回 `480 Temporarily Unavailable`,中鼎返回 `404 Not Found`,百应返回含 PCMA SDP 的 `183 Session Progress` 但 25 秒内无 `200 OK`,均未形成已接通对话(证据:`docs/evidence/20260918-real-sip-provider-calls.md``docs/evidence/20260919-real-sip-provider-calls-retry.md`);后续允许窗口重试仍为数企 `100/183``200`、中鼎 `100` 无最终响应、百应 `100/183/180``200`2026-09-19 新 ECS 对两个白名单目标再次直连真实供应商:数企/百应均无最终 `200`,中鼎第二目标一次信令达到 `200`,但后续媒体 probe 未干净完成,仅捕获 5 个 SIP 包和 1 个非 SIP UDP 包,未验收 RTP/录音,证据:`docs/evidence/20260919-real-provider-ecs-direct.md`;同一 ECS 已直接编译并以 systemd 启动物理 Asterisk 22.10.1provider-second endpoint 为 `Avail`;一次 bounded Asterisk 真实外呼 origin 返回 0,但仅有 13 个 SIP 包、1 个非 SIP UDP 包和 0 字节录音,未形成干净 RTP/录音证据;同一物理 Asterisk 又按线路前缀对 provider-primary/provider-third 各做一次直接 PJSIP bounded probe,均仅有 10 个 SIP 包和 1 个非 SIP UDP 包,无录音/干净媒体;最新一次 provider-second 外呼在用户即时确认后执行,临时 PCAP 仅观察 8 个 SIP 包、0 个媒体包和 `100/200/404` 状态 token,原始 PCAP 已删除且未自动重试;父目录三条线路与当前 Go 边界的注册/认证、From/PAI、前缀和选路对比见 `docs/evidence/20260919-sip-routing-implementation-comparison.md`;新增 `docs/evidence/20260919-mixed-ari-callflow.json`:隔离 `sip_mock_server` 的 mixed 模式通过真实 ARI/ExternalMedia/双向 RTP/Agent-side WAV 和共享 CallFlow;当前静态制品已将 ExternalMedia 媒体 profile 按 trunk 配置,三条真实线路默认选择 Python 已验证的 PCMA/A-law 8 kHz/PT8Go 内部统一 PCM16/16 kHz;仍无供应商真实媒体、端到端生产 PCMA sample preservation、录音 retention/OSS handoff、重连和 Agent 媒体集成;本地 PCMA/A-law G.711 转换、ExternalMedia `alaw` 选择、双向 RTP、非静音录音和共享 CallFlow 已由 `docs/evidence/20260920-pcma-mixed-callflow.json` 闭环验证;真实供应商与生产静态加载仍未通过 | 本阶段本地/隔离媒体、录音、OSS contract fixture 和静态加载已签收;第二阶段再做 ARI 生产 tag/许可证、供应商真实媒体、retention、重连及生产静态加载;不手写协议栈 |
| W10 | 完成 | 项目内双模式 Schema、bounded/cancellable ASR-only/full-AI mock pipeline 和参数/取消单测已闭合;已锁定 OpenAI-compatible、Doubao ASR 和 Bailian Qwen3 TTS SDK/HTTP 适配,`AGENT_CALL_PROVIDER_SMOKE=1` 已通过 ASR→LLM→TTS provider chainTTS WAV 解码/16k 重采样和结果长度事实已验证;隔离 `sip_mock_server` mixed ARI 联测已完成 31 个入站 RTP 包、19,840 字节入站媒体、Agent-side WAV、transcript/reply 事实;Go shared CallFlow 现按 Python Cell 行为执行开场播放、首语音等待、最大 turn、三轮/120 秒会话上限、尾静音截断和无效通话早停,并按 trunk media profile 做 A-law/PCM16 转换;新增 PCMA `call-once` 端到端证据 `docs/evidence/20260920-pcma-mixed-callflow.json`,完成 ARI answered、PCMA/8000/PT8、ExternalMedia `alaw`、110/20 双向 RTP、U1 播放、双端非静音 WAV、transcript/reply;一次 freshly-confirmed provider-second 真实外呼曾进入 Stasis/ExternalMedia 并观察到 220 RX/136 TX RTP,但真实 ASR 为空;当前 capture-first provider-second 重测仍以 `cause=1``StasisStart` 前结束,PJSIP/完整 PCAP 记录 `100 Trying``404 Not Found`,无媒体;provider-primary capture-first 记录 `100/183/486 Busy Here`,同样无媒体;provider-third 则已完成一次约49秒三轮真实 AI、RTP、3段入站/4段出站录音和双方文本事实。证据:`docs/evidence/20260920-real-provider-second-capture-first-v9.json``docs/evidence/20260920-real-provider-primary-capture-first.json``docs/evidence/20260920-real-provider-third-capture-first.json`;禁止以 provider smoke 或 Mock 代签 | 第二阶段在获得真实供应商授权后再重跑 RTP/ASR/LLM/TTS/播放联调;不自动更换通道或号码 |
| W11 | 完成 | W01 OSS upload control-plane Schema、Agent 受限 grant 的 HTTPS/host/size/checksum/expiry/object-key/redirect 防护、直接 PUT client 和 upload metadata RPC handlers 已通过本地测试;Alibaba OSS SDK v2 presigned PUT、PUT/HEAD、SHA-256、SQLite durable grant/completion、`recording.ready` outbox、显式重新申请及幂等已有证据;本阶段按契约结构/fixture/隔离状态机签收 upload-session/complete/verified,真实 SaaS handoff 延期第二阶段 | 本阶段按契约/fixture 和隔离状态机签收完整 upload-session/complete/verified 语义;真实 SaaS handoff、生命周期清理另行授权 |
| W12 | 完成 | 8种事件 strict Schema/fixtures、command.result outbox、`transcript.updated` builder、资源 freshness/unknown、invalid alias rejection 和 contract-backed local flow 已通过本地测试;`internal/calllog` 脱敏业务日志、统一 AgentControl listener、R11 fact durable 去重/冲突和 Dispatcher-owned aggregate version 已有专项证据;本阶段按契约结构、fixture、confirm/outbox 状态机和隔离 RabbitMQ 验收,生产 broker ACL/TLS/application receipt 延期第二阶段 | 本阶段按契约/fixture、outbox/replay/confirm 和状态机签收;生产 receipt、真实 broker 和第二阶段 R13 联调不得写成本阶段已实测 |
| W13 | 完成 | W13-a 可复现构建制品、manifest、非 root 权限/目录、配置样例、capture-first 入口和本地 package smoke 已有证据;最新本地 `gofmt``go build``go test -race ./...``go vet ./...``go mod verify`、契约和 Proto 检查通过;本轮只需在本地/隔离单 Cell 注入重启、断连、证书、磁盘、额度和 OSS 故障,不要求 ECS 或第二 Cell | `20260920-local-p1-acceptance.md` 已冻结单节点 digest/权限/配置/手册和本地故障矩阵;第二阶段另做生产候选 |
| W14 | 完成 | 已有单 Cell 隔离 session/permit 本地测试;历史双 Cell mock 仅作事实记录,不作为本轮门禁;一台 owner-authorized Debian ECS 已创建、加固并完成二进制 mock smoke,并在该主机隔离运行 Asterisk/PJSIP/PJSUA2/ARI compatibility probes;本轮另从固定 EIP 对三条登记 SIP endpoint 完成 OPTIONS `200 OK` reachability probeprovider-primary 本次 capture-first 对 `sip:708915003164745@61.132.228.221:5060` 返回 `100/183/486 Busy Here`provider-second 此前对 `sip:15003164745@60.171.24.90:5060` 返回 `100/404`;两次均有完整失败 PCAP,但未进入媒体;经本次当前会话确认,provider-third `160.202.254.79:5060` 对原始号码 `15003164745` 返回 `100/183/180/200`,完成约49秒三轮真实 AI 通话、3段入站+4段出站 PCM16/16k录音和双方文本事实;新 ECS 两白名单目标的直接真实供应商探针和物理 Asterisk bounded call 见 `docs/evidence/20260919-real-provider-ecs-direct.md`。新增 physical-host systemd package 安装 smoke,但其 manifest 仍为 dirty/non-production,未启动生产服务。仍没有批准生产 broker/SaaS application receipt、真实 provider-third 录音上传闭环、第二 Cell/第二 Asterisk 及完整 3 供应商真实证据(已移出本轮范围);Alibaba OSS grant/PUT/HEAD、15分钟单次 token/显式重新申请、durable completion 和 recording.ready outbox 已完成授权本地及新 ECS mTLS gRPC→OSS 实际上传验证;provider-third 的单 Cell SIP/RTP/AI/录音/文本成功仅为部分证据,隔离 ECS RabbitMQ candidate receipt 仅为部署证据。证据:`docs/evidence/20260918-cloud-host-bootstrap.md``docs/evidence/20260918-rabbitmq-integration.md``docs/evidence/20260918-real-sip-provider-calls.md``docs/evidence/20260919-real-sip-provider-calls-retry.md``docs/evidence/20260919-real-provider-callflow-attempts.json``docs/evidence/20260919-physical-systemd-deployment.md`、W09 evidence2026-09-20 已按用户授权创建并加固 Debian 13 ECS `i-2zeew9pswry8sr33095l`,绑定固定 EIP `123.56.71.98`,安装 Asterisk/Go Agent;一次真实 provider-second 外呼曾观察到双向 RTP但 ASR 为空,新版三轮重试以 `cause=1` 在 StasisStart 前结束;capture-first v4 的 PJSIP logger 明确记录 provider-second 对 `sip:15003164745@60.171.24.90:5060` 返回 `100 Trying``404 Not Found`,但短事务 PCAP 为 header-onlyentrypoint 已加入 drain,真实三轮/录音/双方文本/OSS/MQ 仍未闭合;部署与失败证据:`docs/evidence/20260918-cloud-host-bootstrap.md``docs/evidence/20260920-real-provider-second-15003164745.json``docs/evidence/20260920-real-provider-second-3turn-attempt.json``docs/evidence/20260920-real-provider-second-capture-first-v4.json``docs/evidence/20260920-real-provider-second-capture-first-v7-package.json``docs/evidence/20260920-real-provider-second-capture-first-v9.json``docs/evidence/20260920-real-provider-third-capture-first.json``docs/evidence/20260920-real-provider-primary-capture-first.json``docs/evidence/20260920-real-sip-attempt-ledger.json``docs/evidence/20260920-sip-attempt-guard.md``docs/evidence/20260920-acceptance-status.md``docs/evidence/20260920-real-cloud-inventory.md` | `20260920-local-p1-acceptance.md` 已签收本轮 W13/W14 单节点/单 Cell 本地故障与集成范围;真实 ECS、供应商、生产 receipt 和第二阶段拓扑另行授权,不回填为本轮通过 |
| W11 | 待验证 | W01 OSS upload control-plane Schema、Agent 受限 grant 的 HTTPS/host/size/checksum/expiry/object-key/redirect 防护、直接 PUT client 和 upload metadata RPC handlers 已通过本地测试;Alibaba OSS SDK v2 presigned PUT、PUT/HEAD、SHA-256、SQLite durable grant/completion、`recording.ready` outbox、显式重新申请及幂等已有证据;本阶段按契约结构/fixture/隔离状态机签收 upload-session/complete/verified,真实 SaaS handoff 延期第二阶段 | 本轮补D配置文件/TOKEN接线、SaaS MQ业务会话/complete/verified及恢复;保留D签发能力,旧验证结果不代签,见§8.2 |
| W12 | 待验证 | 8种事件 strict Schema/fixtures、command.result outbox、`transcript.updated` builder、资源 freshness/unknown、invalid alias rejection 和 contract-backed local flow 已通过本地测试;`internal/calllog` 脱敏业务日志、统一 AgentControl listener、R11 fact durable 去重/冲突和 Dispatcher-owned aggregate version 已有专项证据;本阶段按契约结构、fixture、confirm/outbox 状态机和隔离 RabbitMQ 验收,生产 broker ACL/TLS/application receipt 延期第二阶段 | 实现MQ来源/关联和整体结果补传,移除HTTP与重投执行的旧补传路径,见§8.2 |
| W13 | 待验证 | W13-a 可复现构建制品、manifest、非 root 权限/目录、配置样例、capture-first 入口和本地 package smoke 已有证据;最新本地 `gofmt``go build``go test -race ./...``go vet ./...``go mod verify`、契约和 Proto 检查通过;本轮只需在本地/隔离单 Cell 注入重启、断连、证书、磁盘、额度和 OSS 故障,不要求 ECS 或第二 Cell | 依据新MQ合同更新候选配置/手册,重新验证消息及上传故障,见§8.2 |
| W14 | 待验证 | 已有单 Cell 隔离 session/permit 本地测试;历史双 Cell mock 仅作事实记录,不作为本轮门禁;一台 owner-authorized Debian ECS 已创建、加固并完成二进制 mock smoke,并在该主机隔离运行 Asterisk/PJSIP/PJSUA2/ARI compatibility probes;本轮另从固定 EIP 对三条登记 SIP endpoint 完成 OPTIONS `200 OK` reachability probeprovider-primary 本次 capture-first 对 `sip:708915003164745@61.132.228.221:5060` 返回 `100/183/486 Busy Here`provider-second 此前对 `sip:15003164745@60.171.24.90:5060` 返回 `100/404`;两次均有完整失败 PCAP,但未进入媒体;经本次当前会话确认,provider-third `160.202.254.79:5060` 对原始号码 `15003164745` 返回 `100/183/180/200`,完成约49秒三轮真实 AI 通话、3段入站+4段出站 PCM16/16k录音和双方文本事实;新 ECS 两白名单目标的直接真实供应商探针和物理 Asterisk bounded call 见 `docs/evidence/20260919-real-provider-ecs-direct.md`。新增 physical-host systemd package 安装 smoke,但其 manifest 仍为 dirty/non-production,未启动生产服务。仍没有批准生产 broker/SaaS application receipt、真实 provider-third 录音上传闭环、第二 Cell/第二 Asterisk 及完整 3 供应商真实证据(已移出本轮范围);Alibaba OSS grant/PUT/HEAD、15分钟单次 token/显式重新申请、durable completion 和 recording.ready outbox 已完成授权本地及新 ECS mTLS gRPC→OSS 实际上传验证;provider-third 的单 Cell SIP/RTP/AI/录音/文本成功仅为部分证据,隔离 ECS RabbitMQ candidate receipt 仅为部署证据。证据:`docs/evidence/20260918-cloud-host-bootstrap.md``docs/evidence/20260918-rabbitmq-integration.md``docs/evidence/20260918-real-sip-provider-calls.md``docs/evidence/20260919-real-sip-provider-calls-retry.md``docs/evidence/20260919-real-provider-callflow-attempts.json``docs/evidence/20260919-physical-systemd-deployment.md`、W09 evidence2026-09-20 已按用户授权创建并加固 Debian 13 ECS `i-2zeew9pswry8sr33095l`,绑定固定 EIP `123.56.71.98`,安装 Asterisk/Go Agent;一次真实 provider-second 外呼曾观察到双向 RTP但 ASR 为空,新版三轮重试以 `cause=1` 在 StasisStart 前结束;capture-first v4 的 PJSIP logger 明确记录 provider-second 对 `sip:15003164745@60.171.24.90:5060` 返回 `100 Trying``404 Not Found`,但短事务 PCAP 为 header-onlyentrypoint 已加入 drain,真实三轮/录音/双方文本/OSS/MQ 仍未闭合;部署与失败证据:`docs/evidence/20260918-cloud-host-bootstrap.md``docs/evidence/20260920-real-provider-second-15003164745.json``docs/evidence/20260920-real-provider-second-3turn-attempt.json``docs/evidence/20260920-real-provider-second-capture-first-v4.json``docs/evidence/20260920-real-provider-second-capture-first-v7-package.json``docs/evidence/20260920-real-provider-second-capture-first-v9.json``docs/evidence/20260920-real-provider-third-capture-first.json``docs/evidence/20260920-real-provider-primary-capture-first.json``docs/evidence/20260920-real-sip-attempt-ledger.json``docs/evidence/20260920-sip-attempt-guard.md``docs/evidence/20260920-acceptance-status.md``docs/evidence/20260920-real-cloud-inventory.md` | 重新执行MQ-only本地全流程及D1/D2路由fixture,旧验收不覆盖修订,见§8.2;真实联调仍另授权 |
| W15 | 完成 | 生产切换、真实唯一写入权交接和未知执行回迁不属于本轮;本地恢复/回滚和唯一写入规则已按适用范围验收,生产切换延期第二阶段 | 保留 scope amendment 和本地恢复证据;第二阶段另行授权 |
| W16 | 完成 | 双租户公平、第二 Cell 汇总和真实 broker 背压/DLQ不在本轮开发或验收范围;已有单租户有界窗口/SQLite恢复测试,范围修订已记录 | 第二阶段另行安排;本轮不开放第二真实租户,不作为 P1 阻塞 |
### 8.1 本轮文档变更
**本轮结果:**W01W14 的项目内单节点/单 Cell/单租户适用范围已由本地回归、契约/fixture、隔离故障矩阵和 `20260920-local-p1-acceptance.md` 签收。W15生产切换、真实 SaaS/MQ receipt、真实供应商/ECS、双节点/第二 Cell/双租户、容量/N+1属于第二阶段,不阻塞本轮,也不能被本地证据反写成生产已完成。生产服务未来仍须使用 `deploys/` 的 Debian 13/systemd 包,真实外呼继续遵守逐次确认、capture-first、白名单、09:0020:00 Asia/Shanghai 和每日额度规则
- 状态:完成(仅文档)
- 保留此前新增时间泳道文档及计划记录;将其中错误的HTTP目标纠正为全部经MQ,并补齐D唯一身份/独立Topic、请求响应和旧实现差异。
- 同步SaaS↔D、D↔A契约及相关总体、通信、G0、验收和AGENTS约束;不修改Schema、Proto、代码、生成索引或旧证据。
- 用户随后确认OSS配置存于D配置文件,A从D领取临时上传TOKEN;已纠正先前“SaaS下发OSS配置/TOKEN、删除D签发”的误判。TOKEN显式重申请也找D;SaaS业务会话及最终verified校验职责不变。
- 完成条件:目标时序无SaaS↔D直连、所有交互明确MQ路径、独立D接收与租户隔离不冲突、新旧状态分开。
- 本轮OSS来源纠正文档验证已通过:11份文档、75个本地链接/锚点、76张表格、代码围栏/空白及`git diff --check`;55条时序连线保留D配置→D临时TOKEN→A直传及SaaS最终verified,无SaaS↔D直连。旧“SaaS提供OSS配置/TOKEN、删除D签发”目标表述已清理,验收编号未变;代码、Proto、旧契约包/生成索引及历史证据未改。未进行Mermaid渲染或运行测试,不以静态检查替代实现验收。
### 8.2 MQ-only修订后的当前待办与解除条件
| 工作包 / 当前状态 | I/M/G 与下一动作 | 解除条件 |
| --- | --- | --- |
| W01 / 待验证 | 新I未就绪;契约负责人冻结身份生命周期、Topic/队列/绑定、完整路由预算和所有消息/关联/错误/期限 | 新版本、来源/哈希、严格Schema与正反例;旧包原样保留,不能只改图就记I |
| W02 / 待验证 | 原Proto证据保留;重新核验D身份与epoch、MQ异步配置/上传到Unary的衔接 | pending/有界等待/原操作恢复/最终结果可验证;必要Proto变更先冻结再生成 |
| W04 / 待验证 | 汇总新I及相应PoC,受影响G0重新签收 | 新合同与本地消息/恢复PoC证据;真实资源仍另授权 |
| W05/W08/W12 / 待验证 | 新M/G未通过;MQ控制/查询/补传、D身份/定向响应、持久恢复和控制屏障;删除旧SaaS HTTP通道 | 控制accepted/applied、查询、原结果补传及崩溃/重复/乱序通过,不重发执行当补传,不保留HTTP兼容 |
| W07 / 待验证 | 实现MQ配置/授权与持久绑定;不开发旧AI GET | 原租户/版本/摘要及有效授权保持,缓存/撤销/迟到/重启检查通过 |
| W11 / 待验证 | 核验D配置文件/TOKEN/显式重申请;实现SaaS MQ业务会话/complete/verified,与R12/R13联合 | 配置缺失明确失败,不向SaaS取配置/TOKEN;保留D签发、A直传;SaaS verified后才ready,不以D本地验证替代,不新建资产 |
| W13/W14 / 待验证 | 新候选与本地联合G未通过;部署配置/手册取消旧HTTP接入 | 全流程无SaaS↔D HTTPD1/D2消息fixture无串收/竞争,重复ID、错目标、路由长度、不可路由/断连及关联恢复均验证;不扩为双D业务运行 |
实际负责人尚未指定;本轮只完成文档纠正,以上不是实现完成记录。后续顺序:W01新合同及W02衔接核验 → W04对应门禁 → D侧各工作包按新I实现 → W13/W14联合回归。旧通过数和`20260920-local-p1-acceptance.md`不覆盖新修订。
**历史本地结果:**修订前W01–W14 的项目内单节点/单 Cell/单租户适用范围曾由本地回归、契约/fixture、隔离故障矩阵和 `20260920-local-p1-acceptance.md` 签收;本轮受影响项以§8.2待验证为准。W15生产切换、真实 SaaS/MQ receipt、真实供应商/ECS、双节点/第二 Cell/双租户、容量/N+1属于第二阶段,不阻塞本轮,也不能被本地证据反写成生产已完成。生产服务未来仍须使用 `deploys/` 的 Debian 13/systemd 包,真实外呼继续遵守逐次确认、capture-first、白名单、09:0020:00 Asia/Shanghai 和每日额度规则。
后续每次完成任务时按负责人和证据规则更新本节、实际运行说明及证据;只有需求变化才修改§4/阶段范围并注明用户确认,不能用更新进度掩盖变更。
@@ -42,7 +42,7 @@
| 百炼ASR | github.com/devinyf/dashscopegoparaformer)为首选PoC | 仅在批准模型匹配run-task时采用;FunASR/Qwen/NLS不能按名称替换,必要字段/取消不满足先替代或补上游 |
| 火山ASR/TTS | github.com/GizClaw/doubao-speech-go共用一套薄适配底座 | ASR SAUC与选中TTS协议分别验证;可调参数覆盖仍有门禁,见§4.3,不能先宣称已最终锁库 |
| OpenAI兼容LLM | 官方github.com/openai/openai-go/v3,先验证目标Chat Completions流式路径 | SaaS提供受控端点/模型;不自动启用Responses/Realtime/收费OpenAI端点;重试显式关闭 |
| OSS | 标准库net/http消费SaaS签名PUT;只有需要OSS API/受限凭据时引官方aliyun/alibabacloud-oss-go-sdk-v2 | 不自写签名、不持多余长期AK、不另建上传配置源 |
| OSS | D读取自身配置文件,复用官方aliyun/alibabacloud-oss-go-sdk-v2提供临时上传TOKENA以标准HTTP/SDK直传 | A向D领TOKEN,不向SaaS取OSS配置/TOKEN,不持长期凭据;核验TOKEN形态/约束及UploadGrant映射,不自写签名;SaaS最终verified职责不变 |
| 健康/日志/HTTP | github.com/shirou/gopsutil/v4、log/slog、net/http、context、crypto/tls | 指标接入Prometheus时再引client_golangSDK自带WS,不并行引通用WS框架 |
本轮公开搜索/包文档补核可用方向,但raw GitHub/Go proxy读取被工具SSRF保护拒绝,未绕过限制;确切源码、发行tag/hash和全部参数能力仍待受控环境核验。不能把上述优先目标写成已生成go.mod/go.sum或生产准入。
@@ -71,7 +71,7 @@
| WebSocket | SDK 自带传输;必要时 [gorilla/websocket](https://github.com/gorilla/websocket) 或 [coder/websocket](https://github.com/coder/websocket) | Gorilla 为 BSD-2-ClauseCoder 为 ISC,未归档 | 优先沿用被选 SDK 的传输,避免两套并存;不手写帧/握手。原 ASR 服务直接依赖 Gorilla,不能把其 Go 1.26.2 模块一并改版 |
| JSON Schema | [santhosh-tekuri/jsonschema/v6](https://pkg.go.dev/github.com/santhosh-tekuri/jsonschema/v6) | Apache-2.0;本轮 proxy 返回 v6.0.3;仓库默认分支是 boon | 候选用于 2020-12 校验;锁 Go module/release,不克隆默认分支就假设是 Go 包;远端引用访问默认关闭/受控 |
| OpenAPI 生成/解析 | [oapi-codegen](https://github.com/oapi-codegen/oapi-codegen)、必要时 [libopenapi](https://github.com/pb33f/libopenapi) | Apache-2.0 / MIT,未归档 | 当前本地契约是 3.1;用真实发行版跑完整生成/校验 PoC;不把原契约改成3.0,不另手写Schema;不是默认同时引入两个运行库 |
| OSS | 签名 PUT 用标准库 `net/http`;需要 OSS API 时 [阿里云 OSS Go SDK v2](https://github.com/aliyun/alibabacloud-oss-go-sdk-v2) | Apache-2.0,官方、未归档 | Agent 消费 SaaS 授权,不持长期AK/接管签名服务;SDK/HTTP保留 headers会话对象不可覆盖约束 |
| OSS | D提供临时授权复用[阿里云 OSS Go SDK v2](https://github.com/aliyun/alibabacloud-oss-go-sdk-v2)A按获批TOKEN形态用标准HTTP/SDK上传 | Apache-2.0,官方、未归档 | OSS配置仅在D配置文件,A经Unary领临时TOKEN而非SaaS签名,不持长期AK;保留D签发能力,验证headers/会话/对象/期限及不可覆盖约束D本地HEAD不替代SaaS verified |
| 指标 | [prometheus/client_golang](https://github.com/prometheus/client_golang) | Apache-2.0,未归档 | 需要 Prometheus 时直接复用;不得自写 exposition 格式或无限维度指标 |
| 日志/HTTP/TLS/并发 | `log/slog``net/http``crypto/tls``context` 等 Go 标准库 | 随固定工具链交付 | 不新增同功能框架;认证、权限和资源上限仍须落实 |
+47 -39
View File
@@ -4,16 +4,17 @@
本文件保留现有外部命令/事件与内部职责目录,并明确本轮适用范围:**P1为1 Agent/1 Asterisk/单 Cell、至少3家SIP trunk 的契约与协议 fixture、单租户、静态配置、ASR-only与完整AI双模式;真实 SaaS/MQ 联调、双节点、第二 Cell、第二租户和生产切换延期第二阶段。** 全量目录不等于本轮全部实现;当前只交付设计,运行通过记录另见验收证据。
- 外部业务字段以《SaaS交互_OpenAPI与MQ契约规划_v0.1.md》正文v1.0为权威;现有OpenAPI用于接口/字段核对。原文件名不代表正文版本
- **本轮用户已确认SaaS↔Dispatcher全部交互只经RabbitMQ专用Topic,禁止双方HTTP。每个D有全局唯一ID及独立接收Topic/队列,不能共享队列抢收指定D的消息。** 精确身份/拓扑/消息/关联待新版合同冻结,见[MQ-only契约](contracts/saas-dispatcher.md)与[计划§1.2/§8.2](plan-0918.md)。P1仍单活D;D1/D2仅用于本地路由隔离fixture,不开发多D协调
- 旧外部业务字段以《SaaS交互_OpenAPI与MQ契约规划_v0.1.md》正文v1.0及固定包记录为语义来源;其中HTTP传输和旧租户路由已被MQ-only修订替代。旧OpenAPI/哈希只作对照,不手改源包或只读索引,不把中文MQ语义当已发布字段。
- [OpenAPI与MQ字段索引](OpenAPI与MQ字段索引_v0.1.md) 是5份OpenAPI、42个HTTP操作、115个命名组件及2份JSON Schema的只读机器提取快照,记录源哈希,不是第二套手写Schema。
- 下文 **“现有契约”** 不允许自行改字段/语义;**“内部草案”** 是待批准的gRPC方法/数据模型,不冒充已有OpenAPI;**“缺口”** 明确阻塞相应实现/验收。
- 用户已确认保留Unary RPC、Dispatcher维护Agent Endpoint列表、Agent共用一套mTLS证书。OSS配置源于SaaSDispatcher统一承接Agent直传OSS。文本继续实时MQ回传,OSS只作归档。
- 用户已确认保留Unary RPC、Dispatcher维护Agent Endpoint列表、Agent共用一套mTLS证书。OSS配置存于Dispatcher配置文件Agent向D领取临时上传TOKEN后直传OSSSaaS不下发OSS配置/TOKEN,但最终verified校验职责不变。文本继续实时MQ回传,OSS只作归档。
- 准确的文字事件名是 **`transcript.updated`**`call.transcript` 是之前讨论中的泛称,不是合法event_type,不新增该别名。
- 首发AI范围已确认:**百炼/火山ASR、OpenAI兼容LLM、火山TTS**。业务控制参数由Dispatcher按任务版本向SaaS获取,Agent按执行快照使用;不得从源码常量、本地业务配置或SDK默认值形成第二配置源。具体模型/协议/额度仍须批准和PoC。
### 1.1 已对齐部分与仍待补齐的约束
逐字段及SHA-256复核确认,**当前MQ信封与正文对齐**本轮讨论中曾判断存在旧信封漂移,该判断已撤回,不是当前源文件缺陷
此前逐字段及SHA-256复核确认,**MQ信封与对应版本正文对齐**这不代表新增D身份/专用Topic及全MQ请求响应已纳入该版Schema。以下为旧外部源的对齐/缺口记录,项目内旧补齐成果以固定包为准;本轮新差异另见GAP-10
| 项 | 当前正文/Schema一致内容 | 仍须验证 |
| --- | --- | --- |
@@ -26,24 +27,25 @@
**GAP-01只指事件专属payload和未机读化业务约束的覆盖不足,不指信封字段漂移。** P0补齐并跑正反例。本轮不覆盖父项目已有文件;不能把仅通过通用object校验当完整事件验收。
### 1.2 分期不改变外部契约
### 1.2 分期与本轮传输修订
- P1保留call.execute、既有7条SaaS交互路径和8种事件;整体补传是现有交付可靠性能力,不是待建设的通用回放平台
- P1保留call.execute、控制/查询/整体补传/录音协调的既有业务语义及8种业务事件;旧7条HTTP路径全部废弃为接入方式,对应请求响应改经MQ。整体补传恢复原结果,不重新投call.execute执行
- 42操作/115组件仅为上游目录;不把管理平台30操作移入Dispatcher。按实际入口及引用闭包生成校验,来源包/只读索引仍完整留存,不通过删Schema缩小范围。
- 单租户仅指启用策略:保留tenant_key精确路由、租户独立队列/复合幂等键、有界窗口和单 Cell 全局配额;双租户公平、第二 Cell 汇总和多Dispatcher协调另立第二阶段。
- P1不新增MQ模式字段、事件或临时接口。ASR-only的Schema表达须先补GAP-08;R04/R06在线改配延后,但最后许可、控制、静态维护屏障和持久恢复不能延后。
- P1不新增任务MQ模式字段或临时接口。MQ-only所需的控制/查询/配置/上传请求响应必须经GAP-10正式发布,不能伪装为现有8类事件或宽松透传。ASR-only表达沿GAP-08;R04/R06在线改配延后,但最后许可、控制、静态维护屏障和持久恢复不能延后。
## 2. 角色、传输和可靠性边界
| 通道 | 发送方 → 接收方 | 内容 | 接受/交付的含义 |
| --- | --- | --- | --- |
| RabbitMQ命令 | SaaS → Dispatcher | call.execute | Dispatcher事务持久受理/拒绝和outbox后才ACK |
| HTTP现有7路径 | SaaS ↔ Dispatcher5)及Dispatcher → SaaS2) | 控制/查询/整体补传;录音上传申请/complete | 控制202只代表acceptedcomplete为对象验证,不是业务结果回调 |
| 既有AI配置读取 | Dispatcher → SaaS受控配置服务 | 任务引用的不可变AI版本及获授权供应商配置引用 | 复用§6.1的已有GET,不新增HTTP拨号或任务业务回调;Agent不直连SaaS |
| RabbitMQ执行/控制/查询/补传 | SaaS → MQ → 指定D专用Topic;响应经MQ回SaaS | call.execute及既有业务语义 | 校验目标/租户/原请求,持久受理和outbox后才ACKaccepted不等于applied |
| RabbitMQ录音协调 | Dispatcher ↔ MQ ↔ SaaS | 原业务上传会话/资产登记、complete/verified | 响应回原D;不传OSS配置/TOKEN,只有SaaS verified才能ready |
| 临时上传TOKEN | Agent ↔ Unary ↔ Dispatcher | D依配置文件提供TOKEN/受限上传信息;过期显式重新申请 | 长期凭据不交给A,配置无效明确失败,不向SaaS取配置/TOKEN |
| RabbitMQ AI配置/授权 | Dispatcher ↔ MQ ↔ SaaS | 任务引用的不可变AI版本及有效授权 | D专用Topic收原请求响应并持久绑定;旧GET已废弃,Agent不直连SaaS |
| 内部Unary gRPC | Dispatcher ↔ Agent | 执行授权、控制、配置、状态、最终文字、上传元信息 | 每个RPC有独立deadline、权限、请求关联及幂等;不是一条双向数据流 |
| ARI/RTP | Agent ↔ 本Cell Asterisk | 通道/桥/媒体/录音 | 实际拨号副作用不与任何数据库事务原子提交 |
| OSS数据面 | Agent → OSS | P1已封口录音;文本OSS归档后续 | PUT成功不等于SaaS verifiedETag不等于SHA-256 |
| RabbitMQ结果 | Dispatcher → SaaS | 本文8类event_type | publisher confirm只表示broker收妥;SaaS用inbox+业务同事务应用,无额外应用收讫协议 |
| RabbitMQ结果 | Dispatcher → MQ → SaaS专用订阅 | 本文8类业务event_type及新版冻结的响应 | 来源D/租户/请求可关联;confirm只表示broker收妥,持久inbox后ACK,不擅自新增application receipt协议 |
| SIP配置管理 | 管理平台 → 批准静态制品/受控部署 → Agent;D核验准入 | 版本/哈希/目标及实际加载事实,P1维护窗口生效 | 管理平台唯一编辑面;静态交接见GAP-03;在线D推送暂缓 |
Agent不持MQ/SaaS管理凭据、不直接消费SaaS队列,不新增公开HTTP拨号/结果回调。普通Unary同样复用HTTP/2连接,不能按每通电话新建连接。
@@ -52,8 +54,9 @@ Agent不持MQ/SaaS管理凭据、不直接消费SaaS队列,不新增公开HTTP
| 标识/版本 | 范围与用途 |
| --- | --- |
| tenant_id / tenant_key | 前者可信归属,后者原值一对一队列绑定;不清洗/编码/截断,224 UTF-8字节预算不满足时保留原任务并停发 |
| command_id | 租户作用域业务命令幂等键;HTTP Idempotency-Key及正文关联按原契约 |
| Dispatcher逻辑ID(精确字段待冻结) | 全局唯一、独立接收Topic;与tenant/Agent/Cell ID及dispatcher_epoch分开。身份持久化/重复拒绝及请求响应关联须新合同冻结 |
| tenant_id / tenant_key | 前者可信归属,后者原值绑定;D隔离不替代租户隔离。旧224字节预算不能直接套新拓扑,完整长度/通配符边界重新验证,超限保留任务停发,不清洗/编码/截断 |
| command_id | 租户作用域业务幂等身份;旧HTTP Idempotency-Key语义须映射到获批MQ合同,不照搬header或猜字段 |
| execution_id | 租户作用域授权执行,换command_id不得重复拨号 |
| task_id / task_item_id / task_revision | 任务、成员和控制版本;与软件/配置版本无关 |
| call_id / attempt_id | 一次逻辑通话与具体拨号尝试;只有持久化意图后才产生call。P1不启用自动FALLBACK;未来启用仍属原执行并计CPS |
@@ -69,7 +72,7 @@ Agent不持MQ/SaaS管理凭据、不直接消费SaaS队列,不新增公开HTTP
## 4. 现有call.execute完整业务入口
Routing key`agent-call.tenant.{tenant_key}.call.execute`direct exchange;资源命名空间仍为agent-call
旧routing key`agent-call.tenant.{tenant_key}.call.execute`direct exchange,**仅作旧实现对照,不用于新接入**。新版必须定向指定D专用Topic/队列并保留租户隔离,精确命名在GAP-10冻结。下表保留旧业务字段语义,不代表已包含D身份/关联
| 外壳字段 | 语义 |
| --- | --- |
@@ -99,15 +102,15 @@ P1从管理批准的静态route_policy/caller_profile选择供应商trunk与获
每种事件必有:`schema_version``event_id``event_type``tenant_id``tenant_key``trace_id``occurred_at``aggregate_type``aggregate_id``aggregate_version``payload`
Routing key为`agent-call.{event_type}`。SaaS按`(tenant_id,event_id)`去重,inbox与业务更新同事务,成功后ACK。Dispatcher保存版本化快照及outbox,重发保留event_id/内容,不因RPC重报创建第二个业务事件。本文不会把示例里的可选字段擅自升级成机器required;尚缺的payload Schema见GAP-01。
旧routing key为`agent-call.{event_type}`;新版须冻结来源D及业务归属的表达。SaaS按`(tenant_id,event_id)`去重,inbox与业务更新同事务,成功后ACK。Dispatcher保存版本化快照及outbox,重发保留event_id/内容,不因RPC重报创建第二个业务事件。本文不会把示例里的可选字段擅自升级成机器required;尚缺的payload Schema见GAP-01。
| event_type | 事实来源 / 发布者 | 必需或条件业务字段 | 时点与合并 |
| --- | --- | --- | --- |
| command.result | Dispatcher自身受理/控制/执行汇总 → Dispatcher | command_id、command_type、status、reason_code;适用的task/execution/call关联;execute含等待/准入字段;control含requested/applied_task_revision | command聚合;accepted/waiting不代表已拨;202不代表applied;重试不重复递增revision |
| command.result | Dispatcher自身受理/控制/执行汇总 → Dispatcher | command_id、command_type、status、reason_code;适用的task/execution/call关联;execute含等待/准入字段;control含requested/applied_task_revision | command聚合;accepted/waiting不代表已拨;MQ受理响应不代表applied;重试不重复递增revision |
| call.status | Agent经ARI观测+Dispatcher授权账本 → Dispatcher | call_id、execution_id、任务关联、call_state、call_version、attempt_id、attempt状态、实际线路/Cell/出口、时间/原因;尚未定名的键在GAP-01冻结 | 同call/attempt域更新;只有实际证据才dialing/ringing/answered,迟到状态不回退 |
| transcript.updated | Agent的ASR/对话/播放证据 → Dispatcher | call_id、turn_id、segment_id、role、revision、text、is_final、start_ms、end_ms、playback_state | transcript_segment域;同段高revision替换,final不被中间稿覆盖;不等整通话OSS上传 |
| call.finished | Agent终态事实+Dispatcher对账/汇总 → Dispatcher | call_id、execution_id、任务关联、call_version、outcome、起止/时长/原因、attempt汇总、资产处理快照 | 固定通话终态,后处理可pending,不覆盖独立资产的新状态 |
| recording.ready | SaaS complete/独立对象校验成功 → Dispatcher | call_id、recording_id、oss_id、format、channels、sample_rate_hz、duration_ms、size_bytes、checksum_sha256 | recording域;只报告verified资产,不携带上传凭据/公开URL |
| recording.ready | SaaS经MQ返回complete的verified结果 → Dispatcher | call_id、recording_id、oss_id、format、channels、sample_rate_hz、duration_ms、size_bytes、checksum_sha256 | recording域;只报告verified资产,不携带上传凭据/公开URL |
| recording.failed | Agent本地/上传失败、Dispatcher授权/校验失败 → Dispatcher | call_id、recording_id、stage、reason_code、retryable、next_retry_at(若有) | 标记资产失败,不改变通话终态;合法ready可完成恢复 |
| transcript.failed | Agent/Dispatcher发现文字缺段或不可恢复错误 → Dispatcher | call_id、原因、retryable、受影响segment(适用时) | 明确不完整,不能把现有部分文件包装成完整最终稿 |
| contact.opt_out | 获批业务判定 → Agent及时报告 → Dispatcher | call_id、task_id、task_item_id、请求时间、关联turn/segment(若有) | SaaS及时持久禁发并处理关联任务屏障,不等挂断;不自造关键词判定 |
@@ -124,34 +127,37 @@ Routing key为`agent-call.{event_type}`。SaaS按`(tenant_id,event_id)`去重,
- 文本OSS归档不是第9种既有事件,也不能冒充recording.ready;查看实时文字继续用transcript.updated。归档授权/引用扩展见GAP-02,P1不启用且不阻塞实时文字。
- ASR-only仍上报真实customer文字及获批opt-out事实,不伪造agent回答/播放或接通证据。两模式下角色/播放状态/失败分支的合法组合须在GAP-01/GAP-08补齐;不能为省事关闭实时文字或opt-out。
## 6. 现有HTTP接口归属与字段查阅
## 6. SaaS↔Dispatcher 全MQ交互目录
全部参数、请求/响应、错误和Schema见 [字段索引](OpenAPI与MQ字段索引_v0.1.md)。保留的7个SaaS业务交互路径为:
旧7条业务HTTP路径及AI GET均不再作为目标接入。其业务语义由新版MQ合同承接;旧[字段索引](OpenAPI与MQ字段索引_v0.1.md)只作只读对照,不手改生成物。下表中文名称不是已获批消息枚举,详见[SaaS↔D契约§5–§6](contracts/saas-dispatcher.md)。
| 方法/路径 | 新实现归属 | 注意 |
| 业务语义 | MQ请求/响应方向 | 保留的约束 |
| --- | --- | --- |
| POST /internal/v1/outbound/tasks/{task_id}/controls | Dispatcher提供 | expected_task_revision CASpause/resume/stopstop显式drain/hangup及权限;202≠applied |
| GET /internal/v1/outbound/commands/{command_id} | Dispatcher提供 | command聚合快照和等待/准入字段 |
| GET /internal/v1/outbound/calls/{call_id} | Dispatcher提供 | 通话/attempt独立资产状态快照 |
| POST /internal/v1/outbound/calls/{call_id}/replays | Dispatcher提供 | 固定截止点整体补传,不支持局部事件/turn筛选 |
| POST /internal/v1/outbound/commands/{source_command_id}/replays | Dispatcher提供 | command尚无call也可补传;保持原ID,不递归自身结果 |
| POST /internal/v1/outbound/recording-uploads | SaaS提供,Dispatcher调用 | 租户/幂等/录音元信息校验;Agent不直接感知SaaS认证域 |
| POST /internal/v1/outbound/recording-uploads/{upload_id}/complete | SaaS提供,Dispatcher调用 | 对象独立验证verified前不得发布recording.ready |
| 任务控制 | SaaS→指定DD→SaaS | expected_task_revision CASpause/resume/stopdrain/hangupaccepted不等于applied |
| 命令查询 | SaaS→指定DD→SaaS | command及等待/执行事实,结果关联原查询 |
| 通话查询 | SaaS→指定DD→SaaS | 原call/attempt独立资产状态,不按当前配置补历史 |
| call整体补传 | SaaS→指定DD→SaaS | 固定截止点/原事件ID和版本,不支持局部筛选,不重拨 |
| source-command整体补传 | SaaS→指定DD→SaaS | 尚无call也可补传结果,不重发执行命令、不递归自身结果 |
| 业务上传会话 | D→SaaSSaaS→原D专用Topic | 保留原资产/租户/会话登记语义,只传业务元信息,不获取OSS配置/TOKEN |
| complete/verified | D→SaaSSaaS→原D专用Topic | SaaS独立验证verified前不readyD本地验证不能替代 |
| AI配置/授权 | D→SaaSSaaS→原D专用Topic | 原租户/不可变版本/摘要/有效授权,见§6.1 |
若源OpenAPI的实际路径/参数变化,先同步源指纹和索引,不按本表猜测实现。HTTP作用域、Idempotency-Key、tenant/target绑定、404/409/410等保持原语义。AI配置作为实际需要的外部配置源消费,不重建其管理面;管理平台30操作不移入Dispatcher。Cell的3个mTLS操作保留参考,P1可经批准静态制品交接而不暴露旧在线apply面;不能同时让管理平台、部署脚本和Dispatcher各自写同一配置
所有请求响应均持久关联目标/来源D、原租户及业务对象;持久后ACK、状态/outbox同事务、重复/迟到/超时/重启沿原关联恢复。超时不表示未执行,不换D重拨,不回退HTTP。错误分类保留“不存在/冲突/保留过期”等语义,精确MQ错误码及期限待GAP-10冻结,不直接搬HTTP状态码
OSS配置/TOKEN来源不属于上述SaaS MQ目录:OSS配置存于D配置文件,A经Unary向D领取/显式重新申请临时TOKEN,配置缺失/无效明确失败。AI配置管理仍归SaaS,管理平台30操作不移入D;Cell静态制品唯一写入和屏障不变,内部Unary及Agent→OSS直传不受SaaS↔D禁HTTP规则影响。
### 6.1 SaaS任务配置 → Dispatcher → AgentP1必需)
“任务配置”沿用MQ任务的 `agent_version_id` 及获批 `variables` 引用:**Dispatcher调用已有 `GET /internal/v1/ai/agent-versions/{agent_version_id}` 获取SaaS维护的不可变配置**,不新增猜测task-config路径。AI配置发布接口仍由上游拥有,D/Agent不另建编辑面;该GET由哪个SaaS部署入口承载、鉴权/租户绑定及provider_ref的受控解析合同在GAP-09冻结。其不改变前述7个业务交互路径的定义
“任务配置”沿用MQ任务的 `agent_version_id` 及获批 `variables` 引用:**D经MQ向SaaS请求不可变配置及有效授权,SaaS经原D专用Topic响应**。旧AI版本GET已废弃为D接入方式,不开发HTTP client或猜测task-config路径。AI配置编辑仍归SaaS;GAP-09冻结版本/授权与受控引用语义,GAP-10冻结MQ消息/关联/期限
1. D先验MQ可信租户/版本/幂等;历史执行走原事实恢复,不因调参重新执行。新执行在发起前取得对应租户版本,不把模型/音色/timeout等直接加入call.execute。
2. D验证源Schema、不可变内容摘要、租户授权、两种模式和SDK能力,解析该版本批准的provider_ref/credential_ref。SaaS返回的受控供应商配置提供API种类/协议版本、端点、region/资源标识等;缺合同标blocked,不在源码中按供应商名称拼端点或填示例resource_id。
3. 缓存键至少绑定租户和agent_version_id;同版本异内容拒绝并告警。按批准的撤销/新鲜度策略使用已验证缓存;SaaS不可达且无仍有效的已授权快照则暂停/拒绝新准入,遵守原admission_deadline,不用默认模型、其它租户缓存或无限期旧配置顶替。HTTP读取可有界重试,不持SQLite事务等待网络。
3. 缓存键至少绑定租户和agent_version_id;同版本异内容拒绝并告警。按批准的撤销/新鲜度策略使用已验证缓存;SaaS不可达且无仍有效的已授权快照则暂停/拒绝新准入,遵守原admission_deadline,不用默认模型、其它租户缓存或无限期旧配置顶替。MQ请求重投/响应恢复沿原请求关联且有界,不持SQLite事务等待网络;不回退HTTP
4. D将该执行最终有效配置、源版本/摘要及SDK能力匹配绑定到原execution。沿获批R07传送原Schema JSON/摘要或已确认缓存引用;引用缺失可经R03受控获取,**不依赖延后的R04热更新**。A二次校验并报告实际使用版本/摘要,错版本不进入最后许可。
5. Agent从会话局部只读快照生成SDK请求和本地控制器参数;可复用连接/Transport,不修改所有通话共用的model/voice/temperature等全局对象。首发有明确的百炼/火山ASR薄适配,选择一次固定到执行,不建插件、自动AI fallback或同通话动态换供应商。
6. 在已支持并获授权的模型/参数范围内,调参在SaaS发布新AI版本,由新任务显式引用后生效,无需改Go代码、重建镜像或重启D/A。排队旧任务/在途通话固定原版本,不能读取“latest”热改;授权撤销/stop仍按控制协议收敛,不以快照固定为由忽略撤销。
**静态发布仅指SIP/节点部署配置,不意味着AI业务参数写死。** 配置读取不是业务结果HTTP回调;实际音频仍A↔供应商,SaaS/D不代理音频流。
**静态发布仅指SIP/节点部署配置,不意味着AI业务参数写死。** 配置/授权同样必须MQ-only;实际音频仍A↔供应商,SaaS/D不代理音频流。
### 6.2 参数覆盖与缺口(需求索引,不是新Schema)
@@ -182,7 +188,7 @@ Routing key为`agent-call.{event_type}`。SaaS按`(tenant_id,event_id)`去重,
## 7. 内部gRPC公共规则(草案)
以下方法名/字段组用于P0审议,**尚无.proto或代码生成物**,不作为新增SaaS接口。
以下为早期内部方法职责草案,不是当前Proto字段权威;W02已交付的Proto/handler事实见[Dispatcher↔Agent契约](contracts/dispatcher-agent.md)。本轮MQ异步协调仍需核验,不因已有Unary就宣称端到端完成,也不据本文新增SaaS接口。
- 采用官方grpc-go与protobuf,全部Unary;两个角色都可作为受控gRPC客户端/服务端,共用HTTP/2连接池。
- 方向认证:Agent只接受受信Dispatcher角色的管理调用;Dispatcher只接受Agent群组证书和有效节点会话。共享证书只证明群组,不证明agent_id。
@@ -229,8 +235,8 @@ Routing key为`agent-call.{event_type}`。SaaS按`(tenant_id,event_id)`去重,
| R09 ApplyTaskControl | D→A | 原ControlRequest语义、task/租户目标、requested revision、持久控制命令及授权策略 | accepted/applying;真正屏障/挂断确认后回报applied;pause/drain保留已拨出/振铃及已接通的原生命周期,stop hangup另验权限 |
| R10 QueryExecution | D→A | 原执行/通道关联或受限分页对账请求 | 返回Asterisk观测、执行文件/未交付资产状态及证据时间;通道不在当前列表不证明从未拨过 |
| R11 ReportExecutionEvent | A→D | 稳定fact标识/内容摘要、执行/通道归属、观测时间、来源序列、事实类别及源业务数据 | D事务去重并生成/关联权威MQ事件,成功回持久接收结果;调用方不指定aggregate_version跳过D裁决 |
| R12 RequestUpload | A→D | 绑定执行的资产类别/稳定ID、size/checksum及源录音元信息;同一资产的显式重试申请(新15分钟 token) | recording调用SaaS upload-request;返回受限目标/headers/期限/会话。text_archive分支在GAP-02冻结前拒绝,不伪装录音 |
| R13 CompleteUpload | A→D | 原绑定资产/会话、实际文件元信息与完成事实 | D幂等调用SaaS complete、验证后提交资产状态/outboxtext_archive同样受GAP-02门禁,不以PUT回报直接生成ready |
| R12 RequestUpload | A→D | 绑定执行的资产类别/稳定ID、size/checksum及源录音元信息;同一资产的显式重试申请(新15分钟 token) | D依据自身OSS配置文件,经SDK提供临时TOKEN及受限目标/headers/期限,A不持长期凭据;既有SaaS业务会话仍MQ,业务响应pending/有界等待/重取待冻结,不等SaaS签TOKEN。text_archive分支在GAP-02冻结前拒绝,不伪装录音 |
| R13 CompleteUpload | A→D | 原绑定资产/会话、实际文件元信息与完成事实 | D经MQ提交complete,收到并持久校验SaaS verified后提交资产状态/outboxUnary最终结果衔接待冻结text_archive同样受GAP-02门禁,不以PUT回报直接生成ready |
P1的R05/R09及静态维护必须校验目标/版本并收敛R08许可,不长期锁SQLite等网络。未来R06同样纳入屏障;“全部Unary”或“静态配置”都不等于无需业务屏障。
@@ -282,9 +288,9 @@ P1的R05/R09及静态维护必须校验目标/版本并收敛R08许可,不长
1. 接通开始流式记录实际双向音频;实时文字同时走R11,不等资产封口。
2. 完成/取消时正确封口;故障时保留完整段并明确不完整状态。
3. A调用R12,D复用原录音身份向SaaS申请授权;CALL_NOT_REGISTERED保留原文件稍后恢复
4. A按指定HTTPS目标/headers直传OSS,不持长期AK;签名失效续原会话,对象ID/内容绑定不变。
5. A调用R13D完成SaaS独立对象校验,verified事务落资产状态recording.ready outbox。
3. A调用R12向D领取临时上传TOKEN;D按自身OSS配置文件提供受限TOKEN/目标。原业务会话/资产登记仍经SaaS MQCALL_NOT_REGISTERED语义及精确关联/错误待冻结;配置缺失/无效失败,不向SaaS取配置/TOKEN,保留原文件。
4. A按指定目标/headers直传OSS,不持长期凭据;TOKEN失效仅显式向D重新申请,对象ID/内容绑定不变,不自动续期/重传
5. A调用R13D经MQ提交complete并等待SaaS独立对象验证的MQ响应;合法verified持久后才事务落资产状态recording.ready outbox,不无限阻塞Unary
6. D可靠发布MQ;A只有取得“D持久接收”还不够立即删文件,仍须满足既有verified、ready交接、无未决恢复、至少24h测试保留条件。
### 10.3 文本归档
@@ -318,18 +324,20 @@ P1数据流:管理平台审批不可变制品 → 核验来源/版本/哈希
[G0开发准备与契约冻结方案](G0开发准备与契约冻结提案_v0.1.md) D01–D10方向及模式/许可/恢复机制已获用户确认;下表仍跟踪尚未交付的源字段/合同和验证,不再表示已确认方向待用户审批。该文档不是第二套Schema,权威源发布并验证后才关闭相应GAP。
D07确认:Agent通过R12从Dispatcher获取受限OSS配置,随后**Agent→OSS直接上传文件内容**R13只向Dispatcher提交原资产/会话及上传元信息。Dispatcher不代理/转发录音文件,仍负责SaaS会话申请/completeverified后经MQ回传OSS ID;每次下发token有效15分钟过期后仅接受显式重新申请,不自动续期Agent不直连SaaS不等于不能直连OSS
D07补充确认:**OSS配置存于D配置文件,AgentR12向D领取临时上传TOKEN后直传OSS**SaaS不下发OSS配置/TOKEN,D保留SDK签发能力,不转发文件。既有业务会话/资产登记及R13后的complete/verified仍经SaaS MQSaaS独立校验后才ready/OSS ID。原每次TOKEN有效15分钟过期显式重申请的约束保留,重申请对象是D,不向SaaS索取TOKEN;不自动续期Agent不持长期凭据
| ID | 缺口 | 文档处理/退出条件 |
| --- | --- | --- |
| GAP-01 | 信封已对齐,但8种事件payload专属Schema及部分条件规则未完整机读化 | 在上游唯一生成源补齐并验正反例;未覆盖部分阻塞冻结/业务上线,不能以object校验冒充完整验收 |
| GAP-02 | SaaS录音配置来源/作用域、文本归档授权/complete/引用缺口 | P1确认录音配置由SaaS提供并沿用2接口取得受限上传信息,不增加第二配置源;文本归档合同延后,不阻塞录音/实时文字 |
| GAP-02 | D的OSS配置文件/TOKEN约束及SaaS业务会话/verified衔接;文本归档仍缺合同 | 配置/TOKEN由D提供而非SaaS;核验配置格式、SDK及UploadGrant映射、显式重申请、对象定位/校验所需信息;SaaS最终校验仍MQ,原HTTP废弃;文本归档延后 |
| GAP-03 | 静态制品交接与后续在线管理发布适配 | P1先批准静态版本/哈希/目标/来源/加载事实及唯一写入合同,旧直写停用;完整在线发布/回滚和R04/R06延后 |
| GAP-04 | 首发Unary及身份/许可/状态结构尚无批准Proto | P1冻结R01R03/R05/R07R13实际职责、字段/错误/幂等/大小/超时;可获批合并,R04/R06不先造空框架 |
| GAP-05 | 共用证书的单节点授权与全组泄露风险 | 保留用户共用证书决定,但必须有受控Endpoint、独立D身份、自动节点会话、重放隔离及全组轮换/撤销演练;不能宣称节点级证书隔离 |
| GAP-06 | 首发资源保护、模式能力、单 Cell 授权/限额与维护窗口 | P1登记受限profile及基础恢复条件;缺必需能力拒绝,低负载不越额;SIP 外呼增加 Asia/Shanghai `09:00``20:00` 时间门禁。复杂评分/滚动升级后续 |
| GAP-07 | 单活D故障/人工恢复目标与永久资产损失 | P1核验唯一所有权、SQLite备份/恢复/对账、文件损失和RPO/RTO;跨机自动HA后续,不新增PG/NFS共享 |
| GAP-08 | 当前AI Schema强制llm/prompt/tts/asr/conversation且无明确ASR-only表达 | P0由上游批准两模式选择/缺省、条件必填、资源/超时及文字播放/失败语义并生成校验;不增临时MQ字段、不伪造LLM/TTS配置,两种真实模式都通过才可P1签收 |
| GAP-09 | SaaS任务AI配置的读取归属/授权、provider_ref解析、调试参数和有效快照尚未完全机读化 | 确认已有AI版本GET由SaaS承载及租户/版本绑定;在唯一源补§6.2实际参数、单位/默认/范围/能力、缓存撤销及摘要/Unary交接规则;锁定百炼/火山ASR、OpenAI兼容LLM、火山TTS参数映射PoC。不加临时路径或任意透传,SDK缺字段先补库/替代 |
| GAP-09 | SaaS任务AI配置的读取归属/授权、provider_ref解析、调试参数和有效快照尚未完全机读化 | AI配置/授权由SaaS经MQ响应原D,保留租户/不可变版本绑定,旧AI GET不再作为接入;在唯一源补§6.2实际参数、单位/默认/范围/能力、缓存撤销及摘要/Unary交接规则;锁定百炼/火山ASR、OpenAI兼容LLM、火山TTS参数映射PoC。不加临时路径或任意透传,SDK缺字段先补库/替代 |
**GAP-10(本轮新增,已确认方向、未冻结机读合同)**:全MQ请求响应、D全局唯一身份及生命周期、独立Topic/队列/绑定、租户/目标/来源/原请求关联、完整路由预算/通配符边界、错误/期限/重复/迟到/重启恢复,以及R12/R13有界异步衔接。由W01/W02发布新版Schema/拓扑/正反例并核验后解除;不得修改旧不可变包或把图中的中文名称当新枚举。GAP-10是当前P1门禁,不新增88项编号,细则纳入既有C/S/E/L子场景。
具体验收见 [验证与切换验收](验证与切换验收_v0.3.md) §1.1–§1.2。GAP-01/05/07/08/09及GAP-02/03/04/06的P1部分均为首发门禁;延后部分只在相应功能启用前冻结,不能记为通过。“文档齐全”不等于契约已获批。
+29 -23
View File
@@ -6,6 +6,8 @@
本文件是 [Go 重写方案](Go重写方案_v0.3.md) 的执行清单,**不是验收通过报告**。项目内已具备 Go 可执行程序、W01/W02 契约与本地测试入口;下文仍将本地通过、协议 Mock、真实集成和生产签收严格分开。
**本轮MQ-only修订重新打开受影响验收**:SaaS↔D所有请求/响应/事件只能走RabbitMQ,每个D全局唯一ID、独立接收Topic/队列。旧HTTP/仅租户路由/本地OSS验证证据不能覆盖新基线,见[计划§8.2](plan-0918.md)和[SaaS↔D契约](contracts/saas-dispatcher.md)。新增子场景纳入既有88项,不新增虚假通过数;本轮仅改文档,未执行这些运行测试。
每个阶段分别报告:通过、失败、未执行、被外部条件阻塞,以及“本阶段不适用/延后”。延后不得计入通过率或当成实现。旧Python证据不计Go通过数;管理平台、SIP Mock、供应商和Go Agent分别签收。
### 1.1 P1本次上线的10个汇总门禁
@@ -19,7 +21,7 @@
| P1-03 至少3家SIP | 至少3个不同供应商独立trunk;登记单 Cell/出口授权、主叫/前缀/codec/额度,使用配置/路由/协议 fixture 覆盖;未知/未授权组合拒绝;SIP 外呼时间门禁为 Asia/Shanghai `09:00`(含)至 `20:00`(不含),边界外 fail-closed;真实供应商外呼延期第二阶段 | E01–E08/E20;供应商配置矩阵、SIP/ARI/媒体 fixture、`internal/callwindow` |
| P1-04 ASR-only | 百炼/火山两ASR适配分别验证;D从契约 fixture 取批准版本,命令→ASR文字/录音→结果 outbox 闭环;LLM/TTS不可用不影响且调用/额度为0;ASR参数实际生效、无虚假播放 | GAP-08/09、E09/E14/E17/E22、L06/§5.1;单 Cell 隔离组合 |
| P1-05 完整AI | OpenAI兼容LLM+火山TTS在本地/协议隔离环境完成双向对话/取消/打断/超时/背压验证;契约参数直达SDK/控制器,调参不改代码/重启,不硬编码model/voice/speed;无旧实现/静默降级/旧音频重播;真实供应商费用/联调延期 | E09E11/E14/E17/E22、L06/§5.1;协议/参数/音频 fixture |
| P1-06 结果与资产 | 既有7路径/8事件的合法场景和反例通过;实时文字/opt-out及时,call/command整体补传保留原ID;录音verified后ready、授权读取字节一致,失败恢复不重拨 | S01S04/S16/S23、E08/E12/E17E19、A07/A15/A16 |
| P1-06 结果与资产 | 所有SaaS↔D业务改经MQ,8类业务事件及新版请求响应正反例通过,无HTTP兼容/回退;实时文字/opt-out及时,补传保留原事件ID而不重发执行;SaaS MQ verified后readyAgent直传字节一致恢复不重拨 | S01S04/S16/S23、E08/E12/E17E19、A07/A15/A16 |
| P1-07 租户骨架/全局额度 | 只启用1个租户,未启用租户拒绝;独立队列/原值key/复合幂等/窗口受控;单 Cell 竞争租户/供应商/按模式AI额度不超配;双租户和跨 Cell 汇总不在本轮 | C05C07、S08/S09/S18/S20S22SQLite/实际许可计数 |
| P1-08 故障与控制 | 重投10次同一执行只产生一次实际发起;覆盖§4.1崩溃窗口、MQ/Unary/ARI断连及D/A重启,未知不重拨/不释放;pause/stop/CAS/最后许可及 `09:00``20:00` 时间门禁正确,旧库恢复正确 | S01S27的P1部分、E02/E03/E13/E21、A07/A08/A18/A20 |
| P1-09 静态配置/稳定性 | 管理批准制品经维护窗口加载;错误版本/哈希、未排空/未确认、部分失败均不恢复相关准入;两种模式按§9.1受限隔离负载持续稳定测试,告警/录音/资源无未解释泄漏 | E04/E05/E13、A09A14的P1部分、S27;单 Cell profile与原始计数器 |
@@ -49,6 +51,8 @@
P2退出须至少两个模拟租户验证隔离/总额不超配,并用三个等权模拟租户完成§9的首次许可、100许可份额与故障恢复公平测试;通过后才开放第二真实租户。最后许可/fencing和控制屏障已在P1,不随多D协调延期。
D全局唯一ID/独立Topic是当前合同要求;P1增加D1/D2两组标识/Topic的本地消息fixture,证明指定D互不抢收、错目标拒绝,不要求运行第二个业务D或验证双D共享额度/HA。租户独立队列及单租户配额不因此取消。
## 2. 独立验证环境
从P1a开始,本项目自己的CI/本地入口负责;这些入口当前尚未实现:
@@ -70,15 +74,15 @@ P2退出须至少两个模拟租户验证隔离/总额不超配,并用三个
| --- | --- | --- |
| C01 | 独立目录/发行包运行 | 无父目录、Python 或管理平台源码依赖;Mock 下可独立验证 |
| C02 | 契约来源和生成一致性 | 来源/版本/哈希/生成器可追溯;当前MQ信封已对齐,不回退旧字段;8种payload专属约束缺口须先补齐,通用object通过不算事件验收 |
| C03 | Python/Go 黄金报文 | 字段空值数字时区、枚举、幂等/关联 ID 语义一致;没有新增 HTTP 拨号 |
| C03 | 业务黄金报文与MQ-only | 字段/空值/数字/时区/幂等语义保真;新增消息按新Schema校验;SaaS↔D执行/控制/查询/补传/配置授权/上传全经MQ,网络记录证明无双方HTTP,旧入口已移除 |
| C04 | JSON 与配置 SHA-256 | Unicode、字段顺序、空集合、数字边界均符合批准的规范化规则;重复键/歧义输入显式处理 |
| C05 | 原值租户绑定 | 正文、队列/路由、可信身份一一匹配;ASCII/Unicode/分隔符等保持原值;224 UTF-8 字节边界实测 |
| C05 | Dispatcher/原值租户绑定 | 全局唯一D身份与独立Topic/队列匹配,D1/D2无串收/抢收;同D仍隔离租户。原值key不变,完整新routing key/queue长度及Unicode/分隔符/通配符边界实测,不套旧224字节预算 |
| C06 | 不支持的路由或超大消息 | 明确拒绝/停止发布并保留源任务;无截断、无静默清洗、无无限重试 |
| C07 | 信任边界 | 无法通过 MQ 注入 SIP 地址、认证信息、越权主叫、其它租户或任意供应商 URL |
| C08 | 模式隔离 | mock/mixed/real 可辨认;real 拒绝 Mock/测试凭据,Mock 默认不能拨公网电话 |
| C09 | 运行与指标隐私 | 日志、错误、trace、pprof 不泄漏密钥/对话/完整号码;指标无无界高基数标签 |
| C10 | 路径/类型/大小反例 | SaaS既有7路径和响应按Schema验,bool不充int、字符串不得隐式转数值,HTTP 64KiB/MQ256KiB及边界、缺字段/额外字段均有反例;不擅自新增业务路径 |
| C11 | HTTP幂等/权限/目标 | control/replay/upload授权/complete的必需Idempotency-Key、作用域、租户/目标绑定均测;缺头拒绝,同键同内容复用,同键跨目标/异内容冲突,outbound.replay/outbound.hangup不可越权 |
| C10 | MQ消息/类型/大小反例 | 新版全MQ请求/响应严格Schema验,bool不充int、字符串不转数值,MQ256KiB及分消息上限/缺字段/额外字段反例;新包未冻结即阻塞,不用旧HTTP Schema冒充 |
| C11 | MQ幂等/关联/目标 | 控制/查询/补传/配置授权/上传申请/complete/verified均绑定原D、租户、请求和业务对象;缺关联/错目标/同键异内容拒绝,重复/迟到/乱序/超时及重启恢复保留原决定,既有作用域不放宽 |
## 4. MQ、数据库与控制面验收
@@ -90,20 +94,20 @@ P2退出须至少两个模拟租户验证隔离/总额不超配,并用三个
| S04 | confirm 丢失/不可路由 | 重发原事件、正确处理 mandatory/return;不把 confirm 当 SaaS 应用 ACK |
| S05 | broker 重启、断连和 quorum 故障 | 真正经过 AMQP 和持久队列验证;单节点开发通过不代替三节点 HA 结论 |
| S06 | 大租户积压/小租户到达 | 接入、持久窗口、调度及重启恢复都公平;未 ACK/内存/待执行量有界 |
| S07 | Dispatcher单活/恢复 | P1第二进程不得取得并行写入/发许可权;受控恢复前撤销旧所有者并核验代次/占用,两份SQLite不能各自发额度;不是自动选主/HA演练 |
| S07 | Dispatcher身份/单活/恢复 | 验证全局唯一ID、持久恢复与重复身份拒绝,ID不混同epoch;D1离线不将其未决消息改投D2。P1第二进程不得取得并行写入/发许可权;受控恢复前撤销旧所有者并核验代次/占用,两份SQLite不能各自发额度;不是自动选主/HA演练 |
| S08 | 全局并发/CPS | P1单D汇总当前租户、供应商及单 Cell 的预留/拨号/振铃/已接通/未知占用,无超额;ASR-only不占LLM/TTS。第二阶段再扩展多租户/多 Cell 汇总;FALLBACK后续启用仍计CPS |
| S09 | 部分资源不足 | 租户/供应商、Cell/端口、出口或本模式必需AI任一不足时不能拨号;部分预留释放安全,ASR-only不因未用LLM/TTS缺失被拒,完整模式不静默降级 |
| S10 | 心跳丢失/授权过期 | 停止新任务,已有未知通话占用不靠 TTL 自动释放;恢复要对账 |
| S11 | 控制幂等/CAS | 使用 `expected_task_revision`,区分 requested/applied revision;同租户 command 重试不再次加版本,错字段/冲突版本拒绝,同 ID 异载荷冲突 |
| S12 | pause/stop/drain/hangup | HTTP 202不是appliedpause和stop drain均保留已拨出/振铃及已接通的原生命周期,阻止新发起;stop显式drain或hanguphangup缺权限拒绝,部分挂断失败/回执丢失继续 applying/reconciling,全部必要确认后才 applied |
| S12 | pause/stop/drain/hangup | MQ accepted响应不是appliedpublisher confirm/ACK也不是pause和stop drain均保留已拨出/振铃及已接通的原生命周期,阻止新发起;stop显式drain或hanguphangup缺权限拒绝,部分挂断失败/回执丢失继续 applying/reconciling,全部必要确认后才 applied |
| S13 | applied 与最后 originate 竞态 | 在授权/落盘/投递/ARI 窗口注入控制和分区;先确认持久屏障和旧许可收敛,再 applied;不得出现 applied 后旧 revision 的新拨号 |
| S14 | 撤销后的恢复 | 整任务 pause 屏障覆盖积压/重试/已消费未拨/拨号/振铃;SaaS 禁发并过滤撤销对象,旧执行对账后 paused 可按当前生效 revision 和新 command/execution 授权恢复;stopped 拒绝 resume,旧命令不能复活 |
| S15 | 等待/开始时限 | waiting、dial timeout、max duration 按契约区分;资源等待不能伪装为已拨或成功 |
| S16 | call/command 整体补传 | 仅 call_id/source_command_id,受理即固定截止点;保留原事件/顺序/版本,实时优先、分批限速;含尚无 callcommand404/410 区分,拒绝 task/execution/局部筛选,补传自身结果不递归,completed 不是 SaaS 应用收讫 |
| S16 | call/command 整体补传 | 仅 call_id/source_command_id,受理即固定截止点;保留原事件/顺序/版本,实时优先、分批限速;请求/响应均MQ含尚无callcommand不存在/保留过期用新版MQ错误区分,禁止重发call.execute当补传,拒绝task/execution/局部筛选,补传自身结果不递归,completed 不是 SaaS 应用收讫 |
| S17 | 多轮故障与数据库恢复 | 恢复 inbox、控制版本、占用、outbox 和公平进度;按 §9 的完好持久卷边界验证 RTO,而非只证明能启动 |
| S18 | 租户复合幂等键 | A/B 租户用完全相同 command/execution/event ID,事实互不覆盖;数据库按 tenant_id+业务 ID 唯一,异载荷冲突不重拨 |
| S19 | 历史事实优先 | 认证/租户归属通过后,对已完成/已拒绝命令的重投返回原事实;任务暂停、配置失效不能改写原决定;换消息 ID 不绕过 execution 去重 |
| S20 | 拓扑/ACL/启动漂移 | exchange 类型绑定持久化/上限/拒绝策略不符就不 ready;发布者/消费者最小权限,不能跨租户路由或任意声明资源 |
| S20 | 拓扑/ACL/启动漂移 | 每个D有独立Topic/接收队列/绑定;错D响应、共队列抢收或广播过滤均不通过。exchange类型/绑定/持久化/上限/拒绝策略与新版不符就不ready,保留既有租户隔离;验证不可路由与来源归属 |
| S21 | 队列满/blocked | 满队列拒绝新发布、不丢队头,SaaS 留原 ID/任务;内存/磁盘报警与 blocked 限制接入,恢复不爆发无界重试 |
| S22 | DLQ 与安全停用 | 死信恢复回原租户并经过全部配额;禁用/删除租户前对账未决命令、outbox、补传/资产;不能删共享结果队列或悬空任务 |
| S23 | 分域版本/乱序资产 | command/call/segment/recording 分域合并;高版本 call.finished 先到也不吞掉低版本独立资产/attemptrecording.ready 不被结束快照覆盖 |
@@ -121,7 +125,8 @@ P2退出须至少两个模拟租户验证隔离/总额不超配,并用三个
3. 中央账本认领及Agent执行文件持久化后、ARI请求已送达但响应丢失、接通后进程退出。
4. pause accepted 后、Cell 屏障确认前、applied 发布后且旧授权仍在网络途中。
5. 通话终态已落盘但全局结果未确认、结果已发布但本地确认丢失。
6. OSS PUT 中断、上传成功但 complete 失败、verified 后但 `recording.ready` 未发布
6. OSS PUT中断complete MQ已持久但响应丢失,SaaS verified已发但D未持久/ACKD已持久verified但ready未发布;沿原upload/资产恢复,D本地验证不替代SaaS
7. AI配置/授权、控制/查询/补传MQ请求已送达但回复丢失,D重启、响应乱序/迟到及目标错配;保留原关联,不HTTP补查、不重新执行。
每个窗口都检查:实际发起次数、持久状态、有效所有权/控制版本、资源占用、消息原 ID、录音可恢复性。无法证明外部动作未发生时应进入待对账,而不是重试 originate。
@@ -140,7 +145,7 @@ P2退出须至少两个模拟租户验证隔离/总额不超配,并用三个
| E09 | ASR双模式生命周期 | 百炼/火山适配分别验首包/最终结果/结束/取消,模型/语言/中间稿/采样与热词/VAD等获批参数实际生效;ASR-only无LLM/TTS及虚假播放;复用协议但无旧项目运行依赖 |
| E10 | 新LLM/TTS首发必需 | 完整模式使用新批准官方/开源SDK,真实权限/音频/模型/取消/打断/限额/费用验收;未启用或仅Mock即P1 blocked,不调用旧实现或用“兼容OpenAI”替代能力证据 |
| E11 | 打断与慢消费者 | 旧轮次不再播放;生成/排队/发送/播放可区分,缓冲按时长/字节有界 |
| E12 | 录音交接与重放 | OSS 校验 verified 后才出 `recording.ready`哈希/长度/ID 正确,恢复不重拨、不覆盖未交付文件 |
| E12 | 录音交接与重放 | D只在持久校验SaaS MQ verified/oss_id后出ready,本地HEAD/PUT成功/confirm不替代;R12/R13有界pending/原操作恢复和最终结果可验证,哈希/长度/ID正确,不重拨/覆盖未交付文件 |
| E13 | 资源泄漏/优雅退出 | 多轮超时、取消、断网后,goroutine、FD、端口、buffer、临时文件和占用回到可解释基线 |
| E14 | 模式相关依赖缺失 | 必需依赖unknown/缺失拒新任务;ASR-only可在LLM/TTS不可用时正常运行,完整模式不能静默退成仅ASR;未知不是健康 |
| E15 | Cell 集合/重启/世代 | 新增/移除 Cell 分别做 bootstrap/撤销和集合屏障;旧 boot/旧授权世代/乱序序列不能恢复 ready;响应丢失后幂等重试不覆盖漂移事实 |
@@ -158,7 +163,7 @@ P2退出须至少两个模拟租户验证隔离/总额不超配,并用三个
| 子场景 | 通过判定 |
| --- | --- |
| 配置唯一来源 | 两个Agent仅向D取执行快照,D按任务agent_version_id调用已有SaaS AI GET模型/voice/resource/协议端点来自受控配置/引用。断开本地业务env覆盖仍一致,不存在私造task-config或Agent直连SaaS |
| 配置唯一来源 | P1单Agent仅向D取快照;D经MQ按原租户/agent_version_id请求SaaS配置/授权,经本D专用Topic收响应并持久绑定。模型/voice/resource/端点来自受控引用;无AI GET、CLI/env默认覆盖或Agent直连SaaS |
| 逐参数可达 | 交互§6.2所有首发启用字段均有source→effective→SDK字段或控制器映射;每项至少基线/变更值及适用边界。音频实际采样/编码、TTS语速/voice、LLM采样/Token、ASR输出控制、超时/打断/分句/缓存均测,不以请求200代替生效 |
| 零值/缺省/拒绝 | temperature=0、interim=false、allow_interrupt=false及合法空列表不会被默认覆盖;未提供/null与零值区分,默认由批准源版本物化。未支持/越界/额外字段或冲突有原因,不能静默忽略/钳制/透传metadata |
| 无重启调参与隔离 | SaaS发布v2,新任务引用后两Agent均使用新参数;v1排队任务及进行中通话仍用v1,同v1异内容拒绝。两通合成任务使用不同model/voice/阈值并发,不共享可变SDK参数或串凭据 |
@@ -245,11 +250,11 @@ P1运行不依赖旧Python Cell;迁移验证如需临时桥接而旧端不满
开发前的交付/解锁证据见 [G0开发准备与契约冻结方案](G0开发准备与契约冻结提案_v0.1.md) §2/§5/§8。D01–D10方案已获用户确认;项目内 W01 版本和 W02 Proto 已交付,但外部权威签收、D10正式PoC和真实供应商仍未完成;十项交付门禁不增加本方案88项运行验收数量。§5.5内部profile已确认为隔离PoC初始值,未实测、不是生产SLA,也不覆盖下节上游/供应商基线。P1必须实施当前租户及单 Cell 原子配额,P2/第二阶段再扩展多租户公平、跨 Cell 和并行竞争验收。
E12/E19新增明确子场景(不新增用例编号):证明Agent从Dispatcher取得受限配置后直接连接OSS上传,Dispatcher/gRPC链路没有录音文件内容;Dispatcher失联时有效授权可继续直传,过期保留文件,放弃当前token并等待显式重新授权,不回退长期AK。直传成功但Dispatcher/complete不可达时保留原资产和待完成状态,恢复后幂等完成SaaS verified再由Dispatcher发recording.ready及OSS IDPUT成功不得提前发ready。
E12/E19新增明确子场景(不新增用例编号):证明OSS配置来自D配置文件,A经Unary向D领取临时TOKEN后直传,SaaS不下发OSS配置/TOKEN,D/gRPC无文件内容。配置缺失/无效明确失败,不向SaaS取配置、不使用A本地长期凭据;过期由A显式向D重申请,原资产/会话不变。D失联时已有有效TOKEN可继续直传,过期保留文件,不自动续期或重试。直传成功但Dispatcher/complete不可达时保留原资产和待完成状态,恢复后幂等完成SaaS verified再由Dispatcher发recording.ready及OSS IDPUT成功不得提前发ready。
## 9. 继承的量化测试 profile
以下是上游《最终开发部署监控与验收计划_v1.0.md》§5 的**只读测试摘要**,不是新的参数权威,也不是P1全量必跑清单。P0导入带来源/哈希的profile;按§9.1生成有来源和变更理由的阶段profile,不篡改摘要。租约/许可/窗口/保留/恢复等安全值继承,实际费用/能力/生产RPO/RTO另批。
以下是上游《最终开发部署监控与验收计划_v1.0.md》§5 的**只读测试摘要**,不是新的参数权威,也不是P1全量必跑清单。**其中HTTP JSON大小、HTTP连接重试/查询延迟仅为旧来源事实,不能恢复SaaS↔D HTTP;新版MQ响应deadline/延迟profile须随GAP-10冻结,不能直接将HTTP的P95当MQ已通过。**P0导入带来源/哈希的profile;按§9.1生成有来源和变更理由的阶段profile,不篡改摘要。租约/许可/窗口/保留/恢复等安全值继承,实际费用/能力/生产RPO/RTO另批。
| profile 项 | 上游默认测试值/判定 |
| --- | --- |
@@ -297,29 +302,29 @@ E12/E19新增明确子场景(不新增用例编号):证明Agent从Dispatch
| L04 | RTP/音频库 | 使用库完成 packet parse/marshal、A-law/μ-law/PCM/重采样所需能力;黄金音频、长度/端序/SSRC、fuzz、抖动/回绕/长稳通过;不把 Pion G.711 depayloader 当作 PCM 解码器 |
| L05 | AMQP/SQLite/恢复封装 | SDK/封装暴露manual ACK、return/confirm、连接世代和取消;恢复无旧channel ACK/丢confirm/窗口翻倍;真实SQLite验证单writer/短事务/CAS/WAL/Sync/checkpoint/备份/迁移,不能套PG行锁语义 |
| L06 | 选定AI SDK/参数/重试 | 核验百炼/火山ASR、官方openai-go对目标兼容LLM、火山TTS的精确模块/协议/鉴权/音频/取消/费用;§5.1参数必须映射有效、无硬编码默认。火山SDK必要字段缺失则阻塞,OpenAI关闭默认重试;不自写协议或验证所有未选候选 |
| L07 | OSS/HTTP SDK 边界 | Agent 消费 SaaS 签名而不擅自接管签名职责;标准库 PUT/SDK 保留约定 headers对象/会话/哈希取消语义,E19全过;日志不含签名密钥或URL凭据 |
| L07 | OSS/TOKEN SDK边界 | D从配置文件读取OSS设置并复用官方SDK提供临时TOKEN;核验TOKEN形态/UploadGrant映射及有效期/对象约束,不自写签名或假定已有能力齐全。A仅使用临时授权,标准HTTP/SDK保留headers/对象/会话/哈希/取消语义;日志不含密钥/TOKENSaaS不提供OSS配置/TOKEN |
| L08 | 复用审查/替代门禁 | 每个协议边界列出所用库/原生能力和理由;自有代码限业务状态机及薄适配;归档/不兼容候选不得静默替代为手写。先替换库、修上游或报告阻塞;例外须用户另批 |
## 11. 上游需求到 Go 验收追踪
本表保留跨阶段追踪,不改变上游契约。P0按§1.2逐子场景标P1/P2/后续,记录源版本/哈希及入口/参数/证据;未实现、blocked或延后不得计PASS,不能把本表全部条目当P1前置。
本表保留跨阶段业务语义追踪;本轮全MQ传输修订须发布新版本,不手改旧上游包。P0按§1.2逐子场景标P1/P2/后续,记录源版本/哈希及入口/参数/证据;未实现、blocked或延后不得计PASS,不能把本表全部条目当P1前置。
| 上游要求 | 方案章节 | Go 验收 | profile/判定及证据 |
| --- | --- | --- | --- |
| 主契约 §4.14.1.1FIX-05/12V04 | §6.4 | S11S14、E18 | 控制2s基线;revision/权限/目标绑定、ARI发起/挂断、多Cell屏障;paused可恢复/stopped不可恢复 |
| 主契约 §4.4FIX-09/10/12V10 | §6.5 | S16、S23 | call/command固定集合、原ID与404/410outbound.replay、跨目标冲突;分批限速、恢复、不递归 |
| 主契约 §4.4FIX-09/10/12V10 | §6.5 | S16、S23 | MQ call/command固定集合、原ID与不存在/保留过期语义outbound.replay、跨目标冲突;分批限速、恢复、不递归 |
| 主契约 §6.2、§7.2FIX-02/03V01/02 | §6.1/6.3 | C05C07、S01S04、S18/19 | 跨租户同ID、同ID重投10次、换command同execution、异载荷与提交窗口;真实broker/DB计数 |
| 主契约 §5.1.2FIX-04/06/07V03/05 | §6.1/6.2 | S05S10、S20S22 | DEV窗口/100次公平/队列上限、HA quorum、confirm/blocked及源任务保留 |
| 主契约 §7.2/7.3、§9FIX-15/16V08/10 | §6.6/7.2 | S23、E10/11/17/18/22 | 独立SaaS按域收敛文字/播放/opt-out,100轮与时延、同段final异内容冲突 |
| 主契约 §4.3/§8FIX-11/1720V09/10/11 | §7.3 | E08/12/19/21、L07 | 180s通话→交接≤120s、5min中断→10min补齐、70-80-60%/24h;幂等/作用域/绑定/实际字节覆盖反例 |
| 主契约 §4.3/§8FIX-11/1720V09/10/11 | §7.3 | E08/12/19/21、L07 | 180s通话→交接≤120s、5min中断→10min补齐、70-80-60%/24hMQ请求幂等/作用域/原D与资产绑定/实际字节覆盖反例 |
| SIP-27/28/29/30/32 | §8 | E04/05/15、L03 | reload前屏障/新旧集合/bootstrap/幂等/部分成功;管理模拟端、调度和Asterisk三方证据 |
| SIP-13/14/33 | §8/9 | E16 | attempt历史快照/跨午夜/零分母/缺事实补齐、接通后AI失败不冲减接通;与管理固定样例核对,不由Agent重造统计口径 |
| FIX-0608/21/22V03/07/11/12 | §5.3/6.2/9.1 | S07/10/17/2426、E13/21 | 完好卷60s及永久丢盘边界;旧库恢复到新目标核对,不覆盖新防重事实、不删墓碑;告警接收/保护/恢复有证据 |
| FIX-14/17V06/07;既有媒体事故 | §7.1 | E01E09/E20、L03/04 | 真实Asterisk+Mock对端、至少两通不同标识音、实际双向RTP/录音;30s清理及ExternalMedia/桥/末帧;不确定不FALLBACK |
| FIX-01/13/23V01/12SIP-38独立性原则;用户复用要求 | §3/4/9 | C01C11、S27、L01L08 | 无父目录检出、7路径/64KiB/256KiB/类型反例、模式/权限/SDK与许可证;对应管理平台测试不冒充Go验收 |
| FIX-01/13/23V01/12SIP-38独立性原则;用户复用要求 | §3/4/9 | C01C11、S27、L01L08 | 无父目录检出、全MQ消息/新版大小上限/类型反例、模式/权限/SDK与许可证;对应管理平台测试不冒充Go验收 |
| V12V14、M1M3、R1R3 | §911 | 全部适用用例及§6/§7 | 同环境对比、1000路60min、N+1/供应商另验、切换/回滚/备份;Mock不替代真实 |
P0冻结前逐行登记阶段、状态、责任人及风险。本轮运行时均为**未执行**;文件/链接/源指纹可检查,但GAP-01~09的未批准部分不能标通过,后续范围不阻塞无关阶段。
P0冻结前逐行登记阶段、状态、责任人及风险。本轮MQ-only新增运行验证为**未执行**,旧证据按旧版本保留;文件/链接/源指纹可检查,但GAP-01~10的未冻结/未验证部分不能标通过,后续范围不阻塞无关阶段。
## 12. 最新架构验收 A01A20
@@ -327,7 +332,7 @@ P0冻结前逐行登记阶段、状态、责任人及风险。本轮运行时均
| ID | 场景 | 可判定通过条件 |
| --- | --- | --- |
| A01 | 事件/接口全集与覆盖 | 现有42操作/115组件的源哈希、7路径、1种执行命令/8种event_type和原样例均可追踪;旧信封/非法call.transcript拒绝;payload专属Schema及条件规则补齐后才冻结 |
| A01 | 消息/事件全集与覆盖 | 42操作/115组件只读来源哈希保留;新版全MQ消息/响应/Topic与8类业务事件可追踪,身份/关联/错误/期限及正反例齐全;非法call.transcript拒绝,不把旧7条HTTP路径当当前入口 |
| A02 | Cobra一个制品两个业务 | 同module/镜像构建,显式业务子命令仅agent/dispatcher,无默认双业务启动;help/version不触网;agent不初始化MQ/业务DBdispatcher不开RTP/Agentspool;配置错误不泄漏凭据 |
| A03 | Endpoint引导启动 | 业务启动只填D Endpoint;部署提供证书/ARI/目录/批准静态制品。未激活不取敏感配置/接任务,D按单一受控Endpoint绑定后核验自身SIP/OSS/AI版本;缺Asterisk/凭据/静态版本not-ready |
| A04 | Endpoint与实例归属 | 任意自报URL、错误cell/agent、重复Endpoint、克隆spool、新旧boot并存、移除后重连分别测试;仅D预配置绑定生效,不产生SSRF或双执行归属 |
@@ -341,7 +346,7 @@ P0冻结前逐行登记阶段、状态、责任人及风险。本轮运行时均
| A12 | 静态SIP/运行配置 | 部署交付管理批准制品、D按身份核验目标/版本;原Schema/来源/哈希/凭据引用验证,缺密钥/错mode/任意路径/脚本拒绝;首次实际加载确认前not-ready,不要求R04/R06在线推送 |
| A13 | 动态改配/停用 | D固定目标集合、持久发布意图、先关闭准入并收敛必要占用,再ApplyTrunkConfig;每Agent实际加载确认;修改/新增/移除/disabled不逐呼reload、不擅自强挂 |
| A14 | 静态失败/人工恢复/单写 | 版本/哈希冲突、写完未加载、部分失败/确认丢失、恢复旧制品失败/旧代次回报均注入;保持真实installed和阻塞,旧管理直写不可与受控静态入口并行;在线自动回滚后续 |
| A15 | SaaS来源OSS配置 | D受控获取SaaS配置/授权并只向对应Agent/执行/对象发短期信息;显式重新授权/配置轮换不中途换资产归属不泄漏长期AK/签名URL;录音使用recording-uploads和upload_id/complete |
| A15 | D配置文件/临时TOKEN与SaaS最终校验 | D配置文件为OSS唯一配置源,A经Unary领TOKEN且不持长期凭据;缺失/无效失败、过期显式找D,不向SaaS索取配置/TOKEN,保留D签发能力。业务会话/complete/verified仍MQSaaS独立校验成功后才ready;不以D本地验证替代、不换资产归属不泄漏密钥/TOKEN |
| A16 | 实时文字+OSS归档 | transcript.updated/final/opt-out按原时限回SaaS,不等整通话上传;文本归档无批准授权接口时明确未启用,不冒充recording.ready、不生成新event_type;冻结后验证原segment版本/哈希及查看权限 |
| A17 | 维护更新与版本校验 | P1维护关闭准入/排空/更新/重新激活后才恢复;只用已验证组合,不兼容阻塞,Proto字段号不复用,构建版本不冒充AI版本;混合N/N-1与在线滚动后续 |
| A18 | 退出/断联/收尾 | D退出不强挂Agent已授权通话;A保留文件并可在恢复后补报;SIGTERM先关准入/排空,超时未决状态明确;永久丢盘不承诺零丢失,保留/告警按§9 |
@@ -352,12 +357,13 @@ P0冻结前逐行登记阶段、状态、责任人及风险。本轮运行时均
| 要求 | 设计位置 | 验收 |
| --- | --- | --- |
| SaaS↔D全部MQ、D唯一ID/独立Topic | 计划§1.2/§8.2SaaS↔D契约§2/§5/§6;交互GAP-10 | C03/C05/C10/C11、S07/S20、E12/E22、A01/A15D1/D2仅本地路由fixture,无HTTP回退 |
| 同项目Cobra双命令、统一更新 | 主方案§3.1/§5.7 | A02/A17/A20、L01 |
| D SQLite、Agent文件、Unary | 主方案§5.15.4;交互§78/§1011 | A07/A08/A18/A20、S/E恢复用例 |
| Endpoint最小启动、共享mTLS | 主方案§5.45.5;交互R01R03/R05 | A03A06/A19 |
| 健康/负载/版本/供应商和配置版本 | 主方案§5.6;交互§9 | A09A11、E15/E16 |
| P1静态配置;在线发布延后 | 主方案§8;交互§12 | A12/A14、E04/E05的P1部分;A13后续 |
| OSS源SaaS、D统一回传、文本仍MQ | 主方案§7.3;交互§5/§10 | A15/A16、E17E19 |
| OSS配置在D文件、A向D领临时TOKEN、SaaS最终verified不变 | 主方案§7.3SaaS↔D契约§6.1;D↔A契约§6.2;交互§10 | A15/A16、E12/E17E19、L07;配置缺失/无效与显式向D重申请反例 |
| 全部事件/字段与遗漏 | 交互§1–6/§13;只读字段索引 | A01、C02/C10/C11、GAP-0109按阶段 |
| 单节点/单Agent/单Asterisk、至少3SIP fixture | 主方案§1.1/§5/§8 | P1-02/03、E01、授权组合矩阵 |
| 单租户首发;双租户/公平后续 | 主方案§6.2/§10 | P1-07、S18/S08;第二阶段 S06/§9公平profile |