DeepSeek Harness 实战:模型适配层与 LLM Provider——给"有手的公司 AI"换一颗合适的脑
系列导航:概念 / 教程 / 架构 / 插件 / 编码实战 / 框架对比 / 会话日志 / Headless·CI / 自定义工具 /本文(模型适配层)/ Web 协同 / 安全沙箱 / 多 Agent / 二次开发
前面几篇我们把 Harness 的"骨架"和"手脚"都摸了一遍:它能跑任务、接 CI、记日志、写工具。但你有没有想过一个最底层的问题——它到底用什么脑子在思考?
答案是:哪一家的脑子都行。DeepSeek 自家的 V4 只是默认项,不是唯一项。Harness 把"模型"做成了可插拔的一层,官方叫它llm-pi-ai这个插件,社区更习惯叫它"模型适配层"或"Provider 层"。这一层干的事很纯粹:把任何一家厂商的 API,翻译成本框架能听懂的统一接口。
这篇我们就钻进这一层,把"怎么接模型"“怎么接非 DeepSeek 的模型”“怎么在公司网关后面统一调度”“视觉模型和多模态怎么声明”“成本怎么算"一次讲透。看完你会明白:所谓的"Agent 能力取决于模型”,在 DSH 这里被拆成了两个独立旋钮——你能换模型,也能换 Harness,而这恰恰是开源框架相对封闭产品的根本优势。
一、先纠正一个常见误解:模型不是写死的
很多人第一次看到deepseek-harness这个名字,下意识以为"这玩意只能跑 DeepSeek 模型"。这是最大的误会。
框架叫 DeepSeek Harness,是因为它出身于 DeepSeek 团队、默认接入 DeepSeek 自家 API;但它的设计哲学是llm-pi-ai这个插件负责"对接任何模型",DeepSeek 只是其中注册好的一个 Provider。你可以把它理解成:手机出厂预装了一个浏览器(DeepSeek),但系统允许你装任何别的浏览器(OpenAI、Claude、国产大模型、公司自建网关)。出厂预装 ≠ 只能用预装。
官方在文档里也点明:原生支持的认证体系就包括 Bedrock / Vertex / Azure / Codex 等,再加上任意 OpenAI 兼容端点。换句话说,只要你接的端点吐的是 OpenAI 或 Anthropic 兼容格式,它就能被翻译成 Harness 的统一接口。这一步翻译,就是适配层存在的全部意义。
二、适配层在架构里站在哪一层
回到我们第二篇讲过的"一切皆插件"。在 DSH 的插件树里,模型适配层是一个普通插件,没有特权。它挂在llm-pi-ai这个 plugin id 下,对外暴露的能力是"提供一个 chat completion 接口给 agent-loop 消费"。
数据流大致是这样的:
你写的 prompt ↓ agent-loop(不知道模型是谁,只调用统一接口) ↓ llm-pi-ai(适配层:根据 provider 选择,翻译请求/响应格式) ↓ 真实厂商 API(DeepSeek / OpenAI / 公司网关 / 本地 vLLM)关键点在于:agent-loop 不关心底下是谁。它只说"我要一次对话补全,附上工具定义和上下文",适配层负责把这句话翻译成对应厂商的 HTTP 请求,再把厂商的响应翻译回统一结构。这种"中间翻译"模式,让你换模型时完全不用动 agent-loop、不用动工具、不用动会话逻辑——你只是把插头从 A 插座拔下,插到 B 插座。
官方架构文档里甚至专门有个 ADR(架构决策记录)ADR 0010: twin LLM adapters,讨论的就是"双适配器"的设计取舍。这说明适配层不是临时拼凑,而是被当成一等公民认真设计的。
三、最朴素的接入:用 DeepSeek 官方模型
先从默认路径讲起,因为你大概率第一脚就是踩在这上面。
安装后第一次跑,Harness 默认指向https://api.deepseek.com,读环境变量DEEPSEEK_API_KEY。所以最基本的"能用"只需要两件事:
# 设置密钥(或用 Web UI 在 Settings → Models 里填)exportDEEPSEEK_API_KEY=sk-your-key-here# 跑起来npx @deepseek-ai/dsh web如果你只是想体验,连settings.yaml都不用碰。DeepSeek 官方当前主推的模型是deepseek-v4-flash和deepseek-v4-pro(版本号会滚动更新,比如 flash 已更新到 0731、pro 已更新到 0813,但调用名不变,直接用deepseek-v4-flash/deepseek-v4-pro即可拿到最新版)。
几个容易踩的小坑:
- 模型名别带多余后缀。文档明确说"调用方法不变,使用
deepseek-v4-flash、deepseek-v4-pro即可调用最新版本",你手动拼deepseek-v4-flash-0731反而在某些封装里会报UNKNOWN_MODEL。 - 推理强度(reasoning effort)是 DeepSeek 模型的特色参数。适配层把它透传下去,你可以在配置里设默认档位,也可以让模型自己决定。
- 不要以为"开源框架"就等于"免费"。框架 MIT 免费、可自托管,但模型调用是按各家账单单独计费的——你用自己的 API key,付给你的提供方。这一点和所有 Agent 框架一样,框架只管编排,不管你模型的账单。
四、进入正题:settings.yaml 长什么样
当你要接非默认模型、或要设默认模型、或要配公司网关,$DSH_HOME/settings.yaml才是真正的配置中枢。默认$DSH_HOME是~/.dsh,可以用环境变量DSH_HOME覆盖。
模型相关的核心配置块,挂在llm-pi-ai下,结构是这样:
llm-pi-ai:providers:my-gateway:apiKeyEnv:GATEWAY_API_KEYapi:openai-completionsbaseURL:https://gateway.example.com/v1models:-id:model-name-here逐字段解释,这是全篇最重要的硬知识:
providers:一个字典,key 是 Provider ID。你可以同时挂好几个 provider(比如一个 DeepSeek、一个公司网关、一个国产大模型广场),互不影响。apiKeyEnv:密钥从哪个环境变量读。注意是"环境变量名",不是密钥本身。这是安全红线——永远别把明文 key 写进 yaml,让它在运行时从环境注入。Web UI 里填的 key 会存到$DSH_HOME/.credentials.yaml,同样是脱敏引用,不回显明文。api:协议类型,可取值openai-completions、openai-responses、anthropic-messages。这决定了适配层怎么翻译请求。绝大多数自建/国产兼容端点用openai-completions。baseURL:端点地址。OpenAI 兼容的通常是https://xxx/v1这种形态。models:该 provider 下可用的模型 ID 列表。至少要有一个。
记住这套结构,后面接任何厂商都是换这几个值。
五、Provider ID 的命名铁律
文档里对 Provider ID 有一条容易忽略但很关键的规则:小写,且创建后不可改名。
为什么这么硬?因为请求、会话、凭据引用全都依赖这个 ID。你在某次会话里用qiniu这个 provider 跑了个任务,会话日志里记的就是qiniu/deepseek-v4-flash;你哪天手痒把qiniu改成qiniucloud,旧会话的引用就断了对不上了,回放、复现都会出问题。
所以命名建议:
- 用稳定、语义清晰的全小写字符串,比如
deepseek、qiniu、my-gateway、sevenniu。 - 别用带版本号或临时含义的名字(比如
test-0801),过两天你忘了它是什么。 - 一旦定了就别改。要换就新建一个,旧的留在那不影响。
这条规则本质上是"配置即状态"——在 DSH 里,Provider ID 不是临时变量,而是会写进不可变会话记录的历史锚点。
六、协议判断口诀:域名判断 Provider,路径判断 Protocol
DeepSeek 自家同时提供两套兼容端点,这是个很好的理解入口:
- OpenAI 兼容:
https://api.deepseek.com - Anthropic 兼容:
https://api.deepseek.com/anthropic
社区总结了一句很实用的口诀:域名判断 Provider,路径判断 Protocol。
意思是:你用api.deepseek.com这个域名,适配层知道这是 DeepSeek 家(Provider);而具体走 OpenAI 格式还是 Anthropic 格式,看的是路径后缀(/v1/chat/completions还是/anthropic)。同理,你接任何一家时,Provider ID 是你自己起的名字,但api字段和baseURL路径得配套——别出现"路径是 OpenAI 格式、api 却填成 anthropic-messages"的错配,那适配层翻译出来的请求对端根本认不出。
实战里openai-completions是覆盖最广的:api字段接受openai-completions、openai-responses、anthropic-messages三种,国内绝大多数"兼容 OpenAI"的聚合平台都走第一个。
七、接一家国产聚合平台(以七牛云为例)
为了让你看到"真实可落地"的接法,我用社区实测过的七牛云 AI 大模型广场举例(它兼容 OpenAI 接口,一个 key 统一接入 DeepSeek、Kimi、GLM、MiniMax 等 25 个国产模型)。
Web UI 步骤:Settings → Models → Add a custom provider,填:
- Provider ID:
qiniu(全小写,创建后不可改名) - Display name:七牛云AI大模型广场
- Base URL:
https://api.qnaigc.com/v1 - API protocol:OpenAI compatible
- API Key:从控制台获取
- 点 Fetch available models 自动拉模型,勾选所需模型,保存
如果你更偏好生产环境用 yaml(推荐,因为可纳入版本管理与 review),等价于:
llm-pi-ai:providers:qiniu:apiKeyEnv:QINIU_API_KEYapi:openai-completionsbaseURL:https://api.qnaigc.com/v1models:-id:deepseek/deepseek-v4-flash-id:deepseek/deepseek-v4-pro-id:moonshotai/kimi-k3-id:z-ai/glm-5.2-id:minimax/minimax-m3然后启动:
exportQINIU_API_KEY=sk-your-qiniu-key npx @deepseek-ai/dsh web进入模型选择器,选qiniu/deepseek-v4-flash(或你加的任何模型)。这就完成了——你的 Agent 现在跑在七牛云转发的模型上,而 Harness 的所有能力(工具、日志、子代理)原封不动。
八、模型发现:自动拉还是手动填
接自定义 provider 时有个细节:模型列表怎么来?
适配层支持调用 OpenAI 兼容的GET /models端点做模型发现——也就是 Web UI 里那个 “Fetch available models” 按钮背后的逻辑。如果对方服务正常暴露这个端点,你点一下就把可用模型拉下来勾选,省得手敲。
但如果对方服务不提供/models端点(很多内部网关、精简部署不暴露),你就得在models里手动列 ID。这就是为什么models字段是必填且至少一项——适配层没法凭空猜出对方有哪些模型。
排错时常见两个错:
MISSING_CREDENTIAL:模型页没存 key,或你提供的环境变量引用(apiKeyEnv)在运行时没导出来。检查变量名拼写和是否export了。UNKNOWN_MODEL:选了未配置的模型,或往自定义 provider 加了缺失的模型。要么在 provider 的models里补上,要么改用已配置的模型。
这两个错几乎覆盖了 90% 的"接不上模型"问题,记住它们能省半天。
九、视觉/多模态模型:必须手动声明 input 模态
这是适配层一个很隐蔽但很重要的点,社区踩坑总结出来的。
如果你接的模型支持看图(多模态),在自定义 provider 下必须手动声明input: [text, image],否则适配层默认按纯文本处理,你发图过去会被拒。
llm-pi-ai:providers:my-gateway:apiKeyEnv:GATEWAY_API_KEYapi:openai-completionsbaseURL:https://gateway.example.com/v1models:-id:legacy-chat-id:vision-previewinput:[text,image]# 视觉模型需声明模态还有个相关概念叫defaultInput,它是"路由级图片回退值",默认是[text]。简单说:当某次调用没明确指定模态时,用defaultInput兜底。如果你主要跑视觉任务,可以把它调成包含 image,避免每次都要显式声明。
RC.8 之后,DeepSeek 模型适配器已支持原生图片请求,你甚至可以直接通过/goal、/plan等核心指令做图文混合输入——但前提是底层 provider 声明了 image 模态,否则上层的多模态指令发不下去。这再次印证:模态是适配层管的,不是模型自己默默支持的。
十、原生认证 Provider:Bedrock / Vertex / Azure / Codex
前面说的自定义 provider 走的是"API Key + 端点"的轻量模式。还有一类叫原生认证 Provider,文档特别提示:Bedrock / Vertex / Azure / Codex 这些,需要各自的原生凭据(AWS 凭证 / ADC / api-version / OAuth),只填 API Key 是配不通的。
原因是这些云厂商的认证不是简单一个 key 能搞定,可能涉及 IAM 角色、服务账号 JSON、OAuth 令牌轮换等。适配层为它们准备了专门的认证适配器,但前提是你把原生凭据正确放好(比如 AWS 的~/.aws/credentials、GCP 的GOOGLE_APPLICATION_CREDENTIALS)。
实战建议:
- 个人开发者、小团队:用 API Key 模式的自定义 provider 最省事,能覆盖 DeepSeek、OpenAI、国产聚合平台。
- 企业已经在用云厂商模型服务:走原生认证,把凭据交给云厂商自己的凭证链管理,别把长期 key 硬编码。
- 安全敏感场景:优先原生认证 + 短期令牌,避免把长期 API Key 散落各处。
十一、设默认模型:agent-default-model
接了多家之后,你总得有个"默认用谁"。这对应配置项agent-default-model,典型写法是provider/model(有时带reasoningEffort档位)。它决定了新会话不手动选模型时,Agent 默认调用哪个脑子。
你也可以用环境变量一次性覆盖,适合临时切换或脚本场景:
exportDSH_MODEL=deepseek-v4-flash# 若用非默认端点exportDEEPSEEK_BASE_URL=http://127.0.0.1:8000/v1# 设 keyexportDEEPSEEK_API_KEY=sk-your-key-here注意这组环境变量是官方 DeepSeek 适配器的快捷通道:DEEPSEEK_API_KEY、DEEPSEEK_BASE_URL、DSH_MODEL。它们本质上是"给官方 provider 预设值的快捷方式",不等同于自定义 provider 的完整配置——自定义 provider 还是老老实实写在settings.yaml的llm-pi-ai.providers里。
十二、公司网关场景:把 DSH 接进你的统一模型入口
这是企业落地最典型的诉求,单独讲。
很多公司有"统一模型网关"——所有应用都从网关拿模型,网关负责鉴权、限流、审计、成本控制。DSH 接这种网关简直是天作之合,因为适配层的自定义 provider 就是为它设计的:
llm-pi-ai:providers:corp-gateway:apiKeyEnv:CORP_GATEWAY_KEYapi:openai-completionsbaseURL:https://gateway.internal.company.com/v1models:-id:deepseek-v4-flash-id:glm-5.2-id:kimi-k3好处:
- 统一鉴权:网关发一个 key 给 DSH,DSH 不持有各家真实 key,降低泄露面。
- 统一审计:所有模型的调用都过网关,公司能集中看"谁、在哪个会话、调了什么模型、花了多少 token"。
- 统一限流/降级:网关可以对 DSH 限流,避免某个 Agent 把额度打爆。
- 灵活换源:网关后面今天接 DeepSeek、明天接自训模型,DSH 侧零改动。
这正是适配层"翻译"价值的最大化:DSH 永远只认corp-gateway这一个 provider,至于网关后面怎么路由、怎么计费,是网关自己的事。
十三、本地/自建模型:vLLM、Ollama 也能接
适配层不挑"云还是本地",只要端点吐 OpenAI 兼容格式。所以你自己用 vLLM、Ollama、LM Studio 起的本地服务,只要开了 OpenAI 兼容端口,就能当 provider 接:
llm-pi-ai:providers:local-vllm:apiKeyEnv:LOCAL_KEY# 本地可随便填,甚至设为 dummyapi:openai-completionsbaseURL:http://127.0.0.1:8000/v1models:-id:qwen3-32b这种玩法适合:
- 离线/内网环境:模型不出域,数据隐私最强。
- 成本压到零:本地显卡跑,不计云账单。
- 调试/复现:本地模型版本固定,实验结果可复现。
代价是你要自己维护推理服务、自己管显存和并发。所以一般建议:敏感数据/高频调试用本地,通用任务用云端。
十四、Python SDK 里的模型接入
前面都是 CLI / Web UI 视角。如果你要在 Python 程序里程序化调用 Harness,官方提供了deepseek-harness-sdk,模型也是在构造时指定的:
frompathlibimportPathfromdeepseek_harnessimportDeepSeekHarness config=Path("examples/jsonrpc-agent/minimal.cordis.yml").resolve()workspace=Path("/absolute/path/to/workspace").resolve()sessions=Path("/absolute/path/to/sessions").resolve()withDeepSeekHarness(provider="deepseek-official",model="deepseek-v4-flash",max_tokens=49152,cwd=str(workspace),session_root=str(sessions),cordis=str(config),)asharness:result=harness.run("Inspect the repository and fix the failing tests.",session_id="example-001",)print(result.final_response)这里provider和model是显式传入的——又一次印证"模型是参数,不是写死"。注意一个 SDK 细节:复用同一个 harness 与 session id,会保留该会话拥有的 Bash 进程(包括工作目录、已导出变量、shell 函数);独立任务应使用新的 session id。这对"模型适配层"本身影响不大,但提醒你:模型配置和会话状态是两套独立维度,别混为一谈。
十五、成本结构:框架免费,模型计费
前面提过一次,这里展开,因为很多人算不清账。
DSH 的成本 =0(框架本身,MIT 开源)+ 你模型的账单。适配层在这里的角色是"透明中转":它把你的 prompt 和工具 schema 发给模型,把模型的响应和 tool_call 拿回来,token 计数如实反映给了模型多少、模型回了多少。
有社区做过实测对比(DeepSeek Harness vs OpenCode),同一个任务、同一个模型端点(deepseek-ai/deepseek-v4-flash-0731),单次真实调用是:148 prompt tokens 进,6879 output tokens 回,其中 5731 是推理(reasoning)token。这是"地板值"——在 Agent 把任何工具 schema 加进去之前。一旦进入 Agent 循环,工具定义、上下文压缩、多轮重试都会叠加 token。
所以成本优化思路:
- 选对模型档位:flash 够用就别上 pro;简单任务用便宜模型,复杂任务才上强模型。
- 控好上下文:启用 compaction(上下文压缩),别让历史无限膨胀。
- 控好工具面:接的工具越多,每次请求的 schema 越肥,token 越贵。
- 控好重试:适配层失败了会重试,但要设上限,避免"死循环烧钱"。
适配层本身不收你钱,但它决定了"你的钱花得明不明白"——这就是agent-default-model和各家 provider 配置值得认真管的原因。
十六、双协议支持意味着什么
DeepSeek 同时提供 OpenAI 兼容和 Anthropic 兼容端点,这看似一个普通特性,实则很有深意。
它意味着:同一个 Harness,今天可以让模型走 OpenAI 格式(工具调用用tool_calls),明天可以切 Anthropic 格式(工具用input_schema+tool_use)。适配层把这两种"方言"都翻译成内部统一的工具表示,agent-loop 完全无感。
这对你有什么用?
- 某些模型只在某协议下表现好:比如有的模型 OpenAI 格式的工具调用更稳,有的 Anthropic 格式更好。你可以按模型特性选协议,不必迁就。
- 便于对接不同生态:接 Claude 系模型用 anthropic-messages,接 OpenAI 系用 openai-completions,一套适配层通吃。
- 未来-proof:协议演进时,适配层加一种翻译即可,上层不用动。
再次回到那句口诀:域名判断 Provider,路径判断 Protocol。你配api: anthropic-messages+baseURL: https://api.deepseek.com/anthropic,走的就是 Anthropic 方言;配api: openai-completions+baseURL: https://api.deepseek.com,走的就是 OpenAI 方言。
十七、Provider 不止"模型厂商",还包含"子代理 Provider"
这是个容易混淆的点,本篇提一嘴、下一篇(多 Agent)细讲。
适配层管"主模型",但 DSH 里还有一类叫"subagent provider"的东西——它把另一个 Agent 产品(比如 Claude Code、Codex)也抽象成了 Provider。也就是说,主 Agent 可以把子任务委派给"跑在 Claude Code 上的子 Agent",而 Claude Code 在 DSH 眼里也是一个 provider(subagent-claude-code)。
所以"Provider"这个词在 DSH 里有两层含义:
- LLM Provider:提供模型补全能力(本篇主角)。
- Subagent Provider:提供"一个可委派的 Agent 运行时"(下篇主角)。
两者架构同源——都是"可插拔的能力来源",都通过 plugin 注册。理解了这个,你就能看懂为什么 DSH 敢说"异构智能体运行时":因为它连"谁来当脑子"和"谁来当手"都做成了可替换的插槽。
十八、排错速查表(实战必备)
把前面散落的排错点汇总成一张表,建议收藏:
| 现象 | 可能原因 | 解决 |
|---|---|---|
MISSING_CREDENTIAL | key 没存 / 环境变量引用没导出 | 模型页存 key,或export对应apiKeyEnv变量 |
UNKNOWN_MODEL | 选了未配置模型 | 在 provider 的models加该模型,或改用已配置的 |
| 拉模型列表为空 | 对方没暴露GET /models | 手动在models里列 ID |
| 发图被拒 | 视觉模型没声明input: [text, image] | 给模型加input模态声明 |
| 请求格式对方不认 | api与baseURL协议错配 | 核对api字段与端点路径配套 |
| 走错模型厂商 | Provider ID 混淆 | 检查provider/model前缀是否对 |
0.0.0.0绑定被拒 | Web UI 拒绝公网暴露 | 只在 127.0.0.1 用,别强行绑全网卡 |
十九、一个团队落地的小故事
讲个我看到的真实落地形态(脱敏):某团队把 DSH 接进公司网关,网关后面同时挂着 DeepSeek 和自训代码模型。他们定了条规矩——日常编码用自训模型(便宜、数据不出域),需要强推理时人工切 DeepSeek Pro。
落地前他们最担心两件事:一是配置混乱(每人写自己的 yaml),二是成本失控。解决办法是:把settings.yaml里llm-pi-ai这段纳入团队 git 仓库统一管理(不含密钥,密钥走apiKeyEnv注入),agent-default-model默认指向自训模型。结果三个月下来,模型账单比预期低 40%,因为默认就用便宜的,只有真正难的才升级。
这故事说明:适配层不是"接上就行"的技术活,它直接连着你的成本结构和治理规范。把 provider 配置当代码管,是把 DSH 用好的第一步。
二十、和封闭产品的本质差异
最后点题:为什么"模型可换"在 DSH 这里这么重要?
Claude Code 围绕 Anthropic 自家模型构建,模型选择受其产品边界约束;DSH 是"你组合的运行时",模型只是插件树上的一个节点。这带来两个根本不同:
- 你不会被单一模型锁定:今天 DeepSeek 强就用它,明天别家强就换,上层工具/日志/子代理全不动。
- 你能做模型路由:简单任务便宜模型、难任务强模型,甚至一个会话里不同子任务用不同模型(配合子代理 provider)。
用一句收尾:在 DSH 的世界里,模型是插头,不是地基。地基是那套"一切皆插件"的 Cordis 内核,而适配层,就是让任何插头都能插进来的万能转接头。
二十一、下一篇预告
本篇我们把"脑子"讲透了。但脑子再强,也得有个安全的"身体"来防它乱动——下一篇我们讲安全与沙箱:三档权限(read-only / workspace-write / danger-full-access)底层到底怎么用操作系统能力把你 Agent 圈起来,审批流怎么"失败即拒绝",凭据怎么只写不回显,以及 web_fetch 为什么默认被禁。
二十二、模型切换与会话可复现性
配置模型时有个工程纪律必须提:DSH 的会话日志是不可变、可回放的(我们在"会话日志"那篇细讲过)。这意味着某次会话一旦跑在qiniu/deepseek-v4-flash上,日志里就永久记下了这个脑子。你事后回放、复现、审计,都得用同一个 provider——如果那天你把qiniu改名或删了,回放就接不上。
所以模型配置和代码一样,要纳入版本管理(不含密钥),改之前想清楚"旧会话还能不能复现"。这也是为什么 Provider ID 不可改名:它不是技术限制,是为可复现性让路的设计选择。把 provider 当"历史锚点"而不是"临时变量",是用好适配层的心态门槛。
二十三、reasoning effort 怎么调
DeepSeek V4 这类推理模型有个特色参数:推理强度(reasoning effort)。它决定模型"想多久"——低档快但浅,高档慢但深、token 也贵。
适配层把这个参数透传给模型。你可以:
- 在
agent-default-model里设默认档位(比如日常用medium,复杂任务手动切high)。 - 让模型自行决定(某些封装支持 auto)。
- 通过环境变量或 SDK 参数临时覆盖。
经验法则:编码、数学、复杂排查用高档;格式化、翻译、简单问答用低档。一档之差,token 可能差好几倍。适配层不替你做这个决策,但它把"调档"变成了配置项而非代码改动——你调的是旋钮,不是焊点。
二十四、流式与非流式:适配层怎么处理"边想边说"
真实模型 API 大多支持流式(stream)返回。适配层对上是统一接口,对下要把流式 chunk 正确攒成完整消息,还要把中间的 reasoning(思考过程)和最终 content 分开记录——这正是"会话日志能回看模型推理过程"的能力来源。
对使用者来说,流式影响的是体感流畅度(Web UI 里文字一段段蹦出来),不影响最终结果与日志完整性。但对工程化接入(Headless、ACP)来说,流式控制关系到"什么时候算任务结束"“超时怎么算”。适配层在这里提供的价值是:无论底层流式与否,对 agent-loop 暴露的都是一个干净的"补全完成"信号,上游不用关心传输细节。
二十五、适配层与上下文压缩的耦合
模型不是孤立工作的,它和"上下文"强相关。当会话变长,适配层上游的 compaction(上下文压缩)插件会把历史压短再喂给模型——也就是说,真正发给模型的 prompt 长度,是适配层和压缩插件共同决定的。
这点对成本影响极大:同样一个任务,不压缩可能每次都发 50k token 历史,压缩后可能只发 8k。适配层忠实地把当前上下文发走,至于上下文多大,是压缩策略的活。所以"降成本"是组合拳:适配层负责透明计费,compaction 负责瘦身,两者配合才见效。单独调模型档位而不管上下文,省下的钱可能又被膨胀的历史吃回去。
二十六、工具 schema 怎么发给模型
我们在"自定义工具"那篇讲了ctx.tools.register怎么注册工具。但这些工具定义最终是适配层负责塞进模型请求里的——它以 OpenAI 的tools/tool_calls或 Anthropic 的tools/tool_use格式,把你的工具描述翻译给模型。
这意味着一个隐性约束:工具定义的复杂度,直接体现在每次请求的 token 上。你接 30 个工具,每次补全请求都带着 30 份工具描述;模型回的tool_calls再由适配层翻译回内部调用。适配层在这里是"工具与模型之间的翻译官",它保证无论你用哪种协议,工具的输入输出格式都对得上。这也是为什么切换api协议时,工具调用行为要保持一致——翻译官换了方言,但传达的意思不能变。
二十七、失败、重试与超时:适配层的韧性
模型 API 不是永远在线的。网络抖动、限流(429)、服务端 5xx,都可能让一次补全失败。适配层(配合 agent-loop 的超时/重试包装)负责把这类瞬态错误兜住:
- 对可重试错误(网络超时、429、5xx)做有限次退避重试。
- 重试有上限,避免"死循环烧钱"(呼应我们成本那节的提醒)。
- 超过上限则向上报错,由 agent-loop 决定怎么向用户交代。
这里的关键词是"有限次"和"退避"——不是无限重试,也不是一失败就崩。适配层把"模型偶尔抽风"和"会话彻底失败"隔开,让 Agent 在大多数瞬态故障下能自己缓过来。理解这点,你就不会因为偶发一次超时就怀疑整套框架挂了。
二十八、可观测性:模型调用怎么被记下来
因为适配层是"所有模型流量的必经之路",它天然是做可观测性的好位置。DSH 的会话日志里,模型的请求、响应、推理过程、工具调用,全是适配层经手后写进去的。换句话说:你接的每一家模型、花的每一分 token,都在日志里有迹可循。
对企业来说这价值巨大:安全/财务团队想审计"哪个会话调了什么模型、花了多少",不用去各家云控制台翻账单,直接在 DSH 会话日志里看。适配层把分散在各厂商的调用,收敛成了统一、可检索的一条时间线。这也是为什么我反复强调"把 provider 配置当代码管"——配置即治理入口。
二十九、常见企业误配清单
总结几个企业落地时最容易犯的配置错误,帮你避坑:
- 把明文 key 写进 yaml 提交到 git:正确做法是
apiKeyEnv+ 运行时注入,yaml 只留变量名。 - Provider ID 用大写或带特殊字符:必须小写,否则引用链 fragile。
- 视觉模型忘声明
input: [text, image]:发图必被拒,排查半天找不到原因。 api与baseURL协议错配:OpenAI 端点配了anthropic-messages,请求格式对端不认。- 模型列表留空或 ID 拼错:
UNKNOWN_MODEL的根源。 - 生产环境用默认 DeepSeek 却没设预算/限流:网关或 account 层面一定要有限流兜底。
- 把
settings.yaml当个人文件:团队应统一纳管(不含密钥),避免每人一套导致行为不一致。
这些坑本质上都是"把 provider 当成随手配置而非工程资产"导致的。认真对待它,DSH 才稳。
三十、适配层的演进方向(看 ADR 与提交)
从官方仓库的提交动向能看出适配层的演进脉络:有fix(llm-deepseek): fall back when Files resolution fails(文件解析失败时的回退),有 ADR 0010 讨论"双 LLM 适配器",有docs: accuracy sweep, architecture restructure重构架构文档。这些都指向一个方向:适配层会越来越稳、越来越能处理边界情况(失败回退、模态协商、协议兼容)。
作为使用者,你不必追每一个提交,但要记住一条:DSH 是开发者预览,适配层的字段和行为可能变化。所以生产环境请 pin 确切版本(比如@deepseek-ai/dsh@0.1.0-rc.8),别用latest裸奔——模型配置一变,你的会话可复现性和成本结构都可能受影响。
三十一、一句话总结适配层的本质
如果只能用一句话概括本篇:模型适配层是 DSH 把"用谁的脑子"从框架内核里抽出来、做成可插拔插槽的那一层;它用统一的内部接口翻译任何一家厂商的 API,让你换模型像换插头一样简单,又让所有调用在日志里留下统一的痕迹。
记住"模型是插头,不是地基",你就真正抓住了 DSH 区别于封闭产品的核心——它把选择模型的权利,完整地还给了你。
三十二、适配层与"智能体即运行时"的关系
回到更大的视角:业界开始把 Agent 称为软件的"新运行时层"。这个判断的关键支撑,正是适配层这类组件——它把"模型推理"从应用逻辑里抽象出来,变成可被编排、可被替换、可被计费的底层能力。OpenAI 的 ARC-AGI 实验也印证:保持推理状态、做上下文压缩,能把分数从 13.3% 拉到 38.3%,同时把 token 砍到六分之一——这提升来自执行基础设施(含适配层),而非更强的模型。
所以适配层不只是"接模型的胶水"。它是 Agent 作为运行时那一层里,负责"和脑子对话"的协议栈。你日后做二次开发、做多 Agent 编排,几乎绕不开它——因为无论上层怎么编排,最终都要落到"调一次模型"这件事上,而那一步,就是适配层在干活。
三十三、给新手的最小可行配置
如果你刚装好 DSH、只想尽快跑起来又不被配置劝退,给你一份最小可行路径:
export DEEPSEEK_API_KEY=sk-xxx,直接npx @deepseek-ai/dsh web,用默认的 DeepSeek 官方模型。先感受完整能力。- 想换便宜/国产模型,再进 Web UI 的 Settings → Models → Add a custom provider,填 Provider ID + Base URL + 一个模型 ID,完事。
- 团队要统一,再把
llm-pi-ai这段 yaml 抽出来纳管,密钥走apiKeyEnv。 - 真要做企业网关/本地模型,才碰
baseURL、api协议、input模态这些细节。
别一上来就钻 yaml 的每一字段——先跑通,再调优,适配层的设计本来就允许你"渐进式深入"。
三十四、最后的提醒
适配层很灵活,但灵活意味着"责任在你"。它不会替你选最便宜的模型、不会替你限流、不会替你保证数据不出域——这些全是你配置出来的。框架给了万能转接头,但插哪个插头、接哪路电,是你自己的工程决策。
把 provider 当资产管、把密钥当危险品管、把默认模型当治理入口管,适配层就会从"能接模型"升级成"能管模型"——而这,才是企业把 DSH 当基础设施用的起点。
三十五、配置改了不生效?检查"组合来源"
最后讲一个高频困惑:明明改了settings.yaml,模型却没变。原因往往是 DSH 的配置是多来源组合的,不是"读一个文件就完事"——环境变量、Web UI 里存的覆盖、cordis.yml、以及settings.yaml本身,会按优先级叠加。官方甚至提供--dump-config让你看清"最终生效的组合到底是什么",别凭空假设某个文件就是全部。
所以排查"配置不生效"的标准动作是:先跑--dump-config看真实生效值,再反推是哪个来源在覆盖你。这条经验放到整个 DSH 都适用——它处处是插件组合,永远用--dump-config看"实际拼出来的样子",而不是盯着单个文件猜。这既是适配层的坑,也是理解整个框架的钥匙。
三十六、给本篇的一句话收尾
当你哪天能随口说出"这个会话跑在 qiniu 的 flash 上、那个子任务委派给 Claude Code",你就已经把适配层的精髓吃透了——模型不再是枷锁,而是你随手取用的资源。这,才是开源 Agent 框架真正的自由。下次有人问你"DSH 只能跑 DeepSeek 吗",你可以笑着把这篇甩过去。
结语
模型适配层是 DSH "一切皆插件"理念在"脑子"这件事上的落地:它把"用谁的模型"从框架里抽出来,变成你随时可换、可路由、可审计的一个插槽。无论你是个人想白嫖便宜模型、团队想接公司网关、还是企业想数据不出域跑本地模型,适配层都给了一条干净的接法。
如果这篇帮你把第一个非 DeepSeek 模型接进了 Harness,点个关注。实战系列持续更新(概念 / 教程 / 架构 / 插件 / 编码 / 框架对比 / 会话日志 / Headless / 自定义工具 /模型适配/ Web 协同 / 安全沙箱 / 多 Agent / 二次开发)。模型怎么选、成本怎么控,评论区交流。把模型当插头而不是枷锁,你的 Agent 才会真正自由起来。
本文基于 deepseek-ai/deepseek-harness 官方文档、llm-pi-ai配置说明、社区实测(掘金、jb51、Atlas Cloud 等)及 DeepSeek API 文档整理,截至 2026-08。dsh 处于开发者预览阶段,命令与字段以你安装版本为准。