dshkit

在 DeepSeek Harness 里把 Claude Code 和 Codex 当子 agent 调用

dsh 自带六个子 agent provider,其中包括真实的 Claude Code(走官方 Agent SDK)和真实的 Codex(走 codex app-server)。确切的配置行、为什么工具默认是关的、凭证清洗,以及子 agent 到底继承和不继承什么。

更新于 2026-08-143 min
一句话结论

dsh-subagent-claude-code 和 dsh-subagent-codex 这两个包注册的是真实 provider,驱动你本机装好的 claude 和 codex。两者默认都会加载,但完整 agent 预设里对应的工具行是 disabled: true——复制一份预设、删掉那个字段,才会暴露出 subagent_claude_code 或 subagent_codex。凭证形态的环境变量会被清洗掉,所以给子 agent 的 API key 必须显式写在 config.env 里。

「DeepSeek Harness 能把 Claude Code 和 Codex 当子 agent 调用」这个说法在第一周传得很广,但没人拿出过 配置。这件事是真的,而且是第一方的,包名是 @deepseek-ai/dsh-subagent-claude-code@deepseek-ai/dsh-subagent-codex

下面是它们实际在干什么。

Provider 家族

委派是一个 seam——ctx.subagents——而且同一个上下文里可以并存多个具名 provider。自带这些:

子 agent 是什么
dsh-subagent-spawn-in-process全新的进程内子 agent
dsh-subagent-fork-in-process从父级已完成历史起步的进程内子 agent
dsh-subagent-acp经 ACP 的进程外子 agent
dsh-subagent-codex真实的 Codex app-server 子进程
dsh-subagent-claude-code真实的 Claude Code 子进程,走官方 Claude Agent SDK
dsh-subagent-dsh-sdk经 TypeScript SDK 的进程外 Harness 子 agent

dsh-tool-subagent 才是把它们暴露给模型的那个包——你想暴露几个 provider,就实例化几次这个工具包。

Claude Code

provider 在委派方会话的工作区里调用官方 Claude Agent SDK,通过共享的 subprocess 服务解析出原生 claude 可执行文件,提交一段自包含的文本任务,只把最终答案返回。

- id: subagent-claude-code
  name: '@deepseek-ai/dsh-subagent-claude-code'
  config:
    env:
      ANTHROPIC_API_KEY: !!js process.env.ANTHROPIC_API_KEY
 
- id: tool-subagent-claude-code
  name: '@deepseek-ai/dsh-tool-subagent'
  disabled: true
  config:
    provider: claude-code
    toolName: subagent_claude_code
    enableRunInBackground: false
    maxDepth: provider-managed

两行:一行 provider,一行把它暴露出来的工具。注意 !!js——这是一个 YAML 标签,会求值一段 JavaScript 表达式,这里用来读宿主的环境变量。

它用的是你真实的 Claude 安装。 provider 刻意省略了 SDK 的 settingSources 选项,所以官方 SDK 会相对父会话的 cwd 去读你正常的用户级、项目级、本地 Claude 设置,包括原生账号状态。它既不复制也不过滤 这些文件,也不创建或修改登录状态。

每次查询都设 persistSession: false 并禁用 AskUserQuestion。不提供 canUseTool、elicitation 或 对话回调——所以无人值守场景下的交互会通过 SDK 直接失败,而不是卡在一个这个 provider 并不拥有的 用户界面上。

配置默认含义
env{}显式的 SDK/CLI 环境,叠加在被清洗过凭证的父环境之上
disposeGraceMs3000进程树终止各档之间的宽限毫秒数;之后等待整棵树退出

运行时依赖锁定在 @anthropic-ai/[email protected]

Codex

provider 在委派方会话的工作区里启动 codex app-server --stdio,走 initializeinitializedthread/start { cwd, ephemeral: true },提交一个任务,然后等待权威的 turn/completed 通知。

- id: subagent-codex
  name: '@deepseek-ai/dsh-subagent-codex'
  config:
    env:
      OPENAI_API_KEY: !!js process.env.OPENAI_API_KEY
 
- id: tool-subagent-codex
  name: '@deepseek-ai/dsh-tool-subagent'
  disabled: true
  config:
    provider: codex
    toolName: subagent_codex
    enableRunInBackground: false
    maxDepth: provider-managed

有意思的是它怎么处理审批。因为子 agent 无人值守,provider 会在请求提供的选项里挑一个「非批准」的 决定,优先选 cancel;如果请求形态里没有可选决定列表,就退回 decline。它用一个空的、turn 作用域 的权限集合回答权限请求,用「无答案」回答用户输入请求,并拒绝 MCP elicitation。一个没有合法无人值守 回应的请求会让整次运行失败。

换句话说:这个 Codex 子 agent 没法被说服去批准任何东西。 这是刻意的安全属性,同时也意味着需要审批 的任务会失败,而不是硬着头皮往下走。

失败映射也很精确:失败 turn 的 codexErrorInfocontextWindowExceeded 时映射成 max-tokens; 其它远端中断一律 error;本地取消映射成 aborted。这个 provider 永远不产生 refusal

开发期证据锁定在 @openai/[email protected],但那个 npm 包是仅测试用依赖——部署时仍然需要你自己在 PATH 上提供 codex

三个一定会绊到你的点

工具默认是关的

所以「我装好了但没有 subagent_claude_code 这个工具」是预期行为,不是 bug。

不显式传,你的 API key 会被清洗掉

凭证形态的环境变量会在显式 env 覆盖之前被移除。 所以即使你在 shell 里 export 了 ANTHROPIC_API_KEY,子 agent 也看不到——除非你把它写进 config.env,而这正是 !!js process.env.ANTHROPIC_API_KEY 那一行在做的事。

非凭证类变量会活下来:ANTHROPIC_BASE_URLPATHHOME 等普通环境值仍然被继承,除非你覆盖它们。

子 agent 在对话层面什么都不继承

两个 provider 都报告 inheritsParentContext: false。子 agent 收到的是:

  • 那段独立的文本任务
  • 父会话的 cwd

不会收到父对话、人设、工具过滤、深度策略或结构化输出契约。每次运行都有独立的进程、取消控制器, 以及不持久化的产品会话。

据此设计:任务字符串就是全部的交底。 子 agent 需要上下文,就把它写进任务里,或者放进共享工作区 ——工作区是唯一的通道。

生命周期

两个 provider 的 dispose() 都是幂等的:中止运行、关闭协议通道(Codex 还会尽力发一次 turn/interrupt)、触发共享的进程树终止升级,然后等待整棵树退出。

注意这里的纪律:优雅关闭表达的是协议意图,但进程静默与否以 subprocess 句柄为准。 一个只会「好好 商量」的 provider 会漏进程。结果失败和拆卸失败是分开记的。

这件事比看起来更重要

子 agent provider 就是注册在某个 seam 上的普通插件。这意味着把某一步路由给另一家厂商的 agent 是一个 配置决策,不是一个集成项目——而且不管活是谁的模型干的,harness 始终是那个编排者。

这也意味着这些工具之间诚实的比较不是「谁赢」。你完全可以让 DeepSeek 的 harness 当主循环、把 Claude Code 当成里面的专家——这是正经架构,不是骑墙。各自强在哪,见 DeepSeek Harness vs Claude Code

常见问题

Claude Code 子 agent 能看到我的对话吗?

看不到。两个 provider 都报告 inheritsParentContext: false。子 agent 收到的是一段独立的文本任务和父会话的 cwd——不包括父对话、人设、工具过滤、深度策略或结构化输出契约。

为什么这个工具默认用不了?

provider 会在宿主上加载,但完整 agent 预设里的工具行带着 disabled: true,而且在工具被调用之前不会启动任何产品进程。复制一份预设、删掉那个字段,工具就只对用你这份副本组装出来的 agent 可见。

子 agent 用的是哪个账号?

宿主上的原生安装。Claude Code provider 刻意省略了 SDK 的 settingSources 选项,所以官方 SDK 会读你正常的用户级、项目级和本地 Claude 设置。两个插件都不装 CLI、不选模型、不创建产品目录、不登录、也不探测账号。

为什么我的 API key 传不到子 agent?

凭证形态的环境变量会在显式 env 覆盖之前被移除。要给子 agent 的 key 必须写在 config.env 下。非凭证类变量如 ANTHROPIC_BASE_URL、PATH、HOME 仍然会被继承。

接着看