Files
go-sip/docs/thirds/v0.5-proposal.md
T

13 KiB
Raw Blame History

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 消费四步;

SaaS→D  exchange:       agent-call.dispatchers.v1
        routing key:    d.<dispatcher_id>.t.<tenant_id>.in
        binding key:    d.<dispatcher_id>.t.<tenant_id>.in
        queue:          agent-call.d.<dispatcher_id>.t.<tenant_id>.v1
        consumer:       对应 Dispatcher
D→SaaS  exchange:       agent-call.saas.v1
        routing key:    d.<dispatcher_id>.t.<tenant_id>.out
        queue:          agent-call.saas.events.v1
        consumer:       SaaS

1.2 本轮任务队列与事件路由 硬边界:所有 exchange/queue/binding 均由 SaaS 创建、维护和退役;D 只消费 SaaS 创建的任务/控制队列,并向 SaaS 创建的结果 exchange 发布,不声明、绑定或删除队列。

SaaS 创建并绑定:
  exchange:       agent-call.dispatchers.v1 (topic, durable)
  task routing:   d.<dispatcher_id>.task.<task_id>.in
  task queue:     agent-call.d.<dispatcher_id>.task.<task_id>.v1
  control route:  d.<dispatcher_id>.control.in
  control queue:  agent-call.d.<dispatcher_id>.control.v1
  dead-letter:    agent-call.dead-letter.v1
  D -> SaaS exchange: agent-call.saas.v1 (topic, durable)
  result route:   d.<dispatcher_id>.out
  SaaS result queue: agent-call.saas.d.<dispatcher_id>.v1

HTTP请求 SIP列表配置

GET /internal/v1/dispatcher/sip HTTP/1.1
Host: <SaaS 服务地址>
X-DISPATCHER-id: c046b893-8628-4589-ae50-619d049248a6
X-DISPATCHER-SECRET-KEY: <密钥>

响应:

{
  "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

{
  "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"
    }
  ]
}

任务配置

GET /internal/v1/dispatcher/task/{task_id} HTTP/1.1
Host: <SaaS 服务地址>
X-DISPATCHER-id: c046b893-8628-4589-ae50-619d049248a6
X-DISPATCHER-SECRET-KEY: <密钥>

响应体: 仅 ASR 模式(Agent配置仅返回 ASR即可) ASR + LLM + TTS 模式

{
    **"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**
        }
    }
}

任务列表

GET /internal/v1/dispatcher/tasks HTTP/1.1
Host: <SaaS 内网地址>
X-DISPATCHER-id: c046b893-8628-4589-ae50-619d049248a6
X-DISPATCHER-SECRET-KEY: <密钥>

增量请求

GET /internal/v1/dispatcher/tasks?after=1042 HTTP/1.1
Host: <SaaS 内网地址>
X-DISPATCHER-id: c046b893-8628-4589-ae50-619d049248a6
X-DISPATCHER-SECRET-KEY: <密钥>

响应:

{
  "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 获取配额信息

GET /internal/v1/dispatcher/tenant/{tenant-id}/quota HTTP/1.1
Host: <SaaS 内网地址>
X-DISPATCHER-id: c046b893-8628-4589-ae50-619d049248a6
X-DISPATCHER-SECRET-KEY: <密钥>
{
  "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.<dispatcher_id>.control.v1

SIP 线路变更

{
  "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
  }
}

任务控制

{
  "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

{
  "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.<dispatcher_id>.t.<tenant_id>.v1

呼出

{
  "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

开始调度执行

{
  "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",
  }
}

完成结果上报 成功

{
  "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"
    }
  }
}

无应答

{
  // ...
  "payload": {
    //...
    "outcome": "no_answer",
    "reason_code": 480,
    "reason_message": "SIP ring timeout Reason",
    "transcript": [],
    "opt_out": false,
    "recording": {}
  }
}