Nexent 应用层排错:MCP、智能体、数据的 7 个坑
平台装好只是开始。真正耗时间的是应用层:工具连不上、列表是空的、智能体能配不能跑、导入即失败、评测全挂。
这篇按"MCP → 智能体 → 数据"三层记录 7 个真实报错,每个都给定位过程和验证方法。
一、先说结论
| # | 报错 | 根因 | 改法 |
|---|---|---|---|
| 1 | MCP 返回 421 Invalid Host header | SDK 默认开 DNS 重绑定防护 | 内网关防护,公网配白名单 |
| 2 | /tool/list 返回 [] | 超管无租户上下文 | 用租户管理员账号 |
| 3 | Sub-agent has no published version | 子智能体未发布 | 先发子、再发主 |
| 4 | Agent run error / NoneType not iterable | 接口创建的智能体缺字段 | 改用界面创建 |
| 5 | 导入报 Agent import error | model_id 在本环境不存在 | 换成本环境模型 id |
| 6 | 评测全 fail、predict 为 null | 裁判模型 asyncio 冲突 | 自建评测脚本 |
| 7 | 知识库分块内容为空 | 解析依赖未就绪 | 加内存 + 放通外网 |
这七个的共同点:报错信息都不指向真因。421 看起来像反代问题,实际是 SDK 安全策略;"评测全 fail"看起来像智能体不行,实际是裁判模型没跑起来。
二、环境与版本
| 项 | 版本 |
|---|---|
| Nexent | v2.8.2(Docker) |
| MCP Python SDK | 1.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": ""}, ...]}
定位过程:
- 先排除格式问题——换成
.txt重新入库,结果一样为空; - 再看解析组件日志,发现 Ray worker 被 OOM 杀:
(raylet) 2 Workers (tasks / actors) killed due to memory pressure (OOM)
- 还有一条关键报错:
ERROR nlp.stopwords Unable to download stopwords from
https://raw.githubusercontent.com/goto456/stopwords/master/baidu_stopwords.txt
中文停用词下载失败(网络不通),解析器拿不到词表。
结论:这套环境(8 GB + 外网受限)不足以跑通解析链路。要跑通至少需要 16 GB 内存,并放通停用词与小模型的下载地址。
五、应用侧最佳实践
- 接口创建的对象必须回查:创建成功 ≠ 字段齐全,导出来看;
- 先查权限再查配置:列表为空十有八九是租户/角色问题;
- 报错先看
error_message详情:平台常把真因写在详情里,HTTP 状态码只是外壳; - 发布顺序固定:子智能体 → 主智能体;
- 导出文件不当备份:
agent.json不含子智能体关系、不含密钥,换环境要重配; - 地址用占位符交付:别把内网 IP 发给别人;
- 评测要有兜底方案:平台评测不可用时,能用自己的脚本跑指标;
- 工具调没调,看结构化返回:只看回答文字容易误判。
六、交付前检查清单(应用侧)
- MCP 服务在容器内可达,返回
text/event-stream - 租户工具列表里能看到自建工具
- 智能体在界面能对话,工具被真实调用
- 子智能体已发布,主智能体发布成功
- 导出的
agent.json里mcp_url是占位符 - 文档写明"导入前要改 model_id 与 mcp_url"
- 用自建脚本能跑出评测指标(不依赖平台评测)
七、小结
应用层这七个坑,多数栽在同一件事上:把"看起来成功"当成"真的成功"。
- 注册 MCP 返回
success,但租户写错了; - 创建智能体返回
agent_id,但字段是空的; - 评测返回
COMPLETED,但一条都没跑成。
所以每个环节都要有一个"验证动作":查列表、导出来看、看结构化返回。这一步多花一分钟,能省掉后面一小时。
八、参考链接
| 内容 | 地址 |
|---|---|
| MCP 协议规范 | https://modelcontextprotocol.io |
| MCP Python SDK | https://github.com/modelcontextprotocol/python-sdk |
| FastMCP 迁移说明 | https://py.sdk.modelcontextprotocol.io/v2/migration/ |
| Nexent 官方文档 | https://modelengine-group.github.io/nexent |
更多推荐



所有评论(0)