fix real cell RTP bridge readiness
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
{
|
||||
"record_version": "1.0",
|
||||
"recorded_at_utc": "2026-09-17T05:32:21Z",
|
||||
"mode": "real",
|
||||
"scope": "post-security-group-expansion and Cell media/recording code-fix validation",
|
||||
"deployment": {
|
||||
"region": "cn-beijing",
|
||||
"instance_id": "i-2zeimnh6hq53q4srr3o2",
|
||||
"instance_status": "Running",
|
||||
"spot_strategy": "SpotAsPriceGo",
|
||||
"public_ip": "123.56.71.98",
|
||||
"eip_allocation_id": "eip-2zeevfsaxzwuue2szy7xb",
|
||||
"asterisk_container": "agent-call-asterisk"
|
||||
},
|
||||
"security_group": {
|
||||
"diagnostic_rules": [
|
||||
{
|
||||
"source_cidr": "160.202.254.79/32",
|
||||
"protocol": "UDP",
|
||||
"port_range": "1/65535",
|
||||
"rule_id": "sgr-2ze0bq4elp873ybfmphi",
|
||||
"request_id": "01A0ADD1-F69F-54B6-A806-D3CF2E6FFA78"
|
||||
},
|
||||
{
|
||||
"source_cidr": "111.9.126.254/32",
|
||||
"protocol": "UDP",
|
||||
"port_range": "1/65535",
|
||||
"rule_id": "sgr-2ze90x5nqxa788u8tc2g",
|
||||
"request_id": "01A0ADD1-F7AC-5DDE-86A8-F135B9F3B344"
|
||||
}
|
||||
],
|
||||
"scope": "provider signaling/media IPs only; not 0.0.0.0/0"
|
||||
},
|
||||
"code_fixes_under_test": [
|
||||
"Cell seeds RTP send peer from Asterisk UNICASTRTP_LOCAL_ADDRESS/PORT instead of requiring a first inbound packet",
|
||||
"ARI live recordings are stopped with POST /recordings/live/{name}/stop before reading the stored file",
|
||||
"mka755 is accepted as an ASCII alphanumeric dial prefix"
|
||||
],
|
||||
"call": {
|
||||
"original_number": "18625770806",
|
||||
"callee_prefix": "mka755",
|
||||
"effective_sip_user": "mka75518625770806",
|
||||
"trunk": "160.202.254.79:5060",
|
||||
"caller_id": "KQ91526",
|
||||
"attempts": 1,
|
||||
"retries": 0,
|
||||
"execution_id": "exec_cell_1789623117192788072_1",
|
||||
"call_id": "call_a7224f59f1fb400ebcc51b8975e7fbf3",
|
||||
"connected": true,
|
||||
"status": "failed",
|
||||
"reason_code": "CUSTOMER_HANGUP",
|
||||
"duration_ms": 27200.655292000192,
|
||||
"turns": [],
|
||||
"media_packets_in": 0,
|
||||
"media_packets_out": 435,
|
||||
"recording": "no valid stored recording reported",
|
||||
"tcpdump": "not captured; tcpdump exists but sudo policy denies capture command"
|
||||
},
|
||||
"assessment": {
|
||||
"our_previous_rtp_peer_bug": "fixed enough to send 435 outbound RTP packets to Asterisk without waiting for inbound RTP",
|
||||
"provider_to_cell_audio": "still not proven; Cell received zero inbound RTP packets even after provider/media-source full-UDP rules",
|
||||
"remaining_boundary": "without a packet capture on the host/Asterisk side, cannot distinguish provider not sending RTP from Asterisk not forwarding provider RTP into externalMedia",
|
||||
"llm_conversation": "not started; zero turns because no inbound customer audio was captured",
|
||||
"overall": "FAIL_RTP_INBOUND_STILL_ZERO"
|
||||
},
|
||||
"cleanup": {
|
||||
"active_channels_after_call": 0,
|
||||
"temporary_cell_container_removed": true,
|
||||
"temporary_rabbitmq_removed": true,
|
||||
"temporary_remote_credentials_removed": true,
|
||||
"ecs_and_eip_retained": true
|
||||
},
|
||||
"raw_report": {
|
||||
"path": ".local/agent-call-third/llm-call-report-codefix-20260917.json",
|
||||
"sha256": "2b6d1f56f98ebf2ba40ebdf2740c9d7daa5ad4ddc12074d582248934735c027d",
|
||||
"mode": "0600"
|
||||
},
|
||||
"notes": [
|
||||
"This was one newly authorized post-fix attempt; no automatic redial was performed.",
|
||||
"The expanded source-limited UDP rules did not produce inbound RTP at the Cell.",
|
||||
"The next diagnostic step is an explicitly authorized call with host/Asterisk packet capture or provider-side RTP packet evidence; do not claim provider fault solely from the Cell counter."
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
{
|
||||
"record_version": "1.0",
|
||||
"recorded_at_utc": "2026-09-17T06:07:42Z",
|
||||
"mode": "real",
|
||||
"scope": "post externalMedia/bridge/RTP/recording code fixes",
|
||||
"deployment": {
|
||||
"region": "cn-beijing",
|
||||
"instance_id": "i-2zeimnh6hq53q4srr3o2",
|
||||
"instance_status": "Running",
|
||||
"spot_strategy": "SpotAsPriceGo",
|
||||
"public_ip": "123.56.71.98",
|
||||
"eip_allocation_id": "eip-2zeevfsaxzwuue2szy7xb",
|
||||
"asterisk_container": "agent-call-asterisk"
|
||||
},
|
||||
"code_fixes_included": [
|
||||
"externalMedia peer lookup tolerates the pre-Stasis 404 and retries from StasisStart",
|
||||
"bridge addChannel only marks success after the API succeeds, with bounded 400/404/409/422 retry",
|
||||
"answer is accepted only after target and externalMedia are both present in the expected bridge",
|
||||
"RTP receive validates PCMA payload type, expected peer, and stable SSRC",
|
||||
"ARI originate timeout uses seconds",
|
||||
"recording stop/readback uses POST stop and bounded stored-file polling",
|
||||
"mka755 alphanumeric dial-prefix validation"
|
||||
],
|
||||
"security_group": {
|
||||
"source_limited_full_udp": [
|
||||
"160.202.254.79/32",
|
||||
"111.9.126.254/32"
|
||||
],
|
||||
"port_range": "1/65535",
|
||||
"global_udp_open": false
|
||||
},
|
||||
"call": {
|
||||
"original_number": "18625770806",
|
||||
"callee_prefix": "mka755",
|
||||
"effective_sip_user": "mka75518625770806",
|
||||
"trunk": "160.202.254.79:5060",
|
||||
"caller_id": "KQ91526",
|
||||
"attempts": 1,
|
||||
"retries": 0,
|
||||
"execution_id": "exec_cell_1789625261537310100_1",
|
||||
"call_id": "call_17c1bc8f75e3409e8e654d0498847e83",
|
||||
"connected": false,
|
||||
"status": "failed",
|
||||
"reason_code": "CALL_NOT_ANSWERED",
|
||||
"duration_ms": 601.2159009987954,
|
||||
"turns": [],
|
||||
"media_packets_in": 0,
|
||||
"media_packets_out": 0,
|
||||
"recording": {
|
||||
"bytes": 44,
|
||||
"sha256": "4f8734c5e13ac599e168cf247a51c1dd0758537ce00bf16d7fed1a3d14d07041",
|
||||
"valid_wav": false
|
||||
}
|
||||
},
|
||||
"asterisk_observation": {
|
||||
"external_media_channel": "joined bridge",
|
||||
"target_action": "Called mka75518625770806@provider-primary",
|
||||
"termination": "Message ended by control; target answer/SIP final response was not captured",
|
||||
"active_channels_after_test": 0,
|
||||
"raw_log": ".local/agent-call-third/llm-call-fix2-asterisk.log",
|
||||
"raw_log_sha256": "71bf15f74a239eb2753da1522a923ec3fae716b87522f553ed4f76ebdca60330"
|
||||
},
|
||||
"diagnosis": {
|
||||
"our_code": "bridge/externalMedia readiness races are now guarded; this attempt did not reach the answer/media stage",
|
||||
"provider_or_asterisk_boundary": "undetermined for this attempt because PJSIP logger/packet capture was unavailable",
|
||||
"llm_audio": "not started; zero turns",
|
||||
"overall": "FAIL_CALL_NOT_ANSWERED_NEEDS_SIP_TRACE"
|
||||
},
|
||||
"cleanup": {
|
||||
"temporary_cell_removed": true,
|
||||
"temporary_rabbitmq_removed": true,
|
||||
"temporary_credentials_removed": true,
|
||||
"ecs_and_eip_retained": true
|
||||
},
|
||||
"runner_report": {
|
||||
"path": ".local/agent-call-third/llm-call-fix2-report-20260917.json",
|
||||
"sha256": "7a5974fc94fe1277ecb0c0af93e9efa534e8160c6adb866cf045ed511956f217",
|
||||
"mode": "0600"
|
||||
},
|
||||
"next_step": "One separately authorized diagnostic call with PJSIP logger and a working host packet capture is required to distinguish provider no-answer/no-RTP from Asterisk forwarding failure; no automatic redial was performed."
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
{
|
||||
"record_version": "1.0",
|
||||
"recorded_at_utc": "2026-09-17T04:34:08Z",
|
||||
"mode": "real",
|
||||
"authorization": "用户明确要求带 LLM 音频自动对话功能呼出测试",
|
||||
"deployment": {
|
||||
"region": "cn-beijing",
|
||||
"instance_id": "i-2zeimnh6hq53q4srr3o2",
|
||||
"instance_status": "Running",
|
||||
"spot_strategy": "SpotAsPriceGo",
|
||||
"public_ip": "123.56.71.98",
|
||||
"eip_allocation_id": "eip-2zeevfsaxzwuue2szy7xb",
|
||||
"asterisk_container": "agent-call-asterisk",
|
||||
"cell_runtime": "agent-call-real-cell:bjmkkj-20260916 with patched alphanumeric dial-prefix validation",
|
||||
"broker": "RabbitMQ 4.1 management alpine, isolated temporary test container"
|
||||
},
|
||||
"trunk": {
|
||||
"server": "160.202.254.79:5060",
|
||||
"endpoint": "provider-primary",
|
||||
"caller_id": "KQ91526",
|
||||
"callee_prefix": "mka755",
|
||||
"codec": "PCMA"
|
||||
},
|
||||
"call": {
|
||||
"original_number": "18625770806",
|
||||
"effective_sip_user": "mka75518625770806",
|
||||
"attempts": 1,
|
||||
"retries": 0,
|
||||
"execution_id": "exec_cell_1789619650651950061_1",
|
||||
"call_id": "call_6017aab0bd8d4cf698d685de43563022",
|
||||
"connected": true,
|
||||
"status": "failed",
|
||||
"reason_code": "RTP_PEER_MISSING",
|
||||
"duration_ms": 18987.630626999817,
|
||||
"turns": [],
|
||||
"media_packets_in": 0,
|
||||
"media_packets_out": 0,
|
||||
"recording": {
|
||||
"bytes": 44,
|
||||
"sha256": "4f8734c5e13ac599e168cf247a51c1dd0758537ce00bf16d7fed1a3d14d07041",
|
||||
"channels": 1,
|
||||
"sample_rate_hz": 8000,
|
||||
"frames": 0,
|
||||
"valid_wav": false
|
||||
}
|
||||
},
|
||||
"validation": {
|
||||
"sip_signaling": "connected=true in Cell result",
|
||||
"llm_conversation": "not_started; zero turns because RTP peer was not learned",
|
||||
"audio_media": "FAIL; zero RTP packets in/out",
|
||||
"recording": "FAIL; 44-byte zero-frame WAV",
|
||||
"overall": "BLOCKED_BY_RTP_PEER_MISSING"
|
||||
},
|
||||
"cleanup": {
|
||||
"active_channels_after_call": 0,
|
||||
"temporary_cell_container_removed": true,
|
||||
"temporary_rabbitmq_removed": true,
|
||||
"temporary_remote_credentials_removed": true,
|
||||
"ecs_and_eip_retained": true
|
||||
},
|
||||
"raw_report": {
|
||||
"path": ".local/agent-call-third/llm-call-report-20260917.json",
|
||||
"sha256": "fbc6114e4f9ed3a64bcb28a6b1a959b23f01575ba3c6e76e687729b379781df9",
|
||||
"mode": "0600"
|
||||
},
|
||||
"notes": [
|
||||
"The call was initiated once through the tenant RabbitMQ command path and the real Cell worker; no automatic redial was performed.",
|
||||
"SIP connected, but no RTP packet arrived from the provider, so the LLM/TTS/ASR conversation could not start.",
|
||||
"The missing RTP is consistent with the previously observed provider-side SDP/RTP reachability issue and requires provider/media-path investigation before another real call."
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"record_version": "1.0",
|
||||
"recorded_at_utc": "2026-09-17T04:02:46Z",
|
||||
"authorization": "用户明确授权创建 SpotAsPriceGo 实例并测试一次外呼",
|
||||
"deployment": {
|
||||
"region": "cn-beijing",
|
||||
"instance_id": "i-2zeimnh6hq53q4srr3o2",
|
||||
"instance_status": "Running",
|
||||
"charge_type": "PostPaid",
|
||||
"spot_strategy": "SpotAsPriceGo",
|
||||
"public_ip": "123.56.71.98",
|
||||
"ip_kind": "eip",
|
||||
"eip_allocation_id": "eip-2zeevfsaxzwuue2szy7xb",
|
||||
"asterisk_image_digest": "git.ipao.vip/rogee/asterisk-real@sha256:0f5e3cd0e9a86bc9dc4750929abee9ba1faae11ed39739f3e75343d39d837e41"
|
||||
},
|
||||
"trunk": {
|
||||
"server": "160.202.254.79:5060",
|
||||
"auth_mode": "ip",
|
||||
"caller_id_configured": "KQ91526",
|
||||
"codec_configured": "PCMA",
|
||||
"endpoint": "provider-primary",
|
||||
"endpoint_contact_status": "Avail"
|
||||
},
|
||||
"call": {
|
||||
"original_number": "18625770806",
|
||||
"callee_prefix": "mka755",
|
||||
"effective_sip_user": "mka75518625770806",
|
||||
"attempts": 1,
|
||||
"retries": 0,
|
||||
"started_at_utc": "2026-09-17T04:02:15Z",
|
||||
"answered_at_utc": "2026-09-17T04:02:29Z",
|
||||
"ended_at_utc": "2026-09-17T04:02:46Z",
|
||||
"call_id": "b22e4055-0e12-4d59-9cd2-cb343aece9b1",
|
||||
"originate_rc": 0,
|
||||
"sip_responses": [
|
||||
{"code": 100, "phrase": "Trying"},
|
||||
{"code": 180, "phrase": "Ringing"},
|
||||
{"code": 183, "phrase": "Session Progress"},
|
||||
{"code": 200, "phrase": "OK"}
|
||||
],
|
||||
"sip_result": "answered",
|
||||
"local_sdp": {"address": "123.56.71.98", "rtp_port": 10542, "codec": "PCMA/8000"},
|
||||
"remote_sdp": {"address": "111.9.126.254", "rtp_port": 14248, "codec": "PCMA/8000"},
|
||||
"caller_identity_observed": "From display=Anonymous; URI user=KQ91526; no P-Asserted-Identity observed",
|
||||
"caller_identity_validation": "partial",
|
||||
"media_validation": "not_performed",
|
||||
"final_active_channels": 0
|
||||
},
|
||||
"result": "SIP signaling answered; caller presentation and RTP/audio not fully validated",
|
||||
"raw_log": {
|
||||
"path": ".local/agent-call-third/call-20260917-040214-raw.txt",
|
||||
"sha256": "6ea3246b2a5a04f4914ea44f51ad3b0799ec72e7964f6b9d72d3aa3499d9b329",
|
||||
"mode": "0600"
|
||||
},
|
||||
"notes": [
|
||||
"The single test sent INVITE sip:mka75518625770806@160.202.254.79:5060 and received 100/180/183/200.",
|
||||
"The call was originated directly as a PJSIP channel; the observed From display was Anonymous even though the URI user remained KQ91526.",
|
||||
"No automatic redial was performed.",
|
||||
"The Spot instance and EIP were retained; cleanup was not requested.",
|
||||
"SIP 200 OK is not proof of two-way RTP, valid recording, or full AI call acceptance."
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
{
|
||||
"record_version": "1.0",
|
||||
"recorded_at_utc": "2026-09-17T04:45:00Z",
|
||||
"scope": "third SIP provider RTP_PEER_MISSING diagnosis",
|
||||
"conclusion": "我方安全组存在明确配置缺口;已修复,但尚未用新的真实外呼验证供应商是否实际发送 RTP",
|
||||
"evidence": {
|
||||
"failed_llm_call": {
|
||||
"report": "docs/evidence/third-sip-llm-outbound-20260917.json",
|
||||
"call_id": "call_6017aab0bd8d4cf698d685de43563022",
|
||||
"connected": true,
|
||||
"reason_code": "RTP_PEER_MISSING",
|
||||
"media_packets_in": 0,
|
||||
"media_packets_out": 0,
|
||||
"turns": [],
|
||||
"recording_bytes": 44,
|
||||
"recording_valid": false
|
||||
},
|
||||
"provider_sdp": {
|
||||
"sip_server": "160.202.254.79:5060",
|
||||
"invite_uri": "sip:mka75518625770806@160.202.254.79:5060",
|
||||
"remote_media_address": "111.9.126.254",
|
||||
"remote_media_port": 14248,
|
||||
"codec": "PCMA/8000",
|
||||
"sip_result": "100 Trying, 180 Ringing, 183 Session Progress, 200 OK",
|
||||
"raw_log": ".local/agent-call-third/call-20260917-040214-raw.txt",
|
||||
"raw_log_sha256": "6ea3246b2a5a04f4914ea44f51ad3b0799ec72e7964f6b9d72d3aa3499d9b329"
|
||||
},
|
||||
"our_asterisk": {
|
||||
"container_network": "host",
|
||||
"external_signaling_address": "123.56.71.98",
|
||||
"external_media_address": "123.56.71.98",
|
||||
"direct_media": false,
|
||||
"rtp_port_range": "10000/10800",
|
||||
"local_sdp_example": "123.56.71.98:10542",
|
||||
"host_firewall_matching_rules": "none observed in nft query",
|
||||
"active_channels_after_test": 0
|
||||
},
|
||||
"security_group_before_fix": {
|
||||
"rtp_sources_present": [
|
||||
"61.132.228.221/32",
|
||||
"60.171.24.90/32",
|
||||
"160.202.254.79/32"
|
||||
],
|
||||
"missing_source": "111.9.126.254/32",
|
||||
"destination_port_range": "UDP 10000/10800"
|
||||
}
|
||||
},
|
||||
"root_cause_assessment": {
|
||||
"our_side": "confirmed configuration gap: the provider's advertised media IP was not allowed into the RTP port range",
|
||||
"provider_side": "not yet determinable: the provider may still be sending no RTP, using another source IP, or advertising an unreachable SDP address",
|
||||
"why_rtp_peer_missing": "Cell learns its RTP peer only from an inbound RTP packet; the pre-fix security group could drop packets from 111.9.126.254 before Asterisk/Cell received them"
|
||||
},
|
||||
"remediation": {
|
||||
"action": "AuthorizeSecurityGroup",
|
||||
"region": "cn-beijing",
|
||||
"security_group_id": "sg-2zed6d5vmcvojt7zek4r",
|
||||
"rule_id": "sgr-2zehipdrkv23gjpnun8n",
|
||||
"protocol": "UDP",
|
||||
"port_range": "10000/10800",
|
||||
"source_cidr": "111.9.126.254/32",
|
||||
"description": "agent-call third SIP SDP RTP media source",
|
||||
"request_id": "01A0ADBF-329B-570A-A36A-36C144EF4999",
|
||||
"status": "applied_and_verified"
|
||||
},
|
||||
"next_gate": {
|
||||
"required": "one separately authorized real outbound call after this rule change",
|
||||
"no_automatic_retry": true,
|
||||
"pass_condition": "RTP packets arrive, Cell learns a peer, at least one valid AI turn/recording is produced",
|
||||
"provider_fault_if": "post-fix capture still shows no RTP from the provider or the advertised SDP media address is unreachable",
|
||||
"our_fault_if": "post-fix packets reach the host/Asterisk but are not forwarded to externalMedia or Cell"
|
||||
}
|
||||
}
|
||||
@@ -123,6 +123,13 @@ python3 deploy/sip_probe.py --number 18601013734
|
||||
|
||||
脚本会自动构造 `7089` + 原始号码、开启 SIP logger、发起一次外呼并提取首个最终 SIP 响应;`--wait-seconds 3` 可缩短等待,`--output` 可指定证据文件。`Avail`/OPTIONS 只证明探活,不证明外呼接通。
|
||||
|
||||
### 4.1 SIP 与 RTP 白名单、端口范围
|
||||
|
||||
- SIP 信令地址与 SDP 中的媒体地址可能不同;安全组必须分别核对 `5060` 信令来源和 SDP `c=` 媒体来源,不能只放行 SIP 服务端 IP。第三组曾从 `160.202.254.79` 建立信令,但 SDP 宣告媒体源为 `111.9.126.254`,遗漏后会出现 SIP `200 OK` 但 `RTP_PEER_MISSING`。
|
||||
- `10000–10800` 不是 SIP/RTP 协议固定范围,而是本项目单 Cell 测试基线;远端 SDP 的 RTP 端口(例如 `14248`)是对端端口,不应直接替换我方本地端口范围。扩容必须按端口分配、RTCP、externalMedia、文件描述符和带宽实测后版本化调整。
|
||||
- 为排除测试期端口范围因素,可以在明确授权下仅对已登记的供应商信令/媒体 IP 临时放开 UDP 全端口;不得对 `0.0.0.0/0` 全 UDP 放行,规则与授权必须留证,测试结束后回收或收窄。
|
||||
- 当前第三组诊断例外为 `160.202.254.79/32` 和已观测媒体源 `111.9.126.254/32` 入站 UDP `1/65535`;这是用户授权的测试配置,不是生产安全基线。
|
||||
|
||||
## 5. 更新与回收
|
||||
|
||||
- 配置变更:生成新的 UserData/配置目录,核对后再重启容器;不要覆盖正在使用的配置目录。
|
||||
|
||||
@@ -224,6 +224,10 @@ python3 deploy/render_asterisk.py --config .local/asterisk.json
|
||||
|
||||
确认:SIP服务端IP/协议/端口、RTP回程、EIP/NAT、实际VPC网段、管理来源、录音卷目录和权限。安全组只按来源和用途开放;不公开裸ARI,不清空既有防火墙。
|
||||
|
||||
2026-09-17 第三组真实测试发现:SIP 信令来自 `160.202.254.79`,但 SDP 宣告 RTP 媒体源为 `111.9.126.254`;只允许信令 IP 会导致 SIP `200 OK` 但 Cell 收不到 RTP。当前测试例外已对这两个已登记 IP 放行 UDP `1/65535`,未对公网全量放行;完成 RTP/LLM 验收后应收窄为实际媒体端口及供应商媒体源 CIDR。`10000–10800` 只是单 Cell 测试基线,不是 SIP/RTP 协议固定范围,扩大范围前需同步 Asterisk `rtp.conf`、安全组、端口/FD/带宽容量测算。
|
||||
|
||||
真实 Cell 的 `externalMedia` 通道创建后,变量可能要等 `StasisStart` 才可查询;必须在通道就绪后重试读取 `UNICASTRTP_LOCAL_ADDRESS` 与 `UNICASTRTP_LOCAL_PORT`,以此建立向 Asterisk 的 RTP 发送目标。`addChannel` 在通道未进入 Stasis 时的 `400/404/409/422` 只能有限重试,成功后才能标记已入桥;收到目标 `Up` 后还必须通过 `GET /bridges/{id}` 确认 externalMedia 与目标都在同一桥内,不能只看 `Up` 就开始 AI。RTP 接收需校验来源 peer、PCMA payload type 和 SSRC。ARI originate 的 `timeout` 单位是秒,不是毫秒。实时录音必须先 `POST /recordings/live/{name}/stop`,再轮询读取 stored file,不能用 DELETE 丢弃录音后伪装成有效结果。
|
||||
|
||||
```bash
|
||||
# 默认为检查,不启动容器。
|
||||
bash deploy/asterisk.sh
|
||||
|
||||
Reference in New Issue
Block a user