Coshire90-1135/DSH-MnnChat-Provider ↗★ 0

dsh-mnn-chat

接入手机端MNN Chat作为LLM服务商 适合希望将手机端离线运行的MNN模型接入DSH进行对话的局域网用户。

套件
dsh-mnn-chat
相容性
待驗證
版本
0.1.0
最近更新
2026年10月2日

安裝

$npx -p @deepseek-ai/dsh dsh plugin --profile web add github:Coshire90-1135/DSH-MnnChat-Provider

三、当前配置

- id: mnn-chat
  name: dsh-mnn-chat
  config:
    baseURL: http://192.168.1.23:8080   # 手机端地址
    displayName: MNN-chat                  # 选择器里的 provider 分组名(纯展示)
    apiKeyEnv: MNN_CHAT_API_KEY            # 密钥在 DSH 凭据里,此处不写明文
    models:                                # 只是离线兜底;服务端在提供的会自动出现
      - ModelScope/MNN/Qwen3-0.6B-MNN
    modelLabel: prefixed                   # 显示成 mnn-chat/Qwen3-0.6B-MNN(可省,默认就是它)
    contextWindow: 32768
    maxTokens: 2048
字段默认值说明
baseURLhttp://127.0.0.1:8080手机端地址。http://ip:8080、…/、…/v1 三种写法都识别。
models必填(可只写一个)离线兜底名单。服务端此刻在提供的模型无需写在这里也会自动出现在选择器里;写在这里但服务端没提供的会标注出来。模型名是协议字段,必须与服务端报的一致。
providermnn-chat路由名(provider id)。想在 DSH 里挂两台手机就再装一份、改个名。
displayNameMNN Chat选择器里显示的名字,纯展示。
apiKeyEnv无凭据名。MNN Chat 要鉴权,配上它(见下)。解析不到会明确报 MISSING_CREDENTIAL。
apiKey无明文 Key,仅本地调试(会留在配置文件里)。
contextWindow32768上报的上下文窗口。端侧模型通常 4K–32K,别报大,否则 DSH 压缩得晚、请求会被服务端截断。
maxTokens8192单次回复上限。手机端建议 1024–4096。
timeoutMs120000单次请求总超时。
streamIdleTimeoutMs300000流空闲超时:这么久没有任何数据就中断,避免手机掉线后干等。
includeUsagefalse是否请求 stream_options.include_usage。保持 false(见下)。
pathStyleautoauto 先用 /v1/chat/completions,404 时回退 /chat/completions;也可强制 openai / bare。
headers无额外请求头。别覆盖 accept(见下)。
extraBody无透传的额外请求体字段(如 top_p)。不覆盖插件已写的字段。
retryPolicy见下{ mode, maxRetries, retryableCodes, initialDelayMs, maxDelayMs, jitterRatio };默认对 5xx/429/网络错误重试 3 次。
catalogRefreshMs30000模型目录自动拉取的间隔(毫秒):后台每隔这么久探一次手机端 /v1/models,列表变化就更新缓存并持久化成「最近已知列表」,选择器自动跟上手机换模型的动作。0 = 关闭后台轮询(面板里仍可手动「立即刷新」)。
systemPrompt无本插件往系统提示词里插一段话(见「系统提示词改在哪」的方式 3)。不配就完全不插,也能在悬浮面板里直接改。
systemPromptOrder9100那段话在系统提示词里的位置。DSH 的段落顺序见下节。
modelLabelprefixed模型在选择器里的显示名,三档:prefixed = ${provider}/${id 末段}(默认,mnn-chat/Qwen3-0.6B-MNN,前缀取路由名不是 displayName)、tail = 只留末段(Qwen3-0.6B-MNN)、full = 完整 id。只影响显示,不改协议字段(发给服务端的 model 永远是原 id)。尾段撞名时自动带上父段。悬浮面板上也能切。

系统提示词改在哪

系统提示词不是这个插件生成的,而是 dsh-system-prompt 在每个模型步骤前拼装出来的。 按「改动成本从低到高」有四种改法:

方式 1:该会话的人设(最常用)

改 profiles/desktop/cordis.patch.yml 里 dsh-system-prompt 那一行的 personaPrefix / personaSuffix:

- id: system-prompt
  name: '@deepseek-ai/dsh-system-prompt'
  config:
    # 最前面(order 0),身份口径
    personaPrefix: 你是一个只在本机工作的编程助手。
    # 最后(order 10200),口径补充,会盖住前面的说法
    personaSuffix: 回答尽量简短,先给结论再给理由。工作目录是 {{cwd}}。

可用变量:{{cwd}}、{{model}},以及插件注册的其它变量。改完保存即生效。

方式 2:某个会话预设单独一套人设

dsh-persona 按 preset 作用域覆盖 deployment:persona-prefix/suffix,只影响该预设起的会话:

- id: persona
  name: '@deepseek-ai/dsh-persona'
  config:
    prefix: You are a terse systems engineer who answers in short commands.
    # complete: true  → 只用这段 prefix 当整个系统提示词,其它段落全部让位
    # includeRuntimeContext: false → 连运行时上下文快照也不给

方式 3:给 MNN 这条路由加一段专属提示词(本插件)

两种写法,效果完全一样,面板更省事:

  • 悬浮面板 → 「系统提示词」编辑框 → 保存(立刻生效,落在 $DSH_HOME/mnn-chat.panel.json);
  • 或者写进配置:
- id: mnn-chat
  name: dsh-mnn-chat
  config:
    # …前面的配置…
    systemPrompt: |
      你现在跑在手机端侧的 MNN 小模型上,能力有限:
      回答尽量短、不要长篇铺陈;一次只做一件事;不确定就说不知道。
    systemPromptOrder: 9100

它注册成名为 provider:mnn-chat 的独立 section,不会去动 DSH 自带的 deployment:persona-prefix / deployment:persona-suffix(同名重复注册会直接抛错)。 这段文字按字面处理、不做 {{变量}} 展开——因为未注册的变量在 dsh-system-prompt 里是 抛异常(不是留原文),用户随手写一对花括号就会让之后每一次模型调用都失败。要用变量请走方式 1。 DSH 的段落顺序:

-1000 harness 身份 → 0 persona 前缀 → 500–3100 各工具指引 → 5000 Tools SDK
→ 9000 交付物引用 → 【9100 本插件】 → 9900 结构化输出
→ 10000 harness source → 10100 Web surface → 10200 persona 后缀

⚠️ 「极简模式」下方式 3 的段落会被预设整体压掉(这是 DSH 的设计,不是插件的 bug): 极简模式预设的 persona 行带 complete: true,装配后只剩那一段,其它段落(含本插件的) 全部让位,而且不报错。所以本插件额外做了一层兜底:把同一段提示词作为最后一条 system 消息直接挂到请求上(与用哪个预设无关)。也就是说在极简模式下你的提示词依然生效, 只是位置固定在最后、不再受 systemPromptOrder 控制。想精确控制位置就用标准模式。 根因、DSH 源码行号、真机 A/B 验证见 BUG.md 第 5 节。

方式 4:工作区指令文件(AGENTS.md)

profiles/desktop/cordis.patch.yml 里把 agent-instructions 放开即可(当前是 disabled: true):

- id: agent-instructions
  name: '@deepseek-ai/dsh-agent-instructions'
  disabled: false
  config:
    maxBytes: 65536

它按 $DSH_HOME/AGENTS.md → 项目根(以 .git 为标记)→ 会话工作目录的顺序, 把每一层的 AGENTS.md / CLAUDE.md(外加 AGENTS.local.md / CLAUDE.local.md) 拼成一条 user 角色的 `` 消息。注意它是历史消息而不是 system 段落, 权威性低于系统提示词,且受 maxBytes 限制。

别指望在这儿改工具说明:每个工具的介绍文字由拥有该工具的插件自己注册 (TOOL_BASH、TOOL_READ … 各有固定 order),没有面向用户的编辑入口。

哪个能改,哪个不能

东西能不能改说明
displayName随便改纯展示文字,不参与协议。
模型在选择器里显示的名字随便改纯显示层,三档:prefixed(默认,mnn-chat/Qwen3-0.6B-MNN)/tail(Qwen3-0.6B-MNN)/full(ModelScope/MNN/Qwen3-0.6B-MNN)。悬浮面板上也能切。
provider能改DSH 内部路由名。改了要同步改 agent-default-model 的 provider;历史会话记录的是旧名字,会找不到适配器。
models 里的模型名(id)不能乱改DSH 每次请求把它当 model 字段发给服务端,服务端靠它挑模型。显示名能改,这个 id 改不了。

加模型:在手机 App 里加载那个模型即可,什么都不用改——插件会问服务端 /v1/models。想让它在手机掉线时也列出来,再写进 models 兜底。

MNN Chat 一次只加载一个模型,所以服务端通常只报一个 ID。「同时多个可选」要在手机上换着加载; 配置里可以都写上,但只有服务端当前提供的能真正跑通,其余会标注「手机端此刻没有提供它」。

写密钥

密钥存在 DSH 凭据库里 —— $DSH_HOME/.credentials.yaml 的 refs: 段:

refs:
  DEEPSEEK_API_KEY: sk-...
  MNN_CHAT_API_KEY: 你的API密钥      # ← 加这一行

该文件是热监听的(chokidar),保存即生效,不用重启。