Dify_专题一_集成Qwen3.6-27B_cursor.md
这是基于原文生成的 Cursor 版文档。
原文件未改动;本版只做三件事:统一阅读入口、修正已审出的明显问题、补充更适合快速排障的提示。
Cursor 版修订说明
- 统一 PostgreSQL 容器名示例为
docker-db_postgres-1,并建议以docker ps实际结果为准。 - 保留“不要把 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 版纠错)
专题一:在 Dify 集成入口正确添加自定义 Qwen3.6-27B,使其在创建 Agent 时可被选择
专题目标:把 vLLM 上的
qwen3.6-27b作为自定义大模型接入 Dify 的「模型供应商」入口,并确保后续创建 Agent 时能在模型下拉里正常选中它、且不再显示"不兼容"。涉及两处插件代码 patch,本文直接给出可落地的完整代码。
阅读导航(何时打开 · 报错往哪查)
本文是单点修复/排障专题,配合《安装配置手册》使用;下文实战步骤与代码全部保留,此处只做索引。
| 现象 | 优先看本文 | 还可能是 |
|---|---|---|
| 只配了 Base、列表无 qwen | §二 单独添加 | 安装手册步骤 12 |
| 「不兼容」/ 无法选中 | §三~六 patch | 专题十 |
| 保存凭据 Connection error | 前置 vLLM | 专题五·第 2 层 |
0. 为什么"加了模型却选不到 / 显示不兼容"
在 Dify 里让一个自定义模型(尤其非 OpenAI 官方的,如 Qwen)在 Agent 创建时可被选中,要跨过两道门槛:
- 入口必须"单独添加":在 OpenAI 兼容供应商里,
qwen3.6-27b必须作为 customizable-model(自定义模型) 显式添加,光配openai_api_base不够——否则它根本不出现在模型列表。 - 插件必须能"派生出它的 schema":OpenAI 插件的
get_customizable_model_schema()会按 base model 名去查预定义 schema;qwen3.6-27b没有对应基座时会抛出Base model qwen3.6-27b not found,前端就把它标成"不兼容",Agent 下拉里即便出现也无法选中/设默认。
本文按"入口添加 → 设为默认 → patch 插件让 schema 可派生 → 验证可选"的顺序讲清楚。
一、前置条件
- vLLM 容器已起,
http://116.62.10.183:8000/v1/models返回id = "qwen3.6-27b"。 - Dify 已安装
langgenius/openai:1.0.3插件(控制台 → 插件市场)。 - 已拿到控制台管理员登录态(本文
38205036@qq.com/Difyai123456)。
二、在模型供应商入口添加自定义 Qwen3.6-27B
- 控制台 → 模型供应商 → 找到 OpenAI → 点击「设置 / 添加模型」。
- 填供应商级连接信息:
- API Key:
123456 - API Base:
http://116.62.10.183:8000/v1
- 在 自定义模型(Customizable Model) 区域:
- 模型类型:LLM
- 模型名称(Model Name):
qwen3.6-27b(必须和 vLLM 的--served-model-name完全一致)
- 点击「保存」。此时该模型会出现在 OpenAI 供应商下的模型列表里。
注意:模型名称是大小写/字符敏感的,务必与 vLLM
served-model-name完全匹配。
本次实操也可以(推荐)走控制台 API。 模型供应商页虽然存在,但用 dify_api.py 更不容易填错字段。对应接口:
POST /console/api/workspaces/current/model-providers/langgenius/openai/openai/models/credentials
完整程序:/opt/workbuddy/scripts_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_01.py
运行/引用:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_01.py
from dify_api import Dify, pp
d = Dify("38205036@qq.com", "Difyai123456")
d.login()
body = {
"model": "qwen3.6-27b",
"model_type": "llm",
"credentials": {
"openai_api_key": "123456",
"openai_api_base": "http://116.62.10.183:8000/v1",
"openai_organization": "",
"api_protocol": "chat",
},
"name": "qwen3.6-27b",
}
r = d.post(
"/workspaces/current/model-providers/langgenius/openai/openai/models/credentials",
body,
)
pp("add qwen cred", r) # 期望 200/201
r = d.get("/workspaces/current/models/model-types/llm")
pp("llm list", r)
d.close()已存在同一模型时改用 PUT 同一路径,并带上 GET 回来的 current_credential_id(见专题九 align_qwen_ctx.py)。验证:model-types/llm 里 qwen3.6-27b 的 status=active。
三、把它设为默认 LLM(否则默认指向插件自带的 gpt-5.6)
添加后还需把默认 LLM 改为 qwen3.6-27b,否则 Dify 默认还指向插件自带的 gpt-5.6,且 Agent 创建时默认的也是它。
方式 A · 控制台
- 模型供应商 → 点
qwen3.6-27b右侧「设为默认」(System Model → LLM)。
方式 C · 控制台 API(和方式 A 同一接口)
完整程序:/opt/workbuddy/scripts_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_02.py
运行/引用:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_02.py
from dify_api import Dify, pp
d = Dify("38205036@qq.com", "Difyai123456")
d.login()
r = d.post(
"/workspaces/current/default-model",
body={
"model_settings": [{
"model_type": "llm",
"provider": "langgenius/openai/openai",
"model": "qwen3.6-27b",
}]
},
)
pp("set default llm", r)
d.close()若该接口在你的版本返回 404,用方式 B 改 tenant_default_models。
方式 B · 数据库(推荐,确保持久生效) 默认模型记录在 tenant_default_models 表,把 model_type='llm' 的记录从 gpt-5.6 改为 qwen3.6-27b:
完整脚本:scripts_bash_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_01.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_01.sh
#!/usr/bin/env bash
set -euo pipefail
docker exec -it docker-db_postgres-1 psql -U postgres -d dify \
-c "UPDATE tenant_default_models SET model='qwen3.6-27b' WHERE model_type='llm';"修改后重启让配置生效: 完整脚本:scripts_bash_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_02.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_02.sh
#!/usr/bin/env bash
set -euo pipefail
cd /dify/dify-main/docker
docker compose restart api plugin_daemon四、关键 Patch:让 qwen 有可派生的 model schema(消除"不兼容")
这一步是"能否在 Agent 里选中"的核心。OpenAI 插件的 get_customizable_model_schema() 找不到 qwen3.6-27b 的基座就会标"不兼容"。我们让它在找不到基座时 clone 一个通用的 chat schema 作为该模型的 schema。
升级注意(2026-08):
plugin_daemon卷里可能同时存在openai-1.0.3@…与openai-1.0.4@…。运行时只加载当前启用版本;若只给旧目录打补丁,控制台会突然全面「不可用」。推荐脚本(遍历所有 openai 版本):
scripts_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_04_改llm_schema_修改后.py
文件(容器内路径,host 上对应 plugin_daemon 卷;请对每个 openai-*@* 目录检查):
/app/storage/cwd/langgenius/openai-<版本>@<hash>/models/llm/llm.py
# 宿主机常见映射:
/dify/dify-main/docker/volumes/plugin_daemon/cwd/langgenius/openai-*@*/models/llm/llm.py先备份:cp llm.py llm.py.bak_qwenfix 判定是否已补丁:文件中是否含 Fallback for custom / non-OpenAI base models。
改动位置:替换 get_customizable_model_schema 整个函数体。完整代码(已在本环境生效):
完整补丁:patches_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_01.py
运行/引用:见 patches_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_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 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,
},
)要点:
_base_model(model)是插件里按命名规则取基座名的既有函数,无需改动。- 找不到基座时,取第一个
mode == "chat"的预定义模型当模板 clone;context_size默认硬编码为 64000(对齐 vLLM--max-model-len 65536),也可被凭据model_properties.context_size覆盖。 - 不要把
max_tokens写进model_properties:API 端ModelPropertyKey枚举不含MAX_TOKENS,会导致 schema 解析ValueError;max_tokens作为parameter_rule已由克隆的模板提供。 - 改完
docker compose restart plugin_daemon加载。
五、验证:创建 Agent 时已可选、不再"不兼容"
- 控制台 → 创建应用 → Agent(或聊天助手/工作流,凡是选模型的地方)。
- 模型下拉里应能看到
qwen3.6-27b,且不再标"不兼容",可以选中并设为该应用的模型。 - 用 Agent API 实跑一条(必须 streaming):
完整脚本:scripts_bash_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_03.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_03.sh
#!/usr/bin/env bash
set -euo pipefail
curl -s -N -X POST 'http://192.168.110.207/v1/chat-messages' \
-H 'Authorization: Bearer app-v9yYVTQTrUoSFHNqAhufSGmh' \
-H 'Content-Type: application/json' \
-d '{"inputs":{},"query":"你好,介绍一下你自己","response_mode":"streaming","user":"debug"}'返回正常文本、ERROR EVENT 为空,即"选得到且能用"。
六、补充 Patch:Agent 实际调用时合并多条 system(否则会报 400)
上一步只解决"选得到"。但 Agent 真正对话时,Dify 会把多条 system/developer 提示(知识库助手 + Agent 工具 + 空 system + 配置 + 环境变量)堆在前面;而 Qwen3/qwen3_5 的 chat template 只接受一条 system 且必须在最前,否则回 400 System message must be at the beginning。需再 patch chat.py 把多条 system 合并成一条。
文件:
.../openai-1.0.3@<hash>/models/llm/chat.py先备份:cp chat.py chat.py.bak_systemfix
改动 1 · 在模块顶部(from __future__ import annotations 之后)新增 helper:
完整程序:/opt/workbuddy/scripts_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_03.py
运行/引用:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_03.py
def _ensure_system_first(messages: list[PromptMessage]) -> list[PromptMessage]:
"""Qwen3/vLLM chat templates accept ONLY one system message, at the start.
Dify (esp. Agent apps) stacks several system/developer prompts: KB assistant
prompt + agent tool prompt + an empty system + config manifest... Merging them
into a single system message (joined by blank lines, empties dropped) keeps it
at the front; every other message keeps its original relative order.
"""
system_parts: list[str] = []
rest: list[PromptMessage] = []
for m in messages:
if isinstance(m, (SystemPromptMessage, DeveloperPromptMessage)):
text = _text_content(m)
if text.strip():
system_parts.append(text)
else:
rest.append(m)
if system_parts:
rest.insert(0, SystemPromptMessage(content="\n\n".join(system_parts)))
return rest注意:
_text_content()、SystemPromptMessage、DeveloperPromptMessage均为该模块已有定义/导入,无需额外添加。_ensure_system_first必须放在from __future__ import annotations之后,否则整文件SyntaxError、插件直接加载失败。
改动 2 · 在 client.chat.completions.create(...) 的 messages= 处调用它:
原调用: 完整程序:/opt/workbuddy/scripts_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_04.py
运行/引用:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_04.py
response = client.chat.completions.create(
model=model,
messages=cast(Any, [message(item) for item in prompt_messages]),
stream=stream,
**params
)改为(只改 messages= 一行): 完整程序:/opt/workbuddy/scripts_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_05.py
运行/引用:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_05.py
response = client.chat.completions.create(
model=model,
messages=cast(Any, [message(item) for item in _ensure_system_first(prompt_messages)]),
stream=stream,
**params
)改完 docker compose restart plugin_daemon。现在 Agent 既能"选到"、也能"正常调通"。
七、落地清单
- [ ] OpenAI 兼容供应商已添加,API Base 指向 vLLM
/v1。 - [ ]
qwen3.6-27b作为自定义 LLM 模型单独添加,名称与 served-model-name 一致。 - [ ] 已设为默认 LLM(控制台 +
tenant_default_models均指向 qwen3.6-27b)。 - [ ]
llm.py已 patch(clone 通用 chat schema),插件重启生效 → 列表不再"不兼容"。 - [ ] 创建 Agent 时模型下拉可选中
qwen3.6-27b。 - [ ]
chat.py已 patch(合并多条 system),插件重启生效 → Agent 对话不报 400。 - [ ] Agent API streaming 调用返回正常答案。
风险提示:llm.py / chat.py 两处补丁位于插件卷内,升级/重装 openai 插件时会被覆盖,需重打(备份 llm.py.bak_qwenfix、chat.py.bak_systemfix 已留在卷内)。 已根治(2026-09-24 起):model 类插件的自动升级已关闭、并把 langgenius/openai 加入排除名单,补丁不会再被夜间自动升级冲掉;改为「想升级时手动升 → 升完立刻重跑 专题一_04」。做法与复核命令见 专题十七 · §5 根治。 另注:chat.py 那处合并补丁在 openai 插件 1.0.5 已由上游原生实现(_single_leading_system),新版本上通常不必再打,只需盯 llm.py。
八、实战记录(本次部署真实遇到的情况)
本次真实卡住的两处,都和"接入口加模型"直接相关。下面按统一维度拆解——现象(当时看到什么)→ 定位(怎么确认根因)→ 改了什么(文件/命令逐条)→ 怎么重启服务 → 怎么看日志 → 怎么判断正常,方便照着复现与自查。
8.1 模型显示"不兼容"、Agent 选不到
现象:保存 qwen3.6-27b 后,OpenAI 供应商下它被标成"不兼容";创建 Agent 时 LLM 下拉里选不到、悬停提示模型不可用。
定位:
- 控制台看该模型状态 = 不兼容;
- 读插件
llm.py的get_customizable_model_schema,确认它只认预定义 OpenAI 基座(如gpt-4),自定义名qwen3.6-27b找不到就抛Base model qwen3.6-27b not found,前端据此标"不兼容"。
改了什么(文件:.../openai-1.0.3@<hash>/models/llm/llm.py,完整代码见本文第四节):
- 先备份:
cp llm.py llm.py.bak_qwenfix - 把
get_customizable_model_schema改为:找不到基座时 clone 一个通用 chat schema;仅允许凭据model_properties覆盖context_size(默认 64000),不要把max_tokens写进model_properties。
怎么重启服务: 完整脚本:scripts_bash_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_04.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_04.sh
#!/usr/bin/env bash
set -euo pipefail
cd /dify/dify-main/docker && docker compose restart plugin_daemon
sleep 8 && docker ps --filter name=plugin_daemon --format '{{.Names}} {{.Status}}'怎么看日志(确认插件加载成功): 完整脚本:scripts_bash_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_05.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_05.sh
#!/usr/bin/env bash
set -euo pipefail
docker logs docker-plugin_daemon-1 2>&1 | grep -iE 'error|syntax|traceback|openai' | tail -20无 SyntaxError/Traceback、能看到 openai 插件 register 日志 → 加载正常。
怎么判断正常:回到控制台 → 模型供应商 → OpenAI,qwen3.6-27b 状态从"不兼容"变正常;创建 Agent 时 LLM 下拉能选中它。
8.2 新建应用默认仍是 gpt-5.6,发消息报 The model gpt-5.6 does not exist
现象:即使 8.1 修好,新建 Agent 默认 LLM 仍指向不存在的 gpt-5.6,发消息报 The model gpt-5.6 does not exist。
定位:数据库 tenant_default_models 的 llm 行还是 gpt-5.6(插件自带基座),没切到 qwen。
改了什么(见本文第三节 / 专题五第 5 层): 完整脚本:scripts_bash_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_06.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_06.sh
#!/usr/bin/env bash
set -euo pipefail
docker exec -it docker-db_postgres-1 psql -U postgres -d dify \
-c "UPDATE tenant_default_models SET model='qwen3.6-27b' WHERE model_type='llm';"怎么重启服务: 完整脚本:scripts_bash_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_07.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_07.sh
#!/usr/bin/env bash
set -euo pipefail
cd /dify/dify-main/docker && docker compose restart api worker plugin_daemon怎么看日志(确认 api 不再报找不到模型): 完整脚本:scripts_bash_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_08.sh
运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_08.sh
#!/usr/bin/env bash
set -euo pipefail
docker logs docker-api-1 2>&1 | grep -iE 'gpt-5.6|does not exist|error' | tail -20应无 The model gpt-5.6 does not exist。
怎么判断正常:新建 Agent 默认 LLM 自动是 qwen3.6-27b;用 Agent API streaming 调一次(见本文第五节),返回正常答案、ERROR EVENT 为空。
一句话对照:专题一前面是"怎么加",本节是"本次真正卡住的两处"——不兼容 + 默认模型错,两处都修好 Agent 才选得到 qwen3.6-27b。
本专题 Python 程序一览
| 文件 | 用途 | |---|---|| 专题一_01_添加Qwen凭据.py | 专题一 · 步骤 2:控制台 API 添加自定义 Qwen3.6-27B 凭据。 | | 专题一_02_设默认LLM.py | 专题一 · 步骤 3:设默认 LLM(API + tenant_default_models)。 | | 专题一_03_验证vLLM与LLM列表.py | 专题一 · 步骤 5:验证 vLLM 可达且 Dify LLM 列表中 qwen active。 | | 专题一_04_改llm_schema_修改前.py | 专题一 · 步骤 4 · 修改前:确认 llm.py 尚未打 Qwen schema 补丁。 | | 专题一_04_改llm_schema_修改后.py | 专题一 · 步骤 4 · 修改后:patch llm.py 克隆 chat schema,重启 plugin_daemon。 | | 专题一_05_改chat_system_修改前.py | 专题一 · 步骤 6 · 修改前:确认 chat.py 尚未合并多条 system。 | | 专题一_05_改chat_system_修改后.py | 专题一 · 步骤 6 · 修改后:patch chat.py 合并 system,重启 plugin_daemon。 |
专题一_01_添加Qwen凭据.py
运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_01_添加Qwen凭据.py
完整补丁:patches_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_02.py
运行/引用:见 patches_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_02.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题一 · 步骤 2:控制台 API 添加自定义 Qwen3.6-27B 凭据。"""
from __future__ import annotations
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()
body = {
"model": C.QWEN_MODEL,
"model_type": "llm",
"credentials": {
"openai_api_key": C.VLLM_KEY,
"openai_api_base": C.VLLM_BASE,
"openai_organization": "",
"api_protocol": "chat",
},
"name": C.QWEN_MODEL,
}
path = "/workspaces/current/model-providers/%s/models/credentials" % C.OPENAI_PROVIDER
r = d.get(
"/workspaces/current/model-providers/%s/models/credentials?model_type=llm&model=%s"
% (C.OPENAI_PROVIDER, C.QWEN_MODEL)
)
cid = (r.get("json") or {}).get("current_credential_id")
if cid:
body["credential_id"] = cid
r = d.req("PUT", path, body=body)
pp("update qwen cred", r)
else:
r = d.post(path, body=body)
pp("add qwen cred", r)
r2 = d.get("/workspaces/current/models/model-types/llm")
pp("llm list", r2)
return 0 if r.get("code") in ("200", "201") else 1
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())专题一_02_设默认LLM.py
运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_02_设默认LLM.py
完整补丁:patches_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_03.py
运行/引用:见 patches_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_03.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题一 · 步骤 3:设默认 LLM(API + tenant_default_models)。"""
from __future__ import annotations
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.post(
"/workspaces/current/default-model",
body={
"model_settings": [{
"model_type": "llm",
"provider": C.OPENAI_PROVIDER,
"model": C.QWEN_MODEL,
}]
},
)
pp("set default llm (API)", r)
sql = (
"UPDATE tenant_default_models SET model='%s' WHERE model_type='llm';"
% C.QWEN_MODEL
)
out, err = d.sh(
"docker exec %s psql -U postgres -d dify -c \"%s\""
% (C.PG_CONTAINER, sql)
)
print("tenant_default_models:\n", out or err)
out2, err2 = d.sh(
"cd %s && docker compose restart api plugin_daemon" % C.DIFY_DOCKER_DIR,
t=120,
)
print(out2 or err2)
return 0
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())专题一_03_验证vLLM与LLM列表.py
运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_03_验证vLLM与LLM列表.py
完整补丁:patches_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_04.py
运行/引用:见 patches_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_04.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题一 · 步骤 5:验证 vLLM 可达且 Dify LLM 列表中 qwen active。"""
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:
print("=== 1/2 从 207 curl vLLM /v1/models ===")
out, err = d.sh(
"curl -s -m 10 -H 'Authorization: Bearer %s' %s/models"
% (C.VLLM_KEY, C.VLLM_BASE)
)
print(out or err)
try:
data = json.loads(out)
ids = [m.get("id") for m in data.get("data", [])]
print("model ids:", ids)
if C.QWEN_MODEL not in ids:
print("[WARN] vLLM 列表无", C.QWEN_MODEL)
except Exception:
pass
print("\n=== 2/2 Dify model-types/llm ===")
d.login()
r = d.get("/workspaces/current/models/model-types/llm")
pp("llm list", r)
found = False
for p in (r.get("json") or {}).get("data") or []:
if p.get("provider") != C.OPENAI_PROVIDER:
continue
for m in p.get("models") or []:
if m.get("model") == C.QWEN_MODEL:
found = True
print("qwen status:", m.get("status"), "props:", m.get("model_properties"))
if not found:
print("[WARN] Dify LLM 列表未找到", C.QWEN_MODEL)
return 1
return 0
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())专题一_04_改llm_schema_修改前.py
运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_04_改llm_schema_修改前.py
完整补丁:patches_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_05.py
运行/引用:见 patches_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_05.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题一 · 步骤 4 · 修改前:确认 llm.py 尚未打 Qwen schema 补丁。"""
from __future__ import annotations
import sys
import _bootstrap # noqa: F401
import _config as C
import _plugin_util as PU
from dify_api import Dify, pp
def main() -> int:
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
d.login()
path = PU.find_plugin_file(d, "models/llm/llm.py")
print("llm.py:", path)
content = PU.container_read(d, C.PLUGIN_CONTAINER, path)
if PU.LLM_PATCH_MARKER in content:
print("[INFO] 已存在 Qwen schema fallback 补丁,无需再改")
else:
print("[WARN] 尚未打补丁 — 运行 专题一_04_改llm_schema_修改后.py")
snippet = PU.grep_in_container(d, C.PLUGIN_CONTAINER, path, "get_customizable_model_schema")
print(snippet[:1200])
r = d.get("/workspaces/current/models/model-types/llm")
pp("llm list(不兼容时 status 非 active)", r)
return 0
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())专题一_04_改llm_schema_修改后.py
运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_04_改llm_schema_修改后.py
完整补丁:patches_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_06.py
运行/引用:见 patches_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_06.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题一 · 步骤 4 · 修改后:patch llm.py 克隆 chat schema,重启 plugin_daemon。"""
from __future__ import annotations
import sys
import _bootstrap # noqa: F401
import _config as C
import _plugin_util as PU
from dify_api import Dify
def main() -> int:
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
d.login()
path = PU.find_plugin_file(d, "models/llm/llm.py")
print("target:", path)
content = PU.container_read(d, C.PLUGIN_CONTAINER, path)
if PU.LLM_PATCH_MARKER in content:
print("already patched")
return 0
d.sh(
"docker exec %s cp %s %s.bak_qwenfix"
% (C.PLUGIN_CONTAINER, path, path)
)
new_content = PU.patch_llm_schema(content)
PU.container_write(d, C.PLUGIN_CONTAINER, path, new_content)
out, err = d.sh(
"docker exec %s python -m py_compile %s" % (C.PLUGIN_CONTAINER, path)
)
print("py_compile:", out or err or "ok")
print("restarting plugin_daemon ...")
PU.restart_plugin_daemon(d)
print("done — 回控制台确认 qwen 不再「不兼容」")
return 0
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())专题一_05_改chat_system_修改前.py
运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_05_改chat_system_修改前.py
完整补丁:patches_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_07.py
运行/引用:见 patches_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_07.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题一 · 步骤 6 · 修改前:确认 chat.py 尚未合并多条 system。"""
from __future__ import annotations
import sys
import _bootstrap # noqa: F401
import _config as C
import _plugin_util as PU
from dify_api import Dify
def main() -> int:
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
d.login()
path = PU.find_plugin_file(d, "models/llm/chat.py")
print("chat.py:", path)
if PU.CHAT_PATCH_MARKER in PU.container_read(d, C.PLUGIN_CONTAINER, path):
print("[INFO] 已有 _ensure_system_first 补丁")
else:
print("[WARN] 尚未打补丁 — Agent 问答可能 400 System message must be at the beginning")
print(PU.grep_in_container(d, C.PLUGIN_CONTAINER, path, "chat.completions.create")[:800])
return 0
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())专题一_05_改chat_system_修改后.py
运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_05_改chat_system_修改后.py
完整补丁:patches_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_08.py
运行/引用:见 patches_cursor/Dify_专题一_集成Qwen3.6-27B/专题一_内联_08.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题一 · 步骤 6 · 修改后:patch chat.py 合并 system,重启 plugin_daemon。"""
from __future__ import annotations
import sys
import _bootstrap # noqa: F401
import _config as C
import _plugin_util as PU
from dify_api import Dify
def main() -> int:
d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
try:
d.login()
path = PU.find_plugin_file(d, "models/llm/chat.py")
print("target:", path)
content = PU.container_read(d, C.PLUGIN_CONTAINER, path)
if PU.CHAT_PATCH_MARKER in content:
print("already patched")
return 0
d.sh(
"docker exec %s cp %s %s.bak_systemfix"
% (C.PLUGIN_CONTAINER, path, path)
)
new_content = PU.patch_chat_system(content)
PU.container_write(d, C.PLUGIN_CONTAINER, path, new_content)
out, err = d.sh(
"docker exec %s python -m py_compile %s" % (C.PLUGIN_CONTAINER, path)
)
print("py_compile:", out or err or "ok")
print("restarting plugin_daemon ...")
PU.restart_plugin_daemon(d)
print("done — 新开 Agent 对话验证不再 400")
return 0
finally:
d.close()
if __name__ == "__main__":
sys.exit(main())