DeepSeek Harness 自定义模型提供商配置
适用版本:deepseek-harness 0.1.0-rc.5(源码部署)
本文所有事实均来自仓库源码与实测验证,关键出处以
文件:行号标注。
1. 概述
DeepSeek Harness(dsh)的模型接入遵循"能力缝"(capability seam)设计:Service Definition(服务定义)/ Service Provider(服务提供者)/ Consumer(消费方)三角色分离。对用户而言,只需要关心两个 LLM 插件:
| 插件 | 定位 | 默认状态 |
|---|---|---|
llm-deepseek |
官方 DeepSeek API 专用提供商 | 默认挂载,自带 V4 Flash/Pro 模型目录 |
llm-pi-ai |
通用多协议提供商(基于 pi-ai) | 默认休眠:零路由、模型选择器无额外模型 |
核心原则(packages/bundle/base/cordis.patch.yml:92-94):
哪些适配器存在是组合(composition)决定的;哪些提供商运行是用户的 settings 文档决定的。
也就是说:接入自定义提供商不需要改任何代码,只需要写配置文件。
2. 配置文件
2.1 settings.yaml(主配置,热重载)
- 路径:
$DSH_HOME/settings.yaml,DSH_HOME默认~/.dsh(packages/util/home-paths/src/index.ts:12,62) - 热重载 :修改后立即生效,无需重启服务器(
packages/bundle/base/cordis.patch.yml:75) - 结构:顶层键为各插件的 settings 命名空间,如
llm-pi-ai:、llm-deepseek:
2.2 .credentials.yaml(凭据,严格映射)
- 路径:
$DSH_HOME/.credentials.yaml - 格式:严格的
CredentialRef → 字符串映射 ,即"环境变量名: 密钥值"(packages/credentials/credentials-local/src/index.ts:1-27):
yaml
DEEPSEEK_API_KEY: sk-xxxxxxxxxxxxxxxx
STARMAP_API_KEY: sk-yyyyyyyyyyyyyyyy
- 凭据解析优先级:进程环境 >
.credentials.yaml> 当前目录.env>~/.dsh/.env - 凭据引用(
apiKeyEnv)按请求解析 ;Models 页面只写托管文档,从不把密钥注入进程环境(packages/bundle/base/cordis.patch.yml:83-84) - 该文件含明文密钥,注意文件权限
3. llm-pi-ai:通用多协议提供商
3.1 激活机制
插件默认休眠(dormant):零路由、模型选择器无额外模型。一旦 settings 提供 llm-pi-ai: 节,路由即时注册;节清空则路由消失(packages/bundle/base/cordis.patch.yml:88-91)。
3.2 settings 结构
yaml
llm-pi-ai:
providers:
<路由名>: # 路由名即 UI 中的提供商标识
displayName: 显示名
apiKeyEnv: 凭据引用名
api: 协议 # 见 3.3
baseURL: 端点地址
models:
- id: 模型ID
name: 显示名
contextWindow: 上下文窗口
maxTokens: 最大输出
compat: # 模型级协议方言,覆盖路由级
thinkingFormat: deepseek
supportsReasoningEffort: true
官方示例见 packages/llm/llm-pi-ai/src/index.ts:15-53。
3.3 路由级字段(packages/llm/llm-pi-ai/src/config.ts:65-141)
| 字段 | 说明 |
|---|---|
apiKeyEnv |
凭据引用(环境变量名),按请求经 ctx.credentials 解析 |
displayName |
配置界面显示名,默认取路由名 |
api |
路由级线协议,见下 |
baseURL |
端点;目录路由可省略(用内置目录的端点) |
models |
显式模型列表(替换内置目录);省略则用内置目录 |
modelOverrides |
按 id 微调内置目录(仅目录路由可用,命名目录外模型会被拒绝) |
compat |
路由级推理方言(thinkingFormat + supportsReasoningEffort) |
defaultContextWindow |
未声明容量的兜底(默认 262144) |
defaultMaxTokens |
未声明输出的兜底(默认 32768) |
defaultInput |
未声明模态的兜底(默认 [text]) |
headers |
请求头(保留名归 Harness 归属) |
reasoning |
路由级推理级别,应用到路由所有模型 |
thinkingBudgets / cacheRetention / transport |
推理预算 / 缓存保留 / 传输偏好 |
timeoutMs / websocketConnectTimeoutMs / streamIdleTimeoutMs |
超时 |
retryPolicy |
重试策略 |
3.4 模型级字段(config.ts:209-227)
id(必填)、name、contextWindow、maxTokens、input(模态)、reasoningEfforts(级别→线上拼写映射)、compat。
3.5 关键约束(踩坑必读)
api是路由级 :一个路由只能一种协议。合法值(packages/llm/llm-pi-ai/src/provider.ts:48-50):openai-completions(OpenAI 兼容 chat completions)openai-responses(OpenAI Responses API)anthropic-messages(Anthropic Messages API)
reasoning是路由级 :会应用到路由上所有模型(config.ts:126)。合法级别:off | minimal | low | medium | high | xhigh | max(catalog.ts:69-77)。- 模型级
compat覆盖路由级 (config.ts:94-99)。thinkingFormat合法值:openai | deepseek | openrouter | together | zai | qwen | string-thinking | ant-ling(catalog.ts:100-109)。 - 模型级没有
api字段------协议只能由路由决定。
4. llm-deepseek:官方 DeepSeek 提供商
- 提供商 id:
deepseek-official;默认凭据引用:DEEPSEEK_API_KEY;默认端点:https://api.deepseek.com(packages/llm/llm-deepseek/src/index.ts:45,47,104) - 默认模型目录:deepseek-v4-flash / deepseek-v4-pro(1M 上下文,256K 输出上限)
yaml
llm-deepseek:
apiKeyEnv: DEEPSEEK_API_KEY # 默认
baseURL: https://api.deepseek.com # 回退链:$DEEPSEEK_BASE_URL → 此默认值
thinking: enabled
reasoningEffort: high # off | high | max
maxTokens: 256000
defaultContextWindow: 1000000
models: [] # 空数组 = 禁用默认模型目录
baseURL回退链:显式配置 →$DEEPSEEK_BASE_URL环境变量 → 默认值(index.ts:184-187)models: []可禁用默认目录 :解析逻辑是models ?? DEFAULT_MODELS,显式空数组合法且不回退(index.ts:118-120)。当你的自定义路由已覆盖 deepseek 模型时,用它避免模型重复出现。
5. 实战一:接入 opencode-go 网关
opencode-go(https://opencode.ai/zen/go/v1)官方目录共 19 个模型,三种线协议:
| 协议 | 模型 |
|---|---|
| chat/completions(11) | grok-4.5, glm-5.2, glm-5.1, kimi-k3, kimi-k2.7-code, kimi-k2.6, deepseek-v4-pro, deepseek-v4-flash, mimo-v2.5, mimo-v2.5-pro, hy3 |
| responses(1) | gpt-5.6-luna |
| messages/anthropic(7) | minimax-m3, minimax-m2.7, minimax-m2.5, qwen3.8-max, qwen3.7-max, qwen3.7-plus, qwen3.6-plus |
由于 api 是路由级,完整接入需要三个路由:
yaml
llm-pi-ai:
providers:
opencode-go: # 路由 1:OpenAI 兼容
displayName: OpenCode Go
apiKeyEnv: DEEPSEEK_API_KEY
api: openai-completions
baseURL: https://opencode.ai/zen/go/v1
models:
- id: deepseek-v4-flash
name: DeepSeek V4 Flash
contextWindow: 1000000
maxTokens: 384000
compat:
thinkingFormat: deepseek # deepseek 推理方言
supportsReasoningEffort: true
- id: deepseek-v4-pro
name: DeepSeek V4 Pro
contextWindow: 1000000
maxTokens: 384000
compat:
thinkingFormat: deepseek
supportsReasoningEffort: true
- id: glm-5.2
name: GLM-5.2
contextWindow: 1000000
maxTokens: 131072
# ... grok-4.5 / glm-5.1 / kimi-k3 / kimi-k2.7-code / kimi-k2.6 /
# mimo-v2.5 / mimo-v2.5-pro / hy3 同理
opencode-go-responses: # 路由 2:Responses API
displayName: OpenCode Go (Responses)
apiKeyEnv: DEEPSEEK_API_KEY
api: openai-responses
baseURL: https://opencode.ai/zen/go/v1
models:
- id: gpt-5.6-luna
name: GPT 5.6 Luna
contextWindow: 1050000
maxTokens: 128000
opencode-go-anthropic: # 路由 3:Anthropic 协议
displayName: OpenCode Go (Anthropic)
apiKeyEnv: DEEPSEEK_API_KEY
api: anthropic-messages
baseURL: https://opencode.ai/zen/go/v1
models:
- id: minimax-m3
name: MiniMax-M3
contextWindow: 1000000
maxTokens: 131072
# ... minimax-m2.7 / minimax-m2.5 / qwen3.8-max / qwen3.7-max /
# qwen3.7-plus / qwen3.6-plus 同理
要点:
- 三个路由共用同一个
apiKeyEnv(同一网关同一 key) - deepseek 模型必须配
thinkingFormat: deepseek方言(V4 系列要求reasoning_content多轮回传) - 若你的 deepseek 模型已由 llm-pi-ai 提供,记得给
llm-deepseek加models: []去重
6. 实战二:接入 starmap(GLM-5.2)
CSDN starmap 网关是 Anthropic 兼容端点:
yaml
llm-pi-ai:
providers:
starmap:
displayName: Starmap
apiKeyEnv: STARMAP_API_KEY
api: anthropic-messages
baseURL: https://ai.csdn.net/api/model/v1
reasoning: max
models:
- id: glm_for_coding
name: GLM-5.2
contextWindow: 200000
maxTokens: 4096
凭据文件:
yaml
STARMAP_API_KEY: sk-xxxxxxxxxxxxxxxx
7. 验证
7.1 Web UI
浏览器打开 http://127.0.0.1:3080 → Settings → Models,检查模型是否出现。settings.yaml 热重载,改完即生效。
7.2 API(无 UI 时的快速验证)
模型目录 RPC 端点:POST /api/llm.models,请求体为 JSON-RPC 信封(packages/host/apiproxy/src/fetch/client.ts:339-341):
json
{ "type": "client-request", "rpcId": "<uuid>", "method": "llm.models", "payload": {} }
响应 result.value.groups 列出各提供商及其模型,failures 列出注册失败的提供商与原因。
7.3 直连网关冒烟测试
配置前先确认网关与 key 可用(以 OpenAI 兼容端点为例):
bash
curl -X POST https://opencode.ai/zen/go/v1/chat/completions \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"model":"deepseek-v4-flash","max_tokens":64,"messages":[{"role":"user","content":"ping"}]}'
注意:HTTP 200 但 message 无 content、finish_reason 为 null 不是成功------那是异常响应,需检查完整 body。
8. 常见问题
| 现象 | 原因 | 解决 |
|---|---|---|
| 模型选中后请求失败 | 协议放错路由(如 anthropic 模型挂在 openai-completions 路由) | 按官方端点拆路由 |
| 模型选择器出现重复模型 | llm-deepseek 默认目录与自定义路由重叠 | llm-deepseek: models: [] |
| 网关返回 HTTP 500(连无 key 都 500) | 上游网关故障,认证层未达 | 等恢复,非本地问题 |
| 修改 settings 后无变化 | 检查 YAML 缩进/字段名;服务器 stderr 会打印配置拒绝原因 | 修正后热重载自动生效 |
| 凭据不生效 | 优先级:进程环境 > .credentials.yaml > .env | 检查是否有同名环境变量覆盖 |
9. 参考
- 插件文档示例:
packages/llm/llm-pi-ai/src/index.ts:15-53 - 配置 schema:
packages/llm/llm-pi-ai/src/config.ts、packages/llm/llm-deepseek/src/index.ts - 凭据格式:
packages/credentials/credentials-local/src/index.ts - 模型目录 API:
packages/host/apiproxy/src/api/llm.ts