跳到正文

故障排查与典型症状

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

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。
    • 若日志先保存了 srflxrelay,却出现 Choose rtp candidate ... srflx,说明 FreeSWITCH 1.10 按默认 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.10.12 会因实际传输不匹配而不返回 SIP 响应。
    • Nginx 到 FreeSWITCH 的实际源地址为 172.21.38.245
    • internal profile 使用 apply-inbound-acl=domainsdomains 必须允许 172.21.38.245/32
    • 修改 ACL 后执行 reloadacl;修改 Nginx 后先执行 nginx -t 再 reload。无 Authorization 的 REGISTER 应返回 401 Unauthorized,而不是超时或直接注册成功。
  • 现象:外呼接通后未进入通话业务

    • 确认 freeswitch.callbackHost 与监听端口能被 FreeSWITCH 回连。
    • 确认拨号计划 socket 指向 callflow-eslESL 监听端口且 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 会做容错。
    • 查看是否是配置文件变更导致通道变量/主叫变量缺失,或调用顺序错误。

C++ ASR 服务(sherpa-asr-online-server)

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

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

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

    • maxSessions 是会话上限,先观察 GET /healthactiveSessions/maxSessions
    • 处理时先调 ioWorkers/sessionPoolSizemaxSessions,再评估上游调度并发。

C++ FunASR 服务(fanasr-asr-online-server 与 fanasr-asr-offline-server)

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

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

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

    • 检查 config.json 中配置的 modelDironlineModelDirvadDirpuncDir 是否存在于 onnx-platform/models/ 目录下。

C++ TTS 服务(sherpa-tts-server)

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

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

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

    • TTS 无内建 TTL 清理;需要外部清理 public/wav/
    • 长期高并发场景优先复用文本(命中缓存)或配置额外归档策略。

情绪分析服务(emotion-analysis-server)

  • 现象:POST /analyze 成功率低

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

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

    • 确认情绪分析服务返回的是短文件名或短路径,callout 前端会通过 media.emotionAudioBaseUrl 拼完整 URL。
    • 检查 emotion-analysis-serveraudio.processedDir 是否可写、 audio.processedPublicBaseUrl 是否与 callout media.emotionAudioBaseUrl 对齐。
    • 浏览器访问最终 URL,确认静态服务、反代和 MIME 类型可用。

callai-server

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

    • 确认 server.authTokenCALLAI_AUTH_TOKEN 是否配置。
    • 请求头必须携带有效的 X-Callai-Internal-TokenX-Callout-Internal-TokenAuthorization: Bearer <token>
  • 现象:NLU 请求超时或返回 fallback 结果

    • 确认 OpenAI 兼容推理后端可访问。地址有两个来源:config.jsonllm.openaiUrl, 或数据库 callai.ai_llm_configs 中启用的记录(GET /api/v1/ai/llm-config 可读取当前生效值)。
    • 确认模型名配置正确(默认 NLU / Chat qwen3.5-2b、Script qwen3.6-27b-fp8)。
    • 根据模型耗时调大 llm.nlu.timeoutMsllm.chat.timeoutMs
    • 可在 callai-webpage 的 Playground 或「后端模型」页面进行连通性探活 (对应 POST /api/v1/ai/llm-config/testGET /api/v1/models)。
  • 现象:业务 profile 不生效

    • callai-webpage 的「NLU 策略管理」中检查策略是否启用,或访问 /api/v1/nlu/capabilities 确认存在。
    • 请求体里的 action 只能是当前支持动作,例如 decisionextract

callout-server / 外呼系统

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

callai-server / chat 节点

  • 现象:对话节点保存时提示 profile 未注册

    • callai-webpage 确认 chat_profile 已创建并启用。
    • 检查 callout-server 的 callai.baseUrl / callai.token
  • 现象:通话进入 error 出口

    • 检查 callflow-esl 的 callout.chat.baseUrl / callout.chat.token、大模型服务可达性和 SSE complete 事件。
    • 非法 exitKey、SSE 解析失败、LLM 超时和 TTS 播放失败都会走 error,可结合 routeTrace[].chat.reason 排查。
  • 现象:连续无声后进入 timeout

    • 首次静默会播 silence_prompt,第二次静默或达到 max_turns 才进入 timeout,属于节点契约的预期行为。
  • 现象:活动不派发或卡住

    • 检查活动状态必须是 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 和 NLU 摘要结构。
    • 当前接口保留 resultSchemaCode 等业务标识字段,但不要求依赖结果模板管理接口。

使用部署配置时

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

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

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

    • deploy/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 / VITE_CALLAI_API_BASE_URL 在构建期注入, 未设置或为空时默认为同域 /api;部署后不存在运行时配置文件。
    • 确认 callflow 前端站点的 /api upstream 是 callflow-server(默认 127.0.0.1:9913), callout 前端站点的 /api upstream 是 callout-server(默认 127.0.0.1:9920), callai 前端站点的 /api upstream 是 callai-server(默认 127.0.0.1:9930)。
    • 使用自定义 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 是唯一内容源。