Back to home@YJLTF

dsh-thinktune

DeepSeek Harness(dsh)的 Ollama 适配器插件:让 qwen3.8 等思考模型的思考强度成为一个可配置、可选择的一等参数,同时原生支持多模态图片输入,并可在四种常见的思考控制协议之间一键切换。

Stars
0
Language
TypeScript
Created
Sep 9, 2026
Updated
Sep 9, 2026

Introduction

dsh-thinktune

DeepSeek Harness(dsh)的 Ollama 适配器插件:让 qwen3.8 等思考模型的思考强度成为一个可配置、可选择的一等参数,同时原生支持多模态图片输入,并可在四种常见的思考控制协议之间一键切换。

适配版本:dsh 0.1.5-alpha.1@deepseek-ai/dsh-llm@0.1.5-alpha.1)、Ollama 0.33.0-rc2


它解决什么问题

dsh 接 Ollama 跑 qwen3.8 时,有三个痛点:

  1. 思考强度不可控 —— qwen3 默认「开脑洞」,长思考拖慢响应、占用上下文;而 dsh 没有内置 Ollama 适配器,更没有把思考档位暴露给 agent 配置和模型选择器。
  2. 协议碎片 —— 控制「思考与否/思考多少」在生态里有四种互不兼容的写法(Ollama 原生 think 参数、Qwen3 软开关、OpenAI reasoning_effort、vLLM 的 chat_template_kwargs),换一个推理栈就要改代码。
  3. 图片输入缺失 —— qwen3.8 是多模态模型,通用适配器往往只做纯文本。

dsh 的请求在分发前是深度冻结的,插件不能改写请求——提供方专属的转换只能由 LlmAdapter 完成。所以本插件以标准 LLM 适配器形态实现,做三件事:

  • 通过 resolveModel() 向 harness 上报该模型的思考档位off / low / medium / high,可自定义),harness 在发起任何提供方请求之前校验档位合法性,不支持的档位直接拒绝(UNSUPPORTED_REASONING_EFFORT),不会打到 Ollama 才报错;
  • stream() 里把选中的档位翻译成四种控制协议之一
  • 把 Ollama 返回的思考轨迹映射为 harness 的 reasoning 内容块(与正文分离呈现),并把用户消息里的图片引用转换为对应协议的图片载荷。

特性一览

特性说明
统一思考档位off / low / medium / high 默认档位词表,支持自定义档位与 budget 思考预算
四种控制策略native / soft-switch / reasoning-effort / template-kwarg,改一行配置切换,见下文映射表
档位原生校验档位集合由适配器声明、服务端提前校验;非法档位零 I/O 拒绝
默认档位defaultEffort 可为未选择档位的请求物化默认值;不配则保持提供方默认
思考与正文分离Ollama message.thinkingreasoning-delta,Web UI 里独立呈现思考轨迹;正文内联的 <think> 标签也能增量分离
多模态图片输入/api/showvision capability 自动探测;图片经附件服务按像素/字节预算重编码后内联;超预算从最旧图片开始确定性卸载
健壮的流式传输空闲超时(TIMEOUT)、请求中止(ABORTED)、HTTP 400/500/429 → 稳定错误码;NDJSON 与 SSE 双协议解析
会话卫生默认剥离助手历史中的思考块(Qwen3 官方多轮建议),可选保留
凭据安全配置里只写环境变量名apiKeyEnv),永不放明文 key

四种控制策略

档位native(默认)soft-switchreasoning-efforttemplate-kwarg
端点/api/chat (NDJSON)/api/chat (NDJSON)/v1/chat/completions (SSE)/v1/chat/completions (SSE)
线上参数think消息内软开关reasoning_effortchat_template_kwargs
offthink: false追加 /no_thinkreasoning_effort: "none"*{enable_thinking: false}
low/medium/highthink: true**追加 /thinkreasoning_effort: "low"/…{enable_thinking: true, thinking_budget?: N}
自定义档位think: true/think原样透传{enable_thinking: true}

* 由 offSentinel 配置,可改 "minimal""omit"(不发字段)。 ** nativeLevels: true 时改为 think: "low"/"medium"/"high"(gpt-oss 等支持等级的模型用;qwen3 保持 false)。

怎么选

  • 直连 Ollama 跑 qwen3 系 → native(默认,最稳)。
  • Qwen3-2507 之前的混合思考版且端点不支持 think 参数 → soft-switch(注意:Qwen3-2507 分叉版不支持软开关)。
  • 走 Ollama 的 OpenAI 兼容层,或 gpt-oss 系模型 → reasoning-effort
  • 把同一个适配器指向 vLLM / SGLang → template-kwarg(唯一能表达 thinking budget 的策略)。

图片输入(多模态)

  • 能力自动声明/api/show 返回 vision capability(qwen3.8 即如此)时,适配器上报 inputModalities: ['text', 'image'],harness 才会在请求里保留图片;imageCapability: yes/no 可强制覆盖。
  • 请求图预备:图片引用经附件服务 readImageRequest(ref, {maxPixels, maxBytes}) 拿到按预算重编码的请求版本(质量阶梯自动降档),然后按协议内联:
    • native / soft-switch → user 消息的 images: ["<base64>", …]
    • reasoning-effort / template-kwargcontent 变为 [{type:'text'}, {type:'image_url', image_url:{url:'data:image/png;base64,…'}}]
  • 预算卸载:超过 imageMaxPerRequest(默认 8 张)或 imageMaxRequestBytes(默认 32 MiB)时,最旧的图片替换为 dsh-llm 标准占位文本且不再读取字节;imageMaxPerRequest: 0 关闭图片输入。
  • 前置条件:组合里挂了附件服务(ctx.attachments,dsh 全系 profile 默认自带)。未挂载时含图请求报 UNSUPPORTED_CONTENT,纯文本不受影响。

安装

插件以本地 patch 插件形式挂载,三步:

1. 安装插件依赖(供类型检查与本地测试;运行时按鸭子类型对接宿主):

cd F:/project/dsh-thinktune
pnpm install

2. 在 profile 的 patch 层注册插件并指默认模型

编辑 $DSH_HOME/profiles/<你的profile>/cordis.patch.yml(Windows 下通常是 C:\Users\<你>\.dsh\profiles\web\cordis.patch.yml):

- insert:
    # 思考强度控制:Ollama 适配器
    - id: llm-thinktune
      name: 'F:/project/dsh-thinktune/src/index.ts'   # 必须是绝对路径
      config:
        providers:
          - ollama                       # agent 里的 provider 名,可自定义
        endpoint: 'http://127.0.0.1:11434'
        strategy: native                 # native | soft-switch | reasoning-effort | template-kwarg

# 把默认 agent 模型指到 Ollama
- id: agent-default-model
  config:
    provider: ollama
    model: qwen3.8:27b

3. 设置默认思考档位

写在 $DSH_HOME/settings.yaml(与 Web UI 模型选择器落盘的是同一个位置):

agent-default-model:
  provider: ollama
  model: qwen3.8:27b
  reasoningEffort: low        # off | low | medium | high

注意:settings.yamlagent-default-model 节会覆盖 profile patch 里的同名插件配置,而 reasoningEffort 只能通过这个设置节表达(组合配置的 schema 只收 provider/model)。

重启 dsh 即生效:启动日志会有一行 thinktune: provider route(s) ollama registered (strategy native, efforts off/low/medium/high)

使用

切换思考强度(三选一)

  1. Web UI 模型选择器 —— 选中 ollama / qwen3.8:27b 后,模型旁会出现档位下拉(数据来自本适配器的 resolveModel,选择结果自动写回 settings.yaml);
  2. settings.yaml —— 改 agent-default-model.reasoningEffort,保存即热生效;
  3. 会话内 —— 子代理或单次会话可单独指定模型与档位,互不影响默认值。

切换控制协议:改 patch 里 strategy 一行,重启(或 HMR)生效。线上参数映射见上表。

实际效果reasoningEffort: off 时 Ollama 收到 "think": falselow 时收到 "think": true;Ollama 的思考轨迹以独立 reasoning 内容块回到 harness,正文与之分离。已用真实 dsh headless 链路实测验证。

配置参考

字段默认说明
providers['ollama']注册的提供方路由名(agent 的 provider
endpointhttp://127.0.0.1:11434Ollama 基地址
apiKeyEnv''承载 Bearer token 的环境变量名;空表示无凭据
strategynative思考控制策略,见映射表
nativeLevelsfalsenative 下传 think: "low"/"medium"/"high" 而非 true(gpt-oss 系用)
offSentinel'none'reasoning-effortoff 的线上值;'omit' 表示不发字段
assumeThinkingauto思考能力:auto 跟随 /api/showthinking(不可达时视为有);yes/no 强制
effortsoff/low/medium/high公示的档位;条目为字符串或 {id, name, description, budget}budgettemplate-kwargthinking_budget 使用
defaultEffort请求未带档位时物化的默认档位;不配则保持提供方默认
defaultContextWindow32768/api/showcontext_length 时的回退上下文窗口
defaultMaxTokens8192请求未指定时的单次输出上限
streamIdleTimeoutMs300000流式读取的空闲超时(超时→TIMEOUT 错误)
historyThinkingstrip助手历史中的 reasoning 块是否回传;keep 时走 Ollama 的 message.thinking 字段
imageCapabilityauto图片输入能力:auto 跟随 /api/showvisionyes/no 强制
imageMaxPixels1048576单张请求图的像素预算(宽×高,等比投影)
imageMaxBytes4194304单张请求图重编码后的字节目标
imageMaxPerRequest8单请求图片张数上限,超出从最旧卸载;0 关闭图片输入
imageMaxRequestBytes33554432单请求累计图片字节上限
models[]选型目录覆盖:{id, name, description, contextWindow},也是 /api/tags 不可达时的回退目录

测试与验证

pnpm check   # tsc --noEmit
pnpm test    # node --test:26 个用例

测试通过脚本化 mock(test/mock-ollama.mjs,提供 /api/tags/api/show、NDJSON /api/chat、SSE /v1/chat/completions)覆盖:四种策略的线上映射、<think> 标签分离、工具调用与 role:tool 历史、图片 base64/data-URI 载荷与预算卸载、done_reason: length → max-tokens、流中错误与 HTTP 400/500 → 稳定错误码、中止与空闲超时,以及真实 LlmRuntime 的档位校验与 defaultEffort 物化。

对真实 Ollama:把 endpoint 指回 http://127.0.0.1:11434 按上面安装即可。若要复现完整 dsh 链路又不想动本机配置,可用 mock + 隔离目录:

node test/mock-serve.mjs 21434 &
DSH_HOME=<隔离目录> dsh --profile <你的profile> "Reply with exactly: PONG"

已知边界

  • 图片仅支持 user 消息输入侧;工具结果里的图片降级为文本占位,助手输出仍为文本(Ollama 视觉输入的形态决定)。
  • soft-switch 无法表达思考强度分级:off 之外的档位在软开关层面等价于 /think;且 Qwen3-2507 分叉版不支持软开关,请用 native
  • 能力探测依赖 /api/show;Ollama Cloud 等不回 capabilities 的端点请用 assumeThinking / imageCapability 显式声明。

文件结构

src/index.ts      插件入口:Config schema + registerAdapter
src/adapter.ts    OllamaThinkAdapter(能力声明 / 图片预备 / stream 分发)
src/efforts.ts    统一档位词表与四种策略的线上映射
src/messages.ts   harness 消息 → 线上消息(system/user/assistant/tool、图片、reasoning)
src/native.ts     /api/chat 请求构建 + NDJSON 解析
src/openai.ts     /v1/chat/completions 请求构建 + SSE 解析
src/think-tag.ts  <think>…</think> 增量分离器
src/http.ts       归因头、凭据解析、空闲超时读行、错误码映射
src/config.ts     配置归一化