跳到正文

故障排查与典型症状 ​

按服务分组的排查清单。每条包含“现象—优先确认—处理方式”,用于联调和生产应急。

callflow-esl / FreeSWITCH 链路 ​

  • 现象:callflow-esl 启动时报 config businessConfig...

    • 确认已删除旧顶层 ollamaChat / knowledgeBase,并把配置迁入对应稳定业务编码子项。
    • llm-chat-business 子项必填(endpoint 为 OpenAI 兼容后端的 /v1/chat/completions 完整地址); kb-audio-chat-business 可省略,但一旦存在就必须包含 有效 HTTP(S) endpoint、非空 strategy 与正整数 requestTimeoutMs。
    • 未注册业务编码通常是拼写错误;数据库 businessConfig / mappingConfig 不包业务编码。
  • 现象:通话记录 business_config_invalid 且未请求大模型 / 知识库

    • 按“文件业务子项 < call_businesses.businessConfig < 号码 mappingConfig”检查最终字段; 显式 business_code 路由没有号码映射层。
    • 知识库文件配置可缺省,但路由命中后若数据库层仍未补齐 endpoint / strategy / timeout, 会在业务 handler 执行前播放“系统错误”并结束。
  • 现象:5G 软电话能注册和接通,但页面提示“WebRTC 媒体连接失败”,双方无声

    • 检查 FreeSWITCH 的 200 OK SDP。若 c= 和本地 candidate 仍是 172.21.38.245,说明同机 Nginx 回送的 WebSocket 信令被 localnet.auto 误判为本地;WebRTC profile 应设置 local-network-acl=none,让 ext-rtp-ip 参与 SDP。
    • 若日志先保存了 srflx 和 relay,却出现 Choose rtp candidate ... srflx,说明 FreeSWITCH 1.11.2 按默认 wan.auto 优先选择了运营商 NAT 候选。配置只允许 coturn 公网 IP 的 softphone-turn-relay ACL,并在 Sofia profile 使用 apply-candidate-acl=softphone-turn-relay。
    • 修改 ACL 后除了 reloadxml 还必须执行 reloadacl;修改 Sofia profile 后重启对应 profile。正确的新通话应选择 123.57.205.60:31000–31040 relay,本地 SDP 应广告 123.57.205.60:30000–30199,DTLS 不应长期停在 HANDSHAKE。
  • 现象:WSS 握手返回 101,但 SIP 注册约 60 秒后 Request Timeout

    • 首先检查 Nginx 上游必须是 https://172.21.38.245:7443,不能是 http://172.21.38.245:5066。Nginx 将公网 WSS 降级为 WS 后,JsSIP 仍发送 Via: WSS,当前支持的 FreeSWITCH 1.11.2 会因实际传输不匹配而不返回 SIP 响应。
    • Nginx 到 FreeSWITCH 的实际源地址为 172.21.38.245。
    • internal profile 使用 apply-inbound-acl=domains,domains 必须允许 172.21.38.245/32。
    • 修改 ACL 后执行 reloadacl;修改 Nginx 后先执行 nginx -t 再 reload。无 Authorization 的 REGISTER 应返回 401 Unauthorized,而不是超时或直接注册成功。
  • 现象:外呼接通后未进入通话业务

    • 确认 freeswitch.callbackHost 与监听端口能被 FreeSWITCH 回连。
    • 确认拨号计划 socket 指向 callflow-esl 的 ESL 监听端口且 async full 生效。
    • 确认 call_businesses 已有业务并已通过 syncRegisteredCallBusinesses 同步。
  • 现象:uuid_audio_fork 一直未返回识别

    • 确认 ASR WebSocket 地址可达且 mod_audio_fork 使用 16kHz mono PCM16。
    • 检查 ASR 健康:GET /health,并抓一次原始链路(metadata + 二进制帧)。
    • 回退 audioFork 参数:sampleRate / mixType 是否与输入一致。
  • 现象:业务常见命令报错/挂断后报错

    • 对端挂断下部分 execute 命令会被拒绝,属于正常,runtime 会做容错。
    • 查看是否是配置文件变更导致通道变量/主叫变量缺失,或调用顺序错误。
  • 现象:WebRTC 音视频通话接通数秒后异常中断,或通话稳定但黑屏无画面

    • 秒级/十余秒中断:确认拨号计划未误配毫秒级 media_timeout=300;本机开发优先使用 127.0.0.1 宿主机回环,跨网设备使用标准公网 ICE/TURN。
    • 黑屏无画面:检查前端是否在 ontrack / onunmute 中重复创建 MediaStream 导致底层解码流水线反复重置;确认大屏 <video> 是否显式添加 muted 属性以彻底绕过浏览器的 Autoplay 自动播放限制。
    • 详见专题:wsl-webrtc-lan-solutions.md。

Sherpa ASR 服务(sherpa-asr-server,纯 Rust 统一服务端) ​

  • 现象:GET /health 200,但 WebSocket 无法识别

    • 优先确认 server.wsPath 与 maxSessions,是否命中连接数上限。
    • 验证客户端首帧元数据 JSON 是否可解析(uuid / channel_uuid 约定字段)。
    • 按 concurrency 调参:提高 workers/sessionPoolSize 需要配合 CPU/负载评估。
  • 现象:音频内容明显识别错乱

    • 确认音频是 16kHz 单声道 PCM16,ASR 不做自动重采样。
    • 检查是否误发送了分片帧(服务不支持分片)。
    • 开启录音排查:临时打开 recording.enabled=true 校验 Wav 输入。
  • 现象:并发稍大即出现 503

    • maxSessions 是会话上限,先观察 GET /health 的 activeSessions/maxSessions。
    • 处理时先调 workers/sessionPoolSize 与 maxSessions,再评估上游调度并发。

FunASR 服务(funasr-asr-server,纯 Rust 统一服务端) ​

  • 现象:在线流式识别 partial 阶段热词未生效

    • 确认底层原理:FunASR 1-pass 纯流式模型不支持热词;热词是在 2-pass 模式(enable2pass: true)下的句尾离线大模型二次重打分阶段修正生效。
    • 检查客户端首帧或握手 URL 中下发的热词格式是否为以空格分隔的词列表。
  • 现象:离线识别上传 MP3 / M4A 报 Audio decode failed

    • 检查 config.toml 中的 ffmpeg.enabled 是否为 true,以及 ffmpeg.ffmpegPath 是否可在系统 PATH 找到或指向了有效绝对路径。
    • 若 ffmpeg.enabled: false(禁用 ffmpeg),服务仅支持标准 16kHz WAV 音频文件。
  • 现象:服务启动时提示模型目录找不到

    • 检查 config.toml 中配置的 modelDir、onlineModelDir、vadDir、puncDir 是否存在于 onnx-platform/models/ 目录下。

Sherpa TTS 服务(sherpa-tts-server,纯 Rust 统一服务端) ​

  • 现象:流式播报在 mod_shout 上失败

    • 确认 FreeSWITCH 已加载 mod_shout。
    • 确认 mp3EncoderPath 可执行,或 ffmpeg 在 PATH 中可见。
    • 检查 listenHost 与返回 publicBaseUrl 是否一致(调用方是否能回源访问)。
  • 现象:请求偶发 503 / 超时

    • maxQueuedRequests 过小会导致排队拒绝,先看调用方重试策略与峰值并发。
    • 查日志里的 stream busy / request timed out,结合调用并发和上游连接稳定性分析。
  • 现象:文件增长过快

    • TTS 内建 LRU 缓存,但未做物理落盘磁盘容量硬截断;需要外部定期清理 public/wav/。
    • 长期高并发场景优先复用文本(命中缓存)或配置额外归档策略。

情绪分析服务(emotion-analysis-server,纯 Rust 统一服务端) ​

  • 现象:POST /analyze 成功率低

    • 确认 recordingUrl 可访问(只支持 http 与支持的 WAV 采样格式)。
  • 现象:返回统一情绪分数异常

    • 记录 reason / available 字段,判断文本/音频哪一侧缺失。
    • 确认通话录音声道映射是否正确(audio.channelRoles)。
  • 现象:前端情绪复核音频无法播放

    • 确认情绪分析服务返回的是短文件名或短路径,callout 前端会通过 media.emotionAudioBaseUrl 拼完整 URL。
    • 检查 emotion-analysis-server 的 audio.processedDir 是否可写,并确认该目录与 Nginx location /emotion-audio/ 的 alias 指向同一物理目录。
    • callout-server 的 media.emotionAudioBaseUrl 只负责把结果中的短文件名拼成浏览器可访问 URL; 它应指向上述 Nginx 路径,而不是情绪服务的内部监听地址。浏览器直接访问最终 URL,确认静态 文件权限、反代与 MIME 类型可用。

callout-server AI 策略中台(NLU / Chat / Script) ​

  • 现象:GET /api/ai/nlu/capabilities 返回 401

    • 确认 server.serviceApi.authToken 已配置(通过环境变量时使用 CALLOUT_SERVICE_API_AUTH_TOKEN)。
    • 请求头必须携带有效的 X-Callout-Internal-Token、X-Callai-Internal-Token 或 Authorization: Bearer <token>。
  • 现象:NLU 请求超时或返回 fallback 结果

    • 确认 OpenAI 兼容推理后端可访问。地址有两个来源:config.toml 的 llm.openaiUrl, 或数据库 callout.ai_llm_configs 中启用的记录(GET /api/ai/llm-config 可读取当前生效值)。
    • 确认模型名配置正确(默认 NLU / Chat qwen3.5-2b、Script qwen3.6-27b-fp8)。
    • 根据模型耗时调大 llm.nlu.timeoutMs 或 llm.chat.timeoutMs。
    • 可在 callout-webpage 的「AI 策略中心」三栏话术策略工作台(/ai/strategies)或「后端模型」页面(/ai/models)进行连通性探活 (对应 POST /api/ai/llm-config/test 与 GET /api/ai/models)。
  • 现象:流程节点意图判断或字段抽取结果不符合预期

    • 现行架构业务 Prompt 全面内联于节点中。在 callout-webpage 的「话术策略工作台」(/ai/strategies)中选中目标节点,检查内联系统提示词、意图分支描述或抽取的字段清单。
    • 在工作台右栏发起即时拨测,检查大模型思考链(Thinking Process)或原始输出(Raw Response),确认是否因模型输出截断或 JSON 格式错误触发了默认兜底。
    • 请求体里的 action 必须是当前支持动作,例如 decision 或 extract。

callout-server / 外呼系统 ​

  • 现象:结果回写、进度上报、转人工或运行时脚本接口返回 401
    • 确认请求头 X-Callout-Internal-Token 与 callout-server 的 server.serviceApi.authToken 一致;通过环境变量配置时使用 CALLOUT_SERVICE_API_AUTH_TOKEN。

流程脚本 Chat 节点 ​

  • 现象:对话节点保存或执行异常

    • 现行架构对话节点 Prompt 与微调参数直接内联于节点本身。若配置了自定义策略标识,确保算法策略命名合法且符合规范。
    • 检查 callflow-esl 的 callout.baseUrl / callout.token(默认指向 9920)配置是否正确且网络互通。
  • 现象:通话进入 fallback 出口

    • 检查是服务故障还是连续静默:可结合 routeTrace[].chat.reason 与 variables 中的 reason 排查。
    • 若为连续静默:首次静默由大模型上下文动态生成催问话术播报,第二次静默(默认 5 秒)退出至 fallback,属于预期行为。
    • 若为服务异常:检查 callflow-esl 的 callout.baseUrl / callout.token、大模型服务可达性及 SSE 解析状态,系统已自动播放兜底语并走 fallback 出口。
  • 现象:活动不派发或卡住

    • 检查活动状态必须是 running、有可调度联系人且线路已启用。
    • 检查策略(尤其工作时段/号码阻断)是否返回拦截原因。
    • 查看锁竞争日志:是否存在多实例并发调度冲突。
  • 现象:坐席转人工失败/会议会话不收敛

    • 先确认 /api/transfers/route 返回已预占坐席并成功 originate。
    • 检查坐席号码与拨号模板、坐席状态、originateDialStringTemplate。
  • 现象:呼叫明细里有 recordingUrl,但前端不能播放录音

    • 当前 call_attempts.recordingUrl 与 API 响应保存短文件名,例如 call-123.wav, 不再保存完整公网 URL。
    • 检查 GET /api/media/config 返回的 freeswitchRecordingBaseUrl 是否是浏览器可访问地址。
    • 用浏览器直接访问 <freeswitchRecordingBaseUrl>/<encodeURIComponent(recordingUrl)>。
    • 若数据库里仍有完整 URL 或 Windows 路径,新的 /api/call-results 会归一为最后一级文件名; 历史数据需要按同一规则迁移或兼容展示。
  • 现象:情绪分析 worker 找不到录音

    • worker 会用 media.freeswitchRecordingBaseUrl + recordingUrl 短文件名拼完整 URL。
    • 确认 FreeSWITCH / callflow 侧已经真实落盘录音并通过静态服务暴露。
    • recordingUrl 不应写成本机绝对路径或带 token 的临时 URL。
  • 现象:导入或结果回写字段不清楚

    • 先看 apps/callout-server/docs/result-schemas.md,其中记录 /api/call-results 定位规则、字段明细、payload.result、transcript 与系统级级联副作用(DNC/情绪分析/状态联动/重试与任务闭环)。

使用部署配置时 ​

  • 现象:照搬 deploy/ 配置后服务启动失败

    • deploy/aliyun/、deploy/wsl-docker/ 中的配置面向当前这套双主机拓扑,不是通用一体化部署包; 先核对服务工作目录、模型、日志、录音和媒体目录等相对路径是否能在目标主机解析。
    • 检查样例端口与本地开发默认端口的差异(WSL 集群统一使用 2xxxx 段),并确认所有调用方使用相同的最终地址。
    • 确认 PostgreSQL、Redis、OpenAI 兼容推理后端、FreeSWITCH、ffmpeg 和动态库等外部依赖已由部署方安装并启动。
  • 现象:ONNX 服务启动失败或立即退出

    • 确认对应 dist/<platform>/ 交付目录(如 dist/win_x64/ 或 dist/linux_x64/)产物、模型目录和 ONNX Runtime / sherpa-onnx 动态库完整。
    • Linux 上检查构建环境与目标主机的 glibc / C++ 运行库兼容性,并从服务实际工作目录启动一次 以发现相对路径错误。
  • 现象:部署 FreeSWITCH 配置后原有线路或分机异常

    • deploy/freeswitch/ 的容器化基线包与 deploy/aliyun/freeswitch/ 现场快照都不应无差别覆盖 现场 conf_dir;从备份恢复后逐项合并 dialplan、directory、SIP profile、ESL、模块和 TLS 配置。
    • 检查 event_socket.conf.xml 的密码与 ACL、outbound socket 地址、外网 SIP/RTP 地址和证书。
  • 现象:部署后前端的 API 请求返回 404、HTML 或错误后端的数据

    • API 根地址由 VITE_CALLFLOW_API_BASE_URL / VITE_CALLOUT_API_BASE_URL 在构建期注入, 未设置或为空时默认为同域 /api;部署后不存在运行时配置文件。
    • 确认 callflow 前端站点的 /api upstream 是 callflow-server(默认 127.0.0.1:9913), callout 前端站点的 /api upstream 是 callout-server(默认 127.0.0.1:9920)。
    • 使用自定义 API 根地址时,确认构建变量指向浏览器可访问的地址,并在修改后重新构建前端。
    • 使用默认值时确认代理保留 /api 路径;若多个前端共用同一 origin,需要拆分为可独立配置 upstream 的站点或 origin。

网关服务(阿里云 / Azure) ​

  • 现象:TTS/ASR 接口超时或 5xx
    • 确认凭据注入(环境变量优先,避免配置文件硬编码)。
    • 检查网络出口与对端 endpoint 可达性。
    • 比较本地日志中的 tts.stream / asr activeSessions 与调用量趋势。

统一排障步骤(建议) ​

  1. 先确认健康接口:ASR/TTS/Emotion 是否可用。
  2. 再确认网络链路(callbackHost/listenHost/publicBaseUrl/端口映射)。
  3. 再确认协议输入(音频格式、WebSocket 订阅路径、JSON 字段)。
  4. 最后确认业务流(拨号计划、业务路由、回铃与回写)。

证据留存 ​

  • 保留:服务日志、健康检测日志、失败请求参数、故障时间点、并发快照。
  • 链路类问题建议附:
    • 配置片段(脱敏)
    • 关键 cURL/请求样例
    • 时间轴(起止时间、重试次数、错误码分布)

文档与代码在同一仓库维护,现有 Markdown 是唯一内容源。