AI / Skill / MCP 排障
按现象排查 AI 功能:助手不回答或半路中断、Skill 没被用上或装不上、MCP 客户端连不上——每条都能从产品日志核对。
先看现象,再给做法。这页上的每一条都能从产品自己的日志和报错里核对—— 不用去猜模型当时在想什么。
哪一半坏了
会坏的是两件互不相干的事,坏法也不一样:
- 模型那一半——助手、
/a2a、各处的 AI 按钮。需要 LLM 配置。 现象是报错卡片、回答慢或者回答空; - 数据那一半——
/mcp,以及每个回答背后的那些工具。完全不需要模型。 现象是 HTTP 状态码。
一个快速的分界动作:问助手一句*「你能做什么?」*。答得上来,说明模型那一半没问题, 毛病在工具;弹报错卡片,就从模型这边查起。
助手不回答
| 你看到的 | 原因 |
|---|---|
| 卡片:当前环境没有大模型配置 | 没有任何一条配置同时是「启用」且「默认」。两个开关,同一行 |
| 回答吐到一半停了,说请求失败 | 一般是模型自己超时。把那条配置高级设置里的**超时时间(秒)**调大 |
| 等很久什么都没有 | 夜莺所在机器根本够不着那个地址——见下一段 |
单独验证模型这条链路:编辑那条配置,点测试连接,它会拿表单里的值真发一次请求。 失败弹窗会说清是哪一类:
| 类型 | 该改什么 |
|---|---|
| 鉴权 | Key 不对,或者首尾带了空格。Anthropic 的 Key 以 sk-ant- 开头,OpenAI 以 sk- 开头 |
| 端点不存在 | 地址。填到版本根这一级——https://api.openai.com/v1——不是对话路径,也不是光一个域名 |
| 被限流 | 提供商那边的额度。去他们控制台看用量 |
| 回复里没有内容 | 通常是纯思考模型,预算全花在思考上了。把最大 Tokens 调大,或者换一个非思考模型 |
| 看着像网络问题的 | 夜莺所在机器到不了那个地址。在那台机器上验一下,需要就填代理地址 |
逐字段的说明在配置大模型提供方。
回答不对、很空,或者半路停了
「达到最大迭代次数」。 工具调用链撞到预算了,默认 25 次。
要么把问题问窄一点,要么在覆盖这类问题的那个 Skill 的 frontmatter 里调大
max_iterations。
你看得到的数据它看不到。 它跑在提问账号的权限上,所以这是权限问题不是 AI 问题: 去查那个账号的角色,以及它所在团队有哪些业务组。模型见 权限继承与 RBAC。
它编字段名、编指标名。 把对象和时间窗点名,并且让它去查而不是去回忆:
「读一下 42 号规则的定义再告诉我」。文档检索工具存在的意义就是让版本相关的名字
从文档里来——出网到 flashcat.cloud 被挡掉时,这个工具会降级,这个毛病会更明显。
对话一长回答就被截断。 把 LLM 配置里的上下文长度填成模型真实的窗口大小; 一次带多少历史是按它算的,留空就按一个保守的固定预算走。
Skill 没被用上
启用一个 Skill 不等于它会被加载。命中判断看的是描述,所以:
- 先看开关——关掉的 Skill 挂着
OFF标签,永远不被考虑; - 看可见范围。标成仅管理团队可见的 Skill,对团队之外的人是不存在的, 模型也不知道它存在;
- 用用户会打出来的话重写描述,把他们会粘贴进来的告警名和报错串都放进去。 绝大多数情况下就是这条;
- 找一找近似重复。几个描述差不多的 Skill 会同时命中、同时吃上下文,合并掉。
写法和例子在安装、管理与编写 Skills。
Skill 装不上
| 报错 | 怎么办 |
|---|---|
SKILL.md not found in archive root | 你把上一级目录打包进去了。多包一层会自动拆,多包两层不会 |
frontmatter 里 name 不能为空 | 文件要以 --- 开头、YAML 要能解析、name 要有值。三条都要满足 |
archive size exceeds 10MB limit | 上传上限。删点附带文件,或者改用 Git 安装 |
git_url must be an http or https URL | 不支持 ssh 地址,用 https 的 clone 地址 |
git_token is required when git_auth_type=token | 要么填 token,要么把认证方式换回不认证 |
| 私有仓库装不上 | Token 是用 [HTTP.RSA] 的密钥加密存的,那对密钥要先配好。需要用户名的凭据写成 用户名:令牌 |
| 拨启用开关时提示要先设管理团队 | 先打开修改,把管理团队设上 |
| 删除按钮是灰的 | 先把 Skill 停用。内置 Skill 永远删不掉 |
Skill 脚本跑不起来
脚本执行有两道互不相干的门,报错会告诉你撞的是哪一道。
「没有可运行的脚本」。 运行器按 main.py、main.sh、顶层唯一的 .py 或 .sh
这个顺序找。多于一个就得你自己指定。
执行被拒绝。 你设了 RequireIsolation = true,而这台机器搭不出真沙箱。
这是配置在按预期工作。要么在 Linux 上提供一份 python-base 根文件系统,
要么接受这台机器不跑 Skill 脚本。
跑起来了,但启动日志里有 SKILL EXECUTION RUNNING WITHOUT ISOLATION (unsafe-exec)。
脚本是直接在夜莺所在主机上跑的,而且没有网络。就这么放着之前,
先读一遍安装、管理与编写 Skills里的安全那一节。
每次执行都会打 sandbox audit: exec_id=... engine=... network=... exit_code=...,
非零退出码就在这行里。
MCP 客户端连不上
| 现象 | 原因和做法 |
|---|---|
401,响应体是纯文本 unauthorized | Token 不对或者已被删。如果所有 Token 都不行,那是 [HTTP.TokenAuth] 关着——启动日志里有 [A2A] HTTP.TokenAuth.Enable=false |
405,带 Allow: POST | 客户端发的是 GET /mcp。无状态模式没有独立 SSE 流,客户端要按 Streamable HTTP 配,不是旧的 SSE 传输 |
| 415 或 400 | 缺请求头。Content-Type: application/json 和 Accept: application/json, text/event-stream 两个都要 |
| 403 且提到 host | 反向代理没设 proxy_set_header Host,触发了 SDK 自带的 DNS rebinding 防护 |
| 代理返回 404,或者本该是 JSON 的地方回了一段 HTML | 请求根本没到 n9e。/mcp 在根路径,不在 /api/n9e 下面,只转发 /api/n9e/* 的代理不会把它放过去——加一段 location /mcp |
| 连上大约 60 秒后断开 | nginx 的默认值。加 proxy_buffering off,并把 proxy_read_timeout、proxy_send_timeout 放到一小时 |
一份满足上面所有条件的代理配置在启用 MCP 端点。 想把客户端从嫌疑里排除掉,就用那页的 curl 复现一遍。
连上了,但工具不对
| 现象 | 原因 |
|---|---|
| 一个工具都没有 | MCPToolsets 里的名字全被丢掉了。启动日志里每个坏名字对应一条 [MCP] ignoring unknown toolset——一份全是错别字的白名单换来的是零个工具,它不会回退成「全放开」 |
| 工具比预期少 | MCPToolsets 收得比你以为的窄,或者写工具没开。只读是 42 个,全开是 74 个 |
| 明明开了写工具却没有 | MCPEnableWriteTools = true 要重启才生效。之后 tools/list 应该返回 74 个 |
| 工具在,但每次调用都被拒 | Token 主人没有对应权限点,或者他所在团队够不着被点名的那个业务组。让它调一次 list_busi_groups,和那个账号在界面上看到的对比 |
| 出现了你不想要的工具 | 收窄 MCPToolsets。没有按单个工具开关的机制 |
A2A 调用卡住或者没返回
/a2a 驱动的就是内置助手,所以它需要一份能用的 LLM 配置——
前两节的内容在这里要先适用一遍。
| 现象 | 原因 |
|---|---|
| 请求挂了几十秒 | 正常。模型在思考、在调工具;服务端每 30 秒发一次状态心跳,防止网关掐连接 |
| 网关还是把它掐了 | 把 proxy_read_timeout 调大、proxy_buffering 关掉——网关自己的超时是 60 秒的话,心跳救不了 |
明明跑完了,tasks/get 却说任务不存在 | 任务状态在 Redis 里,TTL 24 小时。对话本身还在数据库里 |
| Agent Card 公布的是个内网地址 | BaseURL 留空了,是从请求头猜出来的。显式写上 |
更多在 A2A 端点。
下一步
- 端点本身的配置:启用 MCP 端点
- 认证失败的细节:MCP 认证失败
- 工具缺失的细节:MCP 工具缺失或被拒绝