SaaS Dispatcher 对接文档 v0.5 相关占位说明: `/internal/v1/dispatcher/xxxxx` 路由按系统当前架构路由进行定义即可 `X-DISPATCHER-ID`/`X-DISPATCHER-SECRET-KEY` HEADER 头KEY 获取忽略大小写 Dispatcher 角色下称 D 触发顺序 1.1 MQ 发布消费规则 RabbitMQ 有发布入口 exchange → 发布时指定的 routing key → 预先绑定的 queue → D 消费四步; ```Plain Text SaaS→D exchange: agent-call.dispatchers.v1 routing key: d..t..in binding key: d..t..in queue: agent-call.d..t..v1 consumer: 对应 Dispatcher D→SaaS exchange: agent-call.saas.v1 routing key: d..t..out queue: agent-call.saas.events.v1 consumer: SaaS ``` 1.2 本轮任务队列与事件路由 硬边界:所有 exchange/queue/binding 均由 SaaS 创建、维护和退役;D 只消费 SaaS 创建的任务/控制队列,并向 SaaS 创建的结果 exchange 发布,不声明、绑定或删除队列。 ```Plain Text SaaS 创建并绑定: exchange: agent-call.dispatchers.v1 (topic, durable) task routing: d..task..in task queue: agent-call.d..task..v1 control route: d..control.in control queue: agent-call.d..control.v1 dead-letter: agent-call.dead-letter.v1 D -> SaaS exchange: agent-call.saas.v1 (topic, durable) result route: d..out SaaS result queue: agent-call.saas.d..v1 ``` HTTP请求 SIP列表配置 ```HTTP GET /internal/v1/dispatcher/sip HTTP/1.1 Host: X-DISPATCHER-id: c046b893-8628-4589-ae50-619d049248a6 X-DISPATCHER-SECRET-KEY: <密钥> ``` 响应: ```JSON { "resource": "sip_config", // 资源类型:SIP 配置 "dispatcher_id": "c046b893-8628-4589-ae50-619d049248a6", // 这份配置所属的 Dispatcher "trunks": [ // 该 Dispatcher 获批的线路列表 { "trunk_id": "trunk-mock", // 线路的唯一标识 "provider_id": "provider-mock", // SIP 服务商标识 "codec": "PCMA", // 线路使用的语音编码 "dial_prefix": "", // 该线路拨号时添加的被叫前缀;空串表示不添加 "enabled": true, // 是否启用这条线路 "server_host": "sip.example.invalid", // SIP 服务端地址;此处是 Mock 地址 "server_port": 5060, // SIP 服务端端口 "transport": null, // 传输方式:udp、tcp、tls;null 表示尚未确认 "auth_mode": null, // 鉴权方式:ip、digest、none;null 表示尚未确认 "registration_required": null, // 是否需要 SIP 注册;null 表示尚未确认 "max_concurrent_calls": null, // 分配给该 Dispatcher 的线路并发上限;null 表示尚未确认 "caller_profiles": [ // 这条线路可用的主叫配置 { "caller_profile_id": "caller-profile-mock", // 主叫配置标识,供任务引用 "caller_id": "BD00000000" // 实际主叫标识,保留原值及字母 } ], "schedule": { // 这条线路允许外呼的每周时段 "time_zone": "Asia/Shanghai", // 判断时段使用的时区 "weekly_windows": { // 每天可配置多个时段;空数组表示当天不允许外呼 "monday": [{"start": "09:00", "end": "20:00"}], // 周一 "tuesday": [{"start": "09:00", "end": "20:00"}], // 周二 "wednesday": [{"start": "09:00", "end": "20:00"}], // 周三 "thursday": [{"start": "09:00", "end": "20:00"}], // 周四 "friday": [{"start": "09:00", "end": "20:00"}], // 周五 "saturday": [], // 周六不允许外呼 "sunday": [] // 周日不允许外呼 } } } ] } ``` AI 服务商全量读取 `GET /internal/v1/dispatcher/ai-providers` ```JSON { "resource": "ai_providers", "dispatcher_id": "c046b893-8628-4589-ae50-619d049248a6", "providers": [ { "provider_ref": "asr-provider-a", "role": "asr", "enabled": true, "adapter": "volcengine_asr", "endpoint": "https://asr.example.invalid", "credential": "managed-asr-a" }, { "provider_ref": "llm-provider-a", "role": "llm", "enabled": true, "adapter": "openai_compatible", "endpoint": "https://llm.example.invalid/v1", "credential": "managed-llm-a" }, { "provider_ref": "tts-provider-a", "role": "tts", "enabled": true, "adapter": "volcengine_tts", "endpoint": "https://tts.example.invalid", "credential": "managed-tts-a" } ] } ``` 任务配置 ```HTTP GET /internal/v1/dispatcher/task/{task_id} HTTP/1.1 Host: X-DISPATCHER-id: c046b893-8628-4589-ae50-619d049248a6 X-DISPATCHER-SECRET-KEY: <密钥> ``` 响应体: 仅 ASR 模式(Agent配置仅返回 ASR即可) ASR + LLM + TTS 模式 ```JSON { **"resource"**: **"task_config"**, **"dispatcher_id"**: **"c046b893-8628-4589-ae50-619d049248a6"**, **"tenant_id"**: **"tenant-id-mock"**, **"task_id"**: **"task-mock"**, **"task_revision"**: **2**, **"status"**: **"running"**, **"name"**: **"Mock task"**, **"max_concurrent_calls"**: **2**, **"ring_timeout_ms"**: **30000**, **"max_call_duration_ms"**: **120000**, **"route_policy_id"**: **"route-mock"**, **"caller_profile_id"**: **"caller-profile-mock"**, **"allowed_trunk_ids"**: [ **"trunk-mock"** ], **"schedule"**: { **"time_zone"**: **"Asia/Shanghai"**, **"starts_at"**: **"2026-09-21T00:00:00+08:00"**, **"ends_at"**: **null**, **"weekly_windows"**: { **"monday"**: [ { **"start"**: **"09:00"**, **"end"**: **"11:00"** }, { **"start"**: **"14:00"**, **"end"**: **"18:00"** } ], **"tuesday"**: [ { **"start"**: **"09:00"**, **"end"**: **"18:00"** } ], **"wednesday"**: [ { **"start"**: **"09:00"**, **"end"**: **"18:00"** } ], **"thursday"**: [ { **"start"**: **"09:00"**, **"end"**: **"18:00"** } ], **"friday"**: [ { **"start"**: **"09:00"**, **"end"**: **"18:00"** } ], **"saturday"**: [ ], **"sunday"**: [ ] }, **"excluded_dates"**: [ **"2026-10-01"**, **"2026-10-02"** ] }, **"agent"**: { **"immutable"**: **true**, **"mode"**: **"full_ai"**, **"llm"**: { **"provider_ref"**: **"mock"**, **"model"**: **"mock-chat-v1"**, **"temperature"**: **0.2**, **"max_tokens"**: **256**, **"timeout_ms"**: **5000** }, **"prompt"**: { **"text"**: **"Mock prompt for an isolated test."**, **"allowed_variables"**: [ ], **"max_bytes"**: **32768** }, **"tts"**: { **"provider_ref"**: **"mock"**, **"model"**: **"mock-tts-v1"**, **"voice"**: **"mock-neutral"**, **"speed"**: **1**, **"format"**: { **"encoding"**: **"pcm_s16le"**, **"sample_rate_hz"**: **16000**, **"channels"**: **1** }, **"timeout_ms"**: **5000** }, **"asr"**: { **"provider_ref"**: **"mock"**, **"language"**: **"zh-CN"**, **"input"**: { **"encoding"**: **"pcm_s16le"**, **"sample_rate_hz"**: **16000**, **"channels"**: **1**, **"sample_width_bytes"**: **2** }, **"interim"**: **true**, **"timeout_ms"**: **5000** }, **"conversation"**: { **"opening"**: **"开场白"**, **"hangup_keywords"**: [ **"不用了"**, **"请挂机"** ], **"allow_interrupt"**: **true**, **"silence_timeout_ms"**: **3000**, **"max_duration_ms"**: **120000**, **"max_turns"**: **20**, **"sentence_max_chars"**: **80**, **"max_pending_audio_chunks"**: **32** } } } ``` 任务列表 ```HTTP GET /internal/v1/dispatcher/tasks HTTP/1.1 Host: X-DISPATCHER-id: c046b893-8628-4589-ae50-619d049248a6 X-DISPATCHER-SECRET-KEY: <密钥> ``` 增量请求 ```HTTP GET /internal/v1/dispatcher/tasks?after=1042 HTTP/1.1 Host: X-DISPATCHER-id: c046b893-8628-4589-ae50-619d049248a6 X-DISPATCHER-SECRET-KEY: <密钥> ``` 响应: ```JSON { "schema_version": "task-discovery.v0.2-proposal", "dispatcher_id": "c046b893-8628-4589-ae50-619d049248a6", "cursor": "1042", "tasks": [ { "task_id": "task-a", "tenant_id": 1001, "status": "running", "task_revision": 1 }, { "task_id": "task-old", "tenant_id": 1001, "status": "stopped", "task_revision": 3 } ] } ``` 按租户 ID 获取配额信息 ```HTTP GET /internal/v1/dispatcher/tenant/{tenant-id}/quota HTTP/1.1 Host: X-DISPATCHER-id: c046b893-8628-4589-ae50-619d049248a6 X-DISPATCHER-SECRET-KEY: <密钥> ``` ```JSON { "resource": "tenant_quota", "dispatcher_id": "c046b893-8628-4589-ae50-619d049248a6", "tenant_id": "tenant-id-mock", "quota_revision": 1, "max_concurrent_calls": 3 } ``` 控制事件 > 队列:agent\-call\.d\.\\.control\.v1 > > SIP 线路变更 ```JSON { "event_id": "7cd23165-c88e-4f36-8cf2-b5e9ac67ecdb", "event_type": "sip.config", "dispatcher_id": "c046b893-8628-4589-ae50-619d049248a6", "payload": { "trunk_id": "trunk-mock", "change_type": "disabled", // enabled/removed/created "revision": 8 } } ``` 任务控制 ```JSON { "dispatcher_id": "c046b893-8628-4589-ae50-619d049248a6", "tenant_id": 1001, "issued_at": "2026-09-21T00:00:00Z", "event_id": "7cd23165-c88e-4f36-8cf2-b5e9ac67ecdb", "event_type":: "task.control", "payload": { "task_id": "task-a", "action": "pause", // resume , stop "reason": "local-test", "options": { // 附加控制参数 "active_call_policy": "drain", // 活跃通话策略,仅stop,pause事件生效 [drain|hangup] } } } ``` 任务控制处理回执 > 队列 agent\-call\.saas\.v1 > > ```JSON { "event_id": "7cd23165-c88e-4f36-8cf2-b5e9ac67ecdb", "event_type":: "task.control", "dispatcher_id": "c046b893-8628-4589-ae50-619d049248a6", "tenant_id": 1001, "payload": { "status": "applied" } } ``` 外呼任务队列 > 队列:agent\-call\.d\.\\.t\.\\.v1 > > 呼出 ```JSON { "event_id": "7cd23165-c88e-4f36-8cf2-b5e9ac67ecdb", "event_type":: "call.execute", "dispatcher_id": "c046b893-8628-4589-ae50-619d049248a6", "tenant_id": 1001, "issued_at": "2026-09-18T10:00:00+08:00" "payload": { "task_id": "task-a", "callee": "15003164745" } } ``` 回执 > 队列 agent\-call\.saas\.v1 > > 开始调度执行 ```JSON { "event_id": "7cd23165-c88e-4f36-8cf2-b5e9ac67ecdb", "event_type":: "call.execute", "dispatcher_id": "c046b893-8628-4589-ae50-619d049248a6", "tenant_id": 1001, "issued_at": "2026-09-18T10:00:00+08:00" "payload": { "status": "dispatched", } } ``` 完成结果上报 成功 ```JSON { "event_id": "call-result-001", "event_type": "call.execute.result", "dispatcher_id": "c046b893-8628-4589-ae50-619d049248a6", "tenant_id": 1001, "issued_at": "2026-09-18T10:10:15+08:00", "payload": { "task_id": "task-a", "caller_profile_id": "caller-a", "callee": "15003164745", "trunk_id": "trunk-a", "started_at": "2026-09-18T10:00:00+08:00", "ended_at": "2026-09-18T10:10:00+08:00", "duration_ms": 600000, "outcome": "answered", "reason_code": null, "transcript": [ { "turn_id": "turn-1", "segment_id": "segment-1", "role": "user", "text": "示例转写内容", "start_ms": 1000, "end_ms": 2500 } ], "opt_out": false, "recording": { "status": "uploaded", "bucket": "example-bucket", "object_key": "calls/tenant-a/call-a.wav", "format": "wav", "channels": 1, "sample_rate_hz": 8000, "duration_ms": 600000, "size_bytes": 9600000, "checksum_sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" } } } ``` 无应答 ```JSON { // ... "payload": { //... "outcome": "no_answer", "reason_code": 480, "reason_message": "SIP ring timeout Reason", "transcript": [], "opt_out": false, "recording": {} } } ```