同一套 Agent Runtime,为什么 Web、Headless 和 Python SDK 仍然不是同一个产品

同一个 Agent Runtime,只要换个 UI、包一层 HTTP 或 SDK,是不是就能变成多个产品?
这句话只对了一半。共享 Agent Loop、Session、Tool Registry 与模型适配器,确实可以避免为每个入口复制一套核心;但产品差异并不会因此消失。它们会转移到组合配置、宿主生命周期、输入输出协议、凭据、工作区、持久化和版本冻结上。
DeepSeek Harness 的固定源码把这个边界表达得很清楚:Profile 选择有序 Bundle,Bundle 分发 Cordis 配置行与代码,Profile、Home 和命令行 Patch 继续覆盖,最后得到真正启动的插件树。Web、Headless 与 Python SDK 可以复用底层语义,却不能只靠"核心相同"证明行为相同。
本文只解决一个工程问题:怎样把同一套 Runtime 交付成多个产品面,同时让每次部署仍然可复现、可诊断、可回滚?
最终产物不是一张功能表,而是一份部署冻结清单。
产品面不是皮肤,而是运行合同

固定架构文档把 dsh 描述为启动时由有序层组合出的插件树。固定源码还内置了两个 Profile 模板:
web:dsh-base+dsh-web-app;headless:dsh-base+dsh-headless。
dsh-base 提供模型适配、工具、持久化、Sandbox、审批策略、设置、凭据与遥测等基础能力;dsh-web-app 增加浏览器应用及 Host/Client 相关组件;dsh-headless 增加一次性 Runner,而且明确不带 Host、HTTP 或浏览器层。
因此,Web 与 Headless 的差别不是"有没有页面"这么简单。它们至少改变了四类合同:
- 入口合同:谁创建 Session、怎样提交 Prompt、何时认为一次调用结束;
- 宿主合同:进程是常驻服务、一次性命令,还是由 SDK 管理的子进程;
- 能力合同:启动了哪些插件、工具、Provider、持久化与审批策略;
- 观测合同:输出走终端、浏览器事件、退出码,还是 JSON-RPC 通知。
共享 Runtime 只能说明这些产品面可以复用一组底层原语,不能说明它们对调用者暴露同一种产品语义。
Profile 决定"选什么",Bundle 决定"带什么"


Profile 是命名后的组合入口。它的 package.json 在 dsh.profile.bundles 中列出有序 Bundle,也可以安装树外插件,并保存自己的 cordis.patch.yml。
Bundle 则是分发单元。每个 Bundle 在 package.json 的 dsh.bundle.patch 中指向自己的 Patch 文件,把一组 Cordis 配置行和相应代码送进组合链。它不是一个不可修改的黑盒;更高层仍能定位某个行 ID 并替换整段配置。
固定实现的应用顺序是:
text
empty entry list
→ profile 中声明的 bundles(按顺序)
→ profile/cordis.patch.yml
→ $DSH_HOME/cordis.patch.yml
→ --patch overlays 与命令行派生 patch
→ effective Cordis tree
这里有两个容易踩坑的细节。
第一,Patch 不是任意 YAML 深度合并。它按行 ID 定位并替换整段 config,或者插入新行。只记录"改了 temperature"却没有保存替换后的整行,复盘时可能遗漏同一行中被覆盖的其他字段。
第二,真正启动的不是 Profile 名字,而是最终展开的插件树。两个环境都叫 web,只要 Home Patch、命令行 Patch、树外插件版本或凭据不同,实际产品就可能不同。
所以部署记录不能只写:
text
profile=web
至少还要保存有效配置快照和各层来源。固定文档提供的 dsh --profile web --dump-config,其意义就在于查看机器真正要启动的树,而不是相信模板名称。
Web:共享核心之上,多了一套长期宿主


固定 dsh-web-app 的 package 描述把它定义为 Web Patch 层与 Runtime glue:前端静态资源、Host Web Server、Client/Host Runner、连接、工作区、设置、Session 投影、插件清单、目录选择、UI 节点等都进入依赖集合。
这意味着 Web 产品面要额外回答:
- Host 与 Client 的版本是否匹配;
- 工作区和目录选择由谁授权;
- 页面显示的插件与实际 Runtime 树是否一致;
- 浏览器断连、刷新或多标签页是否改变 Session 所有权;
- Settings 修改何时变成配置事实,是否需要重载插件;
- UI 看见的轨迹是否来自可回放 Session Event,而不是临时内存。
原始研究包记录过一次真实页面检查:Web 服务能够启动,设置页、模型、插件和 Preset 可见;缺少 API Key 时 Commands 与 Send 不可用。这个结果只能证明页面与凭据门禁存在,不能证明真实模型、工具或远端搜索链路成功。
因此,"Web 能打开"是 UI smoke,不是产品 E2E。部署冻结清单必须把页面资产、Host/Client 构建、Runtime 配置和真实模型凭据验证分开记录。
Headless:不是删掉 UI,而是收紧退出语义
固定 dsh-headless 明确是一次性 Runner,没有 Server。它适合脚本、CI、服务包装和配置诊断,但也因此需要更严格的机器合同:
- 输入从参数、stdin 还是文件进入;
- Session ID 与工作区怎样选择;
- 输出中哪些是最终结果,哪些是日志或流式事件;
- 凭据缺失、配置错误、模型失败和工具失败怎样映射退出码;
- 收到取消信号时,是否已经等待后台资源和子进程结算;
- 一次调用结束的条件是第一段文本、Turn 结束,还是 Agent 再次 idle。
研究包中的 Headless dump-config 成功,只能证明组合可解析;最小请求停在 MISSING_CREDENTIAL,只能证明凭据门禁工作。它没有证明模型 E2E,也没有证明脚本调用者已经获得稳定的退出码和日志协议。
换句话说,Headless 的可靠性不来自"没有 UI",而来自调用者能否把每一种终态解释成确定的自动化动作。
Python SDK:复用 Runtime,不等于 Python 重写 Runtime


固定 Python SDK README 的第一句话已经限定了架构:客户端通过 stdio 上按行分隔的 JSON-RPC 驱动 DeepSeek Harness 子进程。
SDK 默认定位随 wheel 安装的 dsh-jsonrpc-agent,并注入配套的 Cordis 配置。该配置包含 JSON-RPC Server、Agent Core、预载 DeepSeek Adapter、JSONL Session 持久化、显式 checkpoint policy、本地 Bash 和文件系统 Provider。
这形成了三层清晰边界:
text
Python 调用代码
→ SDK 生命周期与 JSON-RPC Client
→ bundled dsh runtime 子进程
→ Cordis 配置决定的 Agent 产品
所以 Python SDK 不是另一份 Python Agent Core。SDK 版本、Runtime Binary 版本、Cordis 配置与协议版本共同决定行为。
固定教程还提醒:复用同一个 Harness 和 Session ID,会保留 Session 所有的 Bash 进程状态,包括工作目录、环境变量和 Shell 函数。新任务应使用新 Session ID;只有确实想延续同一对话与 Shell 状态时才复用。
这不是调用示例中的小细节,而是产品隔离合同。若批处理系统只冻结 Prompt,没有冻结 Workspace、Session ID 策略与子进程复用策略,跨 Case 污染就会被误判成模型能力。
三种产品面共享什么,又不能共享什么

可以把边界压缩成一张表:
| 层 | 可以共享 | 不能默认共享 |
|---|---|---|
| Runtime 原语 | Agent Loop、Session Event、Tool Pipeline、LLM Seam | 具体插件集合与 Provider 实现 |
| 组合 | Profile/Bundle/Patch 机制 | Home Patch、CLI Patch、树外插件与解析结果 |
| 状态 | Session Log 的事件词汇 | Session ID 策略、持久化位置、进程内资源 |
| 安全 | Approval、Sandbox、FS 等能力接口 | 每个产品面的实际策略与宿主权限 |
| 交付 | 固定 Git SHA 与包版本 | Web 静态资源、Headless 退出协议、SDK Binary/协议配对 |
| 验证 | 固定任务、断言与证据格式 | UI smoke、单测、模型 E2E 和生产验证的证据等级 |
共享左侧能减少重复实现;没有冻结右侧,仍然会出现"同一个 Runtime 在三个入口行为不一样"。
发布版本漂移,正是产品化风险的一部分


固定研究基线是 Commit 47f9438,仓库包版本为 0.1.0-rc.5。到 2026-08-25 再看官方分发面,npm @deepseek-ai/dsh 已到 0.1.1-rc.2,PyPI deepseek-harness-sdk 已到 0.1.1rc1。PyPI 页面同时明确:SDK 会安装同版本的 deepseek-harness-runtime-bin,默认通过 stdio JSON-RPC 驱动 bundled Runtime。
官方仓库仍把项目标为 Developer Preview,并直接提醒会发生兼容性破坏。这组事实不能推出新版本与固定 rc5 行为相同,也不能只凭版本号判断不兼容。它只说明:发布速度很快,冻结对象必须覆盖 Git 来源、npm CLI、Python SDK、Runtime Binary、内置配置和协议,而不能只记一个 rc 标签。
因此每次升级都应重新核对构建来源、Profile schema、内置 Cordis 配置、协议和最终展开树。本文没有运行 rc.2/rc1 的跨产品面 E2E,版本更新只作为重新验证触发器。
一份最小部署冻结清单

如果要把 Web、Headless 或 Python SDK 放进团队环境,建议每次发布至少冻结以下内容。
1. 来源与分发物
- Git 仓库与完整 Commit SHA;
- npm/PyPI 的精确版本,禁止生产使用浮动
latest; - 安装包或容器镜像摘要;
- Python SDK 与 bundled Runtime Binary 的配对版本;
- Web 前端资产与 Host/Client 构建标识。
2. 组合结果
- Profile 名称和
dsh.profile.bundles有序列表; - 每个 Bundle 的精确版本;
- Profile、Home、CLI Patch 的原文件 SHA;
--dump-config的最终展开快照;- 树外插件及依赖锁文件。
3. 运行环境
- 模型 Provider、模型 ID、Base URL 与凭据来源;
- Workspace、Session Root 与 Session ID 生成/复用策略;
- Sandbox、FS、Shell、网络与审批策略;
- 持久化后端与 checkpoint policy;
- 环境变量白名单,而不是把密钥写进冻结文件。
4. 产品面合同
- Web:Host/Client 兼容、目录授权、断连恢复、设置变更与真实发送 smoke;
- Headless:输入协议、结构化输出、退出码、超时、信号和资源结算;
- SDK:JSON-RPC 协议、通知订阅、子进程启动/关闭、Runtime 缺失与版本不匹配错误;
- CLI:交互输出与脚本输出分离,避免把终端 UX 当稳定 API。
5. 证据等级
SOURCE_CONFIRMED:固定源码或文档直接证明;SCOPED_VERIFIED:某条命令、页面、构建或测试在指定环境通过;NOT_E2E:没有真实凭据、完整 Agent 任务或下游副作用验证;PRODUCTION_VERIFIED:只有目标环境、真实流量和明确验收证据才能使用。
这份清单的核心不是多记元数据,而是能够回答:某次线上行为究竟由哪一棵插件树、哪一个宿主、哪一组状态与哪套协议产生。
什么时候不值得做多产品面
Profile/Bundle/Patch 会减少 Fork,但也会增加组合空间。若团队只有一个稳定入口、很少替换 Provider,也没有树外插件需求,显式单配置或单服务可能更容易维护。
多产品面真正值得的前提是:
- 多个入口确实共享大部分 Runtime 语义;
- 差异能够被组合层清晰表达,而不是靠隐式环境变量;
- 团队愿意保存最终展开树并做兼容验证;
- 插件卸载、Patch 覆盖和宿主退出都有明确诊断;
- 每个产品面仍有独立验收,而不是共享一个"核心测试通过"。
如果做不到这些,插件化只会把代码 Fork 变成配置漂移。
结论

DeepSeek Harness 的 Profile、Bundle 与 Patch 给出了一条很有价值的产品化路线:底层 Agent Runtime 不必为 Web、Headless 和 SDK 各复制一份;产品差异可以被表达为有序插件层、宿主与协议。
但"同一 Runtime"不是交付证明。真正可复现的单位应是:
text
Source SHA
+ exact distributions
+ ordered bundles
+ all patch layers
+ effective config
+ host contract
+ workspace/session policy
+ verification evidence
只有这组信息被一起冻结,Web、Headless 与 Python SDK 才是同一底盘上的三个可解释产品,而不是三个名字相似、行为靠环境碰运气的入口。
参考资料
- DeepSeek Harness 固定 Commit
47f943859bef60e4160492346772ded9b24f765a:docs/architecture.md、packages/boot/app-boot/src/profile.ts、packages/bundle/base、packages/bundle/web-app、packages/bundle/headless。 - 同一固定 Commit:
python/sdk/README.md、python/sdk-runtime/README.md、docs/user/guide/python-sdk.md、BENCHMARK.md。 - DeepSeek Harness 官方仓库 README,检查日 2026-08-25:github.com/deepseek-ai...
- npm 官方注册表
@deepseek-ai/dsh,检查日 2026-08-25:www.npmjs.com/package/@de... - PyPI 官方项目
deepseek-harness-sdk,检查日 2026-08-25:pypi.org/project/dee... - 原始研究包
deepseek-harness-deep-research.zip,SHA-2564a17109171ce70bb16373cfa673fc3831d5040df70134cce71bd0f687628b4b6;其中运行记录仅作SCOPED_VERIFIED,不作本轮重新执行证明。