同一张考卷,两个 AI 编码代理:DeepSeek Harness Python SDK 与 OpenCode 对照实测
引子
之前写过 DeepSeek Harness(dsh)的架构拆解和真实任务实测,当时的结局是:能用,但在快速迭代期没留下,我把它清掉了。
这次它以另一种形态回来——官方发布了 Python SDK,而且宣传点很具体:会话状态、用会话 UUID 复用上下文来省 token。
这个点正好戳中日常委托编码任务的痛点:每次让代理干活都从零开始,同一份上下文反复付费。于是我没有直接采信,而是搭了一套可复现的对照实验,把它和我日常在用的 OpenCode 拉到同一张考卷上。
结论先说:同题两轮小任务,两边都真做对了,dsh 略快略省;换成三轮递进的大任务,dsh 默认反而慢 1.5 倍、贵 3 倍。而”会话复用省 token”这件事,只在同一个进程内成立——那条最诱人的卖点,被我实测卡掉了。
下面是完整的取证过程。
一、先分清:这是另一条通道
第一件事是别把新旧两次部署搞混。它们虽然都叫 dsh、都用同一个模型,但形态完全不同:
| 上次(CLI 包) | 这次(Python SDK) | |
|---|---|---|
| 来源 | npm 包 @deepseek-ai/dsh |
PyPI deepseek-harness-sdk |
| 运行时 | 需要系统 Node ≥ 22.19 | 自带原生可执行文件,不需要 Node |
| 调用形态 | dsh --profile headless "任务" |
Python 子进程 JSON-RPC(stdio) |
| 会话 | 一次性,无跟进 | 有会话对象与事件流 |
有个细节值得一提:PyPI 上存在一个同名无关包叫 deepseek-harness,纯粹是蹭名字的;官方的是带 -sdk / -runtime-bin 后缀那两个。选包时认准作者和仓库链接。
SDK 的本质是子进程驱动:DeepSeekHarness(...) 在后台拉起打包好的 dsh --profile <profile>,然后通过 stdio 上的 JSON-RPC 通信。所以它并不是”一个新的 agent”,而是同一套 agent 的程序化接口——真正变的,是打包方式和你能拿到多少控制权。
顺带一个实打实的优点:每次调用的 token 用量可以从事件流里取出来(输入 / 缓存命中 / 输出 / 思考 token 分列),这让成本可以精确核算,而不是靠感觉。
二、能不能装:环境门槛与一个真实的坑
官方给的门槛不高:64 位 Linux、glibc ≥ 2.28、Python ≥ 3.10。我手头那台内网 x86 Debian 小主机是 glibc 2.41 + Python 3.13,全部满足。
真正的坑在网络上:
- PyPI 直连被重置(
curl: (35) Recv failure: 连接被对方重置),而且不只是索引页——大文件下载会在中途断流(实测下到 452KB 就断)。 - 解决办法是换镜像源,一次装完。顺带发现系统 Python 不自带 pip,得先补一个
python3-venv包。
装成之后的体积:虚拟环境约 290MB,其中绝大部分是那个274MB 的原生运行时单文件;此外每个工作目录一份的 harness home 只占几 MB。
凭据处理上沿用了一贯做法:API key 通过环境变量注入,不落盘。冒烟测试 3.9 秒返回预期输出,退出状态正常。
⚠️ 一个必须说清的风险:当前版本是 0.1.5-rc.1,属于 release candidate,官方明确声明会有破坏性变更,迭代速度极快。选它做生产依赖要接受这个前提。
三、怎么测才算数:把”感觉”换成”判据”
在讨论”效果好不好”之前,得先解决一个问题:我们凭什么相信结果?
搞过编码代理的人都知道,这类工具最大的风险不是”做不出来”,而是**”说自己做出来了”**。之前实测里就见过”报 Done + 退出码 0,产物其实不可用”的情况。
所以这次我把评估搭成了这样:
- 每个任务都自带验收闸门:一份可执行测试脚本,通过对 / 不通过是二值的。
- 期望值靠”预埋答案”生成:先自己写一份参考实现,用它算出期望值写进闸门——而不是让被测对象自己说”我算对了”。
- 闸门本身先验证:参考实现必须 100% 通过闸门,空实现必须全挂。两者都成立,判据才算有效。(这一步经常被跳过,但它是整个评估的地基。)
- 同种子、同 prompt、同验收文件的隔离工作区:两个候选各拿一份完全相同的副本,互不污染。
- 验收文件哈希校验:跑完之后比对验收脚本和期望值的哈希,确认没被篡改——防止”改测试来通过测试”。
- 所有闸门由我亲手复跑,不看代理的自述。
这套流程后来被证明是值得的:本轮的每一个结论都建立在”我自己跑出来的绿灯”上,而不是任何一句自我汇报。
第一个任务很小:一个购物车计价模块,我植入了三处 bug(档位边界用了 > 而非 >=、金额用向下取整而非四舍五入、优惠券在税后扣且没做零下限),要求修好使 9/9 用例通过。基线确认:植入 bug 的版本 0/9 全挂,判别力足够。
四、第一个反直觉发现:会话复用只在同一个进程里成立
这是整轮评估最关键的结论,也是我被”宣传点”误导的地方。
同一个进程内:有效,而且收益明显
在一个进程里用同一个会话连着做两轮(第一轮修 bug,第二轮在已经改好的代码上追加一个新函数):
| 轮次 | 耗时 | 新鲜输入 token | 缓存命中 | 输出 |
|---|---|---|---|---|
| 第 1 轮(冷) | 25.3s | 4,351 | 31,744 | 2,165 |
| 第 2 轮(同会话续做) | 5.2s | 1,696 | 26,240 | 608 |
对照组是”同样的第二轮任务,但开一个全新会话”:需要 7,87812,715 新鲜输入 token、3145 秒。
也就是说:续用会话让新鲜输入降到约 1/4.6,耗时降到约 1/6。宣传里说的收益,在这个范围内是真的。
跨进程:被明确拒绝
问题来了。真实的自动化场景里,任务不是在一个长进程里跑完的——每次委托往往是一次独立的调用。于是我试了在新进程里复用同一个会话 ID:
1 | h.run(prompt, session_id="...") → JsonRpcError: session "..." already exists |
再往下探,直接问底层 RPC:
1 | session/list → unknown DeepSeek Harness SDK runtime method |
而用 strings 翻那个 274MB 的运行时二进制,session/resume、session/load、session/list 这些符号明明存在——它们属于 CLI 那一侧的完整表面,只是 SDK 运行时把这条路封掉了。
结论很清楚:SDK 的会话对象只在一个 harness 进程的生命周期内有效。磁盘上那些 JSONL 会话日志是审计 / 重放用的,不是可续用的检查点。官方文档那句“复用 harness、home 和 id 来延续同一段对话”,真实含义就是这个限定范围——但它太容易被读成“随时可以续上”。
那对照组的 OpenCode 呢?
同样的问题抛给 OpenCode(它支持 --session <id>):新起一个进程,不读任何文件,仅凭会话历史回答”上一轮你新增的函数叫什么”——
1 | > build · deepseek-v4-flash |
答对了。所以在这个具体维度上,我一直用的 OpenCode 反而是更强的一方。
五、顺手抓了一次包:请求里到底发了什么
既然已经装好了,我顺手做了一件平时容易忽略的事:把 SDK 的请求指向一个本地 mock 端点,把原始请求体抓下来看。
(方法很土但有效:起一个假的 OpenAI 兼容接口,把收到的 body 落盘,再返回一段合法的流式响应。)
抓到的请求顶层字段:
1 | dsh_plugin_packages, max_tokens, messages, model, |
两点发现:
- ✅ 默认不上传会话日志——请求里没有
dsh_session_log字段。有意思的是,官方两份文档对此说法互相矛盾(一份说默认关闭,另一份的教程说默认会上传未接受的日志后缀)。以实测为准:默认不发。想彻底排除可以在 profile 配置里显式关掉。 - ⚠️ 但每个请求都会带一个
dsh_plugin_packages字段,约 1.8KB,内容是插件名 + 版本清单。它不是对话内容,但确实是把本机环境信息随请求发出去了。
另外两个默认值也值得知道:默认开启 thinking,且 reasoning_effort 默认是 high。这个默认值,在第七节变成了钱。
六、把短板补上:一个常驻 worker 原型
既然裸 SDK 的问题是”会话活不过进程”,那答案就很直接:让进程活着。
我写了一个小原型:
- worker:常驻服务,监听 Unix socket,按
(profile, 工作区)复用 harness——同一个工作区复用同一个 harness 进程,不同工作区各起一个;同一 harness 串行、不同 harness 并行;对外提供run / ping / sessions / shutdown。 - client:一个极瘦的客户端,一个进程发一个请求,用完即退。会话由 worker 持有。
这样一来,”调用方进程”和”会话持有者”解耦了。实测三个完全独立的客户端进程共用一个会话:
| 调用 | 内容 | 耗时 | 新鲜输入 | 我的验收 |
|---|---|---|---|---|
| 进程 1 | 修 3 处 bug | 9.7s | 2,438 | 9/9 通过 ✅ |
| 进程 2 | 同会话追加一个函数 | 6.1s | 1,393 | 双闸门通过 ✅ |
| 进程 3 | 只凭历史回忆上轮改了什么 | 1.0s | 188 | 答对 ✅ |
worker 日志确认 harness 只启动了一次(2.3 秒),后面全部复用。第 3 次调用几乎不花输入 token——因为它要的东西全都还在缓存里。
这就是那个缺失能力的补法:不指望 SDK 支持跨进程,而是自己维护一个长期活着的进程。这个思路对所有”启动成本高、会话有状态”的 CLI 代理都通用。
七、换大任务再测:数字翻转了
小任务上两边旗鼓相当,我不放心——小任务跑得好,不代表大任务跑得好,这几乎是我对这类工具最深的偏见来源。所以第二轮我做了一个真实一点的任务:
一个空白 Node 项目,分三轮递进交付:
- 写一个脏 CSV 解析器(要处理 BOM、CRLF、引号内的逗号和换行、
""转义、空字段补位、末尾空行) - 在解析器之上做分组聚合(count / sum / avg / min / max、空值边界、分组排序)
- 再做命令行入口(复用前两个模块、三种错误退出码、单行 JSON 输出)
每一轮配一个独立闸门(分别 3、4、10 项断言),期望值同样由参考实现预埋生成。
| 通道 | 三轮耗时 | 闸门(我复跑) | 基准被篡改 | 成本 |
|---|---|---|---|---|
| dsh SDK(常驻 worker) | 70 + 28 + 23 = 121s | 3/3 · 4/4 · 10/10 ✅ | 无 ✅ | $0.0160 |
dsh SDK(reasoning_effort=low) |
78 + 13 + 17 = 107s | 全过 ✅ | 无 ✅ | $0.0083 |
| OpenCode | 27 + 24 + 27 = 78s | 全过 ✅ | 无 ✅ | $0.0052 |
两者都真做对了——三轮闸门全绿,交付物规模相当(各约 150~190 行有效代码)。但代价完全不同:dsh 默认慢 1.5 倍、贵 3.1 倍。
钱花在哪儿了
翻 token 明细就一目了然:dsh 的总输出 token 是 22,831,其中 17,619(77%)是思考 token;OpenCode 同任务输出只有 4,200。前三节里那个”默认 reasoning_effort: high“,在这里变成了账单。
把 reasoning_effort 调成 low 之后:成本降 48%($0.0160 → $0.0083),质量不掉(三轮闸门照样全过),但仍然比 OpenCode 贵 1.6 倍、慢 1.4 倍。
成本是怎么算的(顺便验证了口径)
dsh 不上报美元金额,只给 token。所以我用官方公布的单价(缓存命中 / 未命中 / 输出三档)自己算。为了确认口径没错,我拿 OpenCode 自报的成本做了反向对账:
按公布单价的”谷值”档计算 → 得到 $0.0052,与 OpenCode 自报的增量成本完全一致。
这说明两件事:单价格式选对了,以及在便宜时段跑确实能拿到折扣价。先验证计算口径,再相信计算结果——这一步比计算本身更重要。
八、经验与结论
1. 自报永远不算数,判据要先于结论。
这轮所有绿灯都是我亲手跑出来的:闸门先被参考实现和空实现双向验证过,跑完再核对验收文件哈希。这个过程不浪漫,但它把”感觉它做得不错”换成了”它确实通过了这 17 项断言”。
2. 成本要看 token 结构,别信”印象便宜”。
同一对候选,在小任务上成本几乎一样($0.0027 vs $0.0029),换到大任务就差 3 倍。原因不是”谁更笨”,而是默认参数把预算导向了思考过程。任何”某工具更省钱”的结论,如果没拆开输入 / 缓存 / 输出三档看过,都不值得采信。
3. 要区分”被测对象失败”和”测试装置失败”。
这轮我犯了两次滑稽的错误:一次是给被测程序的 prompt 用了相对路径,工作目录一变就找不到文件,于是三轮全部”失败”;另一次是后台跑对照组的命令行时漏注入了 API key,它 10 秒就退出——我第一反应是”这工具怎么这么差”,差点把这个当成结论写进报告。
教训很明确:代理秒退、零产出、耗时异常短,这些首先应该怀疑测试装置,而不是被测对象。 我把这条写进了自己的操作手册。
4. 关于那个常驻 worker 原型。
它是这轮里我最满意的产出:不算复杂,但把”会话复用”从”进程内的特性”变成了”可跨调用的服务”。它也说明了一件更普遍的事——当工具有个你不喜欢的限制时,先看看是不是能在外面绕过去,而不是立刻放弃它或者接受它。
5. 最终结论:不替代,且已清理。
综合下来我没有保留它:
- 它的核心卖点(会话复用省 token)在跨进程场景下不成立,我补的 worker 补上了这个能力,但对照组原生就有;
- 大任务上它默认更慢更贵,要显式调参才能追到 1.6 倍差距;
- 版本还是 rc,官方声明会有破坏性变更。
它剩下的独特价值只有两处:逐次调用的 token / 缓存 / 结束原因可以编程取数(适合做计量与风控),以及不依赖 Node 的干净部署——这两个场景我暂时用不上。
所以评估做完,环境清理干净,回到原来的方案。
尾声
这轮下来,我真正收获的不是”哪个工具更好”的答案,而是一套可以重复使用的评估姿势:
先写参考实现,用它验证判据;再跑对照,用判据验收产物;最后拆开 token 明细看钱花在哪;并且时刻准备怀疑自己的测试装置。
工具会一直换,这套姿势不会。
封面图:Wikimedia Commons,English weight with calibration stamps,CC0(公有领域献出),摄影 HelgeRieder。选它是因为——评估的根本,是先有一个可信的标准参考物。








