Dify_专题十_模型列表为空Agent创建报错_cursor.md
这是基于原文生成的 Cursor 版文档。
原文件未改动;本版只做三件事:统一阅读入口、修正已审出的明显问题、补充更适合快速排障的提示。
Cursor 版修订说明
- 保留最终正确口径:不要把 max_tokens 写进 model_properties。
阅读提示
- 若正文中的容器名、目录名、IP 与你的环境不一致,请以实际
docker ps、docker compose ps、目录结构为准。 - 若正文属于“本次实战记录”,应理解为环境实录,不等同于所有环境的唯一正确默认值。
运行环境:业务脚本均在 207 固定目录
/opt/workbuddy执行,Python 为/usr/bin/python3。 Windows 开发机仅保留_probe_*调试脚本;同步命令:python scripts_cursor/完整代码约定/00_部署_workbuddy到207.py。
原文主体(已纳入 Cursor 版纠错)
专题十:模型列表为空 / 创建 Agent 报错(插件 schema 校验失败)
阅读导航(何时打开 · 报错往哪查)
本文是单点修复/排障专题,配合《安装配置手册》使用;下文实战步骤与代码全部保留,此处只做索引。
| 现象 | 本文 | 还可能是 |
|---|---|---|
| LLM 列表突然变空 | §三 llm.py patch | max_tokens 进了 model_properties |
| 创建 Agent 一大段 JSON 报错 | §二 根因 | 专题一 接入未完成 |
| patch 后仍空 | §四 重启 plugin | SyntaxError→专题八 |
本专题记录本次部署中一个症状隐蔽、根因在插件 schema 的问题:
OpenAI 插件
llm.pypatch 后,GET /models/model-types/llm返回空列表,创建 Agent 时模型下拉为空并报错;同时涉及「插件改完必须重启验证」的通用教训。
一、现象
- 控制台 → 创建应用 → Agent,模型选择处看不到
qwen3.6-27b,或点保存后弹红色错误(右侧返回大段 JSON)。 - 调用控制台 API:
完整脚本:scripts_bash_cursor/Dify_专题十_模型列表为空Agent创建报错/专题十_内联_01.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题十_模型列表为空Agent创建报错/专题十_内联_01.sh
#!/usr/bin/env bash
set -euo pipefail
GET /console/api/workspaces/current/models/model-types/llm
返回: json {"data": []}
- 但数据库
provider_models里qwen3.6-27b记录存在,is_valid=t。 docker-api-1日志出现ValueError: {"code":0,"message":"success","data":{"model_schema":{"model":"qwen3.6-27b",...}}}。
二、根因
OpenAI 插件 llm.py 的 get_customizable_model_schema patch 中,把 max_tokens 写进了 model_properties:
props["max_tokens"] = 8192 # ❌ 错误位置Dify API 端(graphon.model_runtime.entities.model_entities.ModelPropertyKey)只承认以下 key:
完整程序:/opt/workbuddy/scripts_cursor/Dify_专题十_模型列表为空Agent创建报错/专题十_内联_01.py
运行/引用:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题十_模型列表为空Agent创建报错/专题十_内联_01.py
class ModelPropertyKey(StrEnum):
MODE = auto()
CONTEXT_SIZE = auto()
MAX_CHUNKS = auto()
FILE_UPLOAD_LIMIT = auto()
SUPPORTED_FILE_EXTENSIONS = auto()
MAX_CHARACTERS_PER_CHUNK = auto()
DEFAULT_VOICE = auto()
VOICES = auto()
WORD_LIMIT = auto()
AUDIO_TYPE = auto()
MAX_WORKERS = auto()没有 MAX_TOKENS。插件返回的 schema 里 model_properties.max_tokens 会导致 API 端 Pydantic 校验失败,于是该模型被过滤掉,GET /models/model-types/llm 返回空列表,前端创建 Agent 时报错。
顺带说明:同一次排查还暴露另一个通用坑——插件代码改完后如果存在语法错误(如遗留临时调试代码),
plugin_daemon只在重新 import 时才报错;运行中的实例因为sys.modules缓存旧代码,会"一直正常",直到某天被重新加载才炸。所以改完插件必须立即重启并验证。
三、解决:修正 llm.py patch
3.1 文件位置
容器内:
/app/storage/cwd/langgenius/openai-1.0.3@<hash>/models/llm/llm.pyhost 上对应 plugin_daemon 挂载卷,可用下面命令定位:
完整脚本:scripts_bash_cursor/Dify_专题十_模型列表为空Agent创建报错/专题十_内联_02.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题十_模型列表为空Agent创建报错/专题十_内联_02.sh
#!/usr/bin/env bash
set -euo pipefail
docker exec docker-plugin_daemon-1 sh -c 'find /app/storage/cwd -path "*langgenius/openai*" -name llm.py | head -1'3.2 正确代码
在 get_customizable_model_schema 的 fallback 分支里,只保留 context_size:
完整补丁:patches_cursor/Dify_专题十_模型列表为空Agent创建报错/专题十_内联_01.py
运行/引用:见 patches_cursor/Dify_专题十_模型列表为空Agent创建报错/专题十_内联_01.py
def get_customizable_model_schema(
self, model: str, credentials: dict
) -> AIModelEntity:
schema = {item.model: item for item in self.predefined_models()}.get(
_base_model(model)
)
if schema is None:
# Fallback for custom / non-OpenAI base models served via an
# OpenAI-compatible endpoint (e.g. Qwen, DeepSeek, local vLLM):
# clone a chat-capable predefined schema so the model is listable
# and can be set as the default model.
template = next(
(m for m in self.predefined_models()
if m.model_properties.get("mode") == "chat"),
None,
)
if template is None:
template = self.predefined_models()[0]
props = dict(template.model_properties)
mp = (credentials or {}).get("model_properties") or {}
# Align context_size to the actual vLLM --max-model-len (65536) so Dify
# never lets prompts exceed what the served model can accept. The
# credential-update API does NOT persist model_properties for this
# provider, so we hardcode the safe ceiling here as the default and
# still honor an explicit override when one is supplied.
VLLM_MAX_CTX = 64000
try:
props["context_size"] = int(mp["context_size"]) if "context_size" in mp else VLLM_MAX_CTX
except (ValueError, TypeError):
props["context_size"] = VLLM_MAX_CTX
# NOTE: do NOT put max_tokens into model_properties.
# Dify API-side ModelPropertyKey only allows {mode, context_size, ...}
# and does NOT include MAX_TOKENS; adding it breaks Pydantic validation
# when the API parses the schema from plugin_daemon.
# max_tokens is already exposed as a parameter_rule by the cloned template.
schema = template.model_copy(deep=True, update={"model_properties": props})
return schema.model_copy(
deep=True,
update={
"model": model,
"label": I18nObject(zh_hans=model, en_us=model),
"fetch_from": FetchFrom.CUSTOMIZABLE_MODEL,
},
)3.3 同步修正 align_qwen_ctx.py
如果用了 align_qwen_ctx.py 对齐 context_size,脚本里也只写 context_size:
完整程序:/opt/workbuddy/scripts_cursor/Dify_专题十_模型列表为空Agent创建报错/专题十_内联_02.py
运行/引用:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题十_模型列表为空Agent创建报错/专题十_内联_02.py
# 只保留 context_size,不要写 max_tokens
cred["model_properties"] = {"context_size": NEW_CTX}四、重启与验证
4.1 语法校验(本地做)
把修改后的 llm.py 拿到本地或容器里 py_compile:
完整脚本:scripts_bash_cursor/Dify_专题十_模型列表为空Agent创建报错/专题十_内联_03.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题十_模型列表为空Agent创建报错/专题十_内联_03.sh
#!/usr/bin/env bash
set -euo pipefail
python3 -m py_compile llm.py && echo "SYNTAX OK"4.2 覆盖回容器并重启
完整脚本:scripts_bash_cursor/Dify_专题十_模型列表为空Agent创建报错/专题十_内联_04.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题十_模型列表为空Agent创建报错/专题十_内联_04.sh
#!/usr/bin/env bash
set -euo pipefail
# 假设已把修复后的 llm.py 放到 /tmp/llm_fixed.py
docker cp /tmp/llm_fixed.py docker-plugin_daemon-1:/app/storage/cwd/langgenius/openai-1.0.3@<hash>/models/llm/llm.py
# 重启插件守护进程
cd /dify/dify-main/docker # 或 /dify/dify/docker
docker compose restart plugin_daemon注意:只改插件代码时,理论上只需
restart plugin_daemon;但如果 API 端已经缓存了旧 schema,建议同时restart api worker。
4.3 怎么看日志
完整脚本:scripts_bash_cursor/Dify_专题十_模型列表为空Agent创建报错/专题十_内联_05.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题十_模型列表为空Agent创建报错/专题十_内联_05.sh
#!/usr/bin/env bash
set -euo pipefail
# 1) 确认 plugin_daemon 没有 SyntaxError / Traceback
docker logs docker-plugin_daemon-1 2>&1 | tail -30
# 2) 确认 API 不再报 ValueError
docker logs docker-api-1 2>&1 | grep -iE 'ValueError|model_schema|qwen3.6-27b' | tail -204.4 怎么判断正常
调用控制台 API(可用 dify_api.py):
完整程序:/opt/workbuddy/scripts_cursor/Dify_专题十_模型列表为空Agent创建报错/专题十_内联_03.py
运行/引用:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题十_模型列表为空Agent创建报错/专题十_内联_03.py
from dify_api import Dify
d = Dify("38205036@qq.com", "Difyai123456")
d.login()
r = d.get("/workspaces/current/models/model-types/llm")
print(r["text"])正常应返回:
{"data":[{"provider":"langgenius/openai/openai","models":[{"model":"qwen3.6-27b","model_properties":{"context_size":64000,"mode":"chat"},"status":"active"}]}]}然后再去控制台创建 Agent,模型下拉应能选中 qwen3.6-27b。
五、本次实战(真实遇到的情况)
5.1 现象
用户截图:添加 Agent 时报错,模型列表看不到 qwen3.6-27b。实际此前"问答已经跑通",今天突然不行。
5.2 定位
GET /models/model-types/llm返回{"data":[]}——LLM 列表为空。- 数据库里
qwen3.6-27b记录存在且is_valid=t,排除"凭据丢失"。 docker-api-1日志抓到一个ValueError,内容竟是插件返回的完整 schema JSON;说明 API 端 Pydantic 校验失败。- 查看 schema 内容发现
model_properties带了{"context_size":64000,"max_tokens":8192,"mode":"chat"}。 - 核对 Dify API 源码
ModelPropertyKey枚举,确认没有MAX_TOKENS,这就是校验失败的直接原因。
5.3 改了什么
- 修改 OpenAI 插件
models/llm/llm.py:在get_customizable_model_schemafallback 里删除props["max_tokens"]的赋值,只保留context_size=64000。 - 同步修正
align_qwen_ctx.py:脚本里cred["model_properties"]只写context_size。
5.4 怎么重启
完整脚本:scripts_bash_cursor/Dify_专题十_模型列表为空Agent创建报错/专题十_内联_06.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题十_模型列表为空Agent创建报错/专题十_内联_06.sh
#!/usr/bin/env bash
set -euo pipefail
docker cp /tmp/llm_fixed.py docker-plugin_daemon-1:/app/storage/cwd/langgenius/openai-1.0.3@<hash>/models/llm/llm.py
cd /dify/dify-main/docker && docker compose restart plugin_daemon5.5 怎么看日志
完整脚本:scripts_bash_cursor/Dify_专题十_模型列表为空Agent创建报错/专题十_内联_07.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题十_模型列表为空Agent创建报错/专题十_内联_07.sh
#!/usr/bin/env bash
set -euo pipefail
# plugin_daemon 侧:确认 openai 插件正常加载
docker logs docker-plugin_daemon-1 2>&1 | tail -20
# API 侧:确认不再有 ValueError
docker logs docker-api-1 2>&1 | grep -iE 'ValueError|model_schema' | tail -205.6 怎么判断正常
完整脚本:scripts_bash_cursor/Dify_专题十_模型列表为空Agent创建报错/专题十_内联_08.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题十_模型列表为空Agent创建报错/专题十_内联_08.sh
#!/usr/bin/env bash
set -euo pipefail
# 用 dify_api.py 或 curl 调控制台 API
python - <<'PY'
from dify_api import Dify
d = Dify("38205036@qq.com", "Difyai123456")
d.login()
r = d.get("/workspaces/current/models/model-types/llm")
print(r["text"])
d.close()
PY预期返回中包含:
{"model":"qwen3.6-27b","model_properties":{"context_size":64000,"mode":"chat"},"status":"active"}之后刷新控制台,创建 Agent 时模型下拉可选 qwen3.6-27b,不再报错。
六、防呆清单
- [ ] 改完
llm.py/chat.py后,先在本地python3 -m py_compile做语法校验。 - [ ]
docker cp覆盖回容器后,必须docker compose restart plugin_daemon。 - [ ] 重启后立刻看
docker logs docker-plugin_daemon-1 | tail -30和docker logs docker-api-1 | grep -iE 'error|ValueError',不要等第二天才暴露。 - [ ] 不要把
max_tokens写进model_properties;它是parameter_rule,不是模型属性。 - [ ] 插件里加临时调试日志时,务必在验证后彻底删除,并重启确认干净。
七、关联文档
- 专题一:正确接入 Qwen3.6-27B(含
llm.py完整 patch)。 - 专题八:插件 patch 的
from __future__顺序子坑(同类"改完插件不验证"问题)。 - 专题九·四 / 9.6:
context_size对齐 64000 与凭据级model_properties不持久化问题。
本专题 Python 程序一览
| 文件 | 用途 | |---|---|| 专题十_01_查LLM模型列表.py | 专题十 · 步骤 1:GET model-types/llm,确认 qwen3.6-27b 在列表中且无 schema 校验失败。 | | 专题十_02_修正llm_schema补丁_修改后.py | 专题十 · 步骤 2 · 修改后:修正 llm.py,移除 model_properties 中的 max_tokens。 |
专题十_01_查LLM模型列表.py
运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题十_模型列表为空Agent创建报错/专题十_01_查LLM模型列表.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题十 · 步骤 1:GET model-types/llm,确认 qwen3.6-27b 在列表中且无 schema 校验失败。"""
from __future__ import annotations
import json
import sys
import _bootstrap # noqa: F401
import _config as C
from dify_api import Dify, pp
def main() -> int:
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
d.login()
r = d.get("/workspaces/current/models/model-types/llm")
pp("model-types/llm", r, limit=2000)
data = (r.get("json") or {}).get("data", [])
if not data:
print("\n[FAIL] data 为空 —— 常见原因:model_properties 含非法 max_tokens")
return 1
found = False
for p in data:
if p.get("provider") != C.OPENAI_PROVIDER:
continue
for m in p.get("models", []):
if m.get("model") == C.LLM_MODEL:
found = True
mp = m.get("model_properties") or {}
print("\n[OK] 找到模型:", json.dumps(m, ensure_ascii=False)[:500])
if "max_tokens" in mp:
print("[WARN] model_properties 仍含 max_tokens,可能再次触发校验失败")
if not found:
print("\n[FAIL] 未在列表中找到", C.LLM_MODEL)
return 1
return 0
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())专题十_02_修正llm_schema补丁_修改后.py
运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题十_模型列表为空Agent创建报错/专题十_02_修正llm_schema补丁_修改后.py
完整补丁:patches_cursor/Dify_专题十_模型列表为空Agent创建报错/专题十_内联_02.py
运行/引用:见 patches_cursor/Dify_专题十_模型列表为空Agent创建报错/专题十_内联_02.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题十 · 步骤 2 · 修改后:修正 llm.py,移除 model_properties 中的 max_tokens。"""
from __future__ import annotations
import sys
import _bootstrap # noqa: F401
import _plugin_patch as P
from dify_api import Dify
import _config as C # noqa: F401
def main() -> int:
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
d.login()
llm_path = P.find_plugin_file(d, "llm.py")
content = P.read_remote_file(d, llm_path)
new_content = P.replace_method(
content, "get_customizable_model_schema", P.GET_CUSTOMIZABLE_MODEL_SCHEMA
)
P.deploy_plugin_file(d, llm_path, new_content)
print("已应用专题十 llm.py 补丁(仅 context_size,不含 max_tokens)")
return 0
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())