call-troubleshoot
呼叫问题排查
概述
通过 OpenAPI 接口自动获取呼叫排查工具的会话详情和前端日志,结合 GitLab 源码进行 AI 分析。无需浏览器登录,纯 HTTP 接口调用。
关联项目参考:见 references/projects.md(包含仓库信息、关键目录和常见问题排查思路)
前置条件
用户首次使用时,检查并指引安装:
pip install requests
Token 配置:支持多种方式传入 YS-BASIC-AUTH Token,按优先级:
- 命令行参数
--token <TOKEN> - 环境变量
YS_BASIC_AUTH - 本地缓存文件
temp/auth_token(之前传入过会自动缓存) - 交互式输入(首次使用时终端提示输入,输入后自动缓存)
如需更换 Token,删除 temp/auth_token 文件后重新运行即可。
临时数据目录:所有临时文件(日志输出、Token 缓存等)保存在调用方当前工作目录下的 temp/ 中,支持通过环境变量 PROJECT_ROOT 覆盖。
注:日志获取已改为纯 API 调用,无需 Playwright。仅生成报告截图(Step 4b)时需要 Playwright。
工作流程
Step 1 - 获取通话日志
运行自动化脚本,传入用户提供的会话 ID(sessionId,数字格式):
# 基本用法(Token 从缓存或交互式输入获取)
python3 fetch_call_logs.py <SESSION_ID> --output call_logs_output.json
# 通过命令行传入 Token
python3 fetch_call_logs.py <SESSION_ID> --token <TOKEN>
# 通过环境变量传入 Token
YS_BASIC_AUTH=<TOKEN> python3 fetch_call_logs.py <SESSION_ID>
脚本路径:scripts/fetch_call_logs.py
脚本自动执行(三步流程):
- 获取会话详情 - 调用
POST https://b.163.com/dev-mcp/nuwa/api/commonToolExecute接口,传入 sessionId 和 tabsName(业务层会话详情、日志查询),获取会话概况和日志查询参数 - 提取日志查询参数 - 从返回结果中:
- 从「日志查询」Tab 的「前端日志(按坐席imid)」列提取日志关键词(如
clientId@kf@和ysf.swallow.log) - 从「业务层会话详情」Tab 提取开始时间和结束时间,转换为毫秒级时间戳(前后各扩展 30 秒)
- 从「日志查询」Tab 的「前端日志(按坐席imid)」列提取日志关键词(如
- 获取日志 - 调用
POST https://b.163.com/dev-mcp/nuwa/api/ckLog接口,传入关键词和时间范围,获取前端日志
脚本返回 JSON 结构:
{
"session_id": "15006628683",
"session_info": {"会话ID": "...", "通话类型": "...", "通话状态": "...", ...},
"log_keywords": ["f7572a6f7ab342be5cbac1076c9a@kf@", "ysf.swallow.log"],
"logs": ["日志行1", "日志行2", ...],
"error": null
}
处理异常情况:
- 若
error提示无法获取会话详情:检查 sessionId 是否正确 - 若
logs为空:可能该时间段内没有前端日志记录,建议确认通话是否使用了 Web 客户端 - 若 API 返回认证失败:检查
YS-BASIC-AUTH是否过期
Step 2 - 拉取相关源码(可选)
运行前提醒用户配置 GitLab Personal Access Token:
⚠️ 需要 GitLab Personal Access Token 才能拉取仓库代码
获取方式:https://g.hz.netease.com → 右上角头像 → Preferences → Access Tokens
所需权限:勾选 read_repository → 点击 Create personal access token
确认用户已有 Token 后,运行:
python3 fetch_gitlab_code.py --token <GITLAB_TOKEN>
# 仅拉取指定仓库
python3 fetch_gitlab_code.py --token <GITLAB_TOKEN> --repos kefu
脚本路径:scripts/fetch_gitlab_code.py,仓库默认保存至 temp/repos/
支持的仓库(--repos 参数):
kefu- basic-web-kefu 客服前端主仓库jssip- JsSIP SIP 信令库connect- 7uConnect 呼叫连接层 SDKall- 全部三个仓库(默认)
权限不足时:脚本会输出明确错误并提示:
请联系呼叫开发人员开通仓库访问权限(可在飞书搜索「呼叫前端」群组)
Step 3 - AI 日志分析
读取 call_logs_output.json 中的日志内容,结合以下维度进行分析:
- 时间线梳理:按时间戳排序日志,还原通话完整生命周期
- JsSIP 协议链路详细分析(重点,见下方专项说明)
- 异常识别:定位
error、failed、exception、undefined、null等关键词 - 状态机核验:SIP 状态流转是否正常(
trying → ringing → answered / failed) - 关联源码:若有拉取源码,找到对应的日志打印位置,分析上下文逻辑
- 根因定位:给出可能的问题原因和修复建议
JsSIP 协议链路专项分析(必须执行)
每次分析日志时,必须从日志中提取并详细罗列 JsSIP SIP 协议的完整链路信息,包括但不限于:
A. WebSocket 传输层链路
从日志中提取所有 WebSocket 相关事件,按时间顺序罗列:
| 时间戳 | 事件 | 详情 |
|---|---|---|
| ... | WS 连接建立 | ws://xxx 连接成功 |
| ... | WS 消息发送/接收 | SIP 消息类型 |
| ... | WS 断开/重连 | 断开原因 |
B. SIP 信令交互链路
逐条提取日志中的 SIP 信令消息,按时间顺序罗列完整的信令交互过程:
| 序号 | 时间戳 | 方向 | SIP 方法/响应 | 关键头字段 | 说明 |
|---|---|---|---|---|---|
| 1 | ... | → 发送 | REGISTER | Contact, Expires | UA 注册 |
| 2 | ... | ← 接收 | 200 OK | 注册成功 | |
| 3 | ... | → 发送 | INVITE | From, To, Call-ID, SDP | 发起呼叫 |
| 4 | ... | ← 接收 | 100 Trying | 服务端处理中 | |
| 5 | ... | ← 接收 | 180 Ringing | 对端振铃 | |
| 6 | ... | ← 接收 | 200 OK | SDP | 对端接听 |
| 7 | ... | → 发送 | ACK | 确认 | |
| 8 | ... | → 发送 | BYE / ← 接收 BYE | 挂断 |
关注的 SIP 方法:REGISTER, INVITE, ACK, BYE, CANCEL, UPDATE, REFER, INFO, NOTIFY, OPTIONS
关注的 SIP 响应码:
- 1xx(临时):
100 Trying,180 Ringing,183 Session Progress - 2xx(成功):
200 OK - 3xx(重定向):
301,302 - 4xx(客户端错误):
401 Unauthorized,403 Forbidden,404 Not Found,408 Timeout,480 Unavailable,486 Busy,487 Terminated,488 Not Acceptable - 5xx(服务端错误):
500 Internal Error,502 Bad Gateway,503 Service Unavailable - 6xx(全局错误):
603 Decline
⚠️ CANCEL/BYE 消息 Reason 字段提取(必须执行)
当日志中出现 CANCEL 或 BYE 消息时,必须完整提取消息体中的 Reason 头字段,并在信令交互表中准确标注。这是判断通话结束原因的关键依据,严禁遗漏或错误引用。
提取规则:
- 逐字符提取
Reason:行的完整内容,包括protocol、cause值和text字段 - 在 SIP 信令交互表的"关键信息"列中完整记录 Reason,例如:
Reason: Q.850;cause=19;text="NO_ANSWER" - 在根因分析和总结对比表中引用 Reason 时,必须与原始日志中的值逐字核对,不得凭记忆填写
- 不同通话的 Reason 可能不同,每通通话必须独立提取,严禁复制其他通话的 Reason
常见 Q.850 原因码:
cause=16— Normal Clearing(正常挂断)cause=17— User Busy(用户忙)cause=19— No Answer(无应答超时,通常是服务端定时器到期)cause=21— Call Rejected(呼叫被拒绝)cause=27— Destination Out of Order(目的地设备故障/不可达,可能是用户侧或运营商透传)cause=31— Normal Unspecified(未指定的正常原因)cause=34— No Circuit Available(无可用线路)cause=38— Network Out of Order(网络故障)cause=41— Temporary Failure(临时故障)cause=127— Interworking Unspecified(互通未指定)
不同的 cause 值意味着不同的问题根因方向,分析时需结合 cause 值判断是服务端超时、用户主动挂断、还是网络/设备故障。
C. RTCSession 状态机流转链路
从日志中提取 JsSIP RTCSession 的所有状态变化事件,罗列完整状态流转:
[时间戳] RTCSession 状态: null
↓ (INVITE 发送/接收)
[时间戳] RTCSession 状态: connecting
↓ (1xx 响应)
[时间戳] RTCSession 状态: progress
↓ (200 OK)
[时间戳] RTCSession 状态: accepted
↓ (ACK)
[时间戳] RTCSession 状态: confirmed
↓ (BYE)
[时间戳] RTCSession 状态: ended
标注每次状态变化的触发原因(哪条 SIP 消息导致的),以及状态停留时长。
如果存在异常流转(如 connecting → failed,跳过了 progress),需特别标注并分析原因。
D. ICE/SDP 协商链路
从日志中提取 WebRTC 相关的 ICE 和 SDP 协商过程:
- SDP Offer/Answer 交换时间点
- ICE 候选收集过程(
icecandidate事件) - ICE 连接状态变化:
new → checking → connected → completed(或failed/disconnected) - DTLS 握手状态(如有日志)
- TURN/STUN 服务器使用情况(如有日志)
E. UA(用户代理)事件链路
罗列 JsSIP UA 级别的事件:
registered/unregistered/registrationFailed— 注册相关newRTCSession— 新会话创建connected/disconnected— WebSocket 连接状态sipEvent— 收到的 SIP 事件
分析输出格式:
## 通话 <call_id> 排查报告
### 通话概况
- 开始时间:xxx
- 结束时间:xxx
- 通话时长:xxx
- 结果:成功/失败
### JsSIP 协议链路详情
#### WebSocket 传输层
| 时间戳 | 事件 | 详情 |
|--------|------|------|
| ... | ... | ... |
#### SIP 信令交互(完整链路)
| 序号 | 时间戳 | 方向 | SIP 方法/响应码 | 关键信息 | 说明 |
|------|--------|------|----------------|---------|------|
| 1 | ... | ... | ... | ... | ... |
#### RTCSession 状态流转
连接建立: [时间戳] connecting → [时间戳] progress → [时间戳] accepted → [时间戳] confirmed
通话结束: [时间戳] ended (原因: xxx)
#### ICE/SDP 协商
- [时间戳] SDP Offer 发送
- [时间戳] SDP Answer 接收
- [时间戳] ICE 状态: checking → connected
- ...
#### UA 事件
- [时间戳] UA registered(注册成功)
- [时间戳] UA newRTCSession(新呼叫)
- ...
### 链路分析总结
- 信令耗时:INVITE 到 200 OK 共耗时 xxxms
- ICE 协商耗时:xxxms
- 是否有异常信令:有/无
- 是否有重传:有/无
### 异常发现
1. [时间戳] 错误描述 → 可能原因
### 根因分析
...
### 修复建议
...
Step 4 - 生成分析报告(主动询问)
⚠️ 重要:Step 3 日志分析完成后,必须主动询问用户是否需要生成分析报告及截图。
询问话术:
日志分析已完成。是否需要生成 Markdown 分析报告及对应的 PNG 截图?
用户选择「是」时,执行以下流程:
4a. 生成 Markdown 报告
将 Step 3 的分析结果整理为结构化的 Markdown 文件,保存到 temp/ 目录。
文件命名规则:call_report_<SESSION_ID>.md(如 call_report_14984019777.md)
报告模板结构(根据实际分析内容填充):
# 通话排查报告:<SESSION_ID>
> 排查时间:<当前日期>
> 坐席ID:xxx
> 坐席IP:xxx
> 客户端版本:xxx
---
## 一、问题现象
<客服反馈的问题描述>
## 二、通话概况
<通话基本信息表格>
## 三、失败根因
<根因分析>
## 四、详细分析
<SIP 信令链路、RTCSession 状态流转、ICE/SDP 协商等>
## 五、建议处理方案
<按优先级列出处理建议>
多通会话对比场景:如果一次排查涉及多个 SESSION_ID,合并为一份报告,文件名用第一个 SESSION_ID,报告中增加「会话对比」章节。
4b. 生成 PNG 截图
运行截图脚本,将 Markdown 报告渲染为 PNG 图片:
python3 md_to_screenshot.py <md_file> <png_file>
脚本路径:scripts/md_to_screenshot.py
脚本流程:
- 使用
pandoc将 Markdown 转为 HTML 片段 - 包装为带美观样式的完整 HTML(内置企业级排版样式)
- 使用 Playwright 无头浏览器渲染并截图
命令示例:
python3 scripts/md_to_screenshot.py temp/call_report_14984019777.md temp/call_report_14984019777.png
依赖检查:运行前确认 pandoc 已安装(which pandoc),如未安装提示用户:
- macOS:
brew install pandoc - Linux:
apt install pandoc
输出结果:告知用户生成的文件路径:
- 📄 Markdown 报告:
temp/call_report_<SESSION_ID>.md - 📸 PNG 截图:
temp/call_report_<SESSION_ID>.png
注意事项
YS-BASIC-AUTHToken 缓存于temp/auth_token,如 Token 过期或需更换,删除该文件后重新运行即可- GitLab Token 需要有
read_repository权限 - 日志量较大时,优先关注
error/warn级别日志和状态变化节点 - 报告截图依赖
pandoc和playwright,首次使用前需确认安装