外观
故障排查与典型症状
按服务分组的排查清单。每条包含“现象—优先确认—处理方式”,用于联调和生产应急。
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.10 按默认wan.auto优先选择了运营商 NAT 候选。配置只允许 coturn 公网 IP 的softphone-turn-relayACL,并在 Sofia profile 使用apply-candidate-acl=softphone-turn-relay。 - 修改 ACL 后除了
reloadxml还必须执行reloadacl;修改 Sofia profile 后重启对应 profile。正确的新通话应选择123.57.205.60:31000–31040relay,本地 SDP 应广告123.57.205.60:30000–30199,DTLS 不应长期停在HANDSHAKE。
- 检查 FreeSWITCH 的 200 OK SDP。若
现象: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。 internalprofile 使用apply-inbound-acl=domains,domains必须允许172.21.38.245/32。- 修改 ACL 后执行
reloadacl;修改 Nginx 后先执行nginx -t再 reload。无 Authorization 的 REGISTER 应返回401 Unauthorized,而不是超时或直接注册成功。
- 首先检查 Nginx 上游必须是
现象:外呼接通后未进入通话业务
- 确认
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是否与输入一致。
- 确认 ASR WebSocket 地址可达且
现象:业务常见命令报错/挂断后报错
- 对端挂断下部分 execute 命令会被拒绝,属于正常,runtime 会做容错。
- 查看是否是配置文件变更导致通道变量/主叫变量缺失,或调用顺序错误。
C++ ASR 服务(sherpa-asr-online-server)
现象:
GET /health200,但 WebSocket 无法识别- 优先确认
server.wsPath与maxSessions,是否命中连接数上限。 - 验证客户端首帧元数据 JSON 是否可解析(
uuid/channel_uuid约定字段)。 - 按
concurrency调参:提高ioWorkers/sessionPoolSize需要配合 CPU/负载评估。
- 优先确认
现象:音频内容明显识别错乱
- 确认音频是 16kHz 单声道 PCM16,ASR 不做自动重采样。
- 检查是否误发送了分片帧(服务不支持分片)。
- 开启录音排查:临时打开
recording.enabled=true校验 Wav 输入。
现象:并发稍大即出现 503
maxSessions是会话上限,先观察GET /health的activeSessions/maxSessions。- 处理时先调
ioWorkers/sessionPoolSize与maxSessions,再评估上游调度并发。
C++ FunASR 服务(fanasr-asr-online-server 与 fanasr-asr-offline-server)
现象:在线流式识别 partial 阶段热词未生效
- 确认底层原理:FunASR 1-pass 纯流式模型不支持热词;热词是在 2-pass 模式(
enable2pass: true)下的句尾离线大模型二次重打分阶段修正生效。 - 检查客户端首帧或握手 URL 中下发的热词格式是否为以空格分隔的词列表。
- 确认底层原理:FunASR 1-pass 纯流式模型不支持热词;热词是在 2-pass 模式(
现象:离线识别上传 MP3 / M4A 报
Audio decode failed- 检查
config.json中的ffmpeg.enabled是否为true,以及ffmpeg.ffmpegPath是否可在系统PATH找到或指向了有效绝对路径。 - 若
ffmpeg.enabled: false(禁用 ffmpeg),服务仅支持标准 16kHz WAV 音频文件。
- 检查
现象:服务启动时提示模型目录找不到
- 检查
config.json中配置的modelDir、onlineModelDir、vadDir、puncDir是否存在于onnx-platform/models/目录下。
- 检查
C++ TTS 服务(sherpa-tts-server)
现象:流式播报在
mod_shout上失败- 确认 FreeSWITCH 已加载
mod_shout。 - 确认
mp3EncoderPath可执行,或ffmpeg在 PATH 中可见。 - 检查
listenHost与返回publicBaseUrl是否一致(调用方是否能回源访问)。
- 确认 FreeSWITCH 已加载
现象:请求偶发 503 / 超时
maxQueuedRequests过小会导致排队拒绝,先看调用方重试策略与峰值并发。- 查日志里的
stream busy/request timed out,结合调用并发和上游连接稳定性分析。
现象:文件增长过快
- TTS 无内建 TTL 清理;需要外部清理
public/wav/。 - 长期高并发场景优先复用文本(命中缓存)或配置额外归档策略。
- TTS 无内建 TTL 清理;需要外部清理
情绪分析服务(emotion-analysis-server)
现象:
POST /analyze成功率低- 确认
recordingUrl可访问(只支持 http 与支持的 WAV 采样格式)。
- 确认
现象:返回统一情绪分数异常
- 记录
reason/available字段,判断文本/音频哪一侧缺失。 - 确认通话录音声道映射是否正确(
audio.channelRoles)。
- 记录
现象:前端情绪复核音频无法播放
- 确认情绪分析服务返回的是短文件名或短路径,callout 前端会通过
media.emotionAudioBaseUrl拼完整 URL。 - 检查
emotion-analysis-server的audio.processedDir是否可写、audio.processedPublicBaseUrl是否与 calloutmedia.emotionAudioBaseUrl对齐。 - 浏览器访问最终 URL,确认静态服务、反代和 MIME 类型可用。
- 确认情绪分析服务返回的是短文件名或短路径,callout 前端会通过
callai-server
现象:
GET /api/v1/nlu/capabilities返回 401- 确认
server.authToken或CALLAI_AUTH_TOKEN是否配置。 - 请求头必须携带有效的
X-Callai-Internal-Token、X-Callout-Internal-Token或Authorization: Bearer <token>。
- 确认
现象:NLU 请求超时或返回 fallback 结果
- 确认 OpenAI 兼容推理后端可访问。地址有两个来源:
config.json的llm.openaiUrl, 或数据库callai.ai_llm_configs中启用的记录(GET /api/v1/ai/llm-config可读取当前生效值)。 - 确认模型名配置正确(默认 NLU / Chat
qwen3.5-2b、Scriptqwen3.6-27b-fp8)。 - 根据模型耗时调大
llm.nlu.timeoutMs或llm.chat.timeoutMs。 - 可在
callai-webpage的 Playground 或「后端模型」页面进行连通性探活 (对应POST /api/v1/ai/llm-config/test与GET /api/v1/models)。
- 确认 OpenAI 兼容推理后端可访问。地址有两个来源:
现象:业务 profile 不生效
- 在
callai-webpage的「NLU 策略管理」中检查策略是否启用,或访问/api/v1/nlu/capabilities确认存在。 - 请求体里的
action只能是当前支持动作,例如decision或extract。
- 在
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、大模型服务可达性和 SSEcomplete事件。 - 非法
exitKey、SSE 解析失败、LLM 超时和 TTS 播放失败都会走error,可结合routeTrace[].chat.reason排查。
- 检查 callflow-esl 的
现象:连续无声后进入
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。
- worker 会用
现象:导入或结果回写字段不清楚
- 先看
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 前端站点的
/apiupstream 是callflow-server(默认127.0.0.1:9913), callout 前端站点的/apiupstream 是callout-server(默认127.0.0.1:9920), callai 前端站点的/apiupstream 是callai-server(默认127.0.0.1:9930)。 - 使用自定义 API 根地址时,确认构建变量指向浏览器可访问的地址,并在修改后重新构建前端。
- 使用默认值时确认代理保留
/api路径;若多个前端共用同一 origin,需要拆分为可独立配置 upstream 的站点或 origin。
- API 根地址由
网关服务(阿里云 / Azure)
- 现象:TTS/ASR 接口超时或 5xx
- 确认凭据注入(环境变量优先,避免配置文件硬编码)。
- 检查网络出口与对端 endpoint 可达性。
- 比较本地日志中的
tts.stream/asractiveSessions与调用量趋势。
统一排障步骤(建议)
- 先确认健康接口:ASR/TTS/Emotion 是否可用。
- 再确认网络链路(callbackHost/listenHost/publicBaseUrl/端口映射)。
- 再确认协议输入(音频格式、WebSocket 订阅路径、JSON 字段)。
- 最后确认业务流(拨号计划、业务路由、回铃与回写)。
证据留存
- 保留:服务日志、健康检测日志、失败请求参数、故障时间点、并发快照。
- 链路类问题建议附:
- 配置片段(脱敏)
- 关键 cURL/请求样例
- 时间轴(起止时间、重试次数、错误码分布)