fix real cell RTP bridge readiness

This commit is contained in:
2026-09-17 14:29:37 +08:00
parent b6d0af1a56
commit 668c12e523
10 changed files with 781 additions and 51 deletions
@@ -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/配置目录,核对后再重启容器;不要覆盖正在使用的配置目录。
+4
View File
@@ -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