Files
go-sip/docs/evidence/saas-dispatcher-implementation.md
T

13 KiB
Raw Blame History

SaaS↔Dispatcher 项目内实施证据

改动前基线

  • 基线提交:f5c2d6a92036a579e0b070beb1d9381b0976f81e;执行分支:feat/saas-dispatcher-contract;开始前已拉取并核对 origin/main,无待同步提交;Go 1.27.1。
  • 原工作树已有用户修改:docs/thirds/v0.4.md、docs/thirds/第三方对接事件与请求消费顺序_v0.1.md;未跟踪:docs/plan-saas-dispatcher-v05-v0.1.md、docs/thirds/v0.5-proposal.md、docs/.plan-saas-dispatcher-v05-v0.1.md.swp。这些内容不得作为已提交基线,不能静默删除、暂存或覆盖;历史来源在改名时逐一核验。
  • 全仓路径初筛发现 182 个代次式候选文件(包括历史证据/第三方合法版本,不是 182 个可删除目标);源码、契约与脚本约 119 个候选。去后缀会发生实存碰撞:internal/mq/amqp_v3.go→internal/mq/amqp.go;历史证据文件也有碰撞,历史证据不能按文件名自动改写。
  • 尚未验收源码、契约、Proto 及数据兼容;此处只记录实施前状态,不构成 P01–P08 通过证明。

路径/符号映射与冲突处置(实施时逐项勾稽)

旧路径/符号 目标职责与处置
internal/mq/amqp.go 的旧声明/租户队列 Broker 与 internal/mq/amqp_v3.go 的预建队列 Broker 保留预建队列单一路径,合并必要发布/错误处理后归入 internal/mq/amqp.go 的 Broker;删除旧运行入口,不能直接覆盖。
internal/configread/discovery_v04.go internal/configread/discovery.go,重做新 cursor 语义,删除旧 snapshot/watermark 路径。
internal/dispatcher/task_queue_v3.go / task_control_v3.go / task_runtime_v3.go task_queue.go / task_control.go / task_runtime.go,类型按职责命名,不保留 V3/LocalV01 别名。
internal/dispatcher/call_result_v01.go / local_v01.go call_result.go / local.go;只留已确认的对外事件。
proto/agent/v1/agent.proto / gen/agent/v1/ proto/agent/agent.proto / gen/agent/,同步生成配置、Go 导入与服务名,重新生成而非手改生成物。
contracts/local/v0.x/、docs/contracts/*-v0.x* 与按代次加载/校验器 各领域只有一份无代次当前 Schema、示例及来源/hash;历史来源只能归档,不参与运行校验。
internal/store/migrations/*_local_v0x_*.sql 保留有意义的迁移序号并更换实现代次名;先验证现存数据库的迁移跟踪/未交付状态,不能以改名重跑破坏数据。
docs/plan-saas-dispatcher-v05-v0.1.md / docs/thirds/v0.5-proposal.md 分别成为 docs/plan-saas-dispatcher.md / docs/thirds/saas-dispatcher.md;既有未跟踪内容须先核对,链接/哈希同步更新。

P01:项目内当前通信契约

  • 新增 contracts/local/config-read.schema.json、task-discovery.schema.json、mq.schema.json、mq-topology.json 与 30 份正反例,manifest 覆盖 34 个当前 JSON 文件;contracts.ReadCurrent 只接受这五个当前文件,无旧目录回退。
  • docs/thirds/saas-dispatcher.md 记录五类 HTTP、入站/出站事件、队列归属/共享结果队列及 K01–K16 的业务边界;数字 tenant_id、原值 credential、ASR-only、revision、空录音、真实 outcome/reason_code、48h 失败留待人工等均有字段/行为约定。机器校验拒绝字符串租户、旧 schema_version/credential_ref/call_id、未知 recording 状态、非法控制 policy。
  • 测试:bash scripts/check-current-contracts.sh、go test ./internal/contract ./contracts -count=1、git diff --check 均通过;当前正例未出现旧配置或通话身份字段。来源/内容 SHA-256 在 manifest 中可重算。这是当前合同文件的本地校验,不代表 P03–P08 运行行为、真实 SaaS 或旧合同清理通过。

P02:Proto/生成物分批改造(未整体签收)

  • proto/agent/v1/agent.proto→proto/agent/agent.proto,包 agent;gen/agent/v1/→gen/agent/,源码 79 处导入/标识机械更新;旧生成路径已删除。已有 RequestMeta.protocol_version=agent.v1 是有意义的线协议元数据,没有为了去名改动字段或重编号。
  • 原 Buf STANDARD 的 PACKAGE_VERSION_SUFFIX 与已批准的无代次内部包名冲突;buf.yaml 仅对这一条规则作例外,其余 STANDARD 规则保持。隔离安装 Buf v1.50.0 / protoc-gen-go v1.36.12 / protoc-gen-go-grpc v1.5.1 于 /tmp/sip-go-agent-tools/bin,未改应用依赖。
  • PATH=/tmp/sip-go-agent-tools/bin:$PATH sh scripts/check-proto.sh 通过(lint/build/generate/新包测试/7 文件清单 hash);git diff --check、go test ./internal/rpc ./internal/agent ./internal/dispatcher ./cmd/sip-go-agent -count=1 通过。其他旧实现/契约入口、文件名碰撞和迁移数据安全仍待 P02/P07,不能据此称全仓已无代次或整体完成。

P03:HTTP 读取分批改造(未整体签收)

  • contract.ValidateCurrent 与 configread 按当前 Schema 读取 SIP、provider、task、quota 和 cursor 任务发现;严格检查数字 tenant_id、本 D 归属及不可变配置。provider 凭据原值只保留在内存快照,不写日志;Agent 参数中的显式 0/false 保真;无旧 Schema/旧配置回退。
  • store.OpenCurrent 新建数字租户 SQLite 状态;旧表、旧版当前布局、残缺布局均在写入前拒绝并保留原记录;不执行旧数据迁移或自动清理。启动时完整发现同一快照一次提交,分页增量逐页持久提交后才推进内存 cursor;失败关闭准入,重启重新取完整快照。HTTP 的旧 running 不能解除 MQ 暂停/终止,同 revision 异内容及跨任务 SIP/租户额度冲突拒绝。
  • CurrentBootstrap 先关闭准入,核验 SIP 全量与 Agent/Asterisk 已加载 revision、读取任务和 provider/额度,再排空 MQ 控制积压,最后依据已验证 SIP revision 开准入;有更新的持久 SIP 通知时保持关闭但控制与结果处理仍可继续。CurrentDiscoveryFollower 逐页绑定任务快照;HTTP 错误、失效或授权不一致只失败,不回退旧读取。目前只在隔离运行组件中调用,尚未接入 cmd/sip-go-agent/main.go;provider 向真实 Agent 交付及真实加载尚待 P05/P07。
  • TDD 与回归:go test ./internal/configread ./internal/tenant ./internal/store ./internal/dispatcher -count=1、bash scripts/check-current-contracts.sh、已提交 a0118e3 的干净归档测试通过;旧布局行/表原样保留由 TestCurrentStoreRefusesPreviousCurrentLayoutBeforeModifyingDatabase 覆盖。

P04:隔离 MQ、控制与外呼接纳(仅项目内 Mock)

  • 固定 v1 精确路由与 SaaS 共享结果队列;各 D 控制/任务队列均由 SaaS 预建,D 仅被动检查和消费。隔离 RabbitMQ 实测无 configure 权限、D1/D2 不串收、shared queue 实际收讫、断绑后 mandatory 失败不算交付;畸形控制积压拒绝并阻止启动准入。
  • 执行消息先写持久 inbox 才 ACK;白名单号码格式错误单条拒绝,不暂停其它任务;任务/线路规则不满足时原执行身份留在 SQLite、该任务后续积压留在 SaaS 队列,窗口开放后重新核验才发出 Mock 指令。发指令前两次时窗/SIP/准入检查与持久额度占用;未知 Agent RPC 保持未知占用、不自动重拨。结果 outbox 只在 mandatory/return/confirm 成功后标记已入队,不宣称 SaaS 已处理。
  • pause/stop/resume 控制先持久挡住该任务新呼叫,Agent 确认收到指令后在同一事务提交应用状态和回执;省略策略默认 hangup,重复控制仍执行、重复回执复用事件身份,stopped 同 ID 不可恢复,未接纳旧外呼静默 ACK。SIP 通知先持久关闭准入,旧/未知通话未确认终结、SaaS 新版尚未分发或 Agent/Asterisk 未加载时不重开;等待期间控制和 outbox 仍可处理。
  • TDD 与隔离验证:go test ./internal/store ./internal/dispatcher ./internal/mq -count=1、bash scripts/check-current-mq-mock.sh、git diff --check 通过;Mock 覆盖任务积压、恢复、控制、重复投递、发布失败、SIP revision 栅栏与格式错误。当前 Agent side effect 为注入的假外呼,主 CLI 仍旧;真实 SaaS/RabbitMQ、供应商、线路、录音与 call.result 均未验收,分别留 P05–P08。

P05:Agent 快照与 SDK 隔离链路(项目内组件通过,主入口待 P07)

  • Dispatcher 在启动、增量发现、恢复任务、SIP 更新及发指令前对完整 AI/provider 快照执行能力校验;不支持的任务关闭准入而不占额度或呼叫。已按用户确认保留现行 TTS Schema:火山 TTS V2 SDK 无法表达的 PCMA、0.5–2 倍以外或整数 speech_rate 无法精确表达的速度均明确拒绝,不静默修改配置;不支持的插话配置同样拒绝。
  • ExecuteApproved 只在隔离 Mock 中签发:携带任务原始 JSON、引用的 provider 明文凭据、SIP revision、选定路由/主叫/原始被叫、独立的拨号期限及最大通话时限;任务+provider 原始字节+SIP revision 计算绑定摘要。Agent 对照已激活的 D 会话身份、绑定摘要、SDK 能力及实际观察到的 SIP 加载结果;发出 Mock 指令前仅持久保存摘要和未知占用,失败/重复/重启不自动重拨,文件中不留明文凭据。当前 LoadedSIP 为可注入的隔离 Mock 观察器,不等于真实 Asterisk 已加载核验。
  • ASR-only 不启动 LLM/TTS;full-AI 使用冻结的 model、显式 temperature=0/max_tokens、TTS voice/format/speech_rate 与 provider 原值凭据。真实 ASR SDK WebSocket 发出的音频输入/识别配置、LLM 与 TTS SDK 向隔离 HTTP Mock 发出的实际参数均已捕获核对;已配置的开场白由 TTS 单次合成,失败不自动重播且未完成时不进入对话轮次;空开场白不调用 TTS。关键词仅匹配用户侧最终 ASR,失败/未知挂断不自动重试。ApprovedOriginator 经生成的 Unary gRPC Stub 交付完整快照,SIP 全量 revision 不吻合即拒绝。
  • 对话控制隔离验证:共享 callflow 不再根据转写或回复内的硬编码词推断拒联,仅处理明确的关键词/拒联事实;ASR-only 不播报开场或 TTS 回复。配置的首语音/静默期限、整通话期限与最大轮数交给媒体控制器;回复按 Unicode 字符数分片,火山 TTS 的缓存音频块数超限明确失败而不重试;不支持的插话配置在准入前拒绝。该控制器尚未接入新主 CLI,不能宣称真实通话媒体已验证。
  • Agent 隔离媒体入口 rpc.RunApprovedCall 仅接收签发的执行快照、媒体会话和挂断动作:签发通话期限与 AI 总期限均约束整通电话;ASR-only 不合成开场,full-AI 开场完成才采集语音。父级期限不会被“未检测到语音”掩盖,空媒体 Mock 等到上下文真正结束。当前媒体只处理 16 kHz PCM16,SDK 虽接受、但媒体不能正确处理的 24 kHz ASR/TTS 配置在 Dispatcher 与 Agent 绑定时明确拒绝,不静默播放或识别错速音频。该入口尚未连接主 CLI,也未用于真实拨号。
  • 验证:PATH=/tmp/sip-go-agent-tools/bin:$PATH bash scripts/check-current-contracts.sh、PATH=/tmp/sip-go-agent-tools/bin:$PATH bash scripts/check-proto.sh、go test ./... -count=1、go test -race ./internal/ai ./internal/callflow ./internal/configread ./internal/dispatcher ./internal/rpc -count=1、go vet ./...、go build ./...、git diff --check 均通过。
  • 后续边界: 录音直传/OSS 失败恢复及最终结果属 P06;新主 CLI 接线、真实媒体完整联动及旧路径清理属 P07;隔离端到端和 A01–A12 属 P08。真实 Agent/Asterisk、SaaS、MQ、AI 供应商联调未开展,不能由 Mock 结果代签。

P06:录音/OSS/最终结果(进行中,未签收)

  • 已新增 Agent 内存录音单次 PUT 组件 UploadClient.UploadBytes:复用受限授权、大小和 SHA-256 核对,成功路径不写临时录音或通话结果文件。OSS 明确拒绝保留状态码且不自动重试;传输结果不明时返回专用错误、停止自动重试并隐藏带签名的 URL。空授权头拒绝而非使服务崩溃。这里只验证组件,尚未连接实际录音或最终结果。
  • 已验证:go test ./internal/agent -count=1、go test -race ./internal/agent -count=1、go vet ./...、go build ./...、git diff --check。失败双文件持久化、48 小时恢复、录音生成失败/无录音、Dispatcher 唯一结果 outbox、MQ/重启恢复和端到端回归均未完成,不能宣称 P06 通过。

验收台账

A01–A12 的行为验证及 K01–K16 的运行时验证待 P03–P08 逐项填充;不得用本地 Mock 冒充外部签收。