diff --git a/AGENTS.md b/AGENTS.md index e02c456..a272390 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -44,7 +44,7 @@ - 主叫标识保留原值(包括 `BD`),不能按纯数字手机号清洗,也不能直接当成 Digest 认证用户名;具体 From/PAI 等字段映射仍需确认。 - 业务原始被叫号码保持不变;使用该线路时按其规则构造 `7089<被叫号码>`,避免重复添加或把该前缀带到其他供应商线路。 -- 传输协议、IP/Digest 鉴权、是否注册、编解码及并发限制仍需供应商确认;当前生成器的 UDP/ulaw 基线不代表这些参数已经确认。 +- 传输协议、IP/Digest 鉴权、是否注册及并发限制仍需供应商确认;当前供应商已反馈需使用 PCMA,Asterisk 配置以 `allow=alaw` 表示,仍需真实线路验证。 - 目前只提供一组服务端地址,未提供独立备用地址。不能把同一地址重复填写成主备并宣称具备容灾;现有生成器要求 primary/backup,单线路启动支持尚待调整。 ## 多 SIP 供应商方案(用户已确认,运行代码待实现) diff --git a/deploy/render_asterisk.py b/deploy/render_asterisk.py index 300c23a..53da0d2 100755 --- a/deploy/render_asterisk.py +++ b/deploy/render_asterisk.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Render an explicit, outbound-only UDP baseline. Does not start services.""" +"""Render an explicit, outbound-only UDP/PCMA baseline. Does not start services.""" import argparse import ipaddress @@ -48,7 +48,7 @@ def endpoint(name, data, env): registration = data.get("register", False) if not isinstance(registration, bool) or registration and mode != "digest": raise ValueError("registration requires explicit digest authentication") - text = f"[{name}]\ntype=endpoint\ntransport=transport-udp\ncontext=deny-inbound\ndisallow=all\nallow=ulaw\ndirect_media=no\nrtp_symmetric=yes\nforce_rport=yes\nrewrite_contact=yes\naors={name}-aor\n" + text = f"[{name}]\ntype=endpoint\ntransport=transport-udp\ncontext=deny-inbound\ndisallow=all\nallow=alaw\ndirect_media=no\nrtp_symmetric=yes\nforce_rport=yes\nrewrite_contact=yes\naors={name}-aor\n" from_user = data.get("from_user") if from_user is not None: if not isinstance(from_user, str) or not re.fullmatch( diff --git a/docs/evidence/asterisk-persistent-2026-09-14.md b/docs/evidence/asterisk-persistent-2026-09-14.md new file mode 100644 index 0000000..f61857a --- /dev/null +++ b/docs/evidence/asterisk-persistent-2026-09-14.md @@ -0,0 +1,46 @@ +# 持久竞价 ECS / Asterisk 真实外呼记录(2026-09-14) + +## 环境 + +- 区域:阿里云北京 `cn-beijing` +- ECS:`i-2ze8fa5vhyl4cc8oeol9` +- 规格:`ecs.e-c1m1.large`,Ubuntu 24.04,40 GiB +- 计费:`SpotAsPriceGo` 自动竞价;本次测试后保留实例,不执行停止、删除或释放/解绑固定 EIP +- 固定出口:`123.56.71.98` +- Asterisk:22.10.1,固定 digest 镜像;容器 `agent-call-asterisk` 最终为 `running/healthy` +- PJSIP:`provider-primary` contact `Avail`,测试前 RTT 约 24.522 ms;UDP/ulaw +- 主叫:原值 `BD93205882` +- 被叫规则:原始号码保持不变,实际目标为 `7089` + 原始号码 + +## 外呼一 + +- 时间:2026-09-14 02:20:35 UTC +- 原始被叫:`18601013734` +- 实际目标:`708918601013734` +- 发起结果:Asterisk originate 返回码 `0`;仅发起一次 +- SIP:从 `123.56.71.98:5060` 发出 INVITE,收到 `100 trying`,随后收到 `488 Not Acceptable Here` +- 原因:`Reason: Q.850;cause=88;text="INCOMPATIBLE_DESTINATION"` +- 未收到 `180/183/200`,未接通,未观察到 RTP/录音;发送 ACK 后挂断 + +## 外呼二 + +- 时间:2026-09-14 02:21:31 UTC +- 原始被叫:`18625770806` +- 实际目标:`708918625770806` +- 发起结果:Asterisk originate 返回码 `0`;仅发起一次 +- SIP:从 `123.56.71.98:5060` 发出 INVITE,收到 `100 trying`,随后收到 `488 Not Acceptable Here` +- 原因:`Reason: Q.850;cause=88;text="INCOMPATIBLE_DESTINATION"` +- 未收到 `180/183/200`,未接通,未观察到 RTP/录音;发送 ACK 后挂断 + +## 收尾核验 + +- Asterisk:`2 calls processed`,`0 active channels`,容器 `running/healthy`,重启次数 `0` +- 两次均无自动重试、无 FALLBACK;未修改线路配置 +- 结论:固定出口、SIP OPTIONS/contact 和真实 INVITE 链路可达;供应商对两个目标均返回 `488/Q.850 cause=88`,外呼接通验收未通过。需供应商确认 `7089` 路由、From/主叫格式或提供已知可达测试号码后再复测。 + +## 证据索引 + +- 部署状态:`.local/agent-call-persistent/host-state.json`(本地受控状态文件) +- Cloud Assistant 外呼一:`t-bj06x005ogmre2o` +- Cloud Assistant 外呼二:`t-bj06x008o03do1s` +- 外呼原始结果保存在本地受控目录 `.local/agent-call-persistent/`,未将凭据写入证据或日志。 diff --git a/docs/evidence/asterisk-persistent-pcma-2026-09-14.md b/docs/evidence/asterisk-persistent-pcma-2026-09-14.md new file mode 100644 index 0000000..206fb3b --- /dev/null +++ b/docs/evidence/asterisk-persistent-pcma-2026-09-14.md @@ -0,0 +1,49 @@ +# 持久竞价 ECS / Asterisk PCMA 真实外呼复测(2026-09-14) + +## 变更与环境 + +- 区域:阿里云北京 `cn-beijing` +- ECS:`i-2ze8fa5vhyl4cc8oeol9` +- 计费:`SpotAsPriceGo` 自动竞价;按用户要求继续保留实例 +- 固定出口:`123.56.71.98` +- Asterisk:22.10.1;容器重启后 `running/healthy` +- 变更:Asterisk 生成器及当前实例 PJSIP endpoint 从 `allow=ulaw` 切换为 `allow=alaw`(PCMA) +- 复测前:`provider-primary` 为 `alaw`,无活动通话;PJSIP logger 仅在呼叫期间开启,结束后关闭 + +## 外呼一 + +- 时间:2026-09-14 02:29:44 UTC +- 原始被叫:`18601013734` +- 实际目标:`708918601013734` +- 发起:Asterisk originate 返回码 `0`;仅发起一次 +- SDP:`m=audio ... RTP/AVP 8 101`,`a=rtpmap:8 PCMA/8000`,确认实际提供 PCMA +- SIP:收到 `100 trying`,随后收到 `480 Temporarily Unavailable` +- 原因:`Reason: Q.850;cause=16;text="NORMAL_CLEARING"` +- 未收到该呼叫对话的 `180/183/200`,未接通,未观察到 RTP/录音 + +## 外呼二 + +- 时间:2026-09-14 02:31:12 UTC +- 原始被叫:`18625770806` +- 实际目标:`708918625770806` +- 发起:Asterisk originate 返回码 `0`;仅发起一次 +- SDP:`m=audio ... RTP/AVP 8 101`,`a=rtpmap:8 PCMA/8000`,确认实际提供 PCMA +- SIP:收到 `100 trying`,随后收到 `480 Temporarily Unavailable` +- 原因:`Reason: Q.850;cause=16;text="NORMAL_CLEARING"` +- 未收到该呼叫对话的 `180/183/200`,未接通,未观察到 RTP/录音 + +## 结论与收尾核验 + +- 两次 PCMA 复测均未再出现此前的 `488/Q.850 cause=88 INCOMPATIBLE_DESTINATION`,说明编码切换已生效并越过原编码不兼容错误。 +- 供应商当前返回 `480 Temporarily Unavailable / Q.850 cause=16`;这次仍未接通,需供应商继续确认目标路由、号码可达性及 `480/cause=16` 的具体含义。 +- 无自动重试、无 FALLBACK;结束后 `0 active channels`,容器 `running/healthy`,PJSIP logger 已关闭;ECS 未停止、未删除,固定 EIP 未解绑。 + +## 证据索引 + +- 生成器:`deploy/render_asterisk.py`(当前输出 `allow=alaw`) +- 旧编码结果:`docs/evidence/asterisk-persistent-2026-09-14.md` +- PCMA 切换:Cloud Assistant invocation `t-bj06x00w8y9203k` +- PCMA 外呼一:Cloud Assistant invocation `t-bj06x00z0mmonb4` +- PCMA 外呼二:Cloud Assistant invocation `t-bj06x013qaxvxts` +- 收尾核验:Cloud Assistant invocation `t-bj06x017yu16874` +- 原始结果保存在本地受控目录 `.local/agent-call-persistent/`,未将凭据写入证据或日志。 diff --git a/docs/最终开发部署监控与验收计划_v1.0.md b/docs/最终开发部署监控与验收计划_v1.0.md index 6e8da99..ae5f0c8 100644 --- a/docs/最终开发部署监控与验收计划_v1.0.md +++ b/docs/最终开发部署监控与验收计划_v1.0.md @@ -300,6 +300,8 @@ bash -n deploy/asterisk.sh 下一实施入口是整改计划F0→F1~F4,对D01~D11补齐模式保护、租户隔离、可靠性、协议媒体与验收证据;真实SIP排障及D12外部协调并行,不以线路不通阻塞本地整改。真实替换仍须取得SaaS、SIP/Asterisk、ASR/LLM/TTS、OSS、出口、容量和运维证据;无需重新讨论已接受方案。具体供应商差异单独登记,不让Mock假成功掩盖缺口。 +**新增待办(SaaS 对接前,归入 D04/D12):SIP 编解码配置化。** 不得把 `ulaw`/`alaw` 写死在全局生成器或逐呼配置中;在供应商/Trunk 或受控 `codec_profile` 中维护允许编码及优先顺序,对外使用 `PCMA`/`PCMU` 等规范名称,Asterisk 渲染为 `alaw`/`ulaw`。SaaS 呼叫消息只可选择已授权的 `trunk_id`/`codec_profile_id`,不能任意注入 codec;调度时校验 Trunk 与 Cell/出口能力交集,无交集即拒绝发起,不静默回退、不因编码失败自动重拨,并记录实际 SDP 协商编码。实现前同步补充配置校验、OpenAPI/MQ 字段(如确需暴露)、迁移/发布流程及回归验收。 + ### 10.1 历史执行记录(2026-09-12~13,非当前完整验收结论) 以下保留历史探针及当时环境记录;其中V01~V13、规模与环境保持运行的表述仅描述当次结果,不表示当前资源仍存在。完整门禁状态以第10节纠偏和整改复验为准,当前云资源状态须另行只读核验,不能据本记录自动复用、创建或拨号。 @@ -312,4 +314,6 @@ R1 已在历史持久测试环境完成两次真实 Asterisk/SIP 单路探针: 补充当前执行(2026-09-13):按明确授权重新创建并绑定 ECS `i-2ze4hvi6xvy02g0wz9kk`,探针期间固定 EIP 为 `InUse`;Asterisk `22.10.1` 容器为 `running/healthy`,固定镜像 digest、PJSIP contact `Avail`(RTT约22.164ms)及安全组范围已只读核验。对原始被叫 `18601013734` 单次探针构造 `708918601013734`,收到 `100 trying` 后 `488/Q.850 cause=88 INCOMPATIBLE_DESTINATION`,无 `180/183/200`、RTP 或录音证据;无重试、无 FALLBACK。探针后按用户授权停止并删除该 ECS,系统盘随实例删除,固定 EIP 未释放且已恢复 `Available`;详见 `docs/evidence/asterisk-persistent-2026-09-13.md`。 +补充当前执行(2026-09-14):按用户明确授权,保留竞价 ECS `i-2ze8fa5vhyl4cc8oeol9`(`SpotAsPriceGo`、固定 EIP `123.56.71.98`),先以 ulaw 对 `18601013734`、`18625770806` 各单次外呼,均收到 `100 trying` 后 `488/Q.850 cause=88 INCOMPATIBLE_DESTINATION`。根据供应商反馈将生成器及实例切换为 PCMA(Asterisk `allow=alaw`)后再次各呼出一次:两次 SDP 均出现 `a=rtpmap:8 PCMA/8000`,均收到 `100 trying` 后 `480/Q.850 cause=16 NORMAL_CLEARING`,未出现 `180/183/200`、RTP 或录音证据;无重试、无 FALLBACK。详见 `docs/evidence/asterisk-persistent-2026-09-14.md` 与 `docs/evidence/asterisk-persistent-pcma-2026-09-14.md`。实例当前保持 `Running`,固定 EIP 为 `InUse`。 + 真实接通/RTP、ASR/LLM/TTS/OSS、生产 1000 路完整 AI、N+1、实际CPS/SLO及副作用正确性仍阻塞,现有产物不得据此宣称生产完成。 diff --git a/docs/部署接入_运行说明.md b/docs/部署接入_运行说明.md index 0dd2adc..ecd2ad6 100644 --- a/docs/部署接入_运行说明.md +++ b/docs/部署接入_运行说明.md @@ -12,8 +12,8 @@ | ASR 连接与协议 | 已加固:断开清理、取消、写入/启动时限、最终结果背压、火山帧长度及gzip解压上限 | | 部署底座 | 已实现:非root/read-only ASR容器、回环端口、Asterisk配置生成和显式启动检查 | | 阿里云主机准备 | 已实现CLI驱动的只读计划、受控创建竞价实例、复用主机和绑定既有EIP;默认不修改云资源 | -| 云端实际操作 | 已完成V13一次性专用竞价Cell创建、绑定固定EIP、SCALE-MOCK及回收;另保留一台SpotAsPriceGo真实SIP测试环境 | -| Asterisk真实接入 | 已完成R1底座与两号码SIP探针;OPTIONS/contact可达,但两个真实INVITE均被供应商以488/Q.850 cause=88拒绝,未通过外呼验收 | +| 云端实际操作 | 已完成V13一次性专用竞价Cell创建、绑定固定EIP、SCALE-MOCK及回收;当前保留一台SpotAsPriceGo真实SIP测试环境 `i-2ze8fa5vhyl4cc8oeol9` | +| Asterisk真实接入 | 已完成当前环境R1底座与两号码SIP探针;初始ulaw收到488/Q.850 cause=88,按供应商反馈切换PCMA后两次均收到480/Q.850 cause=16,仍未接通 | | LLM/TTS | 未实现、未启用,等待用户新的供应商/协议/参数规范 | | agent-call Mock 调度、MQ/OSS闭环 | 已实现:租户独立路由、DB状态、CAS控制、outbox/inbox、Mock SIP/ARI/RTP/AI和OSS校验;未替代真实Asterisk/供应商验收 | | ARI业务调度、自动FALLBACK、真实MQ/OSS回传 | 未完成;现有主备trunk配置不能当成自动切换代码 | @@ -151,7 +151,7 @@ if [ ! -e .local/asterisk.json ]; then fi ``` -填写实际`local_net`、主/备SIP服务器、端口、IP或Digest鉴权方式、用户名及是否注册。当前生成器只提供经过明确限制的UDP/ulaw外呼基线,TCP/TLS及特殊号码/主叫策略需要另行确认。 +填写实际`local_net`、主/备SIP服务器、端口、IP或Digest鉴权方式、用户名及是否注册。当前生成器按供应商反馈提供经过明确限制的UDP/PCMA外呼基线(Asterisk `allow=alaw`),TCP/TLS及特殊号码/主叫策略需要另行确认。 通过受控进程环境注入: @@ -238,14 +238,14 @@ python3 deploy/migrate.py data/agent-call.sqlite3 ### 7.3 当前门禁 -本地V01-V12及阿里云专用主机上的V13 Mock探针已通过;最新完整验收结果见`docs/evidence/acceptance-mock-2026-09-13.json`,脚本仍因V13缺持续媒体独立证据和V14需要外部真实证据而按门禁返回非零。V13使用100租户/1200模拟接通、禁用1个Cell;这是Mock协议/持久状态负载证据,不是RTP或生产容量证据。R1历史探针见`docs/evidence/asterisk-real-2026-09-12.md`,当前单次探针见`docs/evidence/asterisk-persistent-2026-09-13.md`:ECS `i-2ze4hvi6xvy02g0wz9kk`、Asterisk容器和固定EIP `123.56.71.98` 已核验,PJSIP contact/OPTIONS可达,但 `18601013734` 对应目标收到 `488/Q.850 cause=88`,未接通、无RTP/录音证据。探针后该 ECS 已按授权停止并删除,固定 EIP 保持 Available。真实接通/RTP、ASR/LLM/TTS/OSS、生产1000路/N+1尚未完成。正式模式拒绝当前Mock profile,不能把测试号码或Mock成功自动升级为真实外呼授权。 +本地V01-V12及阿里云专用主机上的V13 Mock探针已通过;最新完整验收结果见`docs/evidence/acceptance-mock-2026-09-13.json`,脚本仍因V13缺持续媒体独立证据和V14需要外部真实证据而按门禁返回非零。V13使用100租户/1200模拟接通、禁用1个Cell;这是Mock协议/持久状态负载证据,不是RTP或生产容量证据。R1历史探针见`docs/evidence/asterisk-real-2026-09-12.md`;ulaw初始探针见`docs/evidence/asterisk-persistent-2026-09-14.md`,PCMA复测见`docs/evidence/asterisk-persistent-pcma-2026-09-14.md`:ECS `i-2ze8fa5vhyl4cc8oeol9`、Asterisk容器和固定EIP `123.56.71.98` 已核验,PCMA实际SDP为 `PCMA/8000`,两个目标均由原先的 `488/Q.850 cause=88` 变为 `480/Q.850 cause=16`,仍未接通、无RTP/录音证据。该 ECS 按用户要求继续保留并运行,固定 EIP 为 `InUse`。真实接通/RTP、ASR/LLM/TTS/OSS、生产1000路/N+1尚未完成。正式模式拒绝当前Mock profile,不能把测试号码或Mock成功自动升级为真实外呼授权。 ## 8. 下一步需要用户提供/确认 -1. 供应商需确认`7089`前缀/目标号码路由、From域(当前实际出现私网域)及允许的编解码;两个持久环境探针均返回`488/Q.850 cause=88 INCOMPATIBLE_DESTINATION`,未通过外呼验收。 -2. 供应商调整并确认线路规则后,再用只读plan和新授权创建测试实例复测;本次实例已回收,固定EIP保持 Available,不自动重试、不改绑或释放固定EIP。 +1. 供应商需继续确认`7089`前缀/目标号码路由、From域(当前实际出现私网域)及 `480/Q.850 cause=16` 的具体含义;PCMA 已在 SDP 中实际生效,但两个号码仍未接通。 +2. 供应商调整并确认线路规则后,再用当前保留环境复测;未获清理指令前不停止/删除实例、不创建第二台,不自动重试、不改绑或释放固定EIP。 3. ASR测试凭据、批准的模型/资源ID;Web HTTPS域名/证书或SSH访问方案。 4. 用户制定的MQ/OSS ID接口规范,以及新的LLM/TTS协议、参数与取消/打断规则。 5. 真实接通后仍需分别完成RTP、录音、MQ/OSS、ASR/LLM/TTS和生产1000路/N+1验收。 -在供应商规则确认前,不再次拨打真实号码,也不声称完成整个平台;本次测试实例已回收,固定EIP按要求保留。 +PCMA 复测已完成;在供应商进一步确认 `480/cause=16` 对应的路由/号码可达性前,不自动再次拨打真实号码,也不声称完成整个平台。当前竞价测试实例按用户要求保留,后续清理需单独明确授权。