[{"data":1,"prerenderedAt":1036},["ShallowReactive",2],{"\u002F2026-04-17-30agent":3,"\u002F2026-04-17-30agent-rel":453},{"id":4,"title":5,"body":6,"column":437,"date":438,"description":12,"extension":439,"hero_image":440,"meta":441,"navigation":442,"path":443,"seo":444,"series_id":440,"severity":440,"stem":445,"summary":446,"tags":447,"__hash__":452},"posts\u002F2026-04-17-30个自定义agent什么时候值得单独造.md","造到第 30 个 agent，我开始怀疑该不该造",{"type":7,"value":8,"toc":426},"minimark",[9,13,21,25,30,38,44,47,77,80,94,97,101,107,114,134,141,144,148,151,157,177,182,218,224,250,256,296,303,306,309,312,347,350,353,364,367,370,373,399,402,405,419,422],[10,11,12],"p",{},"我的 Claude agent 定义库里现在有 30 份。reviewer 配对给了每种编程语言；流程 agent 分了规划、设计、测试、审查五个阶段；还有 5 个领域特化（数据库、医疗、性能等）。问题是：多少才够？什么时候不该造新的？",[10,14,15,16,20],{},"答案不是\"能不能造\"，而是",[17,18,19],"strong",{},"约束密度","。",[22,23,24],"h2",{"id":24},"三类成立条件",[26,27,29],"h3",{"id":28},"_1-语言成对的-reviewer-和-build-resolver最明显成立","1. 语言成对的 reviewer 和 build-resolver——最明显成立",[10,31,32,33,37],{},"TypeScript、Rust、Go、Java、Python、C++、Kotlin……每种语言都配一对。typescript-reviewer 关注类型安全、异步正确、XSS 防护；rust-build-resolver 关注借用检查器、生命周期、",[34,35,36],"code",{},"cargo"," 依赖。",[10,39,40,41,20],{},"为什么分离？因为 ",[17,42,43],{},"error pattern 真的不一样",[10,45,46],{},"typescript-reviewer 的 HIGH 检查点包括：",[48,49,50,58,64,74],"ul",{},[51,52,53,54,57],"li",{},"非空断言滥用（",[34,55,56],{},"value!"," 后没有前置守卫）",[51,59,60,63],{},[34,61,62],{},"as"," 类型转换绕过检查",[51,65,66,69,70,73],{},[34,67,68],{},"forEach"," 里用 ",[34,71,72],{},"async","（不会等待）",[51,75,76],{},"宽松的 TypeScript 编译器设置",[10,78,79],{},"这些对 Rust 没有意义。Rust 则需要：",[48,81,82,85,88,91],{},[51,83,84],{},"借用冲突（immutable 活跃时不能 mutable）",[51,86,87],{},"生命周期边界太短",[51,89,90],{},"从引用后移出所有权",[51,92,93],{},"trait 实现不匹配",[10,95,96],{},"混在一个通用 reviewer 里会产生两个后果：要么给出泛泛而谈的\"检查类型\"之类的意见，要么为了照顾所有语言而膨胀到无用。所以这类成立。",[26,98,100],{"id":99},"_2-流程-agent思考方式必须隔离","2. 流程 agent——思考方式必须隔离",[10,102,103,104,20],{},"planner、architect、tdd-guide、code-reviewer、security-reviewer 分别对应的是不同的",[17,105,106],{},"思维模式",[10,108,109,110,113],{},"planner 的工作是",[17,111,112],{},"规模化","。它把一个模糊的需求分解成有依赖关系的、分阶段的步骤。格式包括：",[48,115,116,119,122,125,128,131],{},[51,117,118],{},"Requirements 列表",[51,120,121],{},"Architecture Changes 清单",[51,123,124],{},"多个 Phase，每个 Phase 有多个步骤",[51,126,127],{},"每步都标注 Action、Why、Dependencies、Risk",[51,129,130],{},"Testing Strategy",[51,132,133],{},"Success Criteria",[10,135,136,137,140],{},"code-reviewer 的工作是",[17,138,139],{},"细节校验","。它逐行读代码，按优先级分类问题（CRITICAL \u002F HIGH \u002F MEDIUM \u002F LOW），给出代码片段和修复建议。",[10,142,143],{},"如果混在一个 agent 里，它们会互相干扰：规划时陷入代码细节，审查时又开始重新规划。分离后，planner 可以大局思考而不被小语法问题打断，reviewer 可以专注代码质量而不被\"这个步骤设计对吗\"的大问题分神。",[26,145,147],{"id":146},"_3-领域-agent条件性成立必须约束足够密","3. 领域 agent——条件性成立，必须约束足够密",[10,149,150],{},"database-reviewer 是这类的好例子。它不只是\"你是 PostgreSQL 专家\"，而是定义了：",[10,152,153,156],{},[17,154,155],{},"核心职责","：",[48,158,159,162,165,168,171,174],{},[51,160,161],{},"Query Performance",[51,163,164],{},"Schema Design",[51,166,167],{},"Security & RLS",[51,169,170],{},"Connection Management",[51,172,173],{},"Concurrency",[51,175,176],{},"Monitoring",[10,178,179,156],{},[17,180,181],{},"诊断命令",[183,184,189],"pre",{"className":185,"code":186,"language":187,"meta":188,"style":188},"language-bash shiki shiki-themes github-light github-dark","psql -c \"SELECT query, mean_exec_time, calls FROM pg_stat_statements ORDER BY mean_exec_time DESC LIMIT 10;\"\npsql -c \"SELECT relname, pg_size_pretty(...) FROM pg_stat_user_tables ORDER BY ...;\"\n","bash","",[34,190,191,208],{"__ignoreMap":188},[192,193,196,200,204],"span",{"class":194,"line":195},"line",1,[192,197,199],{"class":198},"sScJk","psql",[192,201,203],{"class":202},"sj4cs"," -c",[192,205,207],{"class":206},"sZZnC"," \"SELECT query, mean_exec_time, calls FROM pg_stat_statements ORDER BY mean_exec_time DESC LIMIT 10;\"\n",[192,209,211,213,215],{"class":194,"line":210},2,[192,212,199],{"class":198},[192,214,203],{"class":202},[192,216,217],{"class":206}," \"SELECT relname, pg_size_pretty(...) FROM pg_stat_user_tables ORDER BY ...;\"\n",[10,219,220,223],{},[17,221,222],{},"关键原则","（不只是建议，是规则）：",[48,225,226,229,232,235,238,241,244,247],{},[51,227,228],{},"索引外键——没有例外",[51,230,231],{},"部分索引用于软删除",[51,233,234],{},"覆盖索引避免表查",[51,236,237],{},"队列用 SKIP LOCKED",[51,239,240],{},"游标分页替代 OFFSET",[51,242,243],{},"批量插入，禁止循环内逐行插入",[51,245,246],{},"事务保持短",[51,248,249],{},"锁顺序一致",[10,251,252,255],{},[17,253,254],{},"反模式检查清单","（10+ 条）：",[48,257,258,264,275,281,284,287,293],{},[51,259,260,263],{},[34,261,262],{},"SELECT *"," 在生产代码",[51,265,266,267,270,271,274],{},"ID 用 ",[34,268,269],{},"int","（应该 ",[34,272,273],{},"bigint","）",[51,276,277,280],{},[34,278,279],{},"timestamp"," 无时区",[51,282,283],{},"随机 UUID 做主键",[51,285,286],{},"未参数化查询",[51,288,289,292],{},[34,290,291],{},"GRANT ALL"," 给应用用户",[51,294,295],{},"RLS 策略每行调函数",[10,297,298,299,302],{},"这些",[17,300,301],{},"不是建议","，而是具体的、可验证的、在 PostgreSQL 世界里遵循的法则。database-reviewer 存在是因为它编码了这个领域的专有知识。",[10,304,305],{},"对比一个不该独立存在的例子：假设有个 \"api-design-reviewer\" agent，系统提示只有\"检查 API 设计\"加几条通用的\"RESTful 最佳实践\"。没有针对这个项目的路由规范、没有特定的错误响应格式检查、没有该业务领域的约束……那个 agent 其实不该独立——它只是拿 Claude 通用能力换了个称呼，增加了调度成本却没有添加任何专有约束。",[22,307,308],{"id":308},"过度设计的信号",[10,310,311],{},"一个 agent 定义如果满足以下条件，很可能不该独立存在：",[313,314,315,321,327,333],"ol",{},[51,316,317,320],{},[17,318,319],{},"系统提示是\"你是 X 专家\"加 2-3 条泛泛要求","。真正有价值的 agent 定义会包含诊断步骤、命令、检查清单。",[51,322,323,326],{},[17,324,325],{},"没有输出格式或流程","。强 agent 会规定\"输出必须包含这些节，用这个表格格式\"之类的约束。没有就说明它没有对问题空间的深入理解。",[51,328,329,332],{},[17,330,331],{},"check list 是通用的，不是该领域特有的","。code-reviewer 的\"检查大函数\"对所有语言都有意义，但 typescript-reviewer 还会检查特定的 async 陷阱。如果一个 agent 的检查清单完全是通用的，它不该独立。",[51,334,335,338,339,342,343,346],{},[17,336,337],{},"没有诊断命令","。database-reviewer 给出了 ",[34,340,341],{},"pg_stat_statements"," 查询、",[34,344,345],{},"EXPLAIN ANALYZE"," 步骤。如果 agent 定义里全是文字建议没有可执行的步骤，它没有足够的专有约束。",[22,348,349],{"id":349},"调度的代价",[10,351,352],{},"多 agent 不是免费的。每调用一个 agent 意味着：",[48,354,355,358,361],{},[51,356,357],{},"上下文切换",[51,359,360],{},"一次新的 LLM 推理（可能是更贵的模型）",[51,362,363],{},"并行时的并发管理",[10,365,366],{},"所以不值得的 agent 必须合并。比如\"README-updater\"和\"changelog-updater\"如果约束差不多，就应该合并成单一的 doc-updater。实际上 doc-updater 确实存在，而且它的约束包括：输出结构（docs\u002FCODEMAPS\u002F 的特定文件组织）、代码地图格式、验证步骤。这足以支撑独立存在。",[22,368,369],{"id":369},"实际计数",[10,371,372],{},"30 份 agent 的分布：",[48,374,375,381,387,393],{},[51,376,377,380],{},[17,378,379],{},"语言 reviewer\u002Fbuild-resolver 对","：8 对（TypeScript\u002FRust\u002FGo\u002FJava\u002FPython\u002FC++\u002FKotlin\u002FPyTorch），共 16 份",[51,382,383,386],{},[17,384,385],{},"流程 agent","：planner、architect、tdd-guide、code-reviewer、security-reviewer，共 5 份",[51,388,389,392],{},[17,390,391],{},"领域特化","：database-reviewer、healthcare-reviewer、performance-optimizer，共 3 份",[51,394,395,398],{},[17,396,397],{},"功能专用","：doc-updater、e2e-runner、refactor-cleaner、chief-of-staff、loop-operator、harness-optimizer、docs-lookup，共 7 份",[10,400,401],{},"前三类的成立都有明确的约束理由。第四类是工具性的，每个都解决具体问题（文档更新、E2E 测试、死代码清理）。",[10,403,404],{},"反过来想，如果要造第 31 个 agent，需要问：",[48,406,407,410,413,416],{},[51,408,409],{},"它处理的问题空间有专有模式吗？",[51,411,412],{},"能列出 5 条以上该领域特有的规则或检查点吗？",[51,414,415],{},"有具体的诊断或验证步骤吗？",[51,417,418],{},"还是只是\"我是 X 专家\"加通用建议？",[10,420,421],{},"只要有一条答\"不\"，那就应该把这个职责并入现有 agent，而不是新造。",[423,424,425],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":188,"searchDepth":210,"depth":210,"links":427},[428,434,435,436],{"id":24,"depth":210,"text":24,"children":429},[430,432,433],{"id":28,"depth":431,"text":29},3,{"id":99,"depth":431,"text":100},{"id":146,"depth":431,"text":147},{"id":308,"depth":210,"text":308},{"id":349,"depth":210,"text":349},{"id":369,"depth":210,"text":369},"工程手记","2026-04-17","md",null,{},true,"\u002F2026-04-17-30agent",{"title":5,"description":12},"2026-04-17-30个自定义agent什么时候值得单独造","独立 agent 的价值来自它携带的专有约束量，不是职责名称听起来有多不同。",[448,449,450,451],"agent","Claude","系统设计","编排","KvP3OfLg88tMxT95J-2wcPa_uPo7FY7YDnwFt34AsuM",[454,758,902],{"id":455,"title":456,"body":457,"column":437,"date":746,"description":461,"extension":439,"hero_image":440,"meta":747,"navigation":442,"path":748,"seo":749,"series_id":440,"severity":440,"stem":750,"summary":751,"tags":752,"__hash__":757},"posts\u002F2026-07-29-一年八千次提交AI辅助开发工作流.md","一年八千次提交，我是怎么干的",{"type":7,"value":458,"toc":729},[459,462,465,468,472,475,478,481,485,488,491,505,508,512,515,518,521,525,528,531,551,554,558,561,572,575,579,582,596,599,602,606,609,612,616,619,622,625,629,632,635,659,666,669,675,678,681,684,691,694,697,723,726],[10,460,461],{},"2026 上半年，主要项目累计提交 7787 次（其中 newCodex 因为是 fork 项目，1922 次提交含上游历史；纯新增代码的项目是 yun-claude、yun-claw、new-openclaw 等）。提交数看起来多，但每个提交背后的价值不在数量，而在流程的可重复性。这篇文章把工作流写清楚。",[22,463,464],{"id":464},"流程的六个环节",[10,466,467],{},"整个开发周期分六步：需求澄清 → 设计文档审阅 → 拆实施计划 → 分阶段实现 → 代码审查 → 故障归档。每一步都有明确的输入输出和验证方式。",[26,469,471],{"id":470},"_1-需求澄清","1. 需求澄清",[10,473,474],{},"开始前问清楚，不要猜。典型问题：这个功能要处理哪些用户场景？边界条件是什么？现有系统的哪部分会受影响？",[10,476,477],{},"这一步的产出是一份结构化的需求文档，包括功能范围、约束条件、风险假设。不是长篇幅的铺垫，而是一份清单式的澄清记录。",[10,479,480],{},"在 new-openclaw 项目里，这一步通常是用 Claude 的 superpowers:brainstorming skill 来展开。提问方式很重要：我会列出已知条件，然后问\"这个设计下会遇到什么问题？\"而不是\"你觉得应该怎么做？\"让 AI 在我的约束框架内工作。",[26,482,484],{"id":483},"_2-设计文档与人工审阅","2. 设计文档与人工审阅",[10,486,487],{},"这是整个流程里最关键的检查点。花一小时审一份 200 行的设计文档，比花五小时审一份 2000 行的代码便宜得多。而且回头修改设计的成本远低于修改实现。",[10,489,490],{},"new-openclaw 项目现有 30 份设计文档（specs 目录），每份文档都包括：",[48,492,493,496,499,502],{},[51,494,495],{},"范围：这个设计覆盖什么、不覆盖什么",[51,497,498],{},"决策与权衡：为什么选这个方案，放弃了什么",[51,500,501],{},"接口契约：如果涉及多个模块，清晰定义每个边界",[51,503,504],{},"风险清单：已知的坑和防护措施",[10,506,507],{},"写完设计文档以后，我会读一遍、问几个\"为什么\"，然后提出修改意见。这一步排除了 80% 的方向错误。常见的修改方向有：缩小范围（第一个版本不用处理那么多边界情况）、明确约束（系统资源、网络延迟、并发数的假设）、补充防护（熔断、限流、幂等）。",[26,509,511],{"id":510},"_3-实施计划与-dod-定义","3. 实施计划与 DoD 定义",[10,513,514],{},"设计文档定下来以后，拆成实施计划。计划的粒度是\"一个可验证的功能单元\"——通常是一个小时到半天的工作量，完成后能单独验证成功。",[10,516,517],{},"计划文档里必须写清完成定义（DoD，Definition of Done）。不是\"实现登录功能\"，而是\"写出能拒绝无效格式的登录端点、覆盖单点故障下的重试、有端到端的冒烟测试\"。",[10,519,520],{},"new-openclaw 项目现有 36 份实施计划（plans 目录），跨度从一周的 S0 阶段（骨架 + Mock 后台）到数周的 S1 阶段（真实业务后台）。每份计划都带着清晰的 checklist，这样我在执行时能随时问 Claude：\"下一步应该是什么？\"而不是脑子里模糊地记着进度。",[26,522,524],{"id":523},"_4-分阶段实现与逐步验证","4. 分阶段实现与逐步验证",[10,526,527],{},"有了计划以后，按顺序实现。关键是每一步都要有验证：单元测试、集成测试、或者一个小的 end-to-end 冒烟测试。验证不通过就停在这一步，不往下推。",[10,529,530],{},"这一步会用到三个代理：",[48,532,533,539,545],{},[51,534,535,538],{},[17,536,537],{},"superpowers:test-driven-development"," —— 先写测试，再写实现",[51,540,541,544],{},[17,542,543],{},"superpowers:subagent-driven-development"," —— 复杂任务拆成独立的子任务，并行推进",[51,546,547,550],{},[17,548,549],{},"superpowers:systematic-debugging"," —— 遇到测试失败，用这个代理追根溯源，不要盲目修改代码",[10,552,553],{},"实施过程中如果发现设计假设错了（比如某个接口响应时间远超预期，或者并发场景下出现竞态条件），就停下来回到第 2 步重新审视设计，而不是继续往下推。",[26,555,557],{"id":556},"_5-代码审查","5. 代码审查",[10,559,560],{},"实现完成以后，不是立即合并，而是过一遍 code-reviewer 代理。审查的重点不在代码风格（那个自动工具做），而在：",[48,562,563,566,569],{},[51,564,565],{},"这段代码实现的是设计文档里的哪一部分？偏离了吗？",[51,567,568],{},"错误处理有没有遗漏？边界情况有没有考虑？",[51,570,571],{},"有没有意外改动无关的代码？",[10,573,574],{},"审查通常能抓住两类问题。一类是逻辑问题：某个条件判断漏了一个分支，或者并发场景下两个操作的顺序反了。另一类是\"设计和实现对不上\"：实现了一个设计里没提到的特性，或者某个约束（比如\"这个值不能为空\"）没在代码里强制。",[26,576,578],{"id":577},"_6-故障归档","6. 故障归档",[10,580,581],{},"系统上线以后，bug 是难免的。重要的是怎么处理它。new-openclaw 项目有一套 bug 知识库规范（CLAUDE.md 里定义），每个 bug 修好以后都要新建一份独立文档，包括：",[48,583,584,587,590,593],{},[51,585,586],{},"现象和复现路径",[51,588,589],{},"根因分析：为什么会发生，触发链路是什么",[51,591,592],{},"修复方法：改了什么，为什么选这个方案",[51,594,595],{},"预防措施：代码改进、测试添加、还是构建期检查",[10,597,598],{},"80 份 bug 文档（截至 5 月中旬）不是问题的多，而是追根溯源的记录的多。下次遇到类似现象，能直接查库而不是重新排查。",[22,600,601],{"id":601},"三条铁律",[26,603,605],{"id":604},"_1-假设必须显式声明","1. 假设必须显式声明",[10,607,608],{},"不要猜。不清楚的地方就问，把问题写成澄清清单。\"这个 API 能处理多大的请求体？\"、\"离线场景下要缓存多长时间？\"、\"错误重试间隔是指数退避还是固定时间？\"。",[10,610,611],{},"这些问题看起来小，但决定了实现的复杂度和测试用例的多少。猜错了会导致前期设计精美，但方向错误，后面要推倒重来。",[26,613,615],{"id":614},"_2-修改必须精确","2. 修改必须精确",[10,617,618],{},"只改需要改的部分。这听起来像常识，但在实际工作中容易出现\"顺手改一下边上的代码\"的情况 —— 格式不规范了，变量命名不一致了，某个函数太长了，\"顺便\"重构一下。",[10,620,621],{},"结果是一个改动影响了五个文件，代码审查花了双倍时间，引入了新 bug 的风险。",[10,623,624],{},"规则是：改动必须对应需求的某一行。格式、风格、无关的重构，单独立项，不要混在功能改动里。",[26,626,628],{"id":627},"_3-成功标准必须可验证","3. 成功标准必须可验证",[10,630,631],{},"\"添加验证\"这个说法是模糊的。改写成：\"写出一个测试用例，输入非法邮箱格式，验证 API 返回 400；输入合法邮箱，验证返回 200 和预期数据结构\"。",[10,633,634],{},"每个 plan 文档里的 DoD 都是这样写的。拿一个阶段做例子：",[636,637,638,641],"blockquote",{},[10,639,640],{},"S0 阶段的 DoD：",[313,642,643,646,653,656],{},[51,644,645],{},"openapi\u002Fapi-v1.yaml 包含 spec 全部 11 个端点的字段级 schema，可被 swagger-ui 加载 ✓",[51,647,648,649,652],{},"backend\u002F Mock 服务 ",[34,650,651],{},"npm start"," 后能响应全部端点，返回符合契约的 mock 数据 ✓",[51,654,655],{},"launcher\u002F Rust 项目能编译为 launcher.exe，运行后完成所有阶段扫描并输出 diagnostics.json ✓",[51,657,658],{},"至少一个 end-to-end 冒烟测试通过（launcher 上报 → mock 后台收到 → 审计日志记录） ✓",[10,660,661,662,665],{},"每一条都能通过一个具体的命令来验证。\"能工作\"太模糊，\"运行 ",[34,663,664],{},"npm test"," 且所有测试通过\"才是可验证的。",[22,667,668],{"id":668},"流程失效的情况",[10,670,671,672,20],{},"这套流程在一个关键点会失效：",[17,673,674],{},"需求本身没想清楚时",[10,676,677],{},"我遇到过的例子是这样的。客户说\"要支持 USB 存储检测\"，这个需求很清楚，所以设计文档写了 20 多页，规划了三个阶段，列出了 30 多个 test case。然后两周后，客户补充说\"哦对了，还要处理网络驱动器\"。",[10,679,680],{},"这时前面的设计和计划都要回头改。检测逻辑复杂了，测试场景翻倍，阶段划分要调整。这不是流程的问题，这是需求的问题。流程本身反而帮助我及时暴露了这个风险 —— 如果没有设计文档，可能要到代码审查阶段，甚至系统上线以后才发现这个遗漏。",[10,682,683],{},"应对办法是在第 1 步（需求澄清）多花时间。列出你想到的所有场景，问\"还有其他我忽略的情况吗？\"。不是要求完全预测未来，而是把已知的不确定性显式写出来，而不是假设需求是固定的。",[10,685,686,687,690],{},"另一个失效的情况是",[17,688,689],{},"设计和实现的沟通不畅","。如果设计文档是给另一个人读的（或者给 AI 代理读的），但执行者没有理解透彻，实现出来会偏离设计。预防办法是在开始实施前，再过一遍设计文档，确认\"我清楚要做什么\"。",[22,692,693],{"id":693},"提交数字背后的故事",[10,695,696],{},"为什么能积累 7787 次提交？不是因为每次都在写新功能。真实的分布大概是：",[48,698,699,705,711,717],{},[51,700,701,704],{},[17,702,703],{},"功能实现","：40%",[51,706,707,710],{},[17,708,709],{},"设计文档编写和迭代","：25%",[51,712,713,716],{},[17,714,715],{},"测试编写","：20%",[51,718,719,722],{},[17,720,721],{},"bug 修复与回归测试","：15%",[10,724,725],{},"关键是这些提交都有上下文。每个提交的 message 都指向一个设计文档或一个 plan 的某个环节，或者一个 bug 记录。下次有问题要追查根因时，能快速定位到那个提交，看当时的设计决策是什么。",[10,727,728],{},"另外，有 80 份 bug 记录和 66 份设计 + 计划文档（30 specs + 36 plans）这件事本身说明了一点：文档不是负担，文档是工作的实际产出。代码只是文档的一个落地形式。",{"title":188,"searchDepth":210,"depth":210,"links":730},[731,739,744,745],{"id":464,"depth":210,"text":464,"children":732},[733,734,735,736,737,738],{"id":470,"depth":431,"text":471},{"id":483,"depth":431,"text":484},{"id":510,"depth":431,"text":511},{"id":523,"depth":431,"text":524},{"id":556,"depth":431,"text":557},{"id":577,"depth":431,"text":578},{"id":601,"depth":210,"text":601,"children":740},[741,742,743],{"id":604,"depth":431,"text":605},{"id":614,"depth":431,"text":615},{"id":627,"depth":431,"text":628},{"id":668,"depth":210,"text":668},{"id":693,"depth":210,"text":693},"2026-07-29",{},"\u002F2026-07-29-ai",{"title":456,"description":461},"2026-07-29-一年八千次提交AI辅助开发工作流","从需求澄清到故障归档，一套系统化的 AI 辅助开发流程：先设计文档过审，再分阶段实施验证，最后代码审查与故障存档，三条铁律支撑整个流程。",[449,753,754,755,756],"工作流","AI 辅助开发","代码审查","文档驱动","DN7sqbGmg2UybSvRXTMLW-dj0UURXET3KsSNeW_1V9I",{"id":759,"title":760,"body":761,"column":437,"date":889,"description":765,"extension":439,"hero_image":440,"meta":890,"navigation":442,"path":891,"seo":892,"series_id":440,"severity":440,"stem":893,"summary":894,"tags":895,"__hash__":901},"posts\u002F2026-07-20-远程桌面管理器DPAPI与60万次迭代.md","密码，不该由我保管",{"type":7,"value":762,"toc":883},[763,766,770,773,776,779,793,796,800,803,806,809,812,815,826,829,832,835,841,847,853,863,877,880],[10,764,765],{},"远程服务器资料管理工具需要妥善保存账户凭据。这个工具采用两层加密设计：本机存储用 Windows DPAPI，备份使用基于密码的密钥派生。两层各有边界，理解这些边界对使用决策至关重要。",[22,767,769],{"id":768},"第一层dpapi-的便利与代价","第一层：DPAPI 的便利与代价",[10,771,772],{},"本机存储的凭据使用 Windows DPAPI（Data Protection API）加密，由当前 Windows 用户身份保护。这是 Windows 内置的用户级加密机制，密钥由操作系统管理，与登录用户的 SID 和机器的本地安全数据库绑定。不需要用户记一个额外的主密码，启动应用即可直接使用保存的凭据。",[10,774,775],{},"DPAPI 的好处显而易见：启动应用即用，无需输入密码，用户体验最优。代价是它的加密密钥锁定在当前用户、当前机器。凭据无法直接迁移到另一台电脑或另一个 Windows 用户——这不是工具的限制，而是 DPAPI 的设计约束。Windows 操作系统就是这样设计的，其他工具也无法绕过。",[10,777,778],{},"实际操作中的含义很明确：",[48,780,781,784,787,790],{},[51,782,783],{},"重装 Windows 前必须先导出备份。原有凭据会因为用户 SID 变化和机器密钥更新而无法解密，即便登录同一账户也不行。",[51,785,786],{},"更换电脑前需要先创建备份并妥善保存备份密码，目标电脑上导入时需要重新输入这个备份密码。",[51,788,789],{},"在同一电脑上切换 Windows 用户登录，旧用户的凭据对新用户完全不可见，因为加密密钥是用户级的。",[51,791,792],{},"多用户共享一台电脑的场景下，凭据不会跨用户暴露。",[10,794,795],{},"这些限制会在实际使用中暴露出来——比如忙于工作时重装系统忽略了导出，或者在公用工作电脑上多个人使用。正因为如此，工具强制要求提供备份机制。备份不是可选功能，而是必需的。",[22,797,799],{"id":798},"第二层备份加密的固定迭代设计","第二层：备份加密的固定迭代设计",[10,801,802],{},"备份采用 PBKDF2-SHA256（基于密码的密钥派生函数 2，使用 SHA-256 哈希）加密，固定执行 600,000 次迭代。用户创建备份时设置一个密码，导入时输入这个密码。PBKDF2 通过重复应用哈希函数来增加破解难度，迭代次数越多，从密码派生密钥所需的计算时间越长，攻击者进行暴力破解也需要投入成倍的计算资源。",[10,804,805],{},"600,000 次迭代的来源是什么？这是一个有意识的设计决定，而不是随意选择。迭代次数越多，暴力破解的成本越高，但加密和解密的耗时也越长。设计者需要在两者之间找到平衡点：够强以抵御现代硬件的破解能力，又不能强到让普通用户的导入操作变得难以忍受。600,000 次这个数字反映的是这个平衡的结果。",[10,807,808],{},"为什么固定而非可配置？这是一个纪律问题。可配置听起来更灵活、更给用户掌控权，但在实践中会诱使用户为了更快的备份导入速度而降低迭代次数，从而削弱安全强度。安全不应该由便利让步。人们往往倾向于选择快速方案，尤其当他们没有安全专业知识时。固定的迭代次数消除了这种选择权，保证了所有备份都有相同的防护等级。工具的职责是做出最合理的决定，而不是把这个决定推给用户。",[22,810,811],{"id":811},"性能实测与数据解读",[10,813,814],{},"在 Windows 11 构建机上，对 1 MiB 大小的备份数据进行加密与解密的完整往返，预热缓存后的连续五次耗时分别为：108.919 ms、104.079 ms、100.418 ms、94.933 ms、103.470 ms。中位数为 103.470 ms。这意味着从你按下\"导入备份\"到凭据被解密并加载到内存，大约需要 100 毫秒的等待时间。",[10,816,817,818,821,822,825],{},"这组数据收集的目的是",[17,819,820],{},"记录性能表现","。它提供了一个具体的参考：用户在 Windows 11 系统上可以预期导入备份的延迟大约是这个量级。这对评估工具的可用性很有用。但它明确",[17,823,824],{},"不","作为调整迭代次数的依据。这种表述听起来有些冗余，但它是设计纪律的一部分——必须写下来的目的是防止后续有人看到\"100 毫秒确实有点慢\"就建议降低迭代次数。防止的是这样的推理：因为性能数据显示延迟不够快，所以降低迭代次数。这个逻辑链条在安全工程中是禁止的。",[10,827,828],{},"相反，如果实践证明 100 毫秒对用户体验构成问题，正确的做法是要么接受这个成本作为安全性的代价，要么在未来硬件更新换代后自然加速。绝不是削弱密钥派生强度。性能和安全的权衡应该在上层的需求决策中做，而不是在密码学参数中做。",[22,830,831],{"id":831},"工具的明确边界",[10,833,834],{},"这个工具的安全设计有明确的保护范围和限制：",[10,836,837,840],{},[17,838,839],{},"DPAPI 层的限制","：本机凭据的安全性最终依赖于 Windows 用户密码。如果 Windows 账户被破解，攻击者用该账户登录电脑，DPAPI 解密会自动进行。如果用户以管理员身份运行工具（虽然不需要管理员权限），攻击者获得管理员权限后理论上也可能绕过某些保护。安全链的强度由最弱的一环决定——如果你的 Windows 用户密码很弱，或者电脑物理上被他人访问，DPAPI 的保护就名存实亡。",[10,842,843,846],{},[17,844,845],{},"备份密码的强度","：导入备份时用户设置的密码决定了备份的抗暴力破解能力。PBKDF2 提供的防护再强，也无法弥补一个简单密码的缺陷。\"123456\"这样的备份密码，在 600,000 次迭代和现代 GPU 的破解能力面前，可能在几秒到几分钟内被破解。",[10,848,849,852],{},[17,850,851],{},"系统策略的约束","：工具运行在 Windows 系统上，不会绕过任何系统级的安全机制。Windows SmartScreen 对未签名程序的警告、远程桌面连接的安全确认对话、Group Policy 的限制——这些工具都无法绕过。安装包为未签名的内部制品，Windows SmartScreen 会在首次运行时显示\"未知发布者\"警告。这不是工具的缺陷，而是系统安全策略的正常行为。",[10,854,855,858,859,862],{},[17,856,857],{},"加密设计的范围","：这套两层加密设计防的是",[17,860,861],{},"离线攻击","——攻击者获得了备份文件或本机的加密数据，在没有用户交互的情况下尝试破解。它防不了的情况：",[48,864,865,868,871,874],{},[51,866,867],{},"备份密码通过社工或偷看被直接获取",[51,869,870],{},"备份文件在网络传输过程中被中间人截获（如果使用不安全的传输方式）",[51,872,873],{},"凭据被恶意软件在内存中窃取（工具启动后、密码解密到内存这段时间内）",[51,875,876],{},"Windows 账户本身被已经登录电脑的恶意软件控制",[10,878,879],{},"对这些风险的防护需要用户在安全习惯和网络安全措施上自行补足——设置强密码、在信任的网络上操作、定期更新系统补丁、使用反恶意软件工具。",[10,881,882],{},"使用这套工具前要明确：本机凭据带来了便利，代价是将安全依赖在 Windows 用户身份上；备份凭据提供了迁移能力，代价是密码强度必须由用户自己把关。都不是\"一次设置永久安全\"的方案，都需要持续的安全意识和维护。",{"title":188,"searchDepth":210,"depth":210,"links":884},[885,886,887,888],{"id":768,"depth":210,"text":769},{"id":798,"depth":210,"text":799},{"id":811,"depth":210,"text":811},{"id":831,"depth":210,"text":831},"2026-07-20",{},"\u002F2026-07-20-dpapi60",{"title":760,"description":765},"2026-07-20-远程桌面管理器DPAPI与60万次迭代","两层加密保护远程桌面凭据，本机用 DPAPI 便利性换易用性，备份用 PBKDF2 固定 60 万迭代；为什么迭代次数不可配置，性能数据如何解读。",[896,897,898,899,900],"Windows","DPAPI","PBKDF2","凭据存储","加密设计","TzKUoZoFLtAsO_VcB1TOOdWOpwcoDj6AUwEdS6d7pv0",{"id":903,"title":904,"body":905,"column":437,"date":1023,"description":909,"extension":439,"hero_image":440,"meta":1024,"navigation":442,"path":1025,"seo":1026,"series_id":440,"severity":440,"stem":1027,"summary":1028,"tags":1029,"__hash__":1035},"posts\u002F2026-07-17-把设计文档变成可讲的演示.md","转不成演示的设计文档，本来就没讲清",{"type":7,"value":906,"toc":1017},[907,910,916,919,922,925,928,946,953,957,960,966,976,984,987,990,993,996,999,1002,1005,1008,1011,1014],[10,908,909],{},"一年积累了 200 多份设计文档，最初想法是直接拿这些文档去讲。结果发现这些东西不适合讲。设计文档是给人写的，演示文稿是给人听的，形式完全不同。",[10,911,912,913,20],{},"解决这个问题的思路不是\"写个通用 PPT 编辑器\"，而是\"从结构化文档一键生成演示\"。本质差异在这里——编辑器要处理用户的任意编辑行为，演示工具只要转换",[17,914,915],{},"已有的结构",[22,917,918],{"id":918},"为什么选单向转换而不是编辑器",[10,920,921],{},"设计文档有稳定的模板：背景、方案、架构、取舍、参考。这个顺序不是随意的，恰好就是讲一个设计时的叙述顺序。",[10,923,924],{},"用户不需要\"先生成再改\"，需要的是\"文档秒变幻灯片\"。一旦你改，就回到编辑器的坑里去了——要支持拖拽、删除、排版，工作量爆炸，而且多数用户不会调，生成好的东西就是定版。",[10,926,927],{},"这个判断来自实际数据。我的工具做两个决策：",[313,929,930,940],{},[51,931,932,935,936,939],{},[17,933,934],{},"产物是自包含 HTML","，不是 Office 文件（",[34,937,938],{},".pptx"," 需要可编辑格式，门槛高；HTML 在浏览器里就能放映，自包含意味着内联了所有 CSS、JS、图片）。",[51,941,942,945],{},[17,943,944],{},"内容由 LLM 生成，不开放编辑面板","（用一个\"预览挑选\"的两阶段流程，让用户在 3 种风格的封面里选一个，然后生成整份）。",[10,947,948,949,952],{},"这两个约束听起来很严格，实际上契合了需求的本质：",[17,950,951],{},"文档的目的是记录决策，演示的目的是讲述决策","。不需要演示过程中再改决策，改了就回到文档去改。",[22,954,956],{"id":955},"什么样的结构能转什么样的不能","什么样的结构能转、什么样的不能",[10,958,959],{},"设计文档要是能一键转成演示，必须满足几个条件。",[10,961,962,965],{},[17,963,964],{},"架构图可以直接映射","。我的演示工具支持 AI 生图，也支持用户指定图片 URL。当文档里写了架构设计时，LLM 看到这个描述会生成对应的图，然后内联到演示文稿里。舞台是固定的 1920×1080，所有图片容器有最小尺寸约束（GSAP 时间轴动画要能在任意时间点求值，不能靠动态布局）。",[10,967,968,971,972,975],{},[17,969,970],{},"关键数据用表格记录最不容易犯错","。表格这种结构容易转化——",[192,973,974],{},"待补：具体表格如何转成演示页的实现规则","。如果文档里的数据以段落文字形式写，转成演示时就卡住了，LLM 要从自然语言反推结构。",[10,977,978,156,981],{},[17,979,980],{},"取舍（trade-off）部分",[192,982,983],{},"待补：设计文档中的取舍说明如何映射成演示内容的规则",[10,985,986],{},"一个更深的观察：文档转不出来好演示，通常不是演示工具的问题，是文档本身没讲清楚。",[10,988,989],{},"我见过一类项目文档，有 20 多份模板文件，但只要换个配色其他全一样。这说明什么？说明那些\"模板\"实际上没有结构差异，只有视觉差异。结构不清，自然转不出演示。或者反过来说，如果要证明自己的文档结构是清晰的，试试能不能一键转成演示——转不出来就是信号，说明需要先梳理文档本身。",[22,991,992],{"id":992},"工具的硬约束",[10,994,995],{},"演示不同于其他产物，有独特的硬约束。我的工具选择了 1920×1080 的固定舞台，所有动画用 GSAP 时间轴。这些看起来像限制，实际上是为了保证可靠性。",[10,997,998],{},"固定舞台尺寸意味着设计者写风格预设时要算好留白和排版，不能寄希望于\"容器自适应就行了\"。GSAP 时间轴的特点是能在任意时间点求值（渲染管线会 seek 到任意帧截图），不能用 CSS animation 这种依赖真实时间流逝的东西。这限制了动效，但换来的是确定性——动效一定会在预期时间点发生，不会因为网络卡而错位。",[10,1000,1001],{},"生成流程分两个阶段，有个细节很实用。第一阶段只生成 3 个风格的封面单页，第二阶段才生成整份演示文稿。这个设计看似多一步，实际上优雅地解决了两个问题。一是给用户选择风格的机会（不是非此即彼的\"生成或不生成\"）；二是掩盖异步生图的等待时间（生图 1-2 分钟，正好被用户在选择封面时吸收了）。",[22,1003,1004],{"id":1004},"从文档能否转演示看结构清晰度",[10,1006,1007],{},"最后回到起点。为什么要做这个工具？",[10,1009,1010],{},"表面原因是 200 多份文档用演讲方式讲会更有力。深层原因是这个过程本身就是对文档质量的检验。",[10,1012,1013],{},"一份设计文档如果结构清晰——背景交代得清，方案对比得充分，架构图画得明确，取舍理由说得透彻——那转成演示就是平移内容，不费劲。转不出来、或者转出来很别扭，就是信号，说明某个环节讲得不够好。",[10,1015,1016],{},"这个反馈机制比任何 review 注释都直白。\"这个表述为什么转不成幻灯片？\"往往能逼出真实的问题——\"哦，因为我其实还没想清楚这个方案为什么比另一个好\"。",{"title":188,"searchDepth":210,"depth":210,"links":1018},[1019,1020,1021,1022],{"id":918,"depth":210,"text":918},{"id":955,"depth":210,"text":956},{"id":992,"depth":210,"text":992},{"id":1004,"depth":210,"text":1004},"2026-07-17",{},"\u002F2026-07-17",{"title":904,"description":909},"2026-07-17-把设计文档变成可讲的演示","不做通用PPT编辑器，只做文档→演示的单向转换；关键在识别文档的稳定结构。",[1030,1031,1032,1033,1034],"演示文稿","文档结构","设计工具","HTML","GSAP","Reh87_TKXZ8nl5BhDhHzVOONAzwRS-Im8LTYrIfeuNk",1785406912236]