跳到主要内容

dsh-unsloth

已验证

dsh-unsloth · v0.2.0 · MIT

Unsloth for DeepSeek Harness: local LLM fine-tuning and dataset generation. Bundles a zero-dependency Python MCP server and mounts DSH's own MCP client, exposing model inventory, Data Recipe dataset generation, LoRA/QLoRA training, GGUF export and inferen

安装

dsh plugin add dsh-unsloth

用 dsh --profile default --dump-config 确认 layer 已生效 —— 参见安装指南。

源码

发布到 npm 但没有公开仓库。安装前请检查包内容。

标签

作者

说明文档

dsh-unsloth

把 Unsloth(本地大模型微调 / 微调数据集生成)接进 DeepSeek Harness 的插件。

装上之后,模型就能直接调用 Unsloth:看本机模型清单、用 Data Recipe 生成微调数据集、起 LoRA/QLoRA 训练、看 loss 曲线、导出 GGUF、跑推理自检——全部以原生工具形式出现,名字是 mcp__unsloth__<tool>。


它是什么 / 怎么工作的

DeepSeek Harness
  └── dsh-unsloth(本插件,很薄)
        ├─ 1. 找 Python 解释器(优先用 Unsloth Studio 自带的 venv)
        ├─ 2. 可选:先把 Unsloth Studio 拉起来(autoStartStudio)
        └─ 3. 挂载 DSH 自带的 @deepseek-ai/dsh-mcp-client
                 └── server/unsloth_mcp_server.py(零依赖,纯标准库)
                        ├── HTTP → Unsloth Studio 后端  http://127.0.0.1:8888
                        │        (训练 / Data Recipe / 模型 / 导出 / 推理)
                        └── CLI  → python -m unsloth_cli
                                 (train / export / inference / chat)

设计上刻意不复刻协议层:MCP 客户端用的是 DSH 自己的那一个,所以工具命名(mcp__<serverName>__<tool>)、单次调用超时、断线自动重连的行为,跟手写一条 cordis.patch.yml 条目完全一致;插件只负责"找到解释器 + 找到脚本 + 挂上去"。


环境要求

要求 说明
Unsloth Desktop 装好即可(默认路径 ~/.unsloth/studio)。Studio 的 venv 会被自动用作解释器
Python 3.8+ MCP 服务只用标准库,所以任何 Python 都行;没有 Python 时插件会打印明确的错误而不是静默失败
DSH 带 @deepseek-ai/dsh-mcp-client(dsh-base 的依赖,向来都在)

安装

方式 A:一键脚本(推荐,不需要 pnpm / 不需要联网)

# 在普通 PowerShell 里运行(不要在被文件沙箱限制的环境里跑)
.\install.ps1                       # 默认装到 web 这个 profile
.\install.ps1 -Profile web -AutoStartStudio

脚本做三件事:把插件复制到 <profile>\node_modules\dsh-unsloth;在 profile 的 cordis.patch.yml 里补一条 insert(已有则跳过);备份原配置。改完是热加载生效,不用重启 DSH。

方式 B:手动(等价于脚本)

  1. 把本目录整体复制到 %APPDATA%\dsh-desktop\harness\profiles\web\node_modules\dsh-unsloth
  2. 在 %APPDATA%\dsh-desktop\harness\profiles\web\cordis.patch.yml 末尾追加:
- insert:
    - id: unsloth
      name: dsh-unsloth
      config:
        serverName: unsloth
        hfEndpoint: https://hf-mirror.com   # 国内网络建议
        disableXet: true
        autoStartStudio: true

⚠️ 必须是 insert: 列表形式。 这一层是"id 定向覆盖层",裸写 - id: xxx 会被当成"覆盖已有条目",新 id 找不到目标就被静默忽略(日志里连报错都没有)。

方式 C:作为正式包分发

推到 npm 后即可用 DSH 插件市场安装——package.json 里 dsh.bundle.patch 已指向插件自带的 cordis.patch.yml,市场装完会自动 insert,无需手改 YAML。


配置项

写在 insert 条目的 config: 下即可,全部有默认值:

键 默认 说明
enabled true 关掉整个桥接而不必删条目
serverName unsloth MCP 命名空间,工具名 = mcp__<serverName>__<tool>
python "" 指定解释器绝对路径;留空自动探测(Studio venv → 常见安装位置 → PATH)
studioUrl http://127.0.0.1:8888 Studio 后端地址
hfEndpoint "" 注入 HF_ENDPOINT 给派生的 Unsloth 子进程,如 https://hf-mirror.com
disableXet true 注入 HF_HUB_DISABLE_XET=1。Xet 走 cas-server.xethub.hf.co,镜像环境下必然 401 中断下载
homeDir "" MCP 服务的 config.json 与日志目录;留空 = <DSH_HOME>\unsloth-mcp(故意不放 node_modules,重装不会被清掉)
toolCallTimeoutMs 600000 单次工具调用超时(默认 60s 对训练/导出太短)
failOnStartupError false 初始连接失败是否让插件激活失败
autoStartStudio false 激活时若 Studio 没在监听就用它的 venv 起一个(真香:不用再手动开桌面版)
studioArgs [] 追加给 unsloth studio 的参数,如 ["--no-cloudflare"]

工具清单(20 个,全部来自 MCP 服务)

工具 用途
studio_status Studio 是否在跑 + 版本 + 硬件。动手前先调它
studio_start 启动 Studio(可 api_only),后台常驻
studio_stop 停掉全部 Studio 实例(官方 unsloth studio stop,按 STUDIO_HOME)
studio_api 万能后门:直连 Studio 任意 /api /v1 端点(400+ 路由)
unsloth_cli 原始 CLI 后门:train / inference / chat / export / list-checkpoints / studio(含 run stop verify-install)/ start(把 Claude、Codex、dsh 等指向 Unsloth)
models_list kind=local|cached|loras|checkpoints,外加 checkpoints_cli(扫输出根目录,看得见 CLI 训练的检查点)
export 导出检查点:merged-16bit / merged-4bit / gguf / lora,可推 HuggingFace
recipe_seed_inspect 检查数据集生成的种子(列名 + 预览行)
recipe_validate 校验 recipe(建任务前必做)
recipe_create 建数据集生成任务(preview 试跑 / full 全量)
recipe_status 任务状态 + 分析(无任务时返回 idle,不报错)
recipe_dataset 取回生成的训练样本
unsloth_text 文本训练(CLI 路径)。30+ 训练参数;spawn 前自动 --dry-run 预检;dry_run_only=true 只预检不训练
unsloth_image 视觉 / VLM 训练(Studio API 路径)
unsloth_text_status 文本训练状态:Studio 状态 + 本服务登记的任务(pid 存活、日志 verdict、最新 loss)
unsloth_image_status 视觉训练状态(存活状态取自 Studio 后端)
train_status 【已弃用】不分类型的全局状态,等价于上面两个的并集
train_stop 停止训练:Studio 后端 + 按 pid 结束 CLI 进程树(scope=all|cli|studio)
train_runs 历史训练记录
chat 推理自检;Studio 没加载模型时自动回退到 unsloth inference

0.1.3 曾把训练拆成 unsloth_text / unsloth_image,同时删掉了 train_start; 本 README 之前还列着 train_start,属于文档滞后,已修正。

训练参数(unsloth_text)

参数表直接对照 CLI 的 Typer 定义逐项核对,不是手抄:

model / dataset / local_dataset / format_type / training_type
max_seq_length / load_in_4bit / output_dir
num_epochs / max_steps / save_steps / warmup_steps
learning_rate / batch_size / gradient_accumulation_steps
weight_decay / random_seed / gradient_checkpointing / packing / train_on_completions
lora_r / lora_alpha / lora_dropout / target_modules / use_rslora / use_dora
enable_wandb / wandb_project / enable_tensorboard / tensorboard_dir
config / extra_args / dry_run_only / unload_inference / force

⚠️ training_type 的坑(本版本已自动兜住)

unsloth train 只认 lora 与 full;Studio 的 REST API 只认 LoRA/QLoRA / Full Finetuning / Continued Pretraining。两套词表互不通用。

CLI 用 setattr 覆盖配置、且没开 Pydantic 的 validate_assignment,所以传错值不会报错: 它会一路走到 use_lora = training_type.lower() == "lora" —— "lora/qlora" != "lora", 于是 use_lora=False,load_in_4bit 也被强制成 False,你要的 QLoRA 悄悄变成完全微调。 4GB 卡上这基本等于必挂。

本机历史证据:unsloth-mcp/logs/train-smoke02.log 第 25 行 Full finetuning mode - training all parameters,可训练参数 72.44% —— 那一次请求的正是 LoRA/QLoRA。 本版本会自动归一化,并在返回的 notes 里说明改了什么;修复后同一组参数的实测结果是 LoRA adapters configured successfully、可训练参数 1.75%(8,798,208 / 502,830,976)。

⚠️ output_dir 必须在 outputs 根目录内

这条限制由 trainer 自己强制(studio/backend/core/training/trainer.py 的 resolve_output_dir()),CLI 与 Studio API 两条路径都一样 —— 0.1.2 / 0.1.3 「支持任意输出路径」的说法是不成立的,实测会以 Training error: path escapes root: ... is not under ...\.unsloth\studio\outputs 失败。

现在两个训练工具都会在启动进程之前拦下并给出根目录,可行写法只有两种:

  • 给相对目录名(如 my-run)→ 落到 ~/.unsloth/studio/outputs/my-run
  • 给根目录之内的绝对路径

不传时检查点直接落在 ~/.unsloth/studio/outputs 下。


验证

离线预检(不碰 harness,用桩 ctx 跑 apply(),并让配置通过 mcp-client 自己的 Schema 校验):

cd $env:APPDATA\dsh-desktop\harness\profiles\web
node .\dsh-unsloth-selftest.mjs

预期输出里应有 mounted mcp-client: true、ACCEPTED、interpreter: ... exists: true。

运行期:看 %APPDATA%\dsh-desktop\logs\harness.log。可靠出现的标志是 MCP 服务那一行(它由插件挂载的 mcp-client 启动,stderr 会被转发到日志):

[stderr] [unsloth-mcp] ready (base_url=http://127.0.0.1:8888, cli=...)

然后让模型调一次 mcp__unsloth__studio_status:能返回 Studio 版本与 GPU 信息就说明整条链路通了。

插件自己还有一行 banner([dsh-unsloth] info: ... -> ... (serverName=..., home=...)),它被镜像写到 stderr。 在终端里跑 harness 时一定看得到;在 DSH Desktop 里是否落到 harness.log 取决于宿主对 stderr 的捕获方式, 别把它当成唯一的判断依据——以 [unsloth-mcp] ready 和工具调用为准。


排错

现象 原因 / 解法
工具完全不出现,日志无报错 cordis.patch.yml 里写成了裸 - id: → 改成 insert: 列表形式
改了插件的 JS 代码但行为没变 配置热重载会重新挂插件,但 Node 的 ESM 模块缓存不会失效 → 重启一次 DSH Desktop(实测:改配置后 [unsloth-mcp] ready 次数会增加,说明插件确实重新执行了;但已 import 的旧模块代码可能被复用)
日志出现 serverName "unsloth" is already in use 同时存在另一条 dsh-mcp-client 条目用了同名 serverName → 删掉重复的那条,或改 serverName
no Python interpreter found 设 python: 为绝对路径;或确认 Unsloth Desktop 已安装
工具能调但全部报"连不上 Studio" Studio 没在跑 → 开 autoStartStudio: true,或让模型调 studio_start
下载模型老中断(CAS 401) 保持 disableXet: true,并设 hfEndpoint: https://hf-mirror.com;GUI 里对应 Model hub 顶部的 HTTP/Xet 开关
Studio 启动报 unable to open database file 启动它的进程被文件沙箱限制,写不了 ~/.unsloth → 让 DSH 自己起(autoStartStudio)或在沙箱外手动开桌面版
训练启动几秒后失败,日志只有 Unsloth: No or negligible GPU memory available for fused cross entropy 有推理模型占着显存(4GB 卡上推理与训练不能共存)。两个训练工具都会前置检查并拦下,提示你传 unload_inference: true(工具会先 POST /api/inference/unload,再轮询等显存真正释放)
train_status 里 Studio 一直显示 idle / Ready to train 正常:CLI 训练不会登记进 Studio 的训练后端。看响应里的 jobs——含进程存活、日志 verdict(completed/failed)、最新 loss/epoch。想看 CLI 的检查点用 models_list kind=checkpoints_cli
后台训练秒退,日志只有 No such option: --num_epochs (Possible options: --num-epochs) 0.1.4 之前训练参数表里写错了这个 flag。已修;升级后 --num-epochs 正常
请求 LoRA/QLoRA,日志却是 Full finetuning mode - training all parameters、可训练参数 70%+ training_type 词表错配(见上文「training_type 的坑」)。0.1.4 起会自动归一化到 lora 并在 notes 里说明
Training error: path escapes root: ... is not under ...\.unsloth\studio\outputs output_dir 在 outputs 根目录之外。CLI 与 API 两条路径都受限 → 传相对目录名,或用根目录内的绝对路径。0.1.4 起会在启动前就拦下
视觉训练早已结束,unsloth_image_status 仍报 running: true 0.1.3 及以前把占位 pid "api_train" 当成活进程。0.1.4 起存活状态取自 /api/train/status 的 is_training_running
训练进程偶发 forrtl: error (200): program aborting due to window-CLOSE event Intel Fortran 运行时把 detach 子进程"没有控制台"当成 close 事件而 abort。0.1.5 起已修:注入 FOR_DISABLE_CONSOLE_CTRL_HANDLER=1(Intel 官方开关),实测连续两次训练完整跑完
升级插件报 ERR_PNPM_EPERM ... node_modules\dsh-unsloth 有进程把插件目录当工作目录 → 0.1.1 起所有派生进程的 cwd 都移出包目录(<DSH_HOME>\unsloth-mcp),升级无需先停用插件

与 Unsloth 官方 CLI 的关系(unsloth start dsh)

Unsloth 官方 CLI 自带一组面向编码 agent 的命令,其中就包括 DeepSeek Harness:

unsloth start {claude|codex|openclaw|opencode|hermes|pi|dsh}

unsloth start dsh 做的是反方向的事:它把 DSH 的默认模型指向正在运行的 Unsloth 服务 (写 <DSH_HOME>/settings.yaml 里的 llm-pi-ai.providers.unsloth, 配 baseURL=<base>/v1、apiKeyEnv=UNSLOTH_API_KEY,并把 agent-default-model 设为该模型)。 换句话说:unsloth start dsh 让 DSH 用 Unsloth 当大脑;本插件让 DSH 把 Unsloth 当工具。

两者不冲突,可以同时用。本插件额外把官方 CLI 的能力面接成了原生工具,包括 unsloth studio run|stop|verify-install、unsloth train --dry-run、 unsloth export、unsloth list-checkpoints。

其余子命令与参数请以 CLI 自身为准(unsloth --help、unsloth <cmd> --help)—— 本插件的参数表就是照着它核对的,CLI 升级后如果有出入,以 CLI 为准。


与 unsloth-mcp/ 的关系

本插件的 MCP 服务源码与工作区里的独立版同源:

位置 用途
G:\DSH\DSH_workspace\dsh-unsloth\server\unsloth_mcp_server.py 随插件分发的那一份(自包含,安装即用)
G:\DSH\DSH_workspace\unsloth-mcp\server.py 独立/CLI 版,配 mcp_selftest.py 等调试脚本一起用

两者改动请同步。完整实操手册(数据集构建 → 下载 → 导入 → 训练参数 → 训练 → 导出 → 推理)见 G:\DSH\DSH_workspace\unsloth-mcp\docs\Unsloth-微调全流程实操手册.md。

ZCode 接入

本插件同时可挂到 ZCode:清单在 .zcode-plugin/plugin.json,复用同一份 server/unsloth_mcp_server.py, 工具名为 mcp__plugin_dsh-unsloth_unsloth__<tool>(20 个)。差异与装卸步骤见 ZCODE.md。

License

MIT