平台装好只是开始。真正耗时间的是应用层:工具连不上、列表是空的、智能体能配不能跑、导入即失败、评测全挂。
这篇按"MCP → 智能体 → 数据"三层记录 7 个真实报错,每个都给定位过程和验证方法。


一、先说结论

#报错根因改法
1MCP 返回 421 Invalid Host headerSDK 默认开 DNS 重绑定防护内网关防护,公网配白名单
2/tool/list 返回 []超管无租户上下文用租户管理员账号
3Sub-agent has no published version子智能体未发布先发子、再发主
4Agent run error / NoneType not iterable接口创建的智能体缺字段改用界面创建
5导入报 Agent import errormodel_id 在本环境不存在换成本环境模型 id
6评测全 fail、predict 为 null裁判模型 asyncio 冲突自建评测脚本
7知识库分块内容为空解析依赖未就绪加内存 + 放通外网

这七个的共同点:报错信息都不指向真因。421 看起来像反代问题,实际是 SDK 安全策略;"评测全 fail"看起来像智能体不行,实际是裁判模型没跑起来。


二、环境与版本

项版本
Nexentv2.8.2(Docker)
MCP Python SDK1.30.0(mcp<2)
模型DeepSeek-Chat、BGE-M3
账号租户管理员(非超管)

三、应用层排查思路

环境层的排查是"通不通",应用层要往上一层:“配置对不对、字段全不全”。

我的固定顺序:

1. 通不通   → 容器内 curl 目标地址,看状态码
2. 协议对不对 → 看响应头/响应体,别只看 200
3. 对象全不全 → 数据库或导出文件里,关键字段是否为空
4. 权限够不够 → 当前账号属于哪个租户、哪个组

第 2 步最容易漏。比如 MCP 地址少写 /sse,curl 也返回 200——但返回的是网页 HTML,不是事件流。


四、七个应用坑

坑 1:MCP 返回 421

现象(从容器访问自建 MCP 服务):

HTTP/1.1 421 Misdirected Request
Content-Length: 19

Invalid Host header

在这里插入图片描述

定位过程:先怀疑反向代理,但我是直连容器网桥 IP,没有代理。于是做了个最小复现——起一个只有 ping 工具的 FastMCP 服务,用自定义 Host 头访问:

curl -i -H "Host: 172.17.0.1:8011" http://127.0.0.1:8011/sse
# HTTP/1.1 421 Misdirected Request

确认与服务实现无关,是 SDK 的默认行为。原因是新版 MCP SDK 默认开启 DNS 重绑定防护,只允许 Host 为 localhost、127.0.0.1、[::1]。

改法(内网):

from mcp.server.fastmcp import FastMCP
from mcp.server.transport_security import TransportSecuritySettings

mcp = FastMCP("ticket_analysis",
    transport_security=TransportSecuritySettings(enable_dns_rebinding_protection=False))

如果是公网部署,不要一刀切关掉,而是配白名单:

TransportSecuritySettings(
    enable_dns_rebinding_protection=True,
    allowed_hosts=["mcp.example.com", "mcp.example.com:*"],
)

验证:容器内 curl 应返回 200 且 Content-Type: text/event-stream。

坑 2:工具列表是空的

现象:MCP 注册返回 success,但 /tool/list 返回 [],智能体也勾不到工具。

定位过程:看配置服务日志:

WARNING No tenant relationship found for user <su-id>, using default tenant

再看数据库:

SELECT mcp_id, mcp_name, tenant_id, user_id FROM nexent.mcp_record_t;
-- tenant_id 竟然是字符串 'tenant_id'

也就是说:超管注册 MCP 时没有租户上下文,tenant_id 被写成了占位符,查询时自然匹配不到。

改法:换成租户管理员账号注册和绑定。我建了一个租户管理员,重新注册后工具立刻可见(10 个工具,id 35–44)。

如果已经用超管注册了,可以直接改数据库:UPDATE mcp_record_t SET tenant_id='<真实id>', group_ids='2' WHERE mcp_id=1;,但更推荐重来一次,避免遗漏字段。

坑 3:主智能体发布失败

在这里插入图片描述

现象:

{"message":"Sub-agent 9 has no published version; publish the sub-agent first."}

定位过程:看报错已经写得很清楚——子智能体 9 没有已发布版本。我去查版本列表,确实只有主智能体有 v1。

改法:按顺序发布。

curl -X POST .../agent/9/publish  -d '{"agent_id":9,"version_no":1}'
curl -X POST .../agent/10/publish -d '{"agent_id":10,"version_no":1}'
curl -X POST .../agent/6/publish  -d '{"agent_id":6,"version_no":2}'

顺带一个发现:主智能体发布后版本号会跳到 v3(因为中间失败过一次也占了版本号),所以别把版本号写死在脚本里。

坑 4:Agent run error

现象:界面能对话,但用接口 /agent/run 调用时报:

{"message":"Agent run error."}

定位过程:运行日志里有关键一行:

Agent run error: 'NoneType' object is not iterable

拿它和线上能正常跑的智能体对比数据库字段,发现接口创建的记录里 model_id 是 NULL(只有 model_ids 数组),而运行时代码读的是单值字段。

补上 model_id 后仍然报错,说明还缺别的(is_new、版本快照等),这些只有界面创建流程才会写全。

改法:智能体在界面创建;接口只用于查询、导出、发布这类"操作已有对象"的事。

验证:界面对话里让智能体调一次工具,能返回结构化结果才算通。只回一段自然语言,不代表工具被调用。

坑 5:导入 agent.json 失败

现象:

{"message":"Agent import error."}

定位过程:试了两个文件——自己导出的能导入,从仓库拿的现成文件报错。对比差异,问题在 model_id:

"model_id": 7466   // 别人环境里的模型 id

而我的租户里 DeepSeek 是 1、BGE-M3 是 2。model_id 是租户内自增 id,跨环境必然对不上。

改法:导入前替换 model_id、model_name,以及 mcp_info 里的地址:

"model_id": 1, "model_name": "deepseek-chat",
"mcp_info": [{ "mcp_server_name": "ticket_analysis", "mcp_url": "http://<你的地址>:8001/sse" }]

经验:交付给别人时,agent.json 里的 mcp_url 最好直接写成占位符,并在 README 里写清要改哪几个字段。

坑 6:评测全部失败

在这里插入图片描述

现象:评测任务 COMPLETED,但 score 0.0、pass 0、fail 3,每条用例的 predict 都是 null。

定位过程:先怀疑是智能体答错,但 predict 为 null 说明根本没产生回答。点开 error_message:

评估系统在调用"裁判模型"时出错了。原因是程序内部已经在一个"运行中的事件循环"里,却又尝试启动一个新的,两者冲突导致调用失败。

典型的 asyncio.run() 在已有事件循环中调用的问题。

改法:平台评测暂时用不了,改用自建脚本对齐金标与预测:

gold = [g["gold_类型"] for g in goldens]
pred = [classify(t["诉求内容"])["type"] for t in cases]
acc = sum(a == b for a, b in zip(gold, pred)) / len(gold)

顺带一个接口细节:评测用例的 label 必须是对象 {"expected": "..."},传字符串会报 Input should be a valid dictionary,文档里没写。

坑 7:知识库分块内容为空

现象:文档上传成功、登记成功、检索接口能返回结果,但 content 是空字符串。

{"total": 4, "chunks": [{"content": ""}, {"content": ""}, ...]}

定位过程:

  1. 先排除格式问题——换成 .txt 重新入库,结果一样为空;
  2. 再看解析组件日志,发现 Ray worker 被 OOM 杀:
(raylet) 2 Workers (tasks / actors) killed due to memory pressure (OOM)
  1. 还有一条关键报错:
ERROR nlp.stopwords Unable to download stopwords from
  https://raw.githubusercontent.com/goto456/stopwords/master/baidu_stopwords.txt

中文停用词下载失败(网络不通),解析器拿不到词表。

结论:这套环境(8 GB + 外网受限)不足以跑通解析链路。要跑通至少需要 16 GB 内存,并放通停用词与小模型的下载地址。


五、应用侧最佳实践

  1. 接口创建的对象必须回查:创建成功 ≠ 字段齐全,导出来看;
  2. 先查权限再查配置:列表为空十有八九是租户/角色问题;
  3. 报错先看 error_message 详情:平台常把真因写在详情里,HTTP 状态码只是外壳;
  4. 发布顺序固定:子智能体 → 主智能体;
  5. 导出文件不当备份:agent.json 不含子智能体关系、不含密钥,换环境要重配;
  6. 地址用占位符交付:别把内网 IP 发给别人;
  7. 评测要有兜底方案:平台评测不可用时,能用自己的脚本跑指标;
  8. 工具调没调,看结构化返回:只看回答文字容易误判。

六、交付前检查清单(应用侧)

  • MCP 服务在容器内可达,返回 text/event-stream
  • 租户工具列表里能看到自建工具
  • 智能体在界面能对话,工具被真实调用
  • 子智能体已发布,主智能体发布成功
  • 导出的 agent.json 里 mcp_url 是占位符
  • 文档写明"导入前要改 model_id 与 mcp_url"
  • 用自建脚本能跑出评测指标(不依赖平台评测)

七、小结

应用层这七个坑,多数栽在同一件事上:把"看起来成功"当成"真的成功"。

  • 注册 MCP 返回 success,但租户写错了;
  • 创建智能体返回 agent_id,但字段是空的;
  • 评测返回 COMPLETED,但一条都没跑成。

所以每个环节都要有一个"验证动作":查列表、导出来看、看结构化返回。这一步多花一分钟,能省掉后面一小时。


八、参考链接

内容地址
MCP 协议规范https://modelcontextprotocol.io
MCP Python SDKhttps://github.com/modelcontextprotocol/python-sdk
FastMCP 迁移说明https://py.sdk.modelcontextprotocol.io/v2/migration/
Nexent 官方文档https://modelengine-group.github.io/nexent
Logo

更多推荐