# dsh-lmstudio-router — 本地模型省钱路由 把「不重要但费 token 的请求」自动改走本机 LM Studio 里的本地模型(默认 `qwen3.5-9b`,LM Studio 中的模型 id;此前输入的“qwen3.59b”即该模型,插件会自动按 `/v1/models` 兜底识别), OpenAI 兼容接口),并自动启动 LM Studio;重要任务(改代码、调试、部署、数据、安全等) 继续走远程模型。插件是**零依赖**的 host 插件,不改动 DSH 源码,通过 bundle patch 挂载。 ## 原理 1. 注册一个 `lmstudio-local` 的 LLM adapter(`ctx.llm.registerAdapter`),由它直接调用 LM Studio 的 `/v1/chat/completions`(OpenAI 兼容,流式 SSE),并把返回映射成 harness 的 `StreamChunk` 词汇表; 2. 监听 `agent/request` waterfall(`prepend`,最外层):对会话做「不重要 + 费 token」判定, 命中后把该 step 的 provider/model 换成 `lmstudio-local`,整轮保持粘滞(sticky), 避免同一轮在本地/远程之间来回跳; 3. 监听 `llm/stream` waterfall:`purpose: compaction`(上下文压缩)与 `purpose: session-title`(标题生成)这类辅助调用直接改走本地模型; 4. 本地端不可达时自动启动 LM Studio(查找标准安装路径,`detached` 启动并轮询 `/v1/models`), 仍不可达则**自动回退远程模型**,绝不中断任务; 5. 每次本地请求都会按配置的上下文窗口(默认 **8192**)重新适配:截断 system 提示、 按「最新优先」保留消息、保留最新一条用户消息、丢弃 reasoning 块,输出预算兜底。 ## 路由判定(可配置) - 命中「不重要」关键词(总结/汇总/翻译/改写/润色/整理/排版/格式化/提取/分类…), 且估算 prompt token 达到 `minPromptTokens`(默认 1000),或不重要得分达到 `strongUnimportantScore`(默认 2)→ 本地; - 出现「重要」关键词(代码/调试/部署/数据库/安全/权限/提交/运行/执行/测试/修改…), 或消息里有代码块、文件路径 → 远程; - 会话已有工具调用历史且未开启 `forwardTools` → 远程(本地模型不接工具续写); - 强制标记:消息开头 `[本地]` / `local:` 强制本地,`[远程]` / `remote:` 强制远程, 标记在发往本地模型前会被剥掉; - 纯文本任务默认不带工具定义(本地模型只回最终文本);开启 `forwardTools: true` 后 会在预算内转发工具 schema(适合本地模型续写工具链)。 ## 安装 ```sh dsh plugin --profile <你的 profile> add link:C:/Users/xiaoy/Desktop/奇思妙想/dsh-lmstudio-router ``` 若希望**不重启**就让运行中的 harness 加载(本机即用此方式):在 profile 的 `cordis.patch.yml` 里直接 `insert` 本插件行(包只作为 dependency 安装、不放进 `dsh.profile.bundles`,避免与 bundle patch 重复插行)。 然后在 `~/.dsh/profiles//cordis.patch.yml` 追加配置(按 id 覆盖): ```yaml - id: lmstudio-router name: 'dsh-lmstudio-router' config: baseUrl: http://127.0.0.1:1234/v1 # LM Studio 本地服务器 model: qwen3.5-9b # LM Studio 里的模型 id(/v1/models 实际值) contextWindow: 8192 # 本地模型上下文窗口 autoLaunch: true # 未运行时自动打开 LM Studio apiKey: '' # LM Studio 默认无需 key ``` 最后**完整重启 `dsh web`**(重启后新会话生效;插件同时在系统提示里向 agent 公告自身)。 ## 配置项 | 字段 | 默认 | 说明 | | --- | --- | --- | | `enabled` | `true` | 总开关 | | `baseUrl` | `http://127.0.0.1:1234/v1` | LM Studio OpenAI 兼容根地址 | | `apiKey` | `''` | 接口密钥(LM Studio 一般留空) | | `model` | `qwen3.5-9b` | 本地模型 id(`/v1/models` 里的实际 id);`auto` 自动选第一个 qwen 模型 | | `contextWindow` | `8192` | 本地模型上下文窗口 | | `reserveOutputTokens` | `1024` | 给输出预留的 token | | `systemMaxTokens` | `1400` | 发往本地的 system 提示上限(保留开头) | | `maxTokens` | `2048` | 本地请求输出上限 | | `forwardTools` | `false` | 是否在预算内转发工具 schema | | `autoLaunch` | `true` | 本地服务器不可达时自动启动 LM Studio | | `launchOnStartup` | `true` | 打开 DeepSeek Harness 时同步打开 LM Studio(非阻塞) | | `autoFallbackModel` | `true` | 配置的模型 id 不存在时改用 `/v1/models` 里的模型 | | `lmStudioPath` | `''` | LM Studio 可执行文件路径(留空自动探测标准路径) | | `launchTimeoutMs` | `45000` | 启动后等待服务器就绪的时长 | | `routeCompaction` | `true` | 上下文压缩走本地 | | `routeSessionTitle` | `true` | 会话标题生成走本地 | | `minPromptTokens` | `1000` | 「费 token」的 prompt 估算门槛 | | `strongUnimportantScore` | `2` | 不重要关键词命中数达到即路由(不看 token) | | `announceToAgent` | `true` | 是否向 agent 公告本插件 | | `unimportantKeywords` / `importantKeywords` | 内置列表 | 追加自定义关键词(数组) | ## 行为与限制 - 本地模型的回答按原 provider 落进会话(消息来源标为 `lmstudio-local`); - 本地模式下 system 提示会被截断、工具定义默认不下发,模型可能不知道 skill/工具细节, 只适合「纯文本产出」类任务; - 首次使用请先在 LM Studio 里加载 `qwen3.59b` 并开启本地服务器(Developer 页签), 之后插件即可自动拉起;如果 LM Studio 装在非标准路径,用 `lmStudioPath` 指定; - 本地不可达/模型缺失时自动回退远程,任务不会被插件弄断; - 路由判定是关键词启发式,可能误判:拿不准时用 `[本地]` / `[远程]` 显式指定; - 需要 DSH 0.1.0-rc.6 左右的 host 插件与 `llm/stream`、`agent/request` 钩子; - 插件本身零依赖、不发网络请求(除本地回环)、不增加遥测。 ## 测试 ```sh node scripts/selftest.mjs # 纯离线单测(判定/截断/映射/离线流错误路径) ``` ## 许可 MIT。