ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

DeepSeek 开源 Agent 框架一天 8 万星,TaoToken 视角下它到底在卷什么?

DeepSeek 开源 Agent 框架一天 8 万星,TaoToken 视角下它到底在卷什么?

1. 从 8 万星说起:dsh 到底解决了什么工程问题

DeepSeek 开源 Agent 框架这件事,热度来得快,但真正值得琢磨的不是星数,而是它把「harness」这个概念摆到了台面上。所谓 harness,你可以理解成给模型套的一层壳:模型负责推理,壳负责给它读文件、跑命令、存状态、管会话。Claude Code、Codex CLI 本质上都是这类东西。dsh 的不同点在于,它把这层壳的每一块都拆成了插件。

我第一次看到「Everything is a plugin」这句口号时,反应是:这不就是把 VS Code 的扩展体系搬到 Agent 主循环上吗。但仔细看它的内核 Cordis,会发现比 VS Code 走得更远——插件可以动态挂载、卸载,甚至在线替换 UI 组件,不用重启主程序。对跑长任务的 Agent 来说,这个能力很实在:你正在跑一个多轮工具调用的会话,中途想换模型、加一个搜索工具、调整沙箱策略,传统做法是重启整个会话,上下文全丢;dsh 的做法是热插拔,会话继续。

它预置了四种运行时模式,定位很清晰。Standard 是完整工具集,文件编辑、shell、搜索、技能、规划、子 Agent 全都有;Code 模式在标准能力之上允许模型用 TypeScript 编排多轮工具调用;Minimal 只留 bash 和文件编辑器两个工具,专门用来裸测模型本身的能力;Creator 给造轮子的人用,可以边跑边检查运行时、在内存里测插件。Minimal 这个模式很懂行——做模型对比评测时,harness 的加成会干扰结论,把壳剥干净才能看出模型真实水平。

另一个被社区反复提的点是「Every run is traceable」。系统提示、推理过程、工具调用、子 Agent 调度、每一次上下文注入,全部记进一个 append-only 的会话日志,Trajectory 视图按来源分类展示,支持 resume、fork、搜索、回放。调试 Agent 最痛苦的就是不知道模型到底「看」到了什么,这套留痕机制把可观测性从运维概念搬进了 Agent 开发主流程。

那为什么是 Node.js?社区争论很凶,但结论其实不复杂:异步是标配、跨平台、解释型迭代快,最核心的是 npm 生态天然适合分发插件。Agent 跑起来大部分时间在等网络和 token 流,原始性能不是瓶颈,内存别爆才是。这也是我后面要重点讲的:Node.js 集成路径和插件配置,是判断这个框架接入成本的关键。

2. TaoToken 前置:统一 Key 与 API 通道,先把调用链路跑通

在动手写插件之前,有个现实问题要先解决:dsh 支持接 Anthropic、OpenAI、本地模型,但每接一家就要配一套 Key、一套 Base URL、一套鉴权头。如果你只是想验证插件机制、跑通调用链路,反复切换供应商配置会很烦。我的做法是先用 TaoToken 把 Key 和 API 通道统一掉,这样插件里只需要认一个 Base URL 和一个 Key,模型 ID 按需切换。

TaoToken 在这里的角色是统一入口:官网 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 地址是 https://taotoken.net/api 。注意 API 地址不带 UTM 参数,配置时直接写这个。它的价值在于,你不需要为每个模型供应商单独维护一套环境变量,插件代码里读的是同一组配置。

具体操作上,先去控制台拿 Key。控制台入口:https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite 。拿到 Key 之后,API Keys 管理页在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite ,可以在这里创建、轮换、删除 Key。建议给 dsh 单独建一个 Key,方便后面排查问题时隔离影响面。

模型 ID 这块,不同供应商命名不一样,TaoToken 侧会做映射。你可以在模型对话页先确认目标模型能不能正常响应:https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=model-chat&utm_campaign=rewrite 。这一步别跳过,很多人插件写完报错,最后发现是模型 ID 写错了,白白浪费半小时。

接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,里面有各语言的调用示例和参数说明。如果你用的是 Claude Code 这类工具,Anthropic 兼容接入的说明在 https://taotoken.net/claudecode-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=claudecode-anthropic&utm_campaign=rewrite 。dsh 本身支持 Anthropic 协议,所以这条路径是通的。

这里要强调一点:TaoToken 是统一 API 通道,不是让你绕过什么。它的作用是让你在多个模型之间切换时,配置只改一处。对 dsh 这种插件化框架来说,插件里硬编码供应商地址是很糟糕的做法,统一通道能让插件保持干净。

环境变量建议这样组织,后面插件代码会直接读:

export TAOTOKEN_API_KEY="sk-你的Key" export TAOTOKEN_BASE_URL="https://taotoken.net/api" export DSH_DEFAULT_MODEL="你的模型ID"

把这三行写进你的 shell 配置或者项目的 .env 文件。注意 Base URL 结尾不要多加斜杠,很多 401 和 404 都是路径拼接问题导致的。配完之后先用 curl 验证一下通道是否通:

curl -sS "$TAOTOKEN_BASE_URL/v1/models" \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" | head -c 500

如果返回模型列表,说明 Key 和通道都没问题。如果返回 401,先检查 Key 有没有多余空格;如果返回 404,检查 Base URL 是不是写成了带 /v1 的完整路径又重复拼接了。这一步跑通,再进插件配置。

3. 可复制配置:Node.js 插件片段与 dsh 接入

dsh 的插件体系基于 Cordis,插件本质是一个导出特定结构的模块。下面给一个最小可用的 Node.js 插件片段,作用是注册一个自定义工具,并通过 TaoToken 统一通道调用模型。先建项目:

mkdir dsh-plugin-demo && cd dsh-plugin-demo npm init -y npm install @deepseek-ai/dsh

然后创建插件文件plugin.mjs:

// plugin.mjs export const name = 'taotoken-demo-plugin' export const inject = ['tools', 'models'] export function apply(ctx) { // 注册一个自定义工具:查询当前时间 ctx.tools.register({ name: 'get_current_time', description: '返回当前服务器时间,用于验证插件是否被正确加载', parameters: { type: 'object', properties: {}, required: [] }, async execute() { return { time: new Date().toISOString() } } }) // 注册一个模型供应商,走 TaoToken 统一通道 ctx.models.registerProvider({ id: 'taotoken', baseURL: process.env.TAOTOKEN_BASE_URL, apiKey: process.env.TAOTOKEN_API_KEY, models: [ { id: process.env.DSH_DEFAULT_MODEL, protocol: 'anthropic' } ] }) }

这段代码有两个关键点。第一,inject声明了插件依赖tools和models两个能力,Cordis 会保证在它们就绪后再调用apply,这是热插拔体系的基础。第二,模型供应商的baseURL和apiKey都从环境变量读,不硬编码,这样切换通道只改环境变量。

接下来是 dsh 的配置文件。dsh 支持用配置文件声明加载哪些插件、用哪个模型。在项目根目录建dsh.config.json:

{ "mode": "standard", "model": { "provider": "taotoken", "id": "你的模型ID" }, "plugins": [ "./plugin.mjs" ], "server": { "host": "127.0.0.1", "port": 3080 }, "trace": { "enabled": true, "storage": "./.dsh/traces" } }

这里mode选standard,因为我们要用完整工具集验证插件。trace.enabled打开,方便后面看调用链路。plugins数组里写插件路径,dsh 启动时会动态加载。

如果你更习惯用 TOML,等价配置如下:

mode = "standard" [model] provider = "taotoken" id = "你的模型ID" [plugins] paths = ["./plugin.mjs"] [server] host = "127.0.0.1" port = 3080 [trace] enabled = true storage = "./.dsh/traces"

两种格式选一种就行,别混用。启动命令:

npx @deepseek-ai/dsh web --config ./dsh.config.json

启动后默认在 http://127.0.0.1:3080 。如果端口被占用,改配置里的server.port即可。启动日志里应该能看到插件加载记录,类似plugin loaded: taotoken-demo-plugin。如果没看到,说明插件路径或导出结构有问题,先检查name和apply有没有正确导出。

关于 Coding Plan,如果你打算长期用 dsh 跑编码类 Agent 任务,可以了解下 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite ,它在长任务场景下比按次调用更划算。不过这是后话,先把插件跑通。

4. 验证请求:从工具调用到模型响应的完整链路

配置写完,怎么确认整条链路是通的?分三步验证,每步都有明确的成功标志。

第一步,验证插件加载。启动 dsh 后,在 Web UI 里发一条消息,让它调用get_current_time工具。比如输入「现在几点了,用工具查一下」。如果插件加载成功,模型会触发工具调用,返回一个 ISO 时间戳。这一步验证的是 Cordis 插件机制和工具注册是否正常。

第二步,验证模型通道。在 UI 里发一条普通对话,比如「用一句话解释什么是插件热插拔」。如果模型正常回复,说明 TaoToken 通道、Key、模型 ID 三者都对上了。这一步如果失败,大概率是 401 或模型 ID 错误,回到第 2 节用 curl 再验一遍。

第三步,验证 trace 留痕。打开.dsh/traces目录,应该能看到按会话分文件的日志。用 dsh 的 Trajectory 视图查看,能看到系统提示、推理过程、工具调用、上下文注入的完整事件流。这一步验证的是可观测性,也是 dsh 相比其他 harness 的差异点。

我实测下来,三步都通过的话,说明你的 Node.js 集成路径是完整的。这时候可以开始改插件,加自己的工具。比如加一个调用内部 API 的工具:

ctx.tools.register({ name: 'query_internal_api', description: '查询内部服务状态', parameters: { type: 'object', properties: { service: { type: 'string', description: '服务名' } }, required: ['service'] }, async execute({ service }) { const res = await fetch(`https://internal.example.com/status/${service}`) if (!res.ok) { throw new Error(`查询失败: ${res.status}`) } return await res.json() } })

注意工具执行函数里抛出的错误会被 harness 捕获并作为工具结果返回给模型,模型会据此决定重试还是换策略。这是 Agent 框架和普通函数调用的区别:错误也是上下文的一部分。

如果你想在多个模型之间对比同一个插件的表现,可以在配置里加多个 provider,然后用mode: minimal跑裸测。Minimal 模式只有 bash 和文件编辑器,能最大程度剥离 harness 加成,看出模型本身的差异。这个用法在评测场景下很实用。

验证过程中,Trajectory 视图是排查问题的利器。如果模型没有按预期调用工具,去 trace 里看系统提示是怎么描述工具的,很多时候是 description 写得不够清楚,模型没理解什么时候该用。把 description 改得更具体,比如加上「当用户询问时间相关问题时使用」,命中率会明显提升。

5. 常见报错排查:401、local proxy failed、reading choices、OAuth

这一节按真实报错来,每个都给定位思路和修复动作。

401 Unauthorized。最常见,原因通常是 Key 无效、Key 有多余空格、或者 Base URL 和 Key 不匹配。先确认环境变量有没有正确导出:echo $TAOTOKEN_API_KEY | wc -c,如果长度明显不对,说明没读到。再确认 Base URL 是https://taotoken.net/api,不要带尾部斜杠。如果用的是配置文件里的 apiKey 字段,检查有没有被 JSON 转义搞坏。修复后重启 dsh,别指望热重载能读到新的环境变量。

local proxy failed。这个报错通常出现在你本地配了代理,但代理没起来或者端口不对。dsh 本身不需要代理,如果你环境里有HTTP_PROXY或HTTPS_PROXY变量,先 unset 掉再启动:

unset HTTP_PROXY HTTPS_PROXY ALL_PROXY npx @deepseek-ai/dsh web --config ./dsh.config.json

如果确实需要走网络中间层,确保它监听在配置的地址上,并且 dsh 的server.host和它不冲突。这个报错和 TaoToken 无关,是本地网络环境问题。

reading choices 相关报错。典型信息是Cannot read properties of undefined (reading 'choices')。这说明代码在解析模型响应时,拿到的结构里没有choices字段。原因通常是:模型返回的是 Anthropic 格式(content数组),但插件按 OpenAI 格式(choices)解析。检查插件里protocol字段有没有和实际返回格式对上。dsh 支持多协议,但插件里要声明清楚。如果你在 TaoToken 侧切换了模型,协议可能跟着变,这时候要同步改插件配置。

OAuth 相关报错。如果你用的是 Claude Code 或类似工具,可能会遇到 OAuth token 过期。这类工具走的是 OAuth 流程,和 API Key 是两套鉴权。dsh 本身用 API Key,不涉及 OAuth。但如果你在 dsh 里嵌了别的工具,那个工具可能需要 OAuth。排查方法是看报错来自哪个模块,如果是 dsh 主进程,检查 API Key;如果是子进程,检查子进程的鉴权配置。Claude Code 的 Anthropic 接入说明在 https://taotoken.net/claudecode-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=claudecode-anthropic&utm_campaign=rewrite ,里面有 OAuth 和 API Key 两种方式的区别。

插件加载失败但没报错。dsh 启动日志里没有plugin loaded,但也没报错。这种情况通常是插件导出结构不对。Cordis 要求插件导出name、apply,如果用了inject还要确保依赖的能力存在。检查plugin.mjs有没有语法错误,用node --check plugin.mjs快速验证。另外,ESM 和 CJS 混用也会导致加载失败,统一用.mjs后缀或者package.json里声明"type": "module"。

模型不调用工具。不是报错,但很常见。模型收到工具定义后不调用,通常是 description 太模糊,或者参数 schema 有问题。把parameters写成标准 JSON Schema,required字段别漏。如果还是不行,在系统提示里明确写「你有以下工具可用,需要时请调用」,能显著提升调用率。

排查顺序建议:先 curl 验通道,再验插件加载,再验工具调用,最后看 trace。每一步都有独立成功标志,别跳步。我踩过的坑是插件报错信息被吞了,后来在启动命令加--verbose才看到完整堆栈。

6. 接入成本与扩展边界:什么时候该用 dsh

回到最初的问题:dsh 一天 8 万星,到底在卷什么。我的判断是,它卷的是「壳的可控性」。模型能力在趋同,API 价格在下降,真正拉开差距的是开发者能不能掌控 Agent 的运行环境。dsh 把插件、trace、模式切换都开放出来,等于把控制权交还给开发者。

接入成本方面,Node.js 路径是清晰的:装包、写插件、配 config、启动、验证,一个下午能跑通。真正的成本在插件生态——你要自己写工具、自己调 description、自己处理错误。dsh 提供的是机制,不是开箱即用的解决方案。如果你期待装完就能干活,可能会失望;如果你需要深度定制 Agent 行为,这套机制很值。

扩展边界上,Cordis 的热插拔是核心优势。长任务中途换模型、加工具、调沙箱策略,不用重启会话。这个能力在真实项目里价值很大,尤其是需要多模型协作或者动态调整策略的场景。但要注意,dsh 还是 developer preview,README 明确写了会有破坏性变更。生产环境用之前,先锁版本,别用 latest。

统一 Key 和 API 通道这块,TaoToken 的价值在于让你在多个模型之间切换时,插件代码不用改。模型对话页 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=model-chat&utm_campaign=rewrite 可以先验证模型可用性,接入文档 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 有完整参数说明。API Keys 管理在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite ,建议给不同项目建不同 Key,方便隔离和轮换。

最后给个实用建议:先用 minimal 模式跑一遍你的模型,看裸能力;再用 standard 模式跑同样的任务,对比 harness 加成有多少。这个对比能帮你判断,你的场景到底需不需要 dsh 这层壳。如果 minimal 和 standard 结果差不多,说明你的任务对工具依赖低,用轻量方案就行;如果差距明显,dsh 的插件体系就值得投入。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进