Vibe Coding 速用指南包

Practical Guide · Recipes · 单册实用指南(已按章节自动拆分卡片):方法论、copy-paste 配方、AGENTS.md 范例与 Skills 模板
← 返回教程馆
3阶段
70章节
6源文档
vibecoding仓库目录

📊 我的学习进度 本地自动保存

加载中…
🧭 导览0/0
正文0/0
appendix0/0
examples0/0
💡 每章卡片右上角「✓ 标记已学」;教程间互链已自动映射到本页章节;代码示例请配合 Gitee 仓库 使用。

🧭 DSH Vibe Coding 教程包 README.md

面向 DeepSeek Harness(DSH)的实操教程:项目配置、Skill 写法、Agent 边界、任务拆分、多 Agent 协同。所有规则都对着 DSH 源码或在本机运行时实跑验证过;没能验证的条目都明确标注了「未验证」和自测方法。

从哪开始

你的情况 读这个
第一次用 DSH,想知道该怎么使力 主教程 第 0 章 心智模型
想马上把一个仓库接上 配方 1:让一个新仓库"接上"
想固化团队流程 第 2 章 写好 Skill
担心 agent 越界 第 3 章 Agent 边界
任务一大就翻车 第 4 章 任务拆分
想并行推进多条线 第 5 章 多 Agent 协同
会话越长越笨 第 6 章 上下文工程
出问题了 第 8.2 节 症状→根因→处理

文件清单

vibe-coding-guide.md                  主教程(第 0–8 章)
appendix/copy-paste-recipes.md        10 条 5 分钟配方 + 常用命令一览
validate-guide.py                     校验脚本(见下节"验证方式")
examples/
  profile-patch.example.yml           插件与权限配置补丁(已用 --dump-config 实跑验证)
  settings.example.yaml               用户设置(模型/权限/子 agent 模型/主题)
  AGENTS.example.md                   项目级工作指令模板
  skills/project-standards/SKILL.md   Skill 模板 A:规范类
  skills/run-tests/SKILL.md           Skill 模板 B:流程类
  agent-presets/readonly-agent/       自定义 agent preset:只读评审(不含 write/edit)

验证方式

教程包里的示例做了三层校验,都可复现:

  1. YAML/JSON 语法 + Skill frontmatter 规则 + 链接/锚点

    & 'D:\env\python\python.exe' 'E:\dsh\chat\vibe-coding-guide\validate-guide.py'
    

    校验内容:YAML/JSON 可解析(容忍 !!js 标签)、补丁顶层是数组、skill name 为 kebab-case 且与目录名一致、description 非空且 ≤500 字符、全文链接与章节锚点可达。

  2. profile 补丁实跑

    & 'D:\softwarr\deepseekHARNESS\resources\runtime\cli\bin\dsh.cmd' --profile web `
        --patch 'E:\dsh\chat\vibe-coding-guide\examples\profile-patch.example.yml' --dump-config
    

    退出码 0,且补丁值出现在 dump 里,说明每个 id 与 config 键都被当前版本接受。

  3. skill 规则来源:packages/skill/skill-filesystem(frontmatter 解析)、packages/skill/tool-skill(目录注入与 500 字符截断)。

三条使用须知

  1. 示例文件是模板,不是自动生效的配置。每条配方都写了"复制到哪里"和"怎么验证生效"。
  2. 改配置前先备份、改完必须验证:--dump-config 是唯一可信的"我现在到底跑的是什么"。
  3. 补丁按行整体替换:命中的行 config 全量覆盖,没写的键回到默认值;没有 remove 语法,停用一行写 disabled: true;空补丁文件必须写 []。

本机基准

  • Windows + DSH Web GUI(dsh --profile web)
  • 启动器:D:\softwarr\deepseekHARNESS\resources\runtime\cli\bin\dsh.cmd(未加入 PATH)
  • DSH home:C:\Users\<你>\.dsh(可用 DSH_HOME 环境变量覆盖)
$DSH = 'D:\softwarr\deepseekHARNESS\resources\runtime\cli\bin\dsh.cmd'
& $DSH --profile web --dump-config          # 当前生效的完整配置树
& $DSH --profile web --dump-default-config  # 只看官方默认值

📄 开篇 vibe-coding-guide.md · 开篇

面向 DeepSeek Harness(下称 DSH)的实战手册:怎么配项目、怎么写 skill、怎么划边界、怎么拆任务、怎么让多个 agent 协同。

教程里的每一条规则都能在 DSH 源码或你的本机运行时里找到出处。凡是我没能验证的,都会明确标「未验证」,并给出自测方法——请把它们当假设,不要当结论。

章节 你学到什么 什么时候读
第 0 章 心智模型 四个杠杆、黄金循环、三条反直觉原则 先读,10 分钟
第 1 章 项目配置 profile 补丁、settings、agent preset、MCP 第一次接入一个仓库
第 2 章 写好 Skill frontmatter 精确规范、发现优先级、失败模式 想把团队经验固化下来
第 3 章 Agent 边界 权限预设、沙箱真实语义、读后写、plan mode、AGENTS.md 让 agent 跑得久一点而不出事
第 4 章 任务拆分 拆分判据、七种手法、规模选择决策树 任务一大就翻车的时候
第 5 章 多 Agent 协同 subagent / fork / workflow、六种拓扑、上限 想并行推进多条线
第 6 章 上下文工程 压缩、截断、转存、按需读取 会话越长越笨的时候
第 7 章 端到端实战 一个需求从 0 到验收的完整走法 想直接抄流程
第 8 章 验收与排障 症状→根因→处理表、速查卡 出问题时
附录:复制粘贴配方 10 个 5 分钟配方 想立刻动手

可复制的示例文件(正文里会逐个引用):

examples/
  profile-patch.example.yml                  # → ~/.dsh/profiles/web/cordis.patch.yml
  settings.example.yaml                      # → ~/.dsh/settings.yaml
  AGENTS.example.md                          # → <repo>/AGENTS.md
  skills/project-standards/SKILL.md          # → <repo>/.dsh/skills/project-standards/SKILL.md
  skills/run-tests/SKILL.md                  # → <repo>/.dsh/skills/run-tests/SKILL.md
  agent-presets/readonly-agent/              # → ~/.dsh/.agent-presets/readonly-agent/

📄 5 分钟速查 vibe-coding-guide.md · 5 分钟速查

这四张表是全文的压缩版,细节在对应章节。

一、配置放哪里

想改的东西 放哪 生效方式
工具行为、插件参数、权限预设表 <你的仓库>/.dsh/… 之上一层的 ~/.dsh/profiles/web/cordis.patch.yml 该 profile 配了 patchReload: live 时热重载
想对所有 profile 生效的补丁 ~/.dsh/cordis.patch.yml 优先级高于 profile 补丁
默认模型、默认权限档、子 agent 可用模型、主题语言 ~/.dsh/settings.yaml 热重载;只影响新建会话
项目该怎么被改、怎么验收 <repo>/AGENTS.md 首个请求注入;子目录文件在触碰后补注入
团队流程与判定标准 <repo>/.dsh/skills/<name>/SKILL.md 按需加载(模型的 skill 工具或用户 /name)
一种专用 agent(工具集 + 人设) ~/.dsh/.agent-presets/<id>/ 空白会话里选择

二、边界怎么选

任务风险 权限预设 沙箱 审批 还要做什么
只读分析、评审、答疑 audit-only(自定义) read-only ask 让 agent 先给结论再给证据
日常改代码、跑测试 workspace-write 工作区 + 会话私有临时目录 ask 先 plan 再动手;AGENTS.md 写清禁止项
可信仓库里的大批量重构 danger-full-access 不隔离 never 必须配合版本控制与可回滚的工作区
摸不准的新仓库 read-only 先侦察 read-only ask 侦察完再切档

三、任务怎么拆

规模 用哪个机制
一次对话能完成 直接做;动手前先读现状
多步骤、要人确认方案 /plan → exit_plan_mode 提交计划
跨多轮、有明确完成定义 create_goal + update_goal
需要可见进度 todo_write(全量替换,in_progress 可真并行)
独立、可并行的方向 subagent(后台默认)或 subagent_fork(需要父会话上下文)
几十个同构对象要扫 workflow 脚本扇出

四、协同怎么组织

拓扑 适用 关键纪律
扇出—扇入 多角度研究、多目录审计 每个子任务给固定输出格式
流水线 迁移、逐文件改造 pipeline 无屏障;每阶段带证据
生成者—审查者 高风险改动 审查者必须看原始证据,不是看生成者的总结
计划—执行分离 需求模糊 计划产出物是"可验收的步骤",不是技术方案散文
后台并行 + 主线继续 长耗时命令、外部依赖 别轮询,等通知;别重复已委派的工作
大规模扫描 上百文件 用 workflow,注意 maxTotalAgents = 1000 上限

本教程的机器基准:Windows + DSH Web GUI(dsh --profile web,即你正在看的页面)。dsh 命令在安装目录里,没进 PATH:

& "$env:USERPROFILE\.dsh\..\..\..\..\softwarr\deepseekHARNESS\resources\runtime\cli\bin\dsh.cmd" --help  # 示意,请用下面那种写法
& 'D:\softwarr\deepseekHARNESS\resources\runtime\cli\bin\dsh.cmd' --profile web --dump-config

$DSH_HOME 默认是 C:\Users\<你>\.dsh;可以用同名环境变量覆盖。


第 0 章 心智模型

📄 0.1 vibe coding 不是"少写代码",而是"换个地方使劲" vibe-coding-guide.md · 0.1 vibe coding 不是"少写代码",而是"换个

传统编码里,你的产出是代码。vibe coding 里,你的产出是边界 + 验收标准,代码由 agent 写。于是你的力气应该花在四件事上:

杠杆 你控制的东西 失效时的症状
上下文 agent 此刻能看到什么 它"忘了"约定、反复问同一件事
边界 它能碰什么、什么需要你确认 它改了不该改的文件、越权写系统目录
拆分 一个工作单元有多大 任务做到一半失去方向,改动无法验证
协同 谁来做、结果怎么回流 你成了人肉消息总线,或两个 agent 互相覆盖

一句话判断你有没有用好 DSH:你写的 prompt 越来越短,而仓库里的 AGENTS.md / skill / preset 越来越多。因为前者每次都要重说,后者说一次就一直在。

📄 0.2 黄金循环 vibe-coding-guide.md · 0.2 黄金循环

不管是人做还是 agent 做,一个健康的工作单元都是这五步:

① 读现状   → ② 给计划 → ③ 小步改 → ④ 立即验证 → ⑤ 记录/沉淀
   (证据)      (可验收)   (最小改动)  (真实输出)    (进 AGENTS.md / skill)

最常见的失败是跳过 ① 和 ④:不看现状就开始写(于是重复造轮子、改错地方),改完不验证就宣布完成(于是"应该可以"成了结论)。教程后面反复出现的一句话是:只报实际跑过的结果。

📄 0.3 三条反直觉原则 vibe-coding-guide.md · 0.3 三条反直觉原则

原则一:能力 ≠ 权限

一个工具出现在模型面前,不等于它能成功执行;反过来,工具被拿掉,模型连"想调用"都不存在这个概念。DSH 把这两件事分开:

  • 可见性:工具是否注册、是否被 toolFilter 屏蔽、preset 是否挂载了这个插件行。
  • 权限:沙箱模式(能不能写)、审批策略(要不要问你)。

所以"我怕它乱删文件"这句话有两个完全不同的解法:不挂载 write/edit 插件行(能力层面),或者切到 read-only 沙箱(权限层面)。两者都要用才稳。

原则二:配置是整体替换,不是合并

DSH 的 profile 补丁按 id 命中一行,命中的那一行整个 config 被替换。不会和你原来的值深合并。

后果:如果你只想把 maxInlineBytes 从 50000 改成 20000,你必须把那行原来关心的所有键都重新写清楚——否则没写的键会回到 schema 默认值。写补丁前先看当前行长什么样:

& 'D:\softwarr\deepseekHARNESS\resources\runtime\cli\bin\dsh.cmd' --profile web --dump-config > dump.yml
# 然后在 dump.yml 里找到那个 id,把它附近的 config 整块抄下来再改

另外两个坑:补丁没有 remove 语法(停用一行写 disabled: true);补丁文件如果只有注释、没有 [],启动会失败。

原则三:子 agent 是叶子节点

委派深度是有上限的,默认 3,而且宿主可以把它压到 1。在当前这台机器上实测就是 1:我让一个子 agent 再去委派,返回的是

Error: subagent depth 2 exceeds maxDepth 1

值得注意的是:超限时工具依然可见,报错发生在调用时。所以你会看到子 agent"试图"委派然后失败,而不是干脆没有这个工具。

实践含义:委派提示词必须写成"一个自包含的工人任务",不要写"你再去拆一下"。需要两级以上的结构,就由主 agent 用 workflow 脚本直接编排,而不是让中间层递归。

📄 0.4 术语表(后文只用这些词) vibe-coding-guide.md · 0.4 术语表(后文只用这些词)

术语 含义
profile 一种启动形态(web / headless / sdk / acp),本质是一份分层补丁的组装结果
补丁 / patch 一个 YAML 数组,按 id 覆盖或插入插件行
bundle 官方提供的补丁层,如 @deepseek-ai/dsh-base、dsh-web-app
settings ~/.dsh/settings.yaml,按命名空间分段的用户设置(热重载)
preset 一份"某个 agent 由哪些工具/提示词/skill 组成"的目录定义
skill 一段按需加载的指令(SKILL.md),不增加任何能力
AGENTS.md 工作区指令文件,随会话自动注入
subagent 由当前 agent 创建的、独立上下文的子 agent
workflow 一段 JS 编排脚本,用来成批调度子 agent
job 后台作业(长命令或后台子 agent)
goal / todo 跨轮目标 / 当前执行清单

📄 0.5 本教程的验证约定 vibe-coding-guide.md · 0.5 本教程的验证约定

  • 「实测」= 我在当前这台机器上真的跑过并看到了输出。
  • 「源码」= 来自 D:\project\github\deepseek-harness 的实现或文档。
  • 「未验证」= 我没能确认,给出自测命令,你验证后再当结论用。

第 1 章 项目与工程配置

📄 1.1 四层配置,各管一段 vibe-coding-guide.md · 1.1 四层配置,各管一段

① bundle 层        @deepseek-ai/dsh-base → dsh-web-app     (官方默认值,你不改)
② profile 层       ~/.dsh/profiles/web/cordis.patch.yml    (你的主要战场)
③ home 层          ~/.dsh/cordis.patch.yml                 (对所有 profile 生效)
④ 调用层           dsh --profile web --patch ./extra.yml   (一次性实验)

后写的层按行 id 覆盖先写的层,行内 config 整体替换。查看任意一层的真实结果:

# 目前生效的完整插件树(①–④)
& 'D:\softwarr\deepseekHARNESS\resources\runtime\cli\bin\dsh.cmd' --profile web --dump-config

# 只看 bundle 默认值(①),用来对比"我到底改动了什么"
& 'D:\softwarr\deepseekHARNESS\resources\runtime\cli\bin\dsh.cmd' --profile web --dump-default-config

# 带某份实验补丁看效果(推荐先用这个验证,再写进 profile)
& 'D:\softwarr\deepseekHARNESS\resources\runtime\cli\bin\dsh.cmd' --profile web `
    --patch 'E:\dsh\chat\vibe-coding-guide\examples\profile-patch.example.yml' --dump-config

最后一条我实测过(exit=0),所以 examples/profile-patch.example.yml 里出现的键都是当前版本真实接受的。

改哪些东西要重启?

改动 生效方式
profile 的 cordis.patch.yml 内容 patchReload: live 时热重载(随附 web profile 就是 live)
补丁里增删插件包(dsh plugin ... add) 需要重启 profile
settings.yaml 热重载
.agent-presets/<id>/ 里的文件 每次读名单都重新扫盘;但已按旧版组装的会话继续用旧组装
skill 的 SKILL.md 目录条目变更会被监听;正文每次加载都重读

📄 1.2 补丁条目语法(记住这五个字段) vibe-coding-guide.md · 1.2 补丁条目语法(记住这五个字段)

- id: tool-web                      # 定位一行;命中就替换它的 config
  name: '@deepseek-ai/dsh-tool-web' # 必填,插件包名
  config:                           # 整体替换,不是深合并
    search: true
    fetch: true
    searchTimeoutMs: 60000
  disabled: false                   # 想停用一行就用它,没有 remove 语法

- insert:                           # 往插件树里插入新行(例如接一个 MCP server)
    - id: mcp-filesystem
      name: '@deepseek-ai/dsh-mcp-client'
      config: { ... }

实用细节:

  • 顶层必须是数组;空补丁写 [],只写注释的文件会让启动失败。
  • !!js 表达式只在两个地方被求值:config 里的值和 disabled。写成 !js(单叹号)不合法。
  • 补丁匹配不到任何行会打一条 stderr 警告,不报错——所以拼错 id 时你可能以为生效了其实没有。改完必须 --dump-config 确认。
  • 一个补丁文件里,前面 insert 进来的行可以被后面的条目按 id 引用。

📄 1.3 settings.yaml:用户级、热重载 vibe-coding-guide.md · 1.3 settings.yaml:用户级、热重载

路径 <你>\.dsh\settings.yaml(settings.json/.yml 也行)。结构是「命名空间 → 用户配置」,解析顺序是:schema 默认值 → 插件组合里的 base → 你的用户段。

可直接复制 examples/settings.example.yaml,核心几段:

agent-default-model:          # 新会话默认模型
  provider: deepseek-official
  model: deepseek-flash
  reasoningEffort: high

permission:                   # 新会话默认权限预设
  defaultPreset: workspace-write

subagent-model-selection:     # 允许子 agent 使用哪些模型路由
  enabled: true
  allowedModels:
    - provider: deepseek-official
      model: deepseek-flash

agent-presets:                # 新会话默认 preset
  default: standard

agent-loop:
  maxParallelToolCalls: 8     # 一轮内最多并行几个工具调用

可用的命名空间(源码确认存在的):agent-default-model、agent-loop、permission、subagent-model-selection、agent-presets、ui-theme、locale、ui-conversation、ui-chat、shell、llm-deepseek、llm-pi-ai、web-search-deepseek、ui-onboarding。

两个必须记住的性质:

  1. 只影响新建会话。permission.defaultPreset 只在你新开一个会话时被读一次;正在跑的会话保持它原来的档位(这个设计是对的:中途换权限会让已经发生的工具调用变得不可解释)。
  2. 密钥不在这个文件里。凭据在 ~/.dsh/.credentials.yaml,环境变量优先。

📄 1.4 agent preset:把"一种 agent"变成工程资产 vibe-coding-guide.md · 1.4 agent preset:把"一种 agent"变成

在 Web GUI 下,每个浏览器会话都不共享进程级工具集,而是从一个 preset 组装自己:它的工具、提示词段落、skill。所以"给 agent 收窄能力"最自然的位置就是 preset。

~/.dsh/.agent-presets/<id>/
    preset.yml          # 展示用:name / description / order
    agent.cordis.yml    # 组合:一个插件行列表(跟 profile 补丁同一种语法)

规则:

  • id = 目录名,必须匹配 [a-z0-9][a-z0-9-]*,不能和随附的 standard / ptc / cordis / minimal 重名。
  • 可发现却没有 agent.cordis.yml 的目录会以 "broken" 状态列出并给出原因,不会被静默跳过。
  • 只有空白会话能切换 preset(没有任何消息和工具调用之前)。中途换工具会让历史里已记录的调用无法解释。
  • 组合文件是输入,DSH 不会回写它。
  • 有工作示例:examples/agent-presets/readonly-agent/——一个刻意不挂载 write/edit 的只读评审 agent。

常用玩法:

目标 做法
只读评审 agent 不挂 tool-fs 的写能力;更彻底的做法是配合 read-only 沙箱
极简 agent(省 token) 参考随附 minimal:persona.complete: true 让固定提示词成为整个系统提示词
专用模型 在 preset 里给 agent-default-model 行换 provider/model,或用子 agent 的 agentOptions
加一个新 preset 复制现有 preset 目录再改(复制会保留来源描述,但 id 是自己的)

📄 1.5 一次性实验:`--patch` 与自定义 profile vibe-coding-guide.md · 1.5 一次性实验:`--patch` 与自定义 profi

# 不动任何已安装配置,先试一份补丁
& '...dsh.cmd' --profile web --patch .\extra.yml --dump-config

# 从随附模板派生一个自己的 profile(目标名不能是已有目录、也不能是官方 profile 名)
& '...dsh.cmd' --profile rescue --from-default-profile web
& '...dsh.cmd' --profile rescue --dump-config

--from-default-profile 只复制模板的 bundle 列表与 patchReload 值,不复制你本地 profile 的依赖或补丁;如果 rescue 目录已存在会直接拒绝(不会覆盖,也不会顺带启动)。用 --dump-config + 这个选项可以"只创建、不启动"。

两个容易吃亏的细节:

  • 启动器参数必须放在 app 参数之前。第一个不被识别为启动器参数的 token 之后,全部原样交给 app。例如 dsh --profile web --port 8080 里的 --port 是 web app 的参数,而 dsh --help(没有 profile 可转交)打印的是启动器自己的帮助。
  • --dump-config 不 boot 应用,因此它显示的是"app 参数还没被解析"的组装结果;带 app 参数调用它会被拒绝。

profile 目录长这样:

{
  "name": "dsh-profile-web",
  "private": true,
  "dependencies": {},
  "dsh": { "profile": { "bundles": ["@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app"], "patchReload": "live" } }
}

bundles 是有序的补丁层列表。加一个官方 bundle 用:

& '...dsh.cmd' plugin --profile web add @deepseek-ai/dsh-subagent-codex
# 需要 pnpm 在 PATH 上;装完必须重启该 profile

装完之后 bundle 内的 provider 只是"可用",不等于对模型可见:要让新的 agent 用上它,还得在对应 preset 里启用那一行(随附 preset 里这类行通常是 disabled: true,去掉即可)。

📄 1.6 值得调的运行参数(按影响排序) vibe-coding-guide.md · 1.6 值得调的运行参数(按影响排序)

行 id 键(默认) 什么时候调
tool-fs readLimit 2000、readMaxLineLength 2000、readMaxBytes 51200 仓库里文件很宽(长行)时调大行宽,否则读进来全是截断
tool-fs-search globMaxResults、grepMaxMatches、sampleOverCapGlobResults false 大仓库里 glob 命中过多时收紧
tool-result-pruner thresholdChars 8192、headChars 4096、tailChars 1024 日志型工具输出太多,想更早裁掉
spill-policy maxInlineBytes 50000(省略即关闭) 想让超长结果自动落盘、只留预览
compaction-basic auto true、thresholdRatio 0.8、retainRatio 0.16 长会话想更早/更晚自动压缩
repeat-tool-reminder thresholds [3,5,8]、exclude [] agent 老在原地打转时把阈值调小
agent-instructions maxBytes 65536(必填) 指令文件多,想加大预算
tool-web searchTimeoutMs 60000、fetchTimeoutMs 30000、fetchMaxOutputChars 200000 内网抓取慢、或网页太大
tool-subagent maxDepth 3、backgroundMode continuable、modelSelectionSettings false 想禁掉委派写 0;想让子 agent 指定模型要开 modelSelectionSettings
tool-workflow toolName workflow、maxResultChars 50000 脚本结果太长被截断
tool-todo allowParallelInProgress true 不允许并行推进时改 false
jobs(dsh-jobs-local) maxConcurrentJobsPerOwner 10 并发后台作业撞上限时调大

output-retention 容易踩坑:它是库不是可配置的插件行,配置里根本没有这一项。真正管截断的是 tool-result-pruner 和 spill-policy。

📄 1.7 MCP 接入 vibe-coding-guide.md · 1.7 MCP 接入

一个 id 一个服务器,工具名会被命名空间化成 mcp__<serverName>__<原工具名>。serverName 必填、匹配 [A-Za-z0-9_-]{1,32}、在同一注册作用域内唯一。

- insert:
    - id: mcp-filesystem
      name: '@deepseek-ai/dsh-mcp-client'
      config:
        serverName: fs
        transport: stdio
        command: npx
        args: ['-y', '@modelcontextprotocol/server-filesystem', 'E:\dsh\chat']
        toolCallTimeoutMs: 60000
        failOnStartupError: false
        # reconnect: { enabled: true, initialDelayMs: 500, maxDelayMs: 30000, maxAttempts: 10 }

    - id: mcp-remote-docs
      name: '@deepseek-ai/dsh-mcp-client'
      config:
        serverName: docs
        transport: streamable-http
        url: http://127.0.0.1:3333/mcp
        headers:
          Authorization: !!js '`Bearer ${process.env.MCP_TOKEN ?? ""}`'

两点安全提醒:MCP 服务器进程在 agent 沙箱之外运行(它自带权限);env 里不要写明文密钥,用 !!js process.env.X 求值。

📄 1.8 配置自检清单 vibe-coding-guide.md · 1.8 配置自检清单

改完任何一层配置,按这个顺序确认:

  1. --dump-config 里能看到你的值(不是你以为生效了)。
  2. 补丁文件顶层是数组、没有拼错的 id(拼错只有警告)。
  3. 插件包层面的增删之后重启了 profile。
  4. settings.yaml 的取值是合法枚举(例如 defaultPreset 必须是存在的 preset id)。
  5. GUI 里新开一个会话验证——旧会话不会采用新的默认档。
  6. 没有任何一行把 tools、fs-sandbox、sandbox-policy 这类基础设施行改成互相打架的状态(例如同时挂 fs-local 和 fs-sandbox,profile 会拒绝加载)。

第 2 章 写好 Skill

📄 2.1 Skill 是什么,不是什么 vibe-coding-guide.md · 2.1 Skill 是什么,不是什么

是:一段按需加载的指令。发现阶段只读 frontmatter(便宜),加载阶段把正文原样交给模型。

不是:不是插件。skill 不能注册工具、不能声明依赖、不能限制工具、不能执行代码。DSH 的 frontmatter 里没有 allowed-tools、没有 model、没有 metadata.requires.bins。

你可能会在别人安装的 skill(比如百炼系列的 SKILL.md)里看到 metadata.requires: bins: ["bl"]。那是那个工具自己的约定,DSH 只是把 metadata 原样存下来并不解释它——既不会检查 bl 是否存在,也不会因此报错。别把第三方约定当 DSH 规范。

📄 2.2 frontmatter 精确规范 vibe-coding-guide.md · 2.2 frontmatter 精确规范

键 必填 类型 规则
name ✅ string 非空,且必须匹配 ^[a-z0-9]+(?:-[a-z0-9]+)*$(kebab-case)。目录名不参与校验,但建议一致
description ✅ string 非空。这是目录里唯一被模型看到的字段
whenToUse ❌ string 会被解析和保存,但不会渲染给模型
metadata ❌ mapping 原样保存,DSH 不解释
disable-model-invocation ❌ bool true = 模型看不到、也不能加载
user-invocable ❌ bool false = 用户不能用 /名字 触发

布尔值接受 true/false、yes/no、on/off、1/0(大小写不敏感)。历史写法 disableModelInvocation / modelInvocable / userInvocable 会被明确拒绝并提示改用连字符写法。

最小可用 skill:

---
name: hello-world
description: Print a friendly greeting. Use when asked for a hello world example.
---

Say "Hello, world!" in one line. Do not add commentary.

📄 2.3 放在哪、谁赢 vibe-coding-guide.md · 2.3 放在哪、谁赢

优先级 来源 路径
100 project-dsh <项目根>/.dsh/skills
200 project-agents <项目根>/.agents/skills
300 custom 插件配置的 customSkillDirs
400 user-dsh ~/.dsh/skills
500 user-agents ~/.agents/skills
600 bundled 随包提供
  • 项目根 = 从当前工作目录往上找,第一个含 .git 的目录;找不到就用当前目录。
  • 单层内同名按 rank → 提供方顺序 → 本地顺序取胜者;跨层是最近的作用域层直接赢。
  • 只支持两种形态:<name>/SKILL.md 和扁平的 <name>.md。**/SKILL.md 递归发现不支持——放进 skills/foo/bar/SKILL.md 是找不到的。
  • 用户级 ~/.dsh/skills 会跳过它的 .system 子目录。

实践建议:团队规范进仓库(rank 100–200),个人口味进 ~/.dsh/skills(rank 400)。这样克隆仓库的人自动获得团队 skill,而不会被你的个人习惯覆盖。

📄 2.4 模型到底看到什么(决定你怎么写) vibe-coding-guide.md · 2.4 模型到底看到什么(决定你怎么写)

这是最关键的一节,理解它你就能预判"为什么这个 skill 没被用上"。

第一步:目录。skill 的目录不在系统提示词里。它是在会话的某个 pre-step 阶段,以一条持久的 user 角色 <system-reminder> 消息注入的,内容只有:

<available_skills>
- `run-tests`: Run this repository's test suite and turn failures into ...
- `project-standards`: Enforce this repository's coding standards ...
</available_skills>

第二步:截断。description 会先做空白归一化,再截断到 500 字符(配置项 catalogDescriptionMaxLength,默认 500,最小 3),超长补字面量 ...。

第三步:正文。模型调用 skill({name})(或用户输入 /name)时,正文才被读盘,并且每次都重读(没有缓存)。返回结构:

<skill_content name="run-tests">
<skill_resources> ... 相对路径按 skill 目录解析 ... </skill_resources>
<skill_instructions> ...正文... </skill_instructions>

两个直接推论:

  1. description 的前 500 字符是你唯一的"索引"。触发词必须出现在前面。
  2. 正文可以长(代码里没有长度上限),但加载之后它会一直占用上下文直到压缩。所以正文要"够用",长清单放 reference/。

📄 2.5 失败模式:静默丢弃 vibe-coding-guide.md · 2.5 失败模式:静默丢弃

这是最容易让人抓狂的一点。以下任意一种情况,提供方只打一条警告日志,然后整条 skill 消失,模型完全看不出区别(就像这个 skill 不存在):

  • 没有 frontmatter,或 --- 没闭合;
  • YAML 非法;
  • name / description 缺失、不是字符串或为空;
  • name 不是 kebab-case(例如 Run_Tests);
  • 用了旧写法(disableModelInvocation)或调用策略字段不是布尔。

自检命令(会话里)——让 agent 直接读文件并逐条核对规则,比"我改了应该好了"可靠:

读 <path>/SKILL.md,检查:① frontmatter 是否以 --- 开头并以 --- 结束;
② name 是否匹配 ^[a-z0-9]+(?:-[a-z0-9]+)*$;③ name 与目录名是否一致;
④ description 是否非空且前 500 字符内包含触发词;⑤ 是否用了 disableModelInvocation 这类旧键。
只报结论和证据。

📄 2.6 怎么写出一个"会被用上"的 skill vibe-coding-guide.md · 2.6 怎么写出一个"会被用上"的 skill

2.6.1 description:前 500 字符决定一切

三段式,越靠前越重要:

[做什么:动词开头的具体能力]。[什么时候用:触发场景 + 触发名词]。[可选:反触发 - 什么情况别用]。

三个档次对比:

# 差:全是形容词,没有触发词
description: 帮助团队写更好的代码。

# 中:有场景,但触发词太晚,且没有边界
description: 本 skill 提供一套经过实践检验的、覆盖多个方面的、系统化的代码质量保障方案,包括但不限于命名、注释、错误处理、日志、依赖管理、测试、文档等多个维度,适用于各种规模的团队和项目……

# 好:能力 + 触发名词 + 反触发
description: >-
  Run this repository's test suite and turn failures into a classified, evidence-backed report:
  reproduction command, failing assertion, first divergence, suspected owner package, and a
  recommended next action. Use when asked to run tests, check whether a change broke anything,
  verify a fix, or explain why CI is failing.

2.6.2 正文结构模板

# <标题>

📄 何时使用 vibe-coding-guide.md · 何时使用

  • <触发场景 1>
  • <触发场景 2> 不适用:<反触发>

📄 前置检查 vibe-coding-guide.md · 前置检查

  1. <先读什么>(给出真实路径或命令)

📄 步骤 vibe-coding-guide.md · 步骤

1. <动作>

<可直接复制执行的命令>

📄 判定标准 vibe-coding-guide.md · 判定标准

| 项 | 通过 | 不通过怎么办 |

📄 必须提供的证据 vibe-coding-guide.md · 必须提供的证据

<要跑什么命令、要贴什么输出>

📄 停止条件 vibe-coding-guide.md · 停止条件

  • <什么时候算完成>
  • <同一个问题修 N 次仍失败 → 停下来汇报>

📄 不要做 vibe-coding-guide.md · 不要做

  • <明确的反模式>

四条硬经验:

- **命令要能直接执行**。写 `pnpm --filter <package> test`,不要写"运行相关测试"。
- **给判定标准,不给形容词**。"优雅"不是标准,"公共 API 未删除,或已列出全部调用方"才是。
- **给停止条件**。没有停止条件的 skill 会变成死循环制造机:`同一失败修 3 次仍不通过 → 停止修改,把现象+3 次改动+当前假设交给用户`。
- **写反触发**。"普通问答、写作、翻译不走本 skill"能让模型少犯一半的误触发。

### 2.6.3 渐进式披露

约定俗成的目录(只有 `SKILL.md` 是被代码识别的,其他纯属约定):

```text
my-skill/
  SKILL.md              # 路由 + 判定 + 短步骤
  reference/  长清单、参数表、错误码映射
  assets/     模板、图片、示例数据
  templates/  产出物骨架
  scripts/    可直接调用的脚本

原则:SKILL.md 只放"每次都要用的判断",细节放 reference/,并在正文里用相对链接指过去——加载结果里已经告诉模型"相对路径按 skill 目录解析,按需加载引用文件"。两个真实例子:examples/skills/project-standards/SKILL.md(规范类)、examples/skills/run-tests/SKILL.md(流程类)。

2.6.4 什么时候该写 skill,什么时候不该

该写 不该写
会重复发生的多步流程(跑测试、发布、迁移) 一次性任务
判定标准容易被忘记(验收口径、禁止项) 已经被 AGENTS.md 覆盖的通用规则
需要精确命令与参数的知识 模型本来就会的常识("要写测试")
容易被误触发的能力需要写清边界 应该写成代码/脚本的确定性逻辑

判断法:如果这段话你第二次对 agent 说,就该进 skill 或 AGENTS.md。如果只是这一次的特殊要求,直接写在 prompt 里。

📄 2.7 加载与调用的正确姿势 vibe-coding-guide.md · 2.7 加载与调用的正确姿势

  • 明确点名:用 run-tests skill 检查当前改动——比"帮我看看测试"更稳。
  • 用户手动触发:在输入框里输入 /<skill 名>(只对 user-invocable 不为 false 的 skill 有效;这也是 disable-model-invocation: true 的 skill 唯一的入口)。
  • 想在某个 agent 上关掉某个 skill:user-invocable: false 只是不让用户触发;要让模型也看不到,用 disable-model-invocation: true。想整体换一套 skill 根目录,用 preset 的 skill-filesystem + customSkillDirs。

第 3 章 Agent 边界

📄 3.1 三层边界,别混为一谈 vibe-coding-guide.md · 3.1 三层边界,别混为一谈

① 权限层   沙箱模式 + 审批策略     ← 能写哪、要不要问你
② 能力层   挂哪些工具插件行        ← 它连"想调用"都不存在
③ 指令层   AGENTS.md / persona / plan mode  ← 它知道你希望它怎么做

排障时先定位是哪一层出的问题:

症状 大概率是哪一层
报 [sandbox: file access denied under ... mode] ①
报 the user rejected tool "..." / the user rejected escalating ... ①(审批)
报 工具不存在 / 模型说没有某个工具 ②
agent 改了你不想让它改的地方,但没有任何报错 ③(缺约束)或 ①(档位太松)

关键认知:三层里只有 ① 是强制执行的。③ 是文字约束——它能大幅降低概率,但不能阻止行为。所以"重要禁令"要么进 ①(沙箱、审批),要么进 ②(不挂那个工具),别只写在 AGENTS.md 里。

📄 3.2 权限预设:一个选择器管两个旋钮 vibe-coding-guide.md · 3.2 权限预设:一个选择器管两个旋钮

一个预设 = 一个沙箱模式 + 一个审批策略的组合。

沙箱模式 写入范围 说明
read-only 不授予可写根(除 /dev/null 这类必需出口) 只读分析、评审、答疑
workspace-write 工作区根 + 后端定义的临时区域 默认档;日常开发
danger-full-access 不隔离,按原始方式启动 不设防
审批策略 行为
ask 需要决策时向用户发起审批;没有应答者时默认拒绝(fail closed)
never 服务内部在分发之前就返回拒绝,后续监听器无法绕过

随附预设表:read-only(read-only + ask)、workspace-write(workspace-write + ask)、danger-full-access(danger-full-access + never)。

自定义预设(完整示例见 examples/profile-patch.example.yml 第 1 段):

- id: permission
  name: '@deepseek-ai/dsh-permission-presets'
  config:
    presets:
      audit-only:
        sandbox: read-only
        approval: ask
        name: 只读审计
        description: 自由读取与搜索;任何写入都要人工确认。
      safe-write:
        sandbox: workspace-write
        approval: ask
      trusted:
        sandbox: danger-full-access
        approval: never
    defaultPreset: safe-write

三条规则:

  • custom 是保留名:它表示"当前旋钮组合不匹配任何预设"的派生状态,你无法选中或持久化它,也不能用它命名一个预设。
  • defaultPreset 只在新会话创建时读一次;改配置不影响正在跑的会话。
  • 切换用 /permission(不带参数显示当前档位与可选表,带参数切换)。

Windows 上的真实语义(很容易踩):workspace-write 授予的是「工作区 + 每个会话私有的随机临时目录」,环境变量 TMP/TEMP 会被改写到那个目录,系统的临时根目录不会被隐式授予。执行完整性是 "partial":NTFS 硬链接可以让同一个文件对象被另一个路径访问。另外,被限制的子进程不能用管道 stdio(spawn(..., {stdio:'pipe'}) 会 EPERM)——这是沙箱边界的一部分,不是 bug。

拒绝与升级:被拦时你会看到

[sandbox: file access denied under <mode> mode]
[sandbox: escalation available — retry this exact <subject> once with sandbox_permissions ... + justification; the approval prompt asks the user]

升级阶梯是严格向上:read-only → workspace-write|danger-full-access,workspace-write → danger-full-access;sandbox_permissions 与 justification 必须成对出现。注意本会话的一个反例:当前会话的权限档是 danger-full-access(审批 never),运行时上下文明确说"需要审批的动作会被自动拒绝,不要请求升级"。也就是说在这种档位下,"升级请求"这条路是关掉的——这是刻意的:要么你信任它,要么别给它这个档位。

网络不在沙箱词汇表里。沙箱只管文件效应;HTTP_PROXY/HTTPS_PROXY/NO_PROXY(或 .dsh/.env)影响的是 DSH 自己发出的请求(模型、搜索、抓取、MCP)。

📄 3.3 读后写:为什么会报 "file has not been read" vibe-coding-guide.md · 3.3 读后写:为什么会报 "file has not be

默认挂载了 fs-observation-policy,它把文件工具变成"先看后改":

  • edit 目标必须先被 read 过,否则 FS_NOT_OBSERVED;
  • write 覆盖已存在的文件也必须先 read;
  • 读过一个不存在的路径 = 记录"确认不存在",之后 write 可以走受保护的创建流程;
  • 读之后文件被外部改动 → FS_STALE_VERSION,提示你重读再试。

两个必须知道的后果:

  1. 会话恢复后观察态不持久。接着一个旧会话干活时,第一次 edit 很可能报"没读过"——这是设计,不是故障。正确反应是重读那个文件。
  2. 它保护的是"误覆盖",不是"越权"。越权由沙箱管。

📄 3.4 plan mode:用文字换回控制权 vibe-coding-guide.md · 3.4 plan mode:用文字换回控制权

plan mode 的机制值得说清楚:它不做任何工具拦截,只注册一段系统提示词规则和一个 exit_plan_mode 工具,靠约束模型的行为来生效。段落的措辞很硬("Do not edit or write files … commit"、"用户的口头同意不构成批准")。

用法:

/plan 给这个仓库加一个导出 CSV 的接口

agent 会先探索、再规划,最后调用 exit_plan_mode 提交计划。规则细节:

  • plan 参数必须是以 # 开头的 markdown(正则要求 # 后有非空白字符)。
  • 批准 → 退出计划模式,返回 Plan approved — plan mode exited; carry out the plan starting with your next step.
  • 拒绝 → 工具调用失败,提示"保持计划并重新提交",你可以带上反馈;它不会退回普通模式继续乱改。
  • 评审走的是 用户问答通道而不是审批通道,所以 approval: never 不会绕过它。
  • exit_plan_mode 在两种模式下都注册(保持工具目录稳定),非计划模式下调用会报 only available in plan mode。

实践建议:把"改三个文件以上 / 触碰公共 API / 改数据库结构 / 重构"这几类任务一律先过 plan mode。代价是一轮对话,收益是你能在改动发生前看到它的理解对不对。

📄 3.5 AGENTS.md:最容易被低估的杠杆 vibe-coding-guide.md · 3.5 AGENTS.md:最容易被低估的杠杆

加载链

~/.dsh/AGENTS.md                        (用户全局,跨仓库)
  → <项目根>/AGENTS.md                   (项目根 = 最近的 .git 祖先目录)
  → <项目根>/子目录/AGENTS.md             (越具体越优先)
  • 每个目录的候选名默认是 AGENTS.md、CLAUDE.md;叠加层是 AGENTS.local.md、CLAUDE.local.md(放在 AGENTS.md 之后)。
  • 项目链的渲染顺序是从宽到窄;越具体的越优先,但它们都不能覆盖系统/开发者/直接用户指令。
  • 同一目录里两份内容(去掉首尾空白后)完全相同的候选只渲染一次——所以 CLAUDE.md 软链到 AGENTS.md 不会重复注入。
  • 预算:整条链的渲染上限由 maxBytes 决定,基础 bundle 设的是 65536 字节;单个源文件默认上限 1 MiB。超预算时先整份丢弃更宽的文件,再截断最具体的那个,并留下 Workspace instruction budget ... 提示。

两个非常容易踩的行为

  1. 子目录的指令文件不是一开始就加载的。要等一次成功的 read / write / edit 触碰到那个目录,下一次请求才会补注入;bash 里 cd 不会触发(shell 的目录状态不是可靠的观察点)。需要子目录规则时,明确让 agent 读一遍那个文件。
  2. 刷新是被触碰驱动的,没有 watcher:外部编辑器改了 AGENTS.md,要等下次文件操作或会话恢复才对上。

该写什么

把 AGENTS.md 当入职文档 + 检查表,而不是愿景文档。参考 examples/AGENTS.example.md,它的骨架是:

① 这是什么仓库        (一句话定位 + 命令)
② 常用命令            (只列真实能跑的,带注释)
③ 目录约定            (放什么 / 不放什么)
④ 编码要求            (能判定的规则,不是形容词)
⑤ 测试与验收标准      (必须给出哪些命令的实际输出)
⑥ 硬性禁止            (不改历史迁移、不提交密钥、不 push …)
⑦ 工作方式            (先计划、小步、并行时写清边界)

写 AGENTS.md 的四条纪律:

  • 每句都能被验证。"代码要优雅"没用;"空 catch 必须注释说明吞掉了什么"有用。
  • 命令必须是这个仓库真能跑的。贴一条跑不通的命令,比不贴更糟——agent 会照着试,然后浪费一轮。
  • 写禁止项时给出替代做法。"不要改历史迁移"配一句"需要变更就新建一个",能省一轮来回。
  • 短。超预算会丢文件,你写的精心规则可能就在被丢的那一份里。

persona 与全局风格

  • 部署级的全局前缀在 system-prompt 行的 personaPrefix(基础 bundle 是空串)。
  • 每个 preset 内可以挂 dsh-persona 覆盖它:prefix(必填)、suffix、complete(true 时 prefix 成为整个系统提示词,其他段落都进不来)、includeRuntimeContext(false 时连沙箱/审批的运行时上下文也一起压掉)。示例见 examples/agent-presets/readonly-agent/agent.cordis.yml。

📄 3.6 硬性边界(当前版本的真实上限) vibe-coding-guide.md · 3.6 硬性边界(当前版本的真实上限)

限制 值 / 行为 出处
委派深度 maxDepth 默认 3;可设 0(禁止)或 'provider-managed';本机实测上限为 1 源码 + 实测报错 subagent depth 2 exceeds maxDepth 1
超限表现 工具仍然可见,调用时返回错误结果 源码
子 agent 的审批 委派时父会话的沙箱覆盖被复制,审批一律钉为 never,子 agent 无法自行请求升级 源码
子 agent 工具屏蔽 toolFilter(allow/deny)作用于子 agent;被屏蔽的工具从子 agent 提示词里消失且拒绝执行;写了不存在的名字启动即失败 源码
preset 收窄工具 没有声明式 allow/deny 字段;靠"挂哪些插件行"来体现 源码
工具注册表的全局配置 只有 mode 与 maxParallelSubCalls;没有全局 allow/deny 源码
plan mode 纯文字约束,不拦截工具 源码
scope 明确不是安全边界(它管可见性/归属,不管权限) 源码
workflow 脚本 无文件、网络、定时器、require/process;只有 6 个全局;vm 是"容器而非安全边界" 源码
workflow schema 只支持 type/properties/required/additionalProperties/items/enum/const/oneOf(oneOf 还要 ≥2 分支且不能有同级关键字);pattern/format/数值上下界都不行 源码

📄 3.7 一页边界矩阵 vibe-coding-guide.md · 3.7 一页边界矩阵

任务 预设 子 agent 深度 plan mode AGENTS.md 重点
只读分析/评审 audit-only(read-only+ask) 1(叶子) 不需要 报告格式与证据要求
日常改代码 workspace-write 2 建议先过 命令、目录约定、禁止项
大重构 workspace-write 2 必须 分阶段验收标准
批量迁移 workspace-write 用 workflow 编排 必须 每阶段证据与回滚方式
可信仓库里的危险操作 trusted(danger-full-access+never) 1 建议 明确的"不要做"清单

第 4 章 任务拆分

📄 4.1 拆分的唯一判据:这个单元能不能被独立验收 vibe-coding-guide.md · 4.1 拆分的唯一判据:这个单元能不能被独立验收

一个健康的工作单元必须四要素齐备,缺一个就还得拆:

要素 问自己 反例
输入 起点是什么(文件、接口、数据、commit)? "把系统优化一下"
输出 交付物是什么(能打开的文件、能跑的命令、能看到的页面)? "更好的代码结构"
证据 怎么证明做对了(命令 + 输出、测试名、截图路径)? "看起来没问题"
边界 哪些文件/接口不动?和谁并行时会冲突? 两个 agent 同时改同一个文件

四要素里最容易被跳过的是证据。一旦你要求"证据是命令加输出",拆分质量会自动提升——因为说不清证据的任务,往往本身就是没想清楚的。

📄 4.2 七种拆分手法 vibe-coding-guide.md · 4.2 七种拆分手法

手法 什么时候用 例
按验收标准拆 一个任务有多个互相独立的"通过条件" "接口能返回 CSV" 与 "超过 10 万行时分页"
按子系统拆 改动落在不同目录/包 前端表单 / 后端接口 / 迁移脚本
按阶段拆 需要先有证据再动手 ①复现 ②定位 ③修复 ④回归
按不确定性拆 有未知的技术前提 先做 spike 验证方案可行性,再正式实现
按可验证切片拆 改动可以逐步放出 先读路径 → 再写路径 → 再错误处理
按并行度拆 各部分无共享可写资源 三个目录的同类改造
按风险拆 高风险动作要单独隔离 数据迁移单独一步,先备份后执行

📄 4.3 规模选择决策树 vibe-coding-guide.md · 4.3 规模选择决策树

这个任务……
├─ 一轮对话能做完,且不需要我确认方案?
│    └─ 直接做:先读现状 → 最小改动 → 立刻验证
├─ 能做完,但我希望先看方案?
│    └─ /plan → exit_plan_mode(第 3 章)
├─ 跨多轮、有明确"什么时候算完成"?
│    ├─ 需要自动续跑 → create_goal + update_goal(有轮次上限)
│    └─ 只需要可见进度 → todo_write
├─ 有 2–5 个互相独立的方向?
│    ├─ 子任务不需要父会话历史 → subagent(后台)
│    └─ 子任务需要理解已完成的前文 → subagent_fork
└─ 同构对象几十个以上(文件/目录/模块)?
     └─ workflow 脚本扇出(注意 1000 个 agent 的硬上限)

📄 4.4 执行期契约:goal、todo、job 各管一段 vibe-coding-guide.md · 4.4 执行期契约:goal、todo、job 各管一段

todo_write:当前执行清单

  • 输入是整个列表:每次调用都替换上一次的清单,不是增量更新。所以要改一项,就把完整列表重发一次。
  • status 只有 pending / in_progress / completed;没有 id、优先级、备注字段。
  • in_progress 可以同时有多个(配置 allowParallelInProgress: true)——但只在真的并行时这么做(后台命令、并发子 agent)。
  • 纪律:动手前把清单建好;做完一项立刻标完成,不要攒着一起标。

create_goal / update_goal:跨轮目标

  • create_goal {objective, max_goal_rounds?},默认轮次上限 256。
  • update_goal {goal_id, revision, action, objective?, max_goal_rounds?, blocked_reason?},action ∈ edit|pause|resume|complete|blocked。
  • blocked 有个硬门槛:必须已经连续跑了至少 3 轮同一个阻塞条件才允许标记,否则会被拒绝(blocked requires at least 3 consecutive goal rounds)。这是防止你拿"blocked"当"我不想做了"的挡箭牌。
  • edit / pause / resume 需要人的直接授权;complete / blocked 在目标轮次内也可以由 agent 提出。
  • 轮次用完会提示 goal "<id>" exhausted N goal rounds; increase maxGoalRounds before resuming。

goal 与 todo 的分工:goal 回答"什么时候算完成",todo 回答"现在在做什么"。一个跨轮的 goal 通常会配一张不断更新的 todo 清单。

job_*:后台作业

  • 后台命令或后台子 agent 都会返回 job id;job_output {job_id, wait?, timeout_ms?} 收集,job_kill 停止,job_list 列出。
  • 等待默认 30 秒、上限 600 秒;等待超时返回 running 且作业继续存活(不是失败)。
  • 每个 owner 默认最多 10 个并发后台作业;超限会提示你用 job_kill 停掉不需要的。
  • 作业完成会主动通知你:不要轮询、不要 sleep 等它。等的时候去做独立的工作。

📄 4.5 拆分反例集(照着自己对比) vibe-coding-guide.md · 4.5 拆分反例集(照着自己对比)

反例 为什么坏 改法
验收标准写成形容词 无法判定完成,agent 会不断"优化" 换成命令 + 输出、测试名、可打开的文件
把两个改同一文件的子任务并行 互相覆盖,谁最后写谁赢 按文件所有权拆,或串行
探索和实现塞进同一批 探索结论没定型就动手,白改一轮 先一轮只做探索与计划
拆到无法验证的碎块 每块都没法跑,攒到最后一起炸 每一块都要有一个可执行的验证动作
委派提示词写"你看着办" 子 agent 没有你的上下文,只能瞎猜 给输入、输出格式、证据要求、边界
任务描述里混入两个目标 做完一个就算"完成",另一个悄悄丢掉 一个单元一个目标,或显式列出子目标与验收

📄 4.6 委派提示词模板(拆完直接用) vibe-coding-guide.md · 4.6 委派提示词模板(拆完直接用)

把每个子任务写成下面这块,粘进 subagent 的 prompt:

【目标】用一句话说清要产出什么。

【背景】子 agent 看不到我们的对话,所以这里必须自包含:
- 仓库根:<绝对路径>
- 相关文件:<路径 1>、<路径 2>
- 已知约束:<例如 不允许改公共 API、必须复用现有 logger>

【输入】你要读的东西(路径/命令/接口)。

【输出格式】固定格式,便于我汇总:
1. 结论(≤3 行)
2. 证据:文件:行号 或 命令 + 关键输出片段
3. 未确认项与原因
4. 建议下一步(一条)

【边界】
- 只读 <目录>,不要改任何文件。(或:只允许改 <文件>)
- 不要运行 <危险命令>。
- 不要委派给其它 agent(深度上限为 1,会直接失败)。

【失败处理】做不下去时,不要猜:报告阻塞点、已尝试的做法、需要什么信息。

第 5 章 多 Agent 协同

📄 5.1 工具全景 vibe-coding-guide.md · 5.1 工具全景

工具 本质 上下文 默认等待 何时用
subagent 新建独立子 agent(spawn) 看不到你的对话 run_in_background: false 自包含的独立任务
subagent_fork 从父会话派生(fork) 继承父会话已完成轮次的前缀 默认后台(one-shot) 需要理解前文的后续分析/评审
workflow 一段 JS 编排脚本 脚本协调,子 agent 干活 前台阻塞 几十个同构对象的扇出
job_output / job_kill / job_list 后台作业控制 — — 收集/停止后台工作
send_message 给可继续的子 agent 追加消息 — 立即返回投递确认 子任务需要跟进
interrupt_agent 停止某个 agent 的当前轮 — 立即返回接受确认 方向错了,止损
list_agents 列出我启动的 agent(children/descendants) — — 回忆"我开了哪些"

subagent 的入参(模型可见的默认 schema):description(3–5 词,展示用)、prompt(必填,必须自包含)、run_in_background(默认 false)。开了模型选择后还会多出 provider / model / reasoning_effort,以及一个 list_subagent_models 工具用来查可用路由。

subagent_fork 刻意不允许选模型:provider/model 必须与父一致,这样继承下来的历史才能命中 KV cache。

工具层的固定指引(很值得直接遵守):

默认把 subagent 放到后台跑;把互相独立的委派放在同一条消息里一起启动,然后在它们运行期间继续做有用的工作;只有当下一个动作依赖某个子 agent 的结果时,才设 run_in_background: false。

📄 5.2 六种协同拓扑 vibe-coding-guide.md · 5.2 六种协同拓扑

① 扇出—扇入(最常用)

场景:多角度研究、多目录审计、多方案对比。

我:把下面三块一起查(同一条消息里三个后台 subagent):
    ① apps/ 下的接口层  ② packages/ 下的领域层  ③ 迁移脚本与数据库约束
    每个都按固定格式回报:结论 / 证据(文件:行号) / 未确认项
    (主干继续读 AGENTS.md 与最近提交,不空等)
→ 三个结果到齐后,我做交叉核对与汇总,冲突处单独列出

纪律:固定输出格式是扇入的关键。没有统一格式,汇总阶段你会读到三段风格迥异的散文。

② 流水线(逐对象、无屏障)

场景:迁移、逐文件改造、给一批模块补测试。

用 workflow 的 pipeline:每个 item 独立走完所有阶段,不需要等别人。

pipeline(files, 改造一个文件, 对它跑测试, 生成改动说明)

关键收益:某个文件失败只会让那一个 item 变 null,其余照常推进。真正的错误(选项写错、schema 不支持、超出上限)会直接杀掉整个脚本——这是刻意的"致命错误不许伪装成普通失败"。

③ 生成者—审查者(对抗验证)

场景:高风险改动、改核心逻辑、动数据库。

① 主 agent 实现(或生成者子 agent 实现)
② 另开一个审查者子 agent,只给它"改了什么"和"原始证据",不给实现者的自我总结
   要求:独立复现、列出反例、指出无法证明的部分
③ 主 agent 处理分歧:能复现的当作真问题修,不能复现的写清原因

纪律:审查者必须看原始证据(diff、测试输出、日志),不是看实现者的总结。否则你只是在让第二个 agent 复述第一个 agent 的偏见。

④ 计划—执行分离

场景:需求模糊。

先让一个子 agent 只做"把模糊需求变成可验收步骤",产出必须包含每条步骤的验证方式;方案定稿后再开执行。好处是探索阶段的噪音全部留在子 agent 的上下文里。

⑤ 后台并行 + 主线继续

场景:长耗时命令(构建、全量测试)、外部依赖(拉数据、跑爬虫)。

我:在后台跑全量测试(run_in_background: true)
    同时:读这份配置、写这次改动的说明文档
→ 收到完成通知后再收集结果

纪律:不要轮询。job_list 是"我还开了哪些"的记忆工具,不是等待工具。等待会浪费你的上下文;去做别的。

⑥ 大规模扫描(workflow)

场景:上百个文件/模块的审计、批量改造、多角度研究。

见 5.3 的脚本契约。判据很简单:当你能用"对每个 x 做同一件事,最后汇总"描述任务时,就该用 workflow 而不是一个个委派。

📄 5.3 Workflow 脚本契约(照抄级) vibe-coding-guide.md · 5.3 Workflow 脚本契约(照抄级)

参数

  • meta(必填,纯数据):{ name(短 kebab-case)、description、whenToUse?、phases?: [{title, detail?, provider?, model?}] }。
  • script(必填):纯 JavaScript 函数体。允许顶层 await;不要写 export const meta(会得到明确的 SCRIPT_PARSE 错误);结尾用 return <可 JSON 序列化的值>。
  • args(可选):一个对象,脚本里通过同名全局读取;数组请包一层,例如 {"files": [...]}。

六个全局(多一个都没有)

全局 语义
agent(prompt, opts?) 跑一个子 agent 到完成。没给 schema 时解析为子 agent 的最终文本
pipeline(items, ...stages) 每个 item 独立过所有阶段,阶段之间没有屏障;stage 收到 (prev, item, index)
parallel(thunks) 并发跑一组零参函数并 Promise.all 等待(屏障)
phase(title) 开始一个进度阶段(只影响展示,不产生执行结构)
log(message) 记一条进度
args 工具调用传进来的 args(对象副本)

agent 的 opts 只接受这几个键:label、phase、schema、provider、model。写 effort / isolation / agentType 会明确报不支持;写其他未知键直接抛错。

返回与失败语义(这一节决定脚本怎么写)

情况 结果
子 agent 正常完成,无 schema 子 agent 的最终文本
子 agent 正常完成,带 schema 校验过的对象
子 agent 完成但没有返回 structured null(用 .filter(Boolean) 过滤)
子 agent 非正常完成 null
parallel 里某个 thunk 抛错 那个位置是 null,其余照常
pipeline 里普通阶段抛错 该 item 变 null,跳过它剩余的阶段;其余 item 继续
钩子误用(参数错、未知 opt、schema 越界、触到上限、开始失败、取消) 抛错并杀掉整个脚本

脚本里绝对没有的东西:文件、网络、定时器、require/process。脚本只负责协调,脏活全是子 agent 干。

上限(会实际撞到的)

项 值
并发子 agent maxConcurrentAgents 默认 0 → 自动 min(16, max(1, 可用并行度 - 2))
单次运行子 agent 总数 maxTotalAgents 默认 1000
单次调用元素数 maxItemsPerCall 默认 4096
初始同步片段超时 syncTimeoutMs 默认 5000 ms
收尾宽限 disposeGraceMs 默认 5000 ms,超时强制结算并终止 worker 线程
整体运行时长 没有总墙钟超时
结果渲染上限 tool-workflow 的 maxResultChars 默认 50000 字符,超出截断

另外:workflow 是前台阻塞的,没有"后台启动 + 轮询"的 API;取消会丢弃部分结果并以错误返回。

脚本范例 A:并行审计与结构化汇总

phase('扫描')
log(`共 ${args.files.length} 个文件`)

const schema = {
  type: 'object',
  properties: {
    findings: {
      type: 'array',
      items: {
        type: 'object',
        properties: {
          severity: { type: 'string', enum: ['high', 'medium', 'low'] },
          summary: { type: 'string' },
          evidence: { type: 'string' },
        },
        required: ['severity', 'summary', 'evidence'],
        additionalProperties: false,
      },
    },
  },
  required: ['findings'],
  additionalProperties: false,
}

const results = await parallel(
  args.files.map((file) => () =>
    agent(
      `只读审查文件 ${file}。\n` +
        `只报能证明的问题:每条给出严重度、一句话结论、以及文件:行号或命令输出作为证据。\n` +
        `没有发现就返回空数组,不要为了凑数编造。`,
      { label: `audit:${file}`, phase: '扫描', schema },
    ),
  ),
)

// 失败的子 agent 是 null,必须过滤
const findings = results
  .filter(Boolean)
  .flatMap((r) => r.findings ?? [])

phase('汇总')
const bySeverity = findings.reduce((acc, f) => {
  acc[f.severity] = (acc[f.severity] ?? 0) + 1
  return acc
}, {})

return {
  files: args.files.length,
  failed: results.filter((r) => r === null).length,
  bySeverity,
  findings,
}

脚本范例 B:逐模块迁移流水线(无屏障)

phase('迁移')

const fileSchema = {
  type: 'object',
  properties: {
    changed: { type: 'boolean' },
    files: { type: 'array', items: { type: 'string' } },
    testCommand: { type: 'string' },
    testResult: { type: 'string' },
  },
  required: ['changed', 'files', 'testCommand', 'testResult'],
  additionalProperties: false,
}

const outcome = await pipeline(
  args.modules,
  // 阶段 1:改造
  (prev, mod) =>
    agent(`把模块 ${mod.path} 从旧的 API 用法迁移到新 API。只改这个模块目录内的文件;\n` +
      `完成后给出:改了哪些文件、你实际跑的测试命令、以及该命令的真实输出片段。\n` +
      `不要试图委派给其它 agent。`, { label: `migrate:${mod.name}`, phase: '迁移', schema: fileSchema }),
  // 阶段 2:独立复验(拿阶段 1 的结果当输入,但要求自己重跑)
  (prev, mod) =>
    agent(`独立复验模块 ${mod.name} 的迁移结果。\n` +
      `上一阶段声称:${JSON.stringify(prev)}\n` +
      `请自己重跑那个测试命令,报告真实结果;如果与声称不符,直接说明差异。`,
      { label: `verify:${mod.name}`, phase: '复验' }),
)

const ok = outcome.filter((o) => o !== null)

return {
  total: args.modules.length,
  completed: ok.length,
  dropped: args.modules.length - ok.length,
  details: outcome,
}

调用示例(args 里包一层,保持 wire schema 诚实):

{ "modules": [ { "name": "billing", "path": "packages/billing" }, { "name": "auth", "path": "packages/auth" } ] }

📄 5.4 子 agent 的模型选择 vibe-coding-guide.md · 5.4 子 agent 的模型选择

两步:

  1. 在 settings.yaml 里放开允许的路由:
subagent-model-selection:
  enabled: true
  allowedModels:
    - provider: deepseek-official
      model: deepseek-flash
    - provider: deepseek-official
      model: deepseek-v4-flash
  1. 委派时成对给出 provider + model(只给一个会被拒绝),并且必须命中上面的某一条,否则报 child LLM route "<p>/<m>" is not allowed for this Session。

注意:enabled: true 时 allowedModels 不能为空,否则加载即报错。这个策略在每次新建顶层会话时采样一次,之后由子会话继承,事后改设置不影响已创建的会话。fork 出来的子 agent 不接受路由选择(为了 KV cache)。另外,ACP / Codex / Claude Code 这类外部提供方不支持这些 per-call 选项。

📄 5.5 并发与资源纪律 vibe-coding-guide.md · 5.5 并发与资源纪律

  • 后台作业:每个 owner 默认 10 个并发上限;等不到的用 job_kill 收掉。
  • job_output 等待上限 600 秒;超时只是"还没好",不是失败。
  • list_agents 没有分页也没有上限,长时间、多子 agent 的父会话每次调用都会全量列举——它是"回忆工具",不是轮询工具。
  • 别在父上下文里复制子 agent 的工作:委派出去的目录,自己不要再扫一遍。

📄 5.6 反模式 vibe-coding-guide.md · 5.6 反模式

反模式 后果 正确做法
需要父上下文的活儿交给 subagent 子 agent 只能瞎猜,返回无用结果 用 subagent_fork,或把上下文写进 prompt
让子 agent 再委派 深度超限,报 exceeds maxDepth 白跑一轮 两级结构交给 workflow 编排
用 list_agents/sleep 等结果 浪费上下文与时间 依赖完成通知;期间做独立工作
两个子 agent 写同一批文件 互相覆盖 按文件所有权拆,或串行
子 agent 只回"已完成" 无法验收,等于没做 固定输出格式 + 要求原始证据
把 workflow 当"免费并行" 撞 1000 agent / 4096 items 上限,前台长时间阻塞 先估规模,必要时分批

📄 5.7 协同验收清单 vibe-coding-guide.md · 5.7 协同验收清单

一个多 agent 任务收尾前,逐条过:

  1. 每个子任务都有独立证据(不是"它说做完了")。
  2. 汇总者没有重复子 agent 的工作(抽查一处即可)。
  3. 存在冲突的文件上只有一个写者;串行化的地方在汇报里写明了。
  4. 每个结论都能回溯到文件:行号或命令输出。
  5. 失败/丢弃的项被显式列出(workflow 里 null 的数量、dropped 计数),没有被静默吞掉。
  6. 复盘:哪些规则该沉淀回 AGENTS.md 或 skill。

第 6 章 上下文工程

会话越长越"笨",通常不是模型退化了,是上下文被噪音占满了。DSH 里有四个机制在管这件事,理解它们的分工比记住参数重要。

📄 6.1 四个机制 vibe-coding-guide.md · 6.1 四个机制

机制 管什么 触发时机 关键参数
compaction-basic 整段历史的压缩 达到窗口比例阈值时(也可 /compact 手动) thresholdRatio 0.8、retainRatio 0.16、auto true
tool-result-pruner 单条过大的工具结果 先于对话压缩发生 thresholdChars 8192、headChars 4096、tailChars 1024
spill-policy 超大工具结果转存文件 就地替换成头尾预览 + 文件定位符 maxInlineBytes 50000(省略即关闭)
repeat-tool-reminder 原地打转的循环 同一工具同一参数重复 N 次 thresholds [3,5,8]

注意 thresholdRatio 是按路由后的上下文窗口乘比例算的;retainTokens 与 retainRatio 互斥,且必须小于缩略阈值(写错会在加载时直接失败,不会静默生效)。

📄 6.2 手动压缩的时机 vibe-coding-guide.md · 6.2 手动压缩的时机

  • 主线已经完成一个阶段、要开新阶段时:/compact。这样新阶段带着"结论"而不是"过程"开始。
  • 上下文快到窗口上限、但你还需要继续这个线程时:让它自动压缩(阈值触发),或先 /compact 再继续。
  • 中途中不可用:压缩在轮次边界发生,轮次进行中会提示不可用。

📄 6.3 降低上下文噪音的六条习惯 vibe-coding-guide.md · 6.3 降低上下文噪音的六条习惯

  1. 先搜后读。glob 找路径、grep 找符号,只读命中的那几段。禁止"整个目录都读一遍"。
  2. 分段读大文件。read 本身有 readLimit 2000 行 / readMaxBytes 51200 的上限,超过就分页——别指望一次读完长文件。
  3. 大输出落盘。构建日志、测试报告、数据导出:写进文件,再把文件路径给 agent,用 grep 定位关键行。spill-policy 会自动做一部分,但源头上更省。
  4. 让子 agent 消化噪音。日志解析、跨目录搜索这类"读很多、只要一个结论"的活儿,委派出去,只回收结论与证据。
  5. 别在父上下文里贴代码块。需要引用就让对方读文件路径 + 行号。
  6. timely 的 /compact。阶段收尾压缩一次,比一路膨胀到最后被强制压缩质量高得多。

📄 6.4 一个具体的对照 vibe-coding-guide.md · 6.4 一个具体的对照

# 差:让主 agent 自己读 40 个文件
"看看 apps/ 下所有 Controller 有没有漏掉权限校验,把发现列出来"
→ 主上下文塞进 40 个文件的片段,后面几轮基本没法正常思考

# 好:用 workflow 扇出,只回收结论
phase('扫描') → 并行审查 40 个文件 → 每个返回 {file, missing, evidence}
→ 主上下文只拿到一份紧凑的结构化清单

判据:"读得多、结论少"的任务,一定要委派或脚本化。


第 7 章 端到端实战

需求:给现有仓库加一个"导出订单 CSV"的接口,并补测试,最后给一份验收报告。

📄 7.1 第 0 步:把需求变成验收标准(人来做) vibe-coding-guide.md · 7.1 第 0 步:把需求变成验收标准(人来做)

不要直接说"加个导出接口"。先写清:

目标:GET /api/orders/export 返回 CSV
验收:
  ① 200 + Content-Type: text/csv; charset=utf-8,含 BOM(Excel 直接打开不乱码)
  ② 表头与字段顺序固定:orderId, createdAt, customer, amount, status
  ③ 超过 50000 行时按分页流式返回,不一次性加载进内存
  ④ 未登录返回 401;无权限返回 403
  ⑤ 新增单元测试覆盖 ① ③ ④,并给出实际运行输出
边界:不改公共 API 的导出签名;不改数据库结构;不动前端

这一步的价值:后面所有 agent(包括子 agent)都拿这份标准对准自己。

📄 7.2 第 1 步:计划(plan mode) vibe-coding-guide.md · 7.2 第 1 步:计划(plan mode)

/plan 按上面的验收标准,制定实现计划

要看计划里有没有这几样:具体文件路径、每步的验证方式、对 ③ 的技术选择(游标 vs offset 流式)。没有验证方式的计划退回重写。

批准后,把计划固化:

create_goal objective: "实现订单 CSV 导出接口并补齐测试,验收标准见 ..."
todo_write: 5 条(路由/权限 → 查询流式 → CSV 序列化 → 测试 → 验收报告)

📄 7.3 第 2 步:并行推进两条独立线 vibe-coding-guide.md · 7.3 第 2 步:并行推进两条独立线

这两件事互不依赖、不共享文件:

我:同时开两个后台子任务
    ① subagent:梳理 <repo> 现有的导出类接口实现(路径、公共工具、错误码约定),
       按固定格式回报:可复用的工具 / 现有约定 / 反例
    ② subagent:读 orders 相关的查询层,给出"如何流式读取而不全量加载"的三种候选做法,
       每种给出文件:行号证据与风险
    期间我自己读 AGENTS.md 与最近相关提交
→ 两者到齐后,我决定技术路线,再动手改代码

📄 7.4 第 3 步:小步实现 + 每步验证 vibe-coding-guide.md · 7.4 第 3 步:小步实现 + 每步验证

todo 1/5 in_progress:加路由与权限校验
  → 改完立刻跑该包的类型检查与该路由的现有测试
todo 1/5 completed
todo 2/5 in_progress:查询层流式读取
  → 跑一个针对性的单测(构造 6 万行数据,断言拿到的是迭代器而非数组)
...

纪律:一次只做一条 todo,做完立刻验证,不在脑子里攒批。

📄 7.5 第 4 步:对抗验证(审查者子 agent) vibe-coding-guide.md · 7.5 第 4 步:对抗验证(审查者子 agent)

实现完成后,开一个只读评审,并把原始证据交给它,而不是你的总结:

我:subagent_fork(它能看到已完成的前文)
prompt:
【目标】独立复验这次 CSV 导出改动,找反例。
【你只能读,不要改文件】
【必须做的三件事】
  ① 打开 diff 涉及的文件,逐条对照验收标准 ①②③④
  ② 自己运行新增的测试,贴出真实命令与输出
  ③ 针对 ③ 的分页边界(49999/50000/50001 行)给出推理或反例
【输出】按 严重度 / 结论 / 证据(文件:行号 或 命令输出) 三列列出;无法确认的写清缺什么证据
【不允许】只复述实现说明

(想更严格一点:把 preset 换成 examples/agent-presets/readonly-agent/,它连 write/edit 工具都没有。)

📄 7.6 第 5 步:汇总、收敛、结束 vibe-coding-guide.md · 7.6 第 5 步:汇总、收敛、结束

  • 处理分歧:能复现的问题当真实问题修,不能复现的写清原因。
  • 更新 todo 全绿,update_goal action: complete。
  • 输出验收报告(固定格式):
验收标准          状态    证据
① CSV 头/BOM      通过    命令 + 响应头片段
② 字段顺序        通过    测试名 + 断言输出
③ 分页边界        通过    49999/50000/50001 三个用例输出
④ 401/403         通过    测试名 + 输出
⑤ 测试覆盖        通过    pnpm --filter orders test 输出
未验证项          —

📄 7.7 第 6 步:沉淀(这一步决定你下次是不是还要重说) vibe-coding-guide.md · 7.7 第 6 步:沉淀(这一步决定你下次是不是还要重说)

这次学到的 沉淀到哪
这个仓库的导出接口约定 <repo>/AGENTS.md 的「目录约定」
"怎么跑 orders 包的测试与边界用例" skill run-tests(或新增 orders-tests)
"导出类接口必须带 BOM 且流式" 的判定 skill project-standards
评审时必须看原始证据这条纪律 AGENTS.md 的「工作方式」
常用但啰嗦的权限/超时配置 ~/.dsh/profiles/web/cordis.patch.yml

一次任务的终点不是"做完了",而是"下次不用再解释一遍"。 这是 vibe coding 里复利最高的动作。


第 8 章 验收、故障排查与速查

📄 8.1 验收:什么叫"做完了" vibe-coding-guide.md · 8.1 验收:什么叫"做完了"

一个改动只有在下面四项都满足时才算完成:

  1. 有实际运行的证据:命令 + 关键输出(不是"应该能通过")。
  2. 证据能覆盖验收标准:每条标准对应一条证据,未覆盖的显式列出。
  3. 没有偷偷降低标准:没有 skip、没有 --passWithNoTests、没有调低覆盖率阈值。
  4. 变更范围与声明一致:diff 里没有计划外的改动(尤其是"顺手重构")。

📄 8.2 症状 → 根因 → 处理 vibe-coding-guide.md · 8.2 症状 → 根因 → 处理

症状 常见根因 处理
我写的 skill 不生效,模型像不知道它 frontmatter 问题导致静默丢弃:没 ---、YAML 非法、name 不是 kebab-case、旧键名 按 第 2.5 节的自检清单逐条核对;注意只有日志里有警告
skill 加载了但没被用上 description 前 500 字符里没有触发名词 把"fmt 触发场景"前置,加反触发
子 agent 报 subagent depth 2 exceeds maxDepth 1 委托深度上限(本机为 1,默认 3);且工具仍然可见 不要设计递归委派;两级结构用 workflow 编排,或调大 tool-subagent 的 maxDepth
子 agent 的回答完全跑偏 subagent 看不到父会话 用 subagent_fork,或把输入/约束/输出格式写进 prompt
cannot modify "<path>": file has not been read 读后写策略:edit/覆盖已存在文件前必须先 read;会话恢复后观察态不持久 先 read 那个文件再改(这是设计,不是故障)
FS_STALE_VERSION 读之后文件被外部改动 重读一次再改
[sandbox: file access denied under <mode> mode] 目标在工作区与私有临时目录之外 切到更合适的权限档,或改到工作区内;不要试图绕开
升级请求被直接拒绝 当前档位是 danger-full-access + 审批 never(或审批被禁用) 这是刻意的;要么信任当前档位,要么先切到 workspace-write
改了 profile 补丁但"没变化" ① id 拼错只有警告;② 该行 config 被整体替换,你少写的键回到了默认值;③ 增删插件包需要重启 --dump-config 对比;抄整块 config 再改
补丁文件写好后启动失败 顶层不是数组,或文件只有注释(没有 []) 空补丁写 []
settings.yaml 改了没效果 只影响新建会话;或取值不是合法枚举 新开一个会话;defaultPreset 必须是存在的 preset id
后台作业起不来 owner 并发上限(默认 10) job_kill 掉不再需要的
会话越跑越糊涂 上下文被噪音塞满 第 6 章:先搜后读、大输出落盘、委派消化、及时 /compact
安全相关:agent 改了不想让它改的东西 只写了 AGENTS.md(③ 层,纯文字) 把约束下沉到 ①(沙箱/审批)或 ②(不挂那个工具)
MCP 工具没出现 serverName 不符合 [A-Za-z0-9_-]{1,32}、transport 配置项写错、failOnStartupError 为 false 时启动失败被吞 先改成 failOnStartupError: true 让失败显形,再排查 command/url

📄 8.3 速查卡 vibe-coding-guide.md · 8.3 速查卡

A. 三个"必须先确认"的命令

$DSH = 'D:\softwarr\deepseekHARNESS\resources\runtime\cli\bin\dsh.cmd'

# 1. 我现在的配置到底长什么样
& $DSH --profile web --dump-config

# 2. 我的改动相对于官方默认值是什么
& $DSH --profile web --dump-default-config

# 3. 我的实验补丁能不能被接受(不动物理配置文件)
& $DSH --profile web --patch .\extra.yml --dump-config

B. 边界三问(动手前问自己)

  1. 这个任务最坏会改到什么?(决定沙箱档位)
  2. 它需要这个能力吗?(决定挂不挂那个工具行)
  3. 出错了怎么回滚?(决定要不要先备份/开分支)

C. 委派决策三问

  1. 这个子任务需要看到我们的对话吗?需要 → subagent_fork;不需要 → subagent。
  2. 它需要多久?超过一两分钟 → 后台(run_in_background: true),然后去做别的。
  3. 我要它回什么?先定输出格式,再写 prompt。

D. 上下文三问

  1. 这个信息是"过程"还是"结论"?过程别进主上下文。
  2. 我必须整份读吗?先 grep/glob 定位。
  3. 这个阶段结束了吗?结束就 /compact。

📄 8.4 本教程中标注为「未验证」的项 vibe-coding-guide.md · 8.4 本教程中标注为「未验证」的项

项 怎么自测
本机 maxDepth 为什么是 1 而不是默认 3 在 --dump-config 里查 tool-subagent 行的 maxDepth,以及 preset 里是否有覆盖
hooks(Claude Code / Codex)的完整事件语义与边界条件 挂一行 dsh-hooks-claude-code + 一个 hooks.json,在各事件里 echo 一行看是否触发;事件名见第 1.6 节的表
metadata 字段的实际用途 源码里只做原样保存;可写一个带 metadata 的 skill,观察是否影响任何行为(预期:不影响)
skill-filesystem 的 customSkillDirs 等细节键 在 profile 补丁里设 customSkillDirs: [...],验证该目录下的 skill 是否出现在会话目录中
todo 与 goal 之间是否有状态机关系 建一个 goal、写一份 todo,观察 goal 轮次是否读取 todo 状态(预期:两者独立)
子 agent 会话是否加载 AGENTS.md 让一个子 agent 回答"你看到了哪些工作区指令",与主 agent 的回答对比

📄 8.5 最后一句 vibe-coding-guide.md · 8.5 最后一句

这套东西里,最容易做、回报最高的三件事:

  1. 给每个常用仓库写一份 60 行以内的 AGENTS.md(第 3.5 节)。
  2. 把"你第二次对 agent 说的那句话"变成 skill(第 2.6 节)。
  3. 把"读得多、结论少"的活儿全部委派出去(第 6.4 节)。

配置和 preset 是锦上添花;上面这三件才是日常体感的主要来源。

📄 附录:复制粘贴配方集 copy-paste-recipes.md

十条 5 分钟能做完的配方。每条都给出改哪里 → 具体内容 → 怎么验证生效。

变量约定:

  • $DSH = DSH 启动器,当前机器上是 D:\softwarr\deepseekHARNESS\resources\runtime\cli\bin\dsh.cmd(PowerShell 里用 & $DSH ...)
  • ~/.dsh = C:\Users\<你>\.dsh
  • <repo> = 你的仓库根目录(含 .git 的那一层)

配方 1:让一个新仓库"接上"

场景:第一次在这个仓库里干活,agent 不知道为什么跑测试的命令是这个。

# 1) 复制模板到仓库根
Copy-Item 'E:\dsh\chat\vibe-coding-guide\examples\AGENTS.example.md' '<repo>\AGENTS.md'

# 2) 建团队 skill 目录
New-Item -ItemType Directory -Force '<repo>\.dsh\skills' | Out-Null
Copy-Item -Recurse 'E:\dsh\chat\vibe-coding-guide\examples\skills\run-tests' '<repo>\.dsh\skills\run-tests'

然后编辑 AGENTS.md:把「常用命令」「目录约定」「硬性禁止」三节换成这个仓库的真实内容(只写你真跑过的命令)。

验证:新开一个会话,问它"这个仓库提交前要跑什么命令、在哪里跑",看它是否答对;再问"你怎么知道的",它应该引用 AGENTS.md。


配方 2:加一个团队 skill

场景:"部署流程"每次都靠你口述。

New-Item -ItemType Directory -Force '<repo>\.dsh\skills\deploy-staging' | Out-Null

新建 <repo>\.dsh\skills\deploy-staging\SKILL.md:

---
name: deploy-staging
description: >-
  Deploy the current branch to the staging environment and verify the rollout:
  preflight checks, deploy command, health probe, log tail, and rollback trigger.
  Use when asked to deploy, release, push to staging, or verify a deployed build.
---

# 部署到 staging

## 何时使用
- 用户要求部署/发布到 staging,或验证刚部署的版本。
**不适用**:生产发布(走 `deploy-prod`,另有人工闸门)。

## 前置检查
1. `git status --short` 必须干净。
2. 当前分支已推送:`git rev-parse HEAD` 与远端一致。

## 步骤
1. 部署:`<真实命令>`
2. 健康检查:`<真实命令>`,期望输出 `<期望>`
3. 观察 60 秒日志:`<真实命令>`

## 判定标准
| 项 | 通过 |
|---|---|
| 健康检查 | 返回 200 且 body 含 `"status":"ok"` |

## 停止条件
- 健康检查连续 3 次失败 → 立即执行回滚 `<命令>`,然后把现象交给用户。
- 日志出现 `<错误关键字>` → 停止观察,直接回滚。

验证:新会话里输入 /deploy-staging,看它是否按步骤执行;再输入"帮我看看部署",看模型是否自己加载了这个 skill(如果没触发,说明 description 的触发词不够靠前)。


配方 3:把默认档收紧为工作区写入

场景:默认档是 danger-full-access,你想日常用 workspace-write。

编辑 ~/.dsh/settings.yaml:

permission:
  defaultPreset: workspace-write

验证:新开一个会话,/permission 应显示 workspace-write。注意已存在的会话不会变。


配方 4:临时放开到完全访问

场景:在可信仓库里做大批量重构,反复审批太慢。

会话内直接切(只影响这个会话):

/permission danger-full-access

做完切回来:

/permission workspace-write

验证:/permission 不带参数显示当前档位与可选表。


配方 5:加一个只读审计档(自定义预设)

场景:想一键切到"能读能搜、写操作必问"。

编辑 ~/.dsh/profiles/web/cordis.patch.yml(若为空文件,先写 [] 以外的一个数组):

- id: permission
  name: '@deepseek-ai/dsh-permission-presets'
  config:
    presets:
      audit-only:
        sandbox: read-only
        approval: ask
        name: 只读审计
        description: 自由读取与搜索;任何写入都要人工确认。
      safe-write:
        sandbox: workspace-write
        approval: ask
      trusted:
        sandbox: danger-full-access
        approval: never
    defaultPreset: safe-write

注意 config 是整体替换:内置的 read-only / workspace-write / danger-full-access 三档不会自动保留(read-only 与 danger-full-access 的默认表里本来就有,这里为了让 defaultPreset 指得到 safe-write,把要用的档都列全)。

验证:

& $DSH --profile web --dump-config   # 找到 permission 行,确认 presets 与 defaultPreset

新会话里 /permission 应能看到 只读审计 与 工作区写入。


配方 6:给一个 agent 换模型(子 agent 模型选择)

场景:探索/汇总用便宜的模型,实现用强模型。

~/.dsh/settings.yaml:

subagent-model-selection:
  enabled: true
  allowedModels:
    - provider: deepseek-official
      model: deepseek-flash
    - provider: deepseek-official
      model: deepseek-v4-flash

委派时成对指定:

subagent(description: "扫描迁移脚本", prompt: "...", provider: "deepseek-official", model: "deepseek-v4-flash")

验证:先把 model 填一个没在 allowedModels 里的值,应该报 child LLM route ... is not allowed for this Session;说明策略生效了。


配方 7:接一个 MCP server

场景:想把 MCP 的文件系统/记忆服务接进来。

编辑 ~/.dsh/profiles/web/cordis.patch.yml:

- insert:
    - id: mcp-filesystem
      name: '@deepseek-ai/dsh-mcp-client'
      config:
        serverName: fs
        transport: stdio
        command: npx
        args: ['-y', '@modelcontextprotocol/server-filesystem', 'E:\dsh\chat']
        toolCallTimeoutMs: 60000
        failOnStartupError: true   # 先设 true,让启动失败显形

验证:

& $DSH --profile web --dump-config | Select-String -Pattern 'serverName' -Context 1,3

重启 profile(插件包/插入行变化需要重启),然后在新会话里让它列出可用工具,应该看到 mcp__fs__*。确认没问题后再把 failOnStartupError 改回 false。


配方 8:装一个只读评审 preset

场景:评审时不放心 agent 手滑改文件。

New-Item -ItemType Directory -Force "$env:USERPROFILE\.dsh\.agent-presets" | Out-Null
Copy-Item -Recurse 'E:\dsh\chat\vibe-coding-guide\examples\agent-presets\readonly-agent' `
          "$env:USERPROFILE\.dsh\.agent-presets\readonly-agent"

验证:新开一个空白会话,在 preset 选择器里应看到「只读评审」;让它"改一下某个文件",它应该告诉你 write/edit 工具不存在。

提醒:这个 preset 不挂 write/edit,但 pwsh 还在。要彻底只读,配合配方 5 的 audit-only(read-only 沙箱)使用。


配方 9:把大任务改成 workflow

场景:"审计 80 个文件里的错误处理"。

先让 agent 生成文件清单,再让它用 workflow:

用 workflow 工具审计下面这批文件(清单见 args.files):
每个文件一个子 agent,只读审查;必须用 schema 返回结构化结果:
  { findings: [{ severity: high|medium|low, summary, evidence }] }
evidence 必须是 文件:行号 或 命令输出。
最后汇总:按严重度计数、列出失败(null)的数量、把 high 的条目单独列出来。
并发会超过 16 时就分两批。

参考脚本:vibe-coding-guide.md 第 5.3 节。

验证:返回的 JSON 里 failed 字段应当明确列出丢掉的 item 数;findings 里每条都有 evidence。


配方 10:排查"skill 没加载"

场景:写了 skill,模型像没看见。

帮我排查这个 skill 为什么没被加载:<绝对路径>\SKILL.md
逐条检查并给证据:
① 文件是否以 --- 开头、并在正文前有闭合的 ---
② frontmatter 的 YAML 能否解析
③ name 是否匹配 ^[a-z0-9]+(?:-[a-z0-9]+)*$,且与目录名一致
④ description 是否非空、前 500 字符内是否包含触发词
⑤ 是否使用了 disableModelInvocation / modelInvocable / userInvocable 这类旧键名
⑥ 文件是否放在会被扫描的位置:<repo>/.dsh/skills/<name>/SKILL.md 或 <repo>/.agents/skills/<name>/SKILL.md
结论:能加载 / 不能加载 + 原因 + 最小修复

再确认项目根:从你打开会话的工作目录往上,第一个含 .git 的目录才是 <repo>。放错层级(例如 skills/foo/bar/SKILL.md,多嵌了一层)不会被发现。

验证:修好后新开一个会话,让模型列出它可见的 skill 名称。


附:常用命令一览

$DSH = 'D:\softwarr\deepseekHARNESS\resources\runtime\cli\bin\dsh.cmd'

& $DSH --help                                   # 启动器自身的参数
& $DSH web --help                               # web 表层的参数
& $DSH --profile web --dump-config              # 当前生效的完整插件树
& $DSH --profile web --dump-default-config      # 只看 bundle 默认值
& $DSH --profile web --dump-config-schema       # 补丁条目的 JSON Schema(字段真源)
& $DSH --profile <名> --patch .\extra.yml --dump-config   # 验证一份实验补丁
& $DSH rescue --from-default-profile web        # 从官方模板派生一个自己的 profile
& $DSH plugin --profile web add <包名>           # 往 profile 装插件(之后要重启)
& $DSH headless "跑一下测试"                      # 一次性任务,不用打开 GUI

会话内的斜杠命令:/plan [消息]、/plan off、/permission [预设]、/compact、/goal、/<skill 名>。

📄 AGENTS.md —— 项目级工作指令模板 AGENTS.example.md

这个仓库是什么

  • 一句话定位:<填写:例如“面向内部运营的 NestJS 后台服务,含 Vue 管理台”>
  • 主语言/框架:<填写>;包管理器:<填写:pnpm/npm/uv…>
  • 代码根目录:apps/(可运行程序)、packages/(库)、tests/(跨包测试)

常用命令(只列真实可跑的)

pnpm install            # 安装依赖
pnpm run dev            # 本地启动
pnpm run test:unit      # 单元测试(提交前必跑)
pnpm run typecheck      # 类型检查
pnpm run lint:fix       # 格式化与静态检查

跑某个包:pnpm --filter <package> test。

目录约定

路径 放什么 不要放什么
apps/ 可独立启动的入口与它们的装配 业务通用逻辑
packages/ 可复用库,按领域分包 跨包引用具体实现的胶水
migrations/ 只追加的数据库迁移 修改历史迁移
docs/ 决策与使用说明 临时草稿

编码要求

  • 注释只解释「为什么」,不复述代码;不写 // 这是一段循环 这类注释。
  • 错误处理:能失败的地方显式失败,不要 catch {} 吞掉;确实要吞,写明吞的是什么、为什么安全。
  • 新增依赖前先说明理由;能用标准库或已有依赖解决的不新增。
  • 日志用仓库统一 logger,不用 console.log。
  • 变更后同步更新受影响的 README.md。

测试与验收标准

  • 修 bug:先补一个能复现该 bug 的失败测试,再修,最后贴出该测试通过的命令与输出。
  • 加功能:给主要分支与边界各一个小测试;不要求穷举。
  • 提交前必须给出:pnpm run test:unit 与 pnpm run typecheck 的实际输出(或明确说明未能运行的原因)。
  • 不要为了让检查通过而降低阈值、跳过用例或 --passWithNoTests。

硬性禁止

  • 不改 migrations/ 下已提交的迁移文件;需要变更就新建一个。
  • 不提交任何密钥、token、真实数据导出文件。
  • 不 git push、不 git commit --force、不改写已推送的历史。
  • 不删除或重命名对外导出的公共 API,除非任务明确要求并给出调用方清单。

工作方式

  • 动多个文件前先读现状、给出计划,再动手。
  • 大任务拆成可独立验证的小步;每步做完立刻验证,不攒到最后。
  • 需要跨多个方向并行时,把互不相关、边界清晰的部分交给子 agent,并在委派提示词里写清输入、输出格式与证据要求。
  • 发现自己重复调用同一个工具、参数没变时,停下来分析上一次的结果,换方法或收尾。

与本文件相关

  • 用户全局偏好放在 ~/.dsh/AGENTS.md(不随仓库走)。
  • 每台机器/每个人自己的临时规则放 AGENTS.local.md(默认在 .gitignore 里)。

📄 跑测试并给出可行动的结论 SKILL.md


name: run-tests description: >- Run this repository's test suite and turn failures into a classified, evidence-backed report: reproduction command, failing assertion, first divergence, suspected owner package, and a recommended next action. Use when asked to run tests, check whether a change broke anything, verify a fix, or explain why CI is failing.

这是「可执行流程」类 skill 的模板:重点在判定标准和停止条件,而不是把命令抄一遍。

何时使用

  • 用户说「跑一下测试」「验证这个改动」「CI 为什么红」。
  • 某次修复声称完成,需要独立证据。

不适用:只想看单个函数的用法(直接读代码或文档)。

步骤

1. 选定最小足够的范围(不要一上来就跑全量)

# 只跑受影响的包
pnpm --filter <package> run test

# 需要按文件名过滤时
pnpm --filter <package> exec vitest run tests/<file>.spec.ts

选择依据:本次改动触及的包 + 引用了这些包公共 API 的包。全量只在用户明确要求、或改动跨多个包时跑。

2. 记录原始证据

  • 完整命令(含过滤参数)。
  • 退出码。
  • 失败用例的完整报错首段(断言差异 / 堆栈第一帧),不要只写"失败了"。

3. 给每个失败分类

类别 判断依据 下一步
真实回归 断言差异指向本次改动涉及的行为 修代码,重跑
环境问题 缺依赖、端口占用、路径不存在、需要密钥 记录环境差异,说明如何在本地复现
测试自身过时 断言写死的是被本次变更有意改变的行为 同步更新测试,并在汇报中说明
偶发/时序 重跑一次即通过,且无共享状态假设 标注为可疑并发问题,给出再跑一次的结论

判定不清时不要猜:写明「无法区分 A 与 B,需要 <哪一个信息>」,并把已排除的选项列出来。

4. 汇报格式(固定)

命令:<实际命令>
结果:<通过 / 失败 N 项 / 未能运行(原因)>

失败 1:<用例名>
  类别:<真实回归|环境|过时断言|偶发>
  证据:<报错首段>
  归属:<哪个包/文件>
  建议:<下一步动作>

停止条件(防死循环)

  • 全绿 → 汇报并结束。
  • 同一失败修了 3 次仍不通过 → 停止修改,把「失败现象 + 3 次改动 + 当前假设」交给用户。
  • 同一命令连续两次输出完全一致且仍失败 → 不要再原样重跑,必须换手段(读代码/加日志/缩小用例)。
  • 需要密钥或外部服务而无法获取 → 明确说明,改跑不依赖它们的子集。

不要做

  • 不要为了让测试通过而降低覆盖率阈值、加 --passWithNoTests、skip 用例或注释掉断言。
  • 不要修改测试来掩盖尚未理解的行为。
  • 不要声称"应该可以"——只报实际跑过的结果。

📄 项目编码规范(可执行版) SKILL.md


name: project-standards description: >- Enforce this repository's coding standards when adding or modifying code: naming, error handling, logging, test evidence, and the update-docs rule. Use when the task writes, refactors, or reviews code in this repo, or when the user asks whether a change follows our conventions. user-invocable: true

这是「项目规范」类 skill 的模板:把规范写成 agent 能执行的检查步骤,而不是一堆形容词。 复制到 <repo>/.dsh/skills/project-standards/SKILL.md。

何时使用

  • 在本仓库新增/修改/重构代码。
  • 评审本仓库的 diff。
  • 用户问「这个改动符合我们的规范吗」。

不适用:纯文档排版、纯配置值调整(那属于 run-tests 或直接改)。

前置检查(先读,别猜)

  1. 读仓库根 AGENTS.md,确认命令与目录约定(它优先于本 skill)。
  2. 确认改动落在哪个包:apps/ 还是 packages/。
  3. 找到该包已有的同类实现,照它的写法写,不要引入新范式。

检查清单(逐条给结论,不要只写"已检查")

项 判定标准 不通过时的处理
命名 与该包既有文件命名风格一致(camelCase/PascalCase/kebab-case) 改成与邻居一致
错误处理 能失败处显式抛出;空 catch 必须注释说明吞掉了什么 补错误类型或注释
日志 用仓库 logger;不出现 console.log 替换成 logger
依赖 新增依赖有理由,且仓库内已有等价能力优先 去掉依赖或写明理由
公共 API 未删除/重命名对外导出,或已列出全部调用方 补调用方改动
文档 受影响的 README.md 同批更新 更新文档
注释 只解释为什么,不复述代码 删掉复述性注释

必须提供的证据

改动完成后,输出实际执行的命令与输出,不要只写"应该能通过":

pnpm --filter <package> run typecheck
pnpm --filter <package> run test

任何一项没跑,必须写明原因(环境缺依赖 / 需要密钥 / 用户要求跳过)。

停止条件

  • 两项检查都通过 → 汇报结果并结束。
  • 同一处错误修了 3 次仍失败 → 停下来,把「现象 + 已试过的 3 种改法 + 当前判断」整理给用户,不要继续试第 4 种。
  • 发现规范本身与代码现状冲突 → 先汇报冲突,由用户决定改规范还是改代码。

📁 配套文件目录

vibe-coding-guide/
  README.md
  validate-guide.py
  vibe-coding-guide.md
  appendix/
    copy-paste-recipes.md
  examples/
    AGENTS.example.md
    profile-patch.example.yml
    settings.example.yaml
    agent-presets/
    skills/

完整源码 Gitee 仓库浏览 →