Document boot-scoped AI provider catalog
This commit is contained in:
@@ -87,7 +87,7 @@
|
||||
|
||||
- SaaS→Dispatcher 的**五类只读配置**为 `GET /internal/v1/dispatcher/sip`、`/task/:task_id`、`/tasks`、`/tenant/:tenant_id/quota`、`/ai-providers`;路径前缀固定,均须校验 Dispatcher UUID/资源归属、数字 `tenant_id`、完整快照、来源、有效授权和版本。配置读失败、过期、矛盾或不确定时关新准入;没有旧 MQ 配置回退、通用业务 HTTP、ETag 兜底或偷偷启用旧执行字段。已接纳任务持久绑定原快照。
|
||||
- 呼叫、控制、必要回执和**每通话唯一最终结果**经固定 `v1` RabbitMQ Topic/队列,任务与控制队列由 SaaS 预建,Dispatcher 仅消费、不可自行建/删/绑定;结果进入指定共享 durable 队列。独立 D UUID 和接收队列不能广播后正文过滤;`tenant_key` 原值保留,任务只能由归属 D 执行。入站先校验和持久 inbox 后 ACK,状态/outbox 同事务;persistent、mandatory、无 return、publisher confirm 成功才记交付,confirm **不是** SaaS 应用收讫。失败/确认丢失与重启只重发同身份消息,不重复拨号或捏造结果。
|
||||
- `call.execute` 只带获批 `task_id/callee`,调用线路、主叫、AI 和时限由该任务快照固定;`task.control` 的 start/pause/resume/stop 无旧 CAS/版本字段,控制回 task/action/status 处理结果,stop 不可恢复。任务启动/重启完整读取列表,运行中不定时轮询任务列表;新建任务由 start 读取配置和租户额度,暂停后修改的任务由 resume 重读;没有 edit 事件。启动时全量读取并核验 SIP,运行中只由 `sip.config` 通知触发全量 SIP 读取,无常规定时 SIP 轮询;任务 start/resume 使用已生效 SIP 快照,不自行拉取或向 Agent 核验 SIP。SIP 修订变化待旧呼叫结束且 Agent 已加载后,仅在本地重新绑定任务快照,不重读任务列表;待处理期间新准入关闭。发现分页同快照先全部校验后提交,MQ 控制持久状态高于偶发 HTTP 状态;冲突关准入、resume 须重新核验。历史命令不因消息年龄过期,但实际呼出前仍检查任务/白名单/时段/授权/额度和有效通话上限;任务结束不删未确认结果、恢复、幂等或未知占用。
|
||||
- `call.execute` 只带获批 `task_id/callee`,调用线路、主叫、AI 和时限由该任务快照固定;`task.control` 的 start/pause/resume/stop 无旧 CAS/版本字段,控制回 task/action/status 处理结果,stop 不可恢复。任务启动/重启完整读取列表,运行中不定时轮询任务列表;新建任务由 start 读取任务配置和租户额度,暂停后修改的任务由 resume 重读;全局 AI 服务商列表每次启动只读取一次,本进程全部任务复用,变更须重启后才生效;没有 edit 事件。启动时全量读取并核验 SIP,运行中只由 `sip.config` 通知触发全量 SIP 读取,无常规定时 SIP 轮询;任务 start/resume 使用已生效 SIP 快照,不自行拉取或向 Agent 核验 SIP。SIP 修订变化待旧呼叫结束且 Agent 已加载后,仅在本地重新绑定任务快照,不重读任务列表;待处理期间新准入关闭。发现分页同快照先全部校验后提交,MQ 控制持久状态高于偶发 HTTP 状态;冲突关准入、resume 须重新核验。历史命令不因消息年龄过期,但实际呼出前仍检查任务/白名单/时段/授权/额度和有效通话上限;任务结束不删未确认结果、恢复、幂等或未知占用。
|
||||
- 独立 Dispatcher 的 SQLite 是任务、额度、inbox/outbox 的权威数据;Agent 无业务数据库,录音、执行与上传恢复只写受控私有文件。额度包含未知占用,新 boot/租约到期不得自动清除未知执行;不实现双活数据库、自动跨机热备、多 D 共享额度或第二租户公平。本轮不借本机 D1/D2 隔离夹具宣称多 D 运行。不得建立旧表/旧消息/旧 HTTP 执行兼容通道。
|
||||
- Dispatcher↔Agent 复用 Unary gRPC 和受控 Endpoint;Agent 预绑定 D UUID 与服务端证书指纹,激活/会话代际、peer mTLS/SAN/SNI 和已签发期限须核对,新 boot 不清未知占用。Agent 不自行向 SaaS 取任务/AI/OSS 授权;Dispatcher 只用已经核验的 Agent `GetLoadedSIP` revision 开执行准入。本机 Mock 的加载回报不证明 Asterisk 已实际加载,SIP 配置的唯一编辑/审批面仍是 management。
|
||||
- AI 使用任务内不可变授权快照:仅经获批准百炼/火山 ASR、OpenAI 兼容 LLM、火山 TTS 能表达的参数进入每通话实例;ASR-only 不启动 LLM/TTS,完整 AI 不借旧语音测试的 LLM/TTS。只有最终用户 ASR 文本的明确字面关键词可触发拒联/挂断;不由 SDK 默认值、环境、CLI、metadata 或宽松 Schema 改写业务参数,不因 SDK 重试产生第二次发起/收费或重播。日志只存脱敏版本/摘要/计数,不存密钥、prompt、完整对话或音频。
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"sources": {
|
||||
"docs/archive/sources/v0.5-proposal.md": "612fdaee50aff6aa7fbef16c2d469d99857646c6d2235617d0e67f6098cd7ada",
|
||||
"docs/archive/sources/plan-saas-dispatcher-v05-v0.1.md": "666f39e56ea9f4b55661efcac82edd6f9729848e2d60e5f24cdf5aa3ac97ee87",
|
||||
"docs/thirds/saas-dispatcher.md": "9f9da19f3dca8a3f7d696995944c9e0c14d9442c2b7aa048a118dc099f213bdf"
|
||||
"docs/thirds/saas-dispatcher.md": "f74671b0a6e00f6dc6999dc32e71cf231ba83abe1b4e7988f7a8c496fc69fb11"
|
||||
},
|
||||
"bundle_sha256": "d89302b79228c884ce5e0c413bbe9c688c725724de73cb84892e1d38d638e2a5",
|
||||
"bundle_algorithm": "sha256 of sorted relative-path + space + sha256(file) + newline; only root-level JSON and examples/**/*.json, excluding manifest.json"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# SaaS↔Dispatcher 项目内实施证据
|
||||
|
||||
> 本文记录当时的实施过程。当前运行期间不再定时轮询任务列表,也不再有 `DiscoveryFollower`;新建任务用 `task.control` 的 `start`、暂停后修改用 `resume` 重读任务配置,不重读 SIP。SIP 仅在启动和 `sip.config` 通知待处理时拉取全量;确认 Agent 已加载且旧呼叫结束后,更新本地任务的 SIP 绑定,不重读任务列表。现行规则以 [`docs/thirds/saas-dispatcher.md`](../thirds/saas-dispatcher.md) 为准。
|
||||
> 本文记录当时的实施过程。当前运行期间不再定时轮询任务列表,也不再有 `DiscoveryFollower`;新建任务用 `task.control` 的 `start`、暂停后修改用 `resume` 重读任务配置,不重读 SIP;启动时读取一次全局 AI 服务商列表,运行中的 start/resume 沿用该快照,变更须重启。SIP 仅在启动和 `sip.config` 通知待处理时拉取全量;确认 Agent 已加载且旧呼叫结束后,更新本地任务的 SIP 绑定,不重读任务列表。现行规则以 [`docs/thirds/saas-dispatcher.md`](../thirds/saas-dispatcher.md) 为准。
|
||||
|
||||
## 改动前基线
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> 本表保留 P08 当时的本地测试结果、来源路径与哈希;后续整理未改写验收事实。原计划现位于 `docs/archive/sources/plan-saas-dispatcher-v05-v0.1.md`,完成阶段计划位于 `docs/archive/plan-saas-dispatcher-completed.md`;**当前唯一规范**见 [`../thirds/saas-dispatcher.md`](../thirds/saas-dispatcher.md)。
|
||||
|
||||
**当前补充(不改写 P08 历史结果):**现行运行期间不再定时轮询任务列表,删除了 `DiscoveryFollower` 及其两个测试;新建任务的 `start` 和暂停后修改的 `resume` 均重读任务配置,但不重新拉取/核验 SIP;SIP 仅在启动及通知待处理时独立全量读取,Agent 加载核验和本地任务绑定不依赖重读任务列表。`TestStartReadsNewTaskWithoutAgentControl`、`TestResumeRefreshesModifiedTaskRevision`、`TestStartDoesNotReopenPausedOrStoppedTasks` 与隔离 RabbitMQ 的 `TestRuntimeIsolatedControlBacklogExecuteAndSharedResult` 已通过;本次 `PATH=/tmp/sip-go-agent-tools/bin:$PATH make check`(含 Proto 重新生成、隔离 RabbitMQ 测试)、`go test -race ./...`、`go vet ./...` 和构建通过,业务覆盖率 72.1%;此前含隔离 RabbitMQ 的 Dispatcher 测试覆盖率 75.0%(此轮未重算单包覆盖率)。
|
||||
**当前补充(不改写 P08 历史结果):**现行运行期间不再定时轮询任务列表,删除了 `DiscoveryFollower` 及其两个测试;新建任务的 `start` 和暂停后修改的 `resume` 均重读任务配置,但不重新拉取/核验 SIP;SIP 仅在启动及通知待处理时独立全量读取,Agent 加载核验和本地任务绑定不依赖重读任务列表;全局 AI 服务商列表在 Dispatcher 每次启动时读取一次,start/resume 仅使用该次快照,服务商变更须重启。`TestStartReadsNewTaskWithoutAgentControl`、`TestResumeRefreshesModifiedTaskRevision`、`TestStartDoesNotReopenPausedOrStoppedTasks` 与隔离 RabbitMQ 的 `TestRuntimeIsolatedControlBacklogExecuteAndSharedResult` 已通过;本次 `PATH=/tmp/sip-go-agent-tools/bin:$PATH make check`(含 Proto 重新生成、隔离 RabbitMQ 测试)、`go test -race ./...`、`go vet ./...` 和构建通过,业务覆盖率 72.0%;此前含隔离 RabbitMQ 的 Dispatcher 测试覆盖率 75.0%(此轮未重算单包覆盖率)。
|
||||
|
||||
本记录按 `docs/plan-saas-dispatcher-v05-v0.1.md` §3.1、§6 核对 P01–P08、K01–K16 和 A01–A12。**范围仅限单节点、单 Dispatcher、单 Agent、单 Cell、单租户的隔离 Mock。** 本机证书、RabbitMQ 容器和 OSS/AI 模拟服务不代表 SaaS 应用收讫、供应商验收或真实拨号。原计划是当时的来源事实,不是第二份运行合同;当时的状态入口 `docs/plan-saas-dispatcher.md` 已归档。
|
||||
|
||||
|
||||
@@ -9,12 +9,12 @@
|
||||
| 资源 | 方法和路径 | 应用规则 | 正例 |
|
||||
| --- | --- | --- | --- |
|
||||
| SIP 全量 | `GET /internal/v1/dispatcher/sip` | `revision` 是实际加载版本核对依据;未知 `transport/auth_mode/registration_required/max_concurrent_calls` 为 `null`,不能作为可执行线路默认值;变更时关准入、排空并核验 Agent/Asterisk 实际加载 | [`sip`](../../contracts/local/examples/config-read-sip.json) |
|
||||
| AI provider 全量 | `GET /internal/v1/dispatcher/ai-providers` | `provider_ref` 只标识供应商;将明文 `credential` 原值交给选定角色的 SDK,不引入 ref 查找或交换;缺失、禁用、角色不匹配、无效凭据拒绝执行 | [`providers`](../../contracts/local/examples/config-read-providers.json) |
|
||||
| AI provider 全量 | `GET /internal/v1/dispatcher/ai-providers` | 启动时一次读取全局列表,本进程全部任务共用到下一次重启;运行中 start/resume 不重复拉取,服务商变更须重启后才生效。`provider_ref` 只标识供应商;将明文 `credential` 原值交给选定角色的 SDK,不引入 ref 查找或交换;缺失、禁用、角色不匹配、无效凭据拒绝执行 | [`providers`](../../contracts/local/examples/config-read-providers.json) |
|
||||
| 任务 | `GET /internal/v1/dispatcher/task/{task_id}` | 归属、状态和 `task_revision` 校验后持久绑定同一 `agent`/provider 快照;不可变摘要由本项目计算。同一 revision 内容不同拒绝;ASR-only 只需 ASR,不强制 LLM/TTS;0 和 false 原样保留 | [`ASR-only`](../../contracts/local/examples/config-read-task-asr.json) · [`full AI`](../../contracts/local/examples/config-read-task-full.json) |
|
||||
| 任务列表 | `GET /internal/v1/dispatcher/tasks`,后续 `?after=<cursor>` | 首次/重启完整读到 **带 cursor 且 tasks=[]** 的终止页;非空短页不能提前结束。非空页持久成功后才使用下一 cursor;控制队列积压处理前不开新任务准入。不使用旧 snapshot_id/watermark/mode=snapshot | [`page`](../../contracts/local/examples/task-discovery-page.json) · [`end`](../../contracts/local/examples/task-discovery-end.json) |
|
||||
| 租户额度 | `GET /internal/v1/dispatcher/tenant/{tenant_id}/quota` | `quota_revision` 为业务修订号;未知占用不得算成已释放 | [`quota`](../../contracts/local/examples/config-read-quota.json) |
|
||||
|
||||
HTTP 非 200、正文不合法、缺字段、归属冲突、缓存失效或读取失败时拒绝新的相关准入并记录脱敏错误;已持久接纳的执行继续使用原快照。错误体示例 [`error`](../../contracts/local/examples/config-read-error.json),不能当成功配置解析。任务仅有运行/暂停/终止;终止是不可逆 stop,同一 `task_id` 不再启用。任务配置只能在暂停或停止后修改;没有 `edit` 事件,也不定时刷新任务列表。启动/重启时完整读取任务列表及独立的全量 SIP;运行中新建任务由 `task.control` 的 `start` 触发读取,人工暂停后修改配置由 `resume` 触发重读(含租户配额),两者沿用已生效的 SIP 快照,不另行读取或向 Agent 核验全量 SIP。HTTP 旧 running 不得覆盖已持久的 pause/stop;resume 经最新任务配置核验后只能解除人工暂停。SIP 无常规定时拉取;`sip.config` 持久关准入并触发 SIP 全量读取,等待已接纳呼叫结束及 Agent 加载核验后,将新 SIP 绑定到本地任务快照(不重读任务列表),然后才可恢复准入。通知待处理时可重试,读取或校验失败不回退旧 SIP,也不开准入。
|
||||
HTTP 非 200、正文不合法、缺字段、归属冲突、缓存失效或读取失败时拒绝新的相关准入并记录脱敏错误;已持久接纳的执行继续使用原快照。错误体示例 [`error`](../../contracts/local/examples/config-read-error.json),不能当成功配置解析。任务仅有运行/暂停/终止;终止是不可逆 stop,同一 `task_id` 不再启用。任务配置只能在暂停或停止后修改;没有 `edit` 事件,也不定时刷新任务列表。启动/重启时完整读取任务列表及独立的全量 SIP;运行中新建任务由 `task.control` 的 `start` 触发读取,人工暂停后修改配置由 `resume` 触发重读任务和租户配额,两者沿用启动时的 AI 服务商列表和已生效的 SIP 快照,不另行读取或向 Agent 核验全量 SIP。HTTP 旧 running 不得覆盖已持久的 pause/stop;resume 经最新任务配置核验后只能解除人工暂停。SIP 无常规定时拉取;`sip.config` 持久关准入并触发 SIP 全量读取,等待已接纳呼叫结束及 Agent 加载核验后,将新 SIP 绑定到本地任务快照(不重读任务列表),然后才可恢复准入。通知待处理时可重试,读取或校验失败不回退旧 SIP,也不开准入。
|
||||
|
||||
## MQ:固定 v1 传输,唯一事件清单
|
||||
|
||||
@@ -25,7 +25,7 @@ RabbitMQ 是 Topic,**SaaS 独占创建、绑定、退役 exchange/queue,D
|
||||
| 事件 | 方向和处理规则 | 正例 |
|
||||
| --- | --- | --- |
|
||||
| `sip.config` | SaaS→D;`revision` 触发全量重新读取和实际加载核验,不用通知正文代替全量 | [`notification`](../../contracts/local/examples/mq-sip-change.json) |
|
||||
| `task.control` | SaaS→D;start/pause/resume/stop,无控制去重/CAS 字段。start 读取新任务配置及租户配额并持久绑定,无 Agent 控制动作;resume 重读最新配置和额度后解除人工暂停;均沿用已生效 SIP 快照,不重复拉取或核验 SIP;pause/stop 发给 Agent,省略 `active_call_policy` 默认 **hangup**,显式仅 drain/hangup。配置读取、归属或 AI 核验失败时不接纳,SIP 待处理时新呼叫仍关闭;成功操作后才回应用回执,回执不是已完成排空/挂断的证据;stop 同 ID 不可恢复 | [`start`](../../contracts/local/examples/mq-control-start.json) · [`control`](../../contracts/local/examples/mq-control.json) · [`ack`](../../contracts/local/examples/mq-control-ack.json) |
|
||||
| `task.control` | SaaS→D;start/pause/resume/stop,无控制去重/CAS 字段。start 读取新任务配置及租户配额并持久绑定,无 Agent 控制动作;resume 重读最新任务配置和额度后解除人工暂停;均沿用启动时 AI 服务商列表及已生效 SIP 快照,不重复拉取全局服务商或 SIP,也不额外核验 SIP;pause/stop 发给 Agent,省略 `active_call_policy` 默认 **hangup**,显式仅 drain/hangup。配置读取、归属或 AI 核验失败时不接纳,SIP 待处理时新呼叫仍关闭;成功操作后才回应用回执,回执不是已完成排空/挂断的证据;stop 同 ID 不可恢复 | [`start`](../../contracts/local/examples/mq-control-start.json) · [`control`](../../contracts/local/examples/mq-control.json) · [`ack`](../../contracts/local/examples/mq-control-ack.json) |
|
||||
| `call.execute` | SaaS→D 仅 `{task_id,callee}`;一次指令保留独立消息/执行身份,路由/主叫/AI/时限从绑定任务读取。`dispatched` 表示已发出呼叫指令,**不表示接通**;白名单/单号码格式不合规则回 `rejected,reason_code:null,reason_message`、不拨号不发最终结果、不暂停整任务 | [`execute`](../../contracts/local/examples/mq-execute.json) · [`dispatched`](../../contracts/local/examples/mq-execute-ack.json) · [`rejected`](../../contracts/local/examples/mq-execute-rejected.json) |
|
||||
| `call.execute.result` | D→SaaS;按 `task_id` + 原号码关联,每次呼叫仅一份最终结果;不新增外部 call_id/source_command_id;真正终结且录音成功上传、无录音或预期录音生成失败后才发送 | [`uploaded`](../../contracts/local/examples/mq-result-uploaded.json) · [`empty`](../../contracts/local/examples/mq-result-no-recording.json) |
|
||||
|
||||
|
||||
Reference in New Issue
Block a user