从 Pi 到 DSH:一个可以动态重组的 Agent 运行时
这次我们从论文、源码一路看到 GUI 和自定义模型,顺手把结果整理了下来。
DeepSeek Harness(下面简称 DSH)乍看也是一个 Coding Agent。实际跑过以后,我们发现它和 Pi、Codex 这类产品的重点不太一样。
DSH 先提供一个插件运行时,再用插件拼出具体的 Agent。模型、工具、权限和 Agent Loop 都在这套插件系统里。创造模式(ID 为 cordis)甚至允许 Agent 检查和修改当前运行时。
我们觉得最好用的类比还是:
Pi 像 Vim,DSH 像 Emacs。
这个比喻不严格,但能说明两边的设计取向。下面聊聊我们实际确认了什么。
DSH 还在快速迭代。文中的结论以我们检查时的仓库和运行时为准。
DSH 先搭运行时,再组装 Agent
多数 Coding Agent 都围绕一个固定的 Agent Loop 展开:
读取用户输入
→ 调用模型
→ 执行工具
→ 把结果交还模型
→ 继续循环
模型、工具、权限和界面都接在这个循环上。
DSH 连 Agent Loop 也做成了 Cordis 插件。不过在当前 Web 组装里,并不是所有插件都放进 Preset。Agent Loop、模型路由、会话持久化、Sandbox 和 Approval 等进程级能力留在 Host;Preset 主要负责一个 Agent 面向模型的那一层。
结构大致如下:
DSH Host(进程级共享)
├─ Agent Loop / Agent Factory
├─ 模型路由与 Provider
├─ Session / Persistence
├─ Sandbox / Approval
├─ 工具、Skills、Subagent 等注册表
└─ Agent Preset(按 Agent scope 生效)
├─ Persona 与提示词段
├─ 文件、Shell、Web 等工具
├─ Skills 入口
├─ Plan、Goal、Compaction
├─ 子 Agent 与工作流工具
└─ 可选的 Cordis 动态工具
所以,DSH 里的 Agent 仍然是一组插件的组合,但 Preset 管的是其中按会话变化的部分。进程级基础设施仍由 Host 统一提供。
Cordis 论文到底在讲什么
Cordis 的论文叫 A Programming Paradigm for Spatiotemporal Composability。它要解决的问题很具体:组件在运行时加入、退出或替换以后,系统怎么保持干净、稳定?
论文把动态组装分成两个方向:
- 时间可组合性(temporal composability):一个组件离开以后,它曾经给系统造成的影响能否被完整撤销。
- 空间可组合性(spatial composability):组件之间的依赖关系发生变化以后,系统能否自动重新连接、激活或停用受影响的组件。
这里的“空间”指依赖图,不是机器或进程的位置。“时间”指组件从加载到卸载的生命周期。
时间组装:插件卸载后要收拾干净
很多插件系统都有 dispose() 或 unload()。问题是,少清一个事件监听器或定时器,插件虽然停了,副作用还留在进程里。
Cordis 把一次副作用写成:
e : Γ → Γ × (Γ → Γ)
Γ 是运行时能管理的上下文。副作用 e 改变上下文时,还要返回一个逆操作。
如果加载时按 A → B → C 注册了工具、监听器和定时器,卸载时就按 C⁻¹ → B⁻¹ → A⁻¹ 清理。工程上就是一个 LIFO 清理栈。
Cordis 的卸载会撤销插件通过上下文登记的影响,不动其他插件。这里说的恢复是“观测等价”:不用把内存逐字节还原,只要外部看到的服务和行为恢复了即可。
空间组装:运行时一直维护依赖图
effect 记录组件改了什么,coeffect 记录组件需要什么。论文把依赖环境表示成一张从键到服务值的表:
Σ = (k : K) ⇀ Vₖ
组件声明依赖 d 和供给 p。只有 d 全部满足时,组件才会激活。服务加入、移除或更换提供者时,运行时会重新检查相关组件。
放到 DSH 里看就很直观了。Agent Loop 需要模型、工具和会话服务。模型适配器还没加载时,Loop 不启动;适配器加载后,Loop 自动激活;适配器被替换时,依赖它的组件重新绑定;适配器退出时,依赖者先卸载。
普通依赖注入通常在启动时做一次解析。Cordis 会在整个运行期维护这张依赖图。
时间和空间怎么接到一起
论文把状态、逆操作和依赖表放进同一个递归上下文:
Γ∞ = μΓ. Γ × (Γ → Γ) × Σ
一个上下文同时保存当前状态、清理函数和可用依赖。它还能派生子上下文,最后形成一棵组件树。
论文把组件实例叫作 fiber。每个 fiber 都记录自己的依赖、状态和清理函数。加载组件时执行 effect,卸载时执行 recover;父组件卸载时,子组件也跟着清理。
提供一个服务本身也是可逆 effect。插件卸载时,服务被撤回,依赖它的 fiber 随之停用。时间和空间就是这样接起来的。
论文把一个组件概括为三元组:
Component = Dependencies × Provisions × RevertibleEffect
也就是:我需要什么、我提供什么、运行时会改什么,以及怎么撤销。
在没有运行失败、逆操作正确、依赖声明完整等前提下,最终状态只取决于最后的组件配置,不取决于中间的加载和替换顺序。可以把它理解成:折腾完以后,运行时应该和“从一开始就按最终配置启动”基本一致。
这些公式怎么落到代码里
论文里的概念和 Cordis API 基本是一一对应的:
论文中的概念 |
工程含义 |
Cordis 中的落点 |
|---|
Γ∞ |
可递归组合的运行时上下文 |
ctx 与上下文树 |
effect 与 inverse |
一次状态变化及其撤销方法 |
ctx.effect(callback);回调返回或逐步产出 disposer |
accumulator / recover |
按逆序合成全部清理动作 |
fiber.dispose 与卸载流程 |
Σ、get、set |
服务供给、查询与撤回 |
ctx.get()、ctx.set();set 本身也受 effect 跟踪 |
d、p、e |
组件的依赖、供给和副作用 |
fiber.inject、组件的 provide、fiber.apply |
fiber |
一个组件的运行时实例 |
ctx.use() 创建的实例及其生命周期状态 |
target / committed view |
希望绑定的依赖与当前已提交依赖 |
refresh 驱动激活、卸载和重新加载 |
声明式组件树 |
系统希望达到的最终组合 |
Cordis loader 的配置协调与热更新;DSH 在更上层用预设描述 Agent 组合 |
开发者要做两件事:给副作用配上逆操作,并把依赖声明清楚。清理顺序、依赖变化和生命周期切换交给 Cordis。
当然,这套保证有边界:
- Cordis 不会自动证明 inverse 写对了。
- 没有通过
ctx.effect 等机制登记的修改,Cordis 管不到。
- 已经发出的网络请求和外部消息不会被自动撤回。
所以 DSH 的“自进化”不是随意改写整个进程。Agent 改的是组件图,Cordis 负责把变化局部地应用到运行时,并在需要时撤销。
Preset 到底控制什么
在当前实现里,一个 Agent Preset 就是一个目录,核心文件是 agent.cordis.yml。文件里是一组 Cordis 插件行。某个会话选择 Preset 后,DSH 会把这组插件挂到该 Agent 的 scope 上。
更具体一点,每个 Preset 代际在进程内只挂载一次,使用它的会话通过 scope 加入这份常驻组合。插件内部仍按 Session 或 Agent 保存状态,所以多个会话不会混在一起。修改 Preset 文件时,DSH 会为后续会话建立新代际,已经加入旧代际的会话不会被中途换掉。
它主要控制这些内容:
- Persona、Agent Instructions 和其他系统提示词段
- 模型能看到、也能调用的工具
- 工具以普通 schema 还是 Code Mode SDK 的方式呈现
- 本地 Skills 的发现和加载
- Plan、Goal、Todo、Compaction 等工作方式
- Subagent、Workflow、Ralph 等委派能力
- 是否开放 Cordis 运行时检查和动态插件工具
Preset 会直接改变请求里的系统提示词和工具 schema,所以它不只是界面上的一个模式名称。选择不同 Preset,模型拿到的能力、提示词长度、工具调用方式和长期会话行为都会变化。
它也有明确的边界。当前 Web 部署中,下面这些能力留在 Host,不由 Agent Preset 决定:
- 使用哪个模型和 Provider
- Agent Loop 与各类注册表
- Session 持久化、Credentials 和 Settings
- Sandbox、Approval 和 Permission Preset
- Subagent 的底层 spawn、fork 等进程级后端
所以“Agent Preset”“模型选择”和“权限预设”是三件事。Agent Preset 决定 Agent 有什么能力,模型选择决定由谁来推理,权限预设决定工具执行时受到什么限制。
四种内置 Preset
ID |
界面名称 |
基础能力 |
适合场景 |
|---|
standard |
标准模式 |
完整的原生工具集 |
日常编码 |
code |
PTC 模式 |
标准模式 + Code Mode 工具呈现 |
多步骤、可编程的工具编排 |
minimal |
极简模式 |
持久 Bash + str_replace_editor |
最小实验、模型基线测试 |
cordis |
创造模式 |
标准模式 + Cordis 自修改工具与创作 Skills |
创建 Preset、开发和调试插件 |
标准模式:日常编码的默认组合
标准模式不是一两个常用工具的集合,它基本装上了 DSH 当前完整的模型侧编码能力:
- Linux 和 macOS 使用 Bash,Windows 使用 PowerShell
- 文件读取、编辑和文件系统搜索
- 后台任务的查询与停止
- 本地 Skills 发现、目录查询和 Skill 加载
- Goal、Plan Mode 和 Todo
- 自动上下文压缩、手动
/compact,以及长工具结果裁剪
- Subagent 查询、spawn、fork
- Workflow 和 Ralph 循环
- 向用户提问
- 网页搜索
标准模式还会读取工作区里的 Agent Instructions,当前上限是 64 KiB。它带有完整的 Compaction 组合,长对话不会像极简模式那样一直堆上下文。当前配置还会裁剪超过阈值的长工具结果,保留头尾供模型继续判断。
组装文件中已经放了 Codex 和 Claude Code 子代理工具的模板,但默认是 disabled: true。也就是说,标准模式不会自动获得这两个产品;复制成自定义 Preset 后,可以按需打开对应行。
这套组合最适合日常使用。我们创建自己的 Code Preset 时,也应该优先从 standard 复制,而不是从空文件开始。
PTC 模式:能力不变,工具调用方式变了
PTC 模式的 agent.cordis.yml 基本是标准模式的完整副本,额外增加了一行:
- id: tool-presentation
name: '@deepseek-ai/dsh-agent-tool-presentation'
config:
mode: code
这行不会增加新的文件或 Shell 能力,它改变的是模型看到工具的方式。
在标准模式下,模型直接看到 bash、文件工具、搜索工具、Subagent 等原生 schema。在 PTC 模式下,模型在协议层唯一可以直接调用的工具是 run_code,同时在系统提示词里拿到一份根据当前工具目录生成的 TypeScript SDK。模型可以写一段异步 TypeScript:
- 连续调用多个工具
- 并发执行互不依赖的查询
- 根据前一步结果做分支
- 在程序里过滤和汇总结果,只把必要内容交回模型
原来需要五轮“模型—工具—模型”往返的操作,有机会压成一次 run_code。内部工具仍由 DSH 的工具注册表分发,原来的 Sandbox、Approval 和调用记录不会因为套了一层 TypeScript 就消失。
这段代码是一次 run_code 调用的函数体,可以使用顶层 await 和 return。程序结束后,只把日志和返回值交回模型。它是一次性的工具编排,不是常驻插件,也不会自动写进 Preset。
代价也很明显。一次程序可以做更多事情,执行范围比单个原生工具调用大,审查代码也更重要。它还依赖 Host 提供 Code Runtime;没有 TypeScript Runtime 的部署无法挂载这个 Preset。
PTC 模式适合搜索多个文件、批量读取结果、串联查询和修改等多步骤任务。简单改一个文件时,标准模式通常更直观。
极简模式:真的只剩两个工具
极简模式不是“标准模式关掉几个按钮”,而是一份单独的最小组装。它只向模型提供:
- 一个状态可跨调用保留的持久 Bash
- 一个
str_replace_editor
它使用固定的完整 Persona:You are a helpful software engineer assistant.,并关闭运行时上下文快照。普通的 Agent Instructions、Web 引导和后续提示词贡献不会继续叠加。
它没有标准模式中的文件搜索、Web、Skills、Plan、Compaction、Todo、Subagent 和 Workflow。持久 Bash 的状态会在多次调用间保留,当前超时是五分钟;工具说明也明确写着不能通过它访问互联网。编辑器直接使用本地文件系统,并要求绝对路径。
这个模式适合观察最小 Agent Loop、比较模型基础能力,或者做范围很小的编码任务。长会话和复杂仓库工作不太适合它,尤其是它没有 Compaction。
创造模式:标准模式加上运行时创作能力
创造模式的 ID 是 cordis。它保留标准模式的主要能力,又增加了三部分内容。
第一部分是专门的 Persona。它会告诉 Agent:
- DSH 分成 Host plane 和 Agent Preset plane
- 哪些服务应该全局共享,哪些插件应该放进单个 Preset
- 用户 Preset 在哪里
- 不要修改随 DSH 安装的四个内置 Preset
第二部分是 Cordis 动态工具:
cordis_inspect_list、cordis_inspect_query、cordis_inspect_self
cordis_define、cordis_run、cordis_stop、cordis_undefine
它们可以检查当前服务、事件、工具和动态 Package,也可以在当前 DSH 进程中定义、运行、停止和删除临时插件。
第三部分是两份内置 Skill:
editing-cordis-compositions:教 Agent 创建、修改和验证 Preset
cordis-plugin-development:教 Agent 检查接口并开发动态 Cordis 插件
创造模式适合“帮我做一个新的 Agent”或“先在运行时试验一个插件”。它不适合作为默认日常模式:额外的工具 schema 和说明会增加上下文,而且动态 JavaScript 的权限接近 Shell 访问。临时 Cordis Package 也只存在于当前进程,重启后不会自动变成正式插件。
怎么创建自己的 Preset
DSH 当前采用“复制后修改”,不在网页里提供一个从空白开始的 YAML 编辑器。这是有意的:一个可工作的 Preset 往往包含多个插件行、隔离 realm、Skills 和其他资源,从已知可用的组合复制更稳。
方法一:在 DSH Web 中复制
操作路径是:
- 打开“设置”。
- 进入“Agent 预设”。
- 在最接近需求的 Preset 上点击“复制”。
- 填写标识符和可选的显示名称。
- 创建完成后,打开新 Preset 的目录修改文件。远程或容器部署无法直接打开宿主目录时,页面会显示路径供手动复制。
标识符会直接成为目录名,只允许小写字母、数字和连字符,规则是:
[a-z0-9][a-z0-9-]*
例如 backend-reviewer 可以,Backend Reviewer、my_agent 和 ../test 都不行。标识符创建后不能在界面里改名,也不能与内置或已有 Preset 重名。复制操作不会覆盖已有目录。
默认的用户目录是:
${DSH_HOME:-$HOME/.dsh}/.agent-presets/<id>/
一个自定义 Preset 通常长这样:
backend-reviewer/
├─ preset.yml
├─ agent.cordis.yml
├─ skills/ # 可选
└─ 其他插件或资源 # 可选
两个 YAML 文件的职责不同:
preset.yml 只放界面元数据,主要是 name 和 description。
agent.cordis.yml 才是实际的插件组装,里面每一行都有 id、包名和可选配置。
preset.yml 可以很简单:
name: 后端审查模式
description: 基于标准模式,保留代码检索与审查工具,关闭不需要的委派能力。
复制会带走来源目录里的组装文件、Skills 和资源。系统会保留来源的 description,但去掉来源的 name 和内置排序值,避免副本在列表里看起来和原版一模一样。
四个内置 Preset 属于 system,只能查看和复制,不能直接编辑或删除。用户创建的 Preset 属于 user,可以打开目录、继续复制或删除。
方法二:让创造模式帮你做
DSH Web 的“用「创造模式」创作自定义预设”会开启一个使用 cordis Preset 的空白会话。可以直接描述目标,例如:
基于 standard 创建一个 backend-reviewer。保留文件读取、搜索、Plan 和 Todo,去掉 Ralph 与普通 Subagent,加入一份后端代码审查 Skill。完成后验证它能够正常挂载。
正常流程应该是:
- Agent 加载
editing-cordis-compositions Skill。
- 查看当前 Preset 名单和运行时接口。
- 通过 Preset 服务复制一个内置组合。
- 修改用户目录下的
preset.yml 和 agent.cordis.yml。
- 按 Host plane、Agent plane 和 isolate realm 规则检查插件位置。
- 实际挂载一次做验证。
- 让用户新建会话,检查最终工具列表和提示词是否符合预期。
用户 Preset 目录通常不在当前项目工作区里,写文件时可能需要额外批准。运行动态 Cordis 代码也可能触发批准。这些都是正常的权限边界。
当前 Waku 主要支持选择和使用 Preset,还没有 DSH Web 这套复制、删除和打开目录的完整管理界面。可以在 DSH Web 中完成复制,也可以在 Waku 里进入创造模式,通过对话让 Agent 创建。我们给 Waku 补的“选择器打开时刷新”,就是为了让新 Preset 能及时出现在列表中。
Preset 什么时候生效
Preset 是会话创建期配置,不是随时可以切换的皮肤。
- 新会话页面可以选择一次 Preset;设置里的默认值只影响以后创建的会话。
- 空白会话还可以切换。只要已经产生过对话或工具历史,Host 就会拒绝切换。
- 这样限制是因为历史中的工具调用属于原来的工具 schema。中途换组合,新的 Agent 可能连旧工具名都不认识。
- 修改
agent.cordis.yml 后,新会话会使用新的组装代际,已经运行的会话继续使用旧版本。
- 删除一个自定义 Preset,也不会强行停止已经由它创建的会话。
- Subagent 会继承父 Agent 正在使用的同一份 Preset 代际,不会重新读取磁盘上的最新版。
Host 每次读取 Preset 名单时都会重新扫描目录,所以新建和删除在服务端是实时可见的。客户端如果自己缓存名单,仍然需要主动刷新;我们之前在 Waku 遇到的就是这个问题。
还有一个当前限制:DSH 主要用 agent.cordis.yml 的修改时间和大小判断是否产生了新代际。只修改旁边的 Skill 或资源文件,未必会让新会话立即拿到新版本;这时需要同时触碰组装文件,或者重启 DSH。
最后要注意信任级别。user 只表示“这是本地创作的 Preset”,不代表它运行在低权限沙箱里。Preset 的实际权限取决于它引用了哪些插件。拿到别人分享的 Preset 后,应该先读 agent.cordis.yml,再决定是否使用。
“自进化”目前能做到哪一步
在创造模式(cordis)下,Agent 可以获得一组运行时工具,用来:
- 检查当前服务、事件、工具和插件契约
- 定义新的动态 Package
- 启动或更新 Package
- 停止动态 Run
- 回滚到旧版本
- 删除动态插件
这些工具确实能修改当前运行时,但不会直接改仓库源码,动态 Package 重启后也不一定还在。
目前比较实际的用法是:
运行时实验
→ 检查效果
→ 更新或回滚
→ 确认有效
→ 固化成正式插件
需要长期保存或分享时,再把验证过的代码整理成正式插件或 Preset。运行动态代码通常还要经过用户批准。
Agent Loop 被拔掉怎么办
真把 Agent Loop 停掉,Agent 当然就不能继续对话了。插件化只说明组件可以替换,不代表任意组合都能工作。
一个可用的 Coding Agent 至少需要:
- 一个模型适配器
- 一个 Agent Loop
- 一组消息与会话能力
- 最基本的输入输出路径
目前 Cordis 的动态工具主要管理动态定义的 Package,并不能随便拆掉整个宿主。
不过,DSH 还缺少一套很清楚的保护规则:哪些插件不能拔,哪些可以热替换,哪些修改只能下次会话生效。当前实现里,这部分还有完善空间。
Cordis 就是 DSH 的 Core 吗
可以这么理解。更准确一点,Cordis 是 DSH 的运行时 Core。
传统 Agent 的核心直接规定:
- 循环怎么运行
- 工具怎么调用
- 模型怎么接入
- 状态怎么保存
DSH 固定下来的部分更少,主要负责:
- 启动进程
- 建立 Cordis Context
- 加载插件
- 提供服务和事件契约
- 管理生命周期
具体组装出什么 Agent,由后面的 Preset 和插件决定。这就是我们之前说的“Core 更后置”:核心只管怎么组装,不提前写死组装结果。
这套架构有先例吗
有,而且不少:
- Emacs:编辑器本身也是一个可编程 Lisp 环境
- Smalltalk Image:系统可以在运行时检查和修改自身
- Eclipse/OSGi:功能通过服务化插件动态组合
- Erlang:支持运行时升级和热代码替换
- 微内核系统:核心只保留最小机制,其余能力放到模块中
- 浏览器扩展系统:宿主提供生命周期和权限,扩展决定具体能力
DSH 的新意是把这些做法放进 Agent 场景,还让模型参与插件的检查、生成和运行。以前插件系统主要给开发者用,现在 Agent 自己也开始操作插件系统。能力更灵活,权限、回滚和稳定性也会更难做。
Pi 和 DSH 的差别
两者都支持自定义工具和 Provider,但重心不一样。
Pi 先把 Agent Loop 和 TUI 做稳,再让用户通过 Extension 扩展它。DSH 先做运行时,让 Agent Loop、服务和工具都参与组装。
所以我们用了 Vim 和 Emacs 的类比:
- Pi 像 Vim:一个稳定的编辑核心,加上强大的扩展生态
- DSH 像 Emacs:核心之外还是一个可以继续编程和重组的运行环境
Pi 的扩展也可以很强,DSH 也有固定核心。这个比喻只说明设计重心,不比较谁更强。
日常怎么用 DSH
日常写代码不需要一直开创造模式。我们建议这样分:
- 日常编码使用标准模式、PTC 模式或自己的 Code Preset
- 编写普通仓库插件时仍使用日常编码 Preset
- 需要检查运行时、快速试验动态能力时使用创造模式
- 动态插件验证成功后,再固化成正式插件或预设
创造模式更像实验室。工作流和普通开发差不多:
想法
→ 运行时原型
→ 快速验证
→ 回滚或迭代
→ 固化为工程代码
它省掉的是“改完代码、重启整个 Agent、再验证”这段来回折腾。
Waku、Web 和 ACP
DSH 自带 Web 界面,但写代码时,桌面客户端或 TUI 通常更顺手。我们检查了 Waku 的 DSH 接入,它走的是 DSH Web 后端的 HTTP 和 WebSocket,不是 ACP。
连接关系如下:
Waku GUI
↓ HTTP / WebSocket
DSH Host
↓
Preset / Agent Loop / Cordis / Tools
Cordis 和插件都跑在 DSH Host 里,自进化能力也在服务端。客户端只要能:
- 展示会话
- 发送消息
- 渲染工具调用
- 处理用户批准
- 刷新模型和预设目录
DSH Web 和 Waku 都能使用同一套运行时能力。
ACP 的价值在于统一客户端协议。以后要接更多编辑器或 TUI,可以考虑 ACP;目前用 Waku 验证 DSH 没有问题,没必要先重写一套接入。
动态后端也需要动态 GUI
我们在 DSH 里创建了新 Preset,Waku 的选择器却没有更新。Preset 创建成功了,问题出在 Waku 缓存了 Provider 目录,只在启动或 Provider 重载时读取一次。
后来我们给选择器加了“打开时刷新”,并把修改提交给上游。
这个问题不复杂,但很典型:后端的模型、Preset 和工具会动态变化,客户端就不能一直拿启动时的缓存。还要考虑目录刷新、事件更新和能力失效。
自定义 Provider 的模型能力要自己补
DSH 可以通过通用的 llm-pi-ai 适配器接入 OpenAI-compatible Provider。
接入过程并不复杂:
- 设置 Provider ID
- 填写 Base URL
- 选择协议
- 保存 API Key
- 获取远端模型列表
我们接入 CPA 后,通过 /models 成功拉到了模型列表,但界面里没有 thinking level。
原因是 OpenAI-compatible 的模型列表通常只包含:
这里没有统一的推理能力描述,DSH 也不会根据模型名字猜 thinking level。
下面这项配置:
compat:
supportsReasoningEffort: true
只说明 API 接受 reasoning_effort,具体模型还要单独声明:
models:
- id: custom-reasoner
reasoningEfforts:
off:
low: low
medium: medium
high: high
协议能接什么参数,和模型实际开放什么能力,是两份配置。DSH 当前要求部署者手动补齐模型能力元数据。
DeepSeek 模型名不会触发专用适配
DSH 按 Provider 路由选适配器,不看模型名称。
如果请求是:
provider: custom-gateway
model: deepseek-xxx
上面的请求仍然走通用 llm-pi-ai。只有 deepseek-official 路由会进入 llm-deepseek。
DeepSeek 专用适配器主要处理:
- DeepSeek 原生 thinking 参数
off/high/max 推理等级映射
- 工具调用轮次的
reasoning_content 回传
- DeepSeek 工具消息格式
- DeepSeek SSE 流式响应
- Cache token 统计
- DeepSeek 错误分类
- 模型上下文和输出默认值
这些主要保证协议处理正确,不会额外增强模型能力。
如果自定义网关已经把 DeepSeek 协议完整转换成 OpenAI-compatible API,日常使用的差别可能不大。差异通常出现在:
- 长时间多轮工具调用
- 大量 reasoning 内容
- 上下文压缩
- 流式响应边界
- 网关没有完整透传 DeepSeek 特有字段
普通对话和简单改代码时,两条路径可能感觉不出区别。多轮工具调用和长 reasoning 场景下,专用适配器会更稳一些。
最后怎么理解 DSH
现在我们更愿意把 DSH 看成一个 Agent 运行时:
- Cordis 用可逆 effect 管理组件在时间上的进入与退出
- Cordis 用响应式 coeffect 管理组件在空间依赖图中的连接与重组
- Preset 决定 Agent 的组装方式
- Plugin 提供具体能力
- Agent Loop 只是其中一个组件
- GUI 是运行时的观察和控制界面
- ACP 或 WebSocket 是客户端接入协议
- 创造模式让 Agent 开始参与自身能力的构建
DSH 现在的优势不一定是“写代码比别的 Agent 更快”,而是可以更灵活地拆分、替换和试验 Agent 能力。
它还不成熟,下面这些问题都没有完全解决:
- 动态能力的持久化与分享
- 必要插件的保护机制
- 前端对动态目录的完整支持
- 自定义模型能力元数据
- 权限和审批边界
- 运行时实验如何转化为可维护代码
Pi 更关注把一个小而稳定的 Agent 做好,DSH 更关注怎样继续组装出不同的 Agent。回到开头的类比:一个更像 Vim,一个更像 Emacs。