Windmill「Design It Twice」并行子代理模式:为一个深模块生成并比较多套接口设计
Windmill「Design It Twice」并行子代理模式为一个深模块生成并比较多套接口设计【免费下载链接】windmillOpen-source developer platform to power your entire infra and turn scripts into webhooks, workflows and UIs. Fastest workflow engine (13x vs Airflow). Open-source alternative to Retool and Temporal.项目地址: https://gitcode.com/GitHub_Trending/wi/windmill本文围绕 Windmill 仓库中 vendored 的 Agent 技能文件 .agents/skills/codebase-design/DESIGN-IT-TWICE.md 展开完整讲解其「Design It Twice」并行子代理设计模式先框定问题空间、再并行派发 3 个以上带着互不相同设计约束的子代理各自产出一套“截然不同”的接口方案、最后按深度depth、局部性locality与接缝seam位置逐一比较并给出有立场的推荐。读完你应能理解该模式在 Windmill 架构改进流程中的确切触发位置并掌握可直接复用的子代理提示词模板、输出清单与比较维度把「第一个想到的接口未必是最优」这一原则落地为可操作的工作流。该模式在 Windmill 仓库中的位置Windmill 在.agents/skills/下维护了一组供 AI 编码助手Claude Code、Codex、Pi 等 CLI调用的技能文件。.agents/skills/codebase-design/目录内含三个文件构成一个自洽的小技能簇SKILL.md — 定义“深模块”设计的共享词汇表module、interface、seam、adapter、leverage 等与设计原则DEEPENING.md — 在给定依赖条件下如何安全地把一组浅模块“加深”定义了四类依赖分类与接缝纪律DESIGN-IT-TWICE.md — 本文主角当用户想为某个已选定的“加深候选”deepening candidate探索替代接口时使用这套并行子代理模式。三者的调用关系可以从源码结构中直接看到SKILL.md 末尾的 “Going deeper” 一节明确指向另外两个伴生文件分别对应“带依赖地加深一个模块簇”与“探索替代接口”。而.agents/skills/codebase-design/DESIGN-IT-TWICE.md又位于更大的架构改进流程末端——improve-codebase-architecture/SKILL.md 的 “Grilling loop” 阶段写道“想为加深后的模块探索替代接口运行/codebase-design技能并使用它的 design-it-twice 并行子代理模式。”从 UPSTREAM.md 可以确认codebase-design、improve-codebase-architecture、grilling、grill-me、domain-modeling这五个技能是从外部仓库 vendored 而来并钉死在特定 commit 上的它们形成一个依赖闭包improve-codebase-architecture的架构词汇取自codebase-design领域模型维护取自domain-modeling删掉任何一个都会破坏其余部分。理解这一点有助于理解为什么DESIGN-IT-TWICE.md里反复引用兄弟文件的词汇而不是就地重复定义——它被设计为技能簇中的一环而非独立文档。模式本身的思想来源在文档首行即被标明基于 Ousterhout 的 “Design It Twice”——你的第一个想法很可能不是最好的。因此该模式不是“让 AI 再想想”而是制度性地强制生成多个激进差异化的候选设计再按统一标准裁决。前置词汇子代理必须使用的统一语言在讲流程之前必须先交代词汇表因为DESIGN-IT-TWICE.md的步骤 2 明确要求每个子代理的简报brief中都要同时包含 SKILL.md 的架构词汇与CONTEXT.md的领域词汇让每个子代理“用一致的命名来称呼事物”。这些术语来自 SKILL.md 的 Glossary且规定“精确使用这些词——不要用 component、service、API、boundary 来替代”。核心术语如下术语定义要点Module模块任何拥有接口与实现的东西刻意规模无关——一个函数、一个类、一个包或跨层切片均可Interface接口调用方要正确使用模块所必须知道的一切类型签名之外还包括不变量invariants、顺序约束、错误模式、必需配置与性能特征Implementation实现/ Adapter适配器实现是模块内部适配器是在某个接缝处满足接口的具体事物描述“角色”它填哪个槽位而非“物质”里面是什么Depth深度接口处的杠杆leverage调用方或测试每单位需要学习的接口所能调动的行为量。大量行为藏在小接口后 深接口与实现复杂度相当 浅Seam接缝源自 Michael Feathers一个“可以改变行为而无需在该处编辑”的位置即模块接口所在的位置。接缝放在哪里本身就是一个独立设计决策Leverage杠杆/ Locality局部性深度带给调用方的回报是 leverage一份实现跨 N 个调用点与 M 个测试持续回报带给维护者的回报是 locality变更、bug、知识与验证集中在一处“修一次处处修好”此外还有几条贯穿整个设计过程的原则它们直接决定了后续比较与裁决的尺度深度是接口的属性不是实现的属性。深模块内部可以由小的、可 mock、可替换的部分组成只要这些部分不属于接口模块既可以在接口处有外部接缝也可以在实现内部有内部接缝仅供自己的测试使用。删除测试The deletion test想象删掉这个模块——如果复杂度随之消失它是透传层如果复杂度会在 N 个调用方身上重新出现说明它赚到了自己的存在价值。接口即测试面。调用方和测试跨越同一条接缝如果你需要测试到接口“后面”说明模块形状可能不对。一个适配器 假设性接缝两个适配器 真实接缝。除非某处确实存在两种变化不要引入接缝。DESIGN-IT-TWICE.md的“展示与比较”步骤步骤 3要求的三个比较维度——depth接口处的杠杆、locality变更集中在哪里、seam placement接缝放在哪里——全部来自这套词汇表。没有这张表后续步骤中“radically different”“where leverage is high, where its thin”等表述都无法被一致执行。流程第一步框定问题空间Frame the problem space在派发任何子代理之前编排者主代理要先为所选的加深候选写一份面向用户的问题空间说明。按文档规定这份说明必须包含三样东西约束任何新接口都需要满足的约束条件依赖及其类别模块将要依赖什么以及每个依赖属于 DEEPENING.md 定义的哪一类见下文“四类依赖”一节粗略的示意性代码草图illustrative code sketch——注意其定位是“让约束变得具体”的手段不是一个提案。它用来把抽象约束落到代码形状上避免子代理在错误的约束下发散。文档随后给了一条关键的流程纪律把这份说明展示给用户之后立即进入步骤 2不要等待。理由写在原文里“用户一边读一边思考子代理在并行地干活。”这是一个刻意的时延设计——人类阅读与 AI 并行计算同时发生问题空间文档既不是等待确认的提案也不是阻塞点。流程第二步并行派发子代理Spawn sub-agents这是整个模式的核心。文档规定至少并行派发 3 个子代理每个必须为该模块产出一套“截然不同的”radically different接口。“radically different”不是措辞上的客气——为此文档给每个子代理分配了互相冲突的设计目标从源头保证候选方案之间的差异是结构性的Agent 1“最小化接口——目标最多 1–3 个入口点。最大化每个入口点的杠杆。”Agent 2“最大化灵活性——支持尽可能多的用例与扩展。”Agent 3“为最常见的调用方优化——让默认场景变得平凡trivial。”Agent 4如适用“围绕 ports adapters 设计跨接缝依赖。”可以看到Agent 1 与 Agent 2 在“接口规模”这一轴上是对立的极小 vs. 极宽Agent 3 则代表“为多数优化”的现实主义路线Agent 4 把解耦问题ports adapters本身作为设计约束。四个方向恰好覆盖了接口设计中最常出现分歧的决策轴。每个子代理的输入独立的技术简报文档要求每个子代理收到一份独立的技术简报technical brief内容包括相关文件路径耦合细节依赖类别引用 DEEPENING.md 的分类接缝后面behind the seam是什么。并特别强调这份简报与步骤 1 中面向用户的问题空间说明是相互独立的两份材料。一个是给人类读者看的约束叙述一个是给子代理执行用的工程输入——两者不能互相替代。此外简报必须同时注入 SKILL.md 的架构词汇与CONTEXT.md的领域词汇使每个子代理产出的命名与架构语言、项目领域语言保持一致这一点对多代理并行尤其重要词汇不统一时后续的比较与合成都无法进行。Windmill 的领域词汇表就在仓库根目录的 CONTEXT.md 中它为项目特有概念钉死命名例如Stepflow 中的一个节点代码中类型为FlowModule刻意避免用“module”一词以防与架构意义的 module 混淆、Step settingretries、timeout、concurrency limit 等逐步运行时选项、Trigger steppolling flow 的第一步、Member / Role / Owner权限体系。当子代理为某个 Windmill 模块设计接口时这些术语保证候选方案谈的是同一个领域对象。每个子代理的输出五项交付物文档为每个子代理规定了统一的输出格式五项缺一不可Interface—— 类型、方法、参数外加不变量、顺序约束、错误模式注意这比“类型签名”宽得多呼应 SKILL.md 对 interface 的定义Usage example—— 展示调用方如何使用它实现隐藏了什么—— 接缝后面的内容依赖策略与适配器—— 对应 DEEPENING.md 的依赖分类与接缝纪律Trade-offs—— 杠杆在哪里高、在哪里薄。这份输出清单本身值得注意它把“接口设计”从写类型签名的活动扩展为同时交付不变量、错误模式、用法示例与权衡分析的活动——这五项恰好就是“接口即测试面”原则所需要的全部信息因为后续测试要跨越的正是这个完整接口。支撑机制DEEPENING.md 的四类依赖步骤 1 要求标注依赖类别、步骤 2 的简报要求携带依赖类别、子代理输出的第 4 项要求给出“依赖策略与适配器”——三者都锚定在 DEEPENING.md 的依赖分类上。该文件把候选模块的依赖分为四类类别决定了加深后的模块如何跨越接缝被测试类别特征加深策略1. In-process进程内纯计算、内存状态、无 I/O总是可加深——合并模块直接通过新接口测试无需适配器2. Local-substitutable本地可替代存在本地测试替身如 PGLite 之于 Postgres、内存文件系统存在替身则可加深加深后的模块用替身在测试套件中运行接缝是内部接缝模块外部接口上不设 port3. Remote but owned远端但自有自己拥有的跨网络边界服务微服务、内部 API在接缝处定义 port接口深模块拥有逻辑传输以适配器形式注入测试用内存适配器生产用 HTTP/gRPC/队列适配器4. True external真外部不控制自己的第三方服务Stripe、Twilio 等加深后的模块以注入 port 的形式接收外部依赖测试提供 mock 适配器配套的两条接缝纪律与测试策略同样被设计流程反复引用“一个适配器 假设性接缝两个适配器 真实接缝”除非至少两个适配器都有正当理由典型是生产 测试否则不要引入 port——单适配器接缝只是间接层。内部接缝与外部接缝要分清不要把内部接缝仅仅因为测试用到就暴露到接口上。测试策略是“替换不是叠加”replace, dont layer加深后针对旧浅模块的单元测试就成了垃圾删掉新测试写在加深后模块的接口处测试断言的是通过接口可观察的结果而非内部状态测试应当能挺过内部重构——如果一个测试在实现变化时必须跟着改说明它测试越过了接口。对DESIGN-IT-TWICE而言这套分类的实际作用是它让四个子代理在“依赖策略与适配器”这一输出项上有共同的坐标系比较时才谈得通。流程第三步展示、比较与裁决Present and compare文档对呈现方式的规定同样具体顺序呈现而非并列铺开。设计逐个展示让用户能够消化absorb每一个然后再比较。这与步骤 2 的并行生产形成对照——生产并行消费串行。用散文prose比较且比较维度固定为三个depth接口处的杠杆有多大、locality变更集中在哪里、seam placement接缝放在哪里。这三个维度全部来自前置词汇表不允许临场发明比较标准。给出自己的推荐。比较之后编排者必须表态你认为哪套设计最强、为什么。如果不同设计中的元素可以很好地组合提出混合方案hybrid。原文的要求是 “Be opinionated — the user wants a strong read, not a menu.”要有立场——用户要的是一个强判断而不是一份菜单。最后一条是整个模式的收尾价值观并行生成是为了防止锚定在第一直觉上但流程的终点不是“民主投票”而是编排者给出有依据的强推荐。多方案是手段裁决是交付物。在整个架构改进流程中的位置把 improve-codebase-architecture/SKILL.md 的完整流程读一遍可以看清DESIGN-IT-TWICE处在链条的哪一环Explore探索先定范围再扫描YAGNI——用户指明了方向就照做否则走一遍git log --oneline找近期变更的热点区域先读CONTEXT.md领域词汇表然后派子代理有机地走读代码注意理解一个概念要在多少小模块间跳转、哪些模块是浅的、纯函数被抽出仅为可测但真正的 bug 藏在调用方式里没有 locality、哪些模块跨接缝泄漏、哪些部分未被测试或难以通过现有接口测试。对任何疑似浅模块跑一遍删除测试。Present candidates as an HTML report以 HTML 报告呈现候选把每个加深候选渲染成卡片涉及文件、问题、方案、以 locality/leverage 表述的收益、Before/After 图、推荐强度徽章写到系统临时目录不入仓库以可视化方式呈现最后问用户“你想深入探索哪一个” 该报告刻意不在此阶段提出接口。Grilling loop拷问循环用户选定候选后跑/grilling技能走决策树——约束、依赖、加深后模块的形状、接缝后是什么、哪些测试存活过程中通过/domain-modeling技能随时更新领域模型给深模块起了CONTEXT.md中没有的名字就地加词。而当走到“想为加深后的模块探索替代接口”这一步时才进入DESIGN-IT-TWICE.md描述的并行子代理模式。也就是说DESIGN-IT-TWICE是整个架构改进流水线上最靠近“定稿”的环节候选已经选出improve-codebase-architecture、约束与依赖已经过拷问grilling此时才值得投入多个并行子代理去竞争性地设计接口。这也解释了为什么它的前置条件写得如此精确——“当用户想为已选定的加深候选探索替代接口时”。小结DESIGN-IT-TWICE.md 篇幅不长但它把“Design It Twice”这一设计直觉压缩成了一个可执行协议先框定约束含依赖类别与示意草图展示后立即推进再并行派发 3 个带互斥设计目标的子代理最小接口 / 最大灵活性 / 最常见调用方 / ports adapters每个按五项固定清单交付接口、用法、隐藏内容、依赖策略、权衡最后顺序呈现并按 depth、locality、seam placement 三个维度散文比较给出有立场的推荐或混合方案。其可执行性建立在两个配套文件之上SKILL.md 提供统一的架构词汇与原则深度、删除测试、接口即测试面、双适配器才算真实接缝DEEPENING.md 提供依赖四分类与“替换不叠加”的测试策略。对 Windmill 这样多语言、多工作空间的代码库该模式的价值在于把“接口怎么设计”从一次性直觉判断变成多候选并行生成、统一标准裁决的重复流程——这正是 “your first idea is unlikely to be the best” 的操作化。【免费下载链接】windmillOpen-source developer platform to power your entire infra and turn scripts into webhooks, workflows and UIs. Fastest workflow engine (13x vs Airflow). Open-source alternative to Retool and Temporal.项目地址: https://gitcode.com/GitHub_Trending/wi/windmill创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考