skills/skills.netease.im/call-troubleshoot

call-troubleshoot

SKILL.md

呼叫问题排查

概述

通过 OpenAPI 接口自动获取呼叫排查工具的会话详情和前端日志,结合 GitLab 源码进行 AI 分析。无需浏览器登录,纯 HTTP 接口调用。

关联项目参考:见 references/projects.md(包含仓库信息、关键目录和常见问题排查思路)


前置条件

用户首次使用时,检查并指引安装:

pip install requests

Token 配置:支持多种方式传入 YS-BASIC-AUTH Token,按优先级:

  1. 命令行参数 --token <TOKEN>
  2. 环境变量 YS_BASIC_AUTH
  3. 本地缓存文件 temp/auth_token(之前传入过会自动缓存)
  4. 交互式输入(首次使用时终端提示输入,输入后自动缓存)

如需更换 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

脚本自动执行(三步流程):

  1. 获取会话详情 - 调用 POST https://b.163.com/dev-mcp/nuwa/api/commonToolExecute 接口,传入 sessionId 和 tabsName(业务层会话详情、日志查询),获取会话概况和日志查询参数
  2. 提取日志查询参数 - 从返回结果中:
    • 从「日志查询」Tab 的「前端日志(按坐席imid)」列提取日志关键词(如 clientId@kf@ysf.swallow.log
    • 从「业务层会话详情」Tab 提取开始时间和结束时间,转换为毫秒级时间戳(前后各扩展 30 秒)
  3. 获取日志 - 调用 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 呼叫连接层 SDK
  • all - 全部三个仓库(默认)

权限不足时:脚本会输出明确错误并提示:

请联系呼叫开发人员开通仓库访问权限(可在飞书搜索「呼叫前端」群组)

Step 3 - AI 日志分析

读取 call_logs_output.json 中的日志内容,结合以下维度进行分析:

  1. 时间线梳理:按时间戳排序日志,还原通话完整生命周期
  2. JsSIP 协议链路详细分析(重点,见下方专项说明)
  3. 异常识别:定位 errorfailedexceptionundefinednull 等关键词
  4. 状态机核验:SIP 状态流转是否正常(trying → ringing → answered / failed
  5. 关联源码:若有拉取源码,找到对应的日志打印位置,分析上下文逻辑
  6. 根因定位:给出可能的问题原因和修复建议

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 头字段,并在信令交互表中准确标注。这是判断通话结束原因的关键依据,严禁遗漏或错误引用

提取规则:

  1. 逐字符提取 Reason: 行的完整内容,包括 protocolcause 值和 text 字段
  2. 在 SIP 信令交互表的"关键信息"列中完整记录 Reason,例如:Reason: Q.850;cause=19;text="NO_ANSWER"
  3. 在根因分析和总结对比表中引用 Reason 时,必须与原始日志中的值逐字核对,不得凭记忆填写
  4. 不同通话的 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

脚本流程

  1. 使用 pandoc 将 Markdown 转为 HTML 片段
  2. 包装为带美观样式的完整 HTML(内置企业级排版样式)
  3. 使用 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-AUTH Token 缓存于 temp/auth_token,如 Token 过期或需更换,删除该文件后重新运行即可
  • GitLab Token 需要有 read_repository 权限
  • 日志量较大时,优先关注 error / warn 级别日志和状态变化节点
  • 报告截图依赖 pandocplaywright,首次使用前需确认安装
Installs
2
First Seen
Apr 18, 2026