从 DSH 的 --default-only 看默认组合,TaoToken 补凭据
发布时间:2026/9/18 12:34:08 锦皓数字建站

1. 从--default-only的 llm 行缺凭据说起先拿到 /tmp/base.yml如果你第一次跑 DSH很可能会遇到这种场景Web profile 能启动但一到模型对话就提示凭据缺失或者你执行pnpm dsh --profile web --dump-config --default-only看到llm行里只有 provider、model、超时之类的默认值却没有baseUrl和apiKey。这不是 DSH 配置坏了而是你正在看的是bundle 层默认组合。默认组合负责给出可运行的骨架不负责携带你的个人密钥。这时正确的动作不是去改 bundle也不是把整棵配置树重写而是去 TaoToken 官网创建 API Key再用用户补丁层补进llm行。TaoToken 的入口在这里https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentdsh_default_only_intro 。拿到 Key 后DSH 的 Base URL 固定用https://taotoken.net/api不要在后面附加多余路径也不要在 Base URL 上挂 UTM。本篇只做 DSH 强化训练里的练习 1只看 bundle 层产出/tmp/base.yml再叠加用户补丁做对比。你需要建立三个直觉--default-only输出的是 bundle 默认组合不含 profile 补丁、家目录补丁和命令行--patch。llm行属于“设置凭据”分组默认只给结构凭据应在用户层补。补丁是整行替换不是深合并。想保住原来的字段就要重述整行。先准备实验目录并导出两份配置mkdir -p /tmp/dsh-lab cd /path/to/your/dsh/workspace # 只看 bundle 层不包含用户补丁 pnpm dsh --profile web --dump-config --default-only /tmp/base.yml # 完整组合包含 profile 补丁、家目录补丁等用户层 pnpm dsh --profile web --dump-config /tmp/full.yml # 对比两份观察多出来的行来自哪一层 diff -u /tmp/base.yml /tmp/full.yml | tee /tmp/base-vs-full.diff如果diff输出很多不要慌。DSH 的配置树本质上是“行的组合”每一行代表一个插件或服务。你要先抓住七行llm、session、agent-loop、tools、system-prompt、sandbox-policy、agent-presets。它们是 Web profile 里最容易影响体验的功能分组。可以先用文本过滤把关键行拎出来# 假设输出顶层是 YAML 列表 yq .[] | select(.name llm or .name session or .name agent-loop or .name tools or .name system-prompt or .name sandbox-policy or .name agent-presets) /tmp/base.yml如果你机器上没有yq也可以直接用grep -ngrep -nE name: (llm|session|agent-loop|tools|system-prompt|sandbox-policy|agent-presets) /tmp/base.yml此时你大概率会看到llm行的config里没有apiKey也可能没有baseUrl。这是正常的因为 bundle 默认组合不应该硬编码任何人的密钥。对 DSH 初学者来说这一步的核心不是“修好它”而是先确认默认组合提供了什么用户补丁层应该补什么。如果你还没有 Key可以先去 TaoToken 控制台创建https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentdsh_default_only_keys 。创建时用YOUR_API_KEY作为占位符写在配置里真实 Key 不要提交到 Git。2. diff 两份配置树bundle 层与用户补丁层分别多了什么练习 1 的验收目标是能不看文件说出 base 的七个功能分组基础设施、编排、设置凭据、持久化、安全、模型面工具、委派。你可以先把/tmp/base.yml和/tmp/full.yml做结构化对比而不是一行行肉眼扫。# 把两份都转成按 name 排序的简表方便看差异 yq -r .[] | [.name, (.disabled // false)] | tsv /tmp/base.yml | sort /tmp/base.names.tsv yq -r .[] | [.name, (.disabled // false)] | tsv /tmp/full.yml | sort /tmp/full.names.tsv diff -u /tmp/base.names.tsv /tmp/full.names.tsv你可能会发现full.yml多出来的行不一定来自 profile 补丁也可能来自家目录级补丁。DSH 的组合层优先级大致是bundle 默认 → profile 补丁 → 家目录补丁 → 命令行 --patch越靠后优先级越高同一行如果后写就会覆盖前面的值。注意这里说的是“行级覆盖”和“整行替换”不是把两个对象的字段深度合并。很多初学者写补丁时只写- name: llm config: apiKey: YOUR_API_KEY结果provider、model、timeout等默认字段全没了然后开始怀疑 DSH 是不是坏了。其实是你把整行替换成了一个不完整的llm行。正确做法是你想覆盖llm行就重述这一行里你希望最终存在的字段。回到七个分组可以这样建立直觉分组代表行你该关注什么基础设施session会话生命周期、存储位置、恢复策略编排agent-loop主循环、停止条件、工具调用节奏设置凭据llmbaseUrl、apiKey、模型名、超时持久化session相关配置历史记录、工作区状态、缓存安全sandbox-policyshell 沙箱、文件访问边界模型面工具tools暴露给模型的工具面委派agent-presets子代理、预设、角色委派system-prompt往往横跨“设置”和“编排”它决定 persona 和系统提示词sandbox-policy则和安全强相关。至于bash-sandbox与pwsh-sandbox为什么互斥可以理解成同一个宿主运行时里默认 shell 沙箱只能选中一个。它们依赖的底层服务和平台能力不同两个都激活会争抢同一个服务键或同一个执行入口所以组合层通常用互斥方式表达。你在full.yml里看到其中一个处于启用状态另一个可能是disabled: true而不是被删除。这里有一个重要习惯不要通过删行来禁用功能。上层补丁如果还想覆盖这一行删掉后可能找不到目标补丁会静默失效。DSH 的 web-app 层更推荐“禁用而非删除”- name: pwsh-sandbox disabled: true这样配置树里仍然有这一行只是不激活。后续要排查bash-sandbox和pwsh-sandbox的互斥关系也能从disabled状态一眼看出来。3. 给 llm 行补 TaoTokenBase URL、Key 与整行替换规则现在进入本篇最关键的动作给默认llm行补凭据。你需要 TaoToken 的 API Key入口在官网https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentdsh_llm_credential 。创建后先放到环境变量里避免把明文写进补丁文件export TAOTOKEN_API_KEYYOUR_API_KEY然后编辑 profile 级补丁。路径通常是$DSH_HOME/profiles/web/cordis.patch.yml其中$DSH_HOME默认是~/.dshmkdir -p ~/.dsh/profiles/web $EDITOR ~/.dsh/profiles/web/cordis.patch.yml写入一个完整的llm行。注意name以你--dump-config实际输出的包名为准下面用llm表示该行# ~/.dsh/profiles/web/cordis.patch.yml - name: llm disabled: false config: provider: openai-compatible baseUrl: https://taotoken.net/api apiKey: !!js process.env.TAOTOKEN_API_KEY model: claude-sonnet-4-5 timeoutMs: 60000这里有两个重点第一baseUrl必须用https://taotoken.net/api不要写成https://taotoken.net/api/v1或其他猜测路径。Base URL 是给 DSH 的 LLM 插件拼接请求用的多一层少一层都可能导致 404。第二apiKey用!!js process.env.TAOTOKEN_API_KEY表示在挂载时读取环境变量。如果你暂时不想用环境变量也可以先写占位符apiKey: YOUR_API_KEY但不要把真实 Key 提交到仓库。对初学者来说更稳妥的方式是本地 shell 里export补丁文件里只保留!!js process.env.TAOTOKEN_API_KEY。改完后重新导出完整配置pnpm dsh --profile web --dump-config /tmp/full-with-taotoken.yml # 只看 llm 行最终值 yq .[] | select(.name llm) /tmp/full-with-taotoken.yml如果输出里baseUrl是https://taotoken.net/apiapiKey已经被替换成你的环境变量值或占位符说明用户补丁层已经生效。接下来做一次“叠加验证”# 用 --patch 临时再覆盖一次观察后写层是否赢 pnpm dsh --profile web --dump-config \ --patch /tmp/override-llm.yml /tmp/full-with-patch.yml其中/tmp/override-llm.yml可以写- name: llm config: provider: openai-compatible baseUrl: https://taotoken.net/api apiKey: YOUR_API_KEY model: claude-sonnet-4-5 timeoutMs: 120000对比timeoutMs是60000还是120000你就能亲眼看到“后写的层赢”。同时也要注意--patch同样是整行替换所以上面把五个字段都重述了。如果你只写timeoutMs: 120000那前面 profile 补丁里的apiKey和baseUrl可能会消失最终llm行变成不完整配置。4. 把覆盖写进 profile 补丁家目录级、profile 级与 --patch 的优先级练习 1 的产物不只是/tmp/base.yml还包括一份你自己写的 profile 补丁。它以后会成为所有 DSH 排障的对照基线。你现在可以按下面顺序做一次完整实验。第一步确认 profile 级补丁cat ~/.dsh/profiles/web/cordis.patch.yml第二步再写一条家目录级补丁让它对所有 profile 生效$EDITOR ~/.dsh/cordis.patch.yml内容可以覆盖llm的默认模型但记得重述整行# ~/.dsh/cordis.patch.yml - name: llm disabled: false config: provider: openai-compatible baseUrl: https://taotoken.net/api apiKey: !!js process.env.TAOTOKEN_API_KEY model: claude-sonnet-4-5 timeoutMs: 90000第三步观察优先级。根据 DSH 的规则家目录级补丁优先于 profile 级补丁。所以如果 profile 级写了timeoutMs: 60000家目录级写了90000最终应该看到90000pnpm dsh --profile web --dump-config /tmp/final.yml yq .[] | select(.name llm) /tmp/final.yml第四步再用--patch临时覆盖验证最高优先级pnpm dsh --profile web --dump-config \ --patch /tmp/override-llm.yml /tmp/final-with-cli-patch.yml yq .[] | select(.name llm) /tmp/final-with-cli-patch.yml这样你就能画出一行配置的最终值推导过程bundle 默认 llm.apiKey 空 → profile 补丁 llm.apiKey !!js process.env.TAOTOKEN_API_KEY → 家目录补丁 llm.baseUrl https://taotoken.net/api → --patch llm.timeoutMs 120000 最终 llm 行 最后写入层提供的完整字段集这里有几个初学者最容易踩的卡点。第一补丁文件为空或只有注释会加载失败。因为补丁文件需要能被解析成列表空文件解析结果不是列表只有注释也可能被当成空内容。处理方式是保留合法列表项或者暂时不要创建这个补丁文件。第二禁用某行不要删行用disabled: true。删掉之后上层补丁想覆盖它时可能找不到目标导致你以为补丁写了实际没生效。第三每改一行配置先想清楚它属于哪个平面是宿主组合还是 agent preset。宿主组合影响 DSH 进程本身agent preset 影响模型被注入怎样的提示词、工具和委派规则。把两层混在一起改排查会非常痛苦。对练习 1 来说llm行属于宿主组合里的设置凭据平面不要把它写到 agent preset 里。如果你在验证过程中发现apiKey没生效可以按三步排查--dump-config看最终值diff定位来源层修改对应层后再导出验证。不要靠猜。5. 读懂 !!js 与 inject为什么行序不重要服务键才重要练习 1 还要求你建立对!!js和加载顺序的直觉。虽然本篇重点是默认组合和补凭据但如果不理解这两个概念补丁很容易写错。!!js的核心规则是在挂载时求值不是每次读取时求值。也就是说DSH 加载配置树时会把表达式算一次之后插件读到的就是那个结果。它可用的 Loader 上下文包括process、ctx、dshHomePath、baseUrl等。例如- name: llm config: apiKey: !!js process.env.TAOTOKEN_API_KEY baseUrl: !!js baseUrl ?? https://taotoken.net/api这里process.env.TAOTOKEN_API_KEY在挂载时读取环境变量。如果你启动 DSH 的 shell 里没有TAOTOKEN_API_KEY求值结果就是undefined之后再怎么刷新 Web UI 也不会自动补上。另一个表达式baseUrl ?? https://taotoken.net/api表示优先使用 Loader 上下文里的baseUrl没有就回退到 TaoToken 的 API 地址。但为了避免不确定性给 DSH 的llm行补凭据时建议直接写死baseUrl: https://taotoken.net/api只在apiKey这类敏感字段上用!!js process.env...。再比如ctx.serviceName它只有在inject激活之后才可引用。你不能在一个插件还没等到依赖服务时就假设ctx.serviceName可用。这也是 DSH 里“加载顺序”和“激活顺序”容易混淆的地方。在组合文件中行的先后顺序没有语义。你把llm行放在第一行还是最后一行不会决定它是否先激活。真正决定激活的是服务依赖- name: session inject: [llm] - name: agent-loop inject: [llm, session, tools]如果某一行声明了inject: [llm]那么它进入 waiting 状态直到llm服务出现并激活。所以调试“为什么某个插件没跑”时不应该盯着 YAML 行序而应该看服务键grep -n inject /tmp/full.yml grep -n name: /tmp/full.yml | head -80如果llm本身被disabled: true那么依赖它的session、agent-loop就可能一直等不到服务。此时你要检查的是llm行的disabled状态、baseUrl、apiKey而不是把它挪到文件最上面。回到练习 1 的验收你应该能不看文件说出 base 的七个功能分组并解释bash-sandbox与pwsh-sandbox为什么互斥。前者是基础设施/安全层面的 shell 执行沙箱后者是另一种 shell 沙箱它们在同一条激活链上争夺同一个执行服务所以组合层会禁用其中一个。你还应该能说清楚行声明了什么服务依赖比它写在文件哪个位置重要得多。6. 同一台机器上的 Claude Code、Codex、CC Switch 如何统一到 TaoTokenDSH 补完凭据后很多人会顺手把 Claude Code、Codex、CC Switch 也配到同一个供应商。这里必须分清工具不能把ANTHROPIC_*套到 Codex 上。Claude Code 使用settings.json常见位置是~/.claude/settings.json或项目级.claude/settings.json{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_API_KEY: YOUR_API_KEY, ANTHROPIC_MODEL: claude-sonnet-4-5 } }如果你使用 Claude Code 的文档配置方式可以参考 TaoToken 的 Claude Code 文档https://taotoken.net/doc/ClaudeCodeAnthropic?utm_sourcetaotoken_aicg_blog_endutm_contentdsh_claude_code_doc 。注意这里仍然是 Claude Code 的ANTHROPIC_*环境变量不要混到 Codex。Codex 使用config.toml常见位置是~/.codex/config.tomlmodel gpt-5 model_provider taotoken [model_providers.taotoken] name TaoToken base_url https://taotoken.net/api env_key TAOTOKEN_API_KEY然后在你启动 Codex 的 shell 里设置export TAOTOKEN_API_KEYYOUR_API_KEY注意Codex 的配置里没有ANTHROPIC_API_KEY也没有ANTHROPIC_BASE_URL。如果你把 Claude Code 的环境变量抄到 Codex轻则不生效重则报鉴权错误。CC Switch 可以按“三件套”理解供应商名称、Base URL、API Key。新增一个供应商时填供应商名称TaoToken Base URLhttps://taotoken.net/api API KeyYOUR_API_KEY 默认模型claude-sonnet-4-5如果 CC Switch 里还要选择协议或模型列表按你实际使用的客户端来填。核心仍然是三件套地址、密钥、模型。DSH 的llm行、Claude Code 的settings.json、Codex 的config.toml、CC Switch 的供应商配置都使用同一个 Base URLhttps://taotoken.net/api。如果你还没有统一 Key可以回到 TaoToken 控制台创建https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentdsh_cc_switch_keys 。创建后建议按工具分开命名例如dsh-web、claude-code、codex方便以后排查和吊销。7. 收尾三步排查与高转化 CTA练习 1 的最终产物应该包括两个文件/tmp/full.yml以及一份你写的 profile 补丁。它们是你后续所有 DSH 练习的对照基线。以后遇到“为什么行为是这样”的问题按三步走用pnpm dsh --profile web --dump-config看最终值。用diff和yq定位这个值来自 bundle、profile、家目录还是--patch。修改对应层再重新 dump 验证。不要一上来就改 bundle也不要用删除行的方式禁用功能。默认组合是骨架用户补丁才是你的个人凭据和偏好。给llm行补 TaoToken 时记住三个固定值Base URL 用https://taotoken.net/apiKey 用YOUR_API_KEY或环境变量补丁整行替换而不是深合并。如果你已经完成/tmp/base.yml和/tmp/full.yml的对比下一步可以体验 TaoToken 的模型对话确认 Key 和模型名是否匹配https://taotoken.net/models/detail/chat?utm_sourcetaotoken_aicg_blog_endutm_contentdsh_chat_cta 。如果准备把 DSH、Claude Code、Codex 一起接入可以看 Coding Plan 是否适合你的使用频率https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentdsh_plan_cta 。需要创建或轮换 Key 时从控制台进入https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentdsh_keys_cta 。Claude Code 的具体环境变量和 settings.json 写法以文档为准https://taotoken.net/doc/ClaudeCodeAnthropic?utm_sourcetaotoken_aicg_blog_endutm_contentdsh_doc_cta 。官网总入口在这里https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentdsh_final_checklist 。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。