
LLM 作为共同设计者:把 AI 从「答题机」变成设计评审伙伴
LLM 作为共同设计者:把 AI 从「答题机」变成设计评审伙伴
多数人把大模型当成答题机:抛出一个问题,等一段代码。本文换一种用法——把 Spec 和设计草案交给模型,让它找矛盾、提替代方案、比较取舍、设计验证方式,再由人做最终判断。文章给出从准备工作到完整走一遍的步骤,并说明为什么该看 Plan 与判断依据而不是内部思考,以及如何把对话结论沉淀回 Spec。
很多人用大模型的方式是「提问—拿答案」:描述需求,等它吐出一段代码,复制粘贴,收工。这种用法在需求明确、任务独立时没问题,但一旦涉及架构取舍、兼容性约束、跨模块影响,模型给出的第一版方案往往看起来合理、实际经不起推敲。本文讲的另一种用法:把大模型当作共同设计者——你给它 Spec 和设计草案,让它去找矛盾与遗漏、提出替代方案、比较取舍、设计验证方式,最后由人来判断采纳什么。适合已经能用大模型写代码、但发现「它写的东西我不敢直接合进主干」的开发者,也适合需要把设计讨论过程留痕的团队。
准备工作
这套方法对工具的要求不高,但对输入材料的要求很高。开始之前,先确认下面几件事。
一个能读写代码库的 Agent 环境
纯聊天窗口也能做设计评审,但价值会打折。真正有用的是能读代码、能跑命令、能看测试结果的环境——因为设计争论最终要落到「这个改动会不会破坏现有调用方」这类可以被验证的问题上。具备文件读写与命令执行能力的编码 Agent 都可以,具体支持哪些能力、有哪些配额限制,以该产品官网当前信息为准。
一份可以反复修改的 Spec
Spec 不需要一开始就完美。它的作用是承载讨论结论:每轮对话里定下来的前提、约束、Done 条件,都要写回去。没有这份文档,换一个会话或换一个 Agent,之前谈好的前提就全丢了。建议至少包含:目标、约束、不做什么、验收条件。
可运行的测试与可检索的代码库
共同设计不是嘴上说服对方。要让「这个改动可能破坏既有 API」这种担忧变成可验证的问题,你需要能搜索调用方、能跑既有测试、能对比实际响应。如果项目里连一个能跑的测试都没有,先补最小可运行的测试,再开始这套流程。
明确谁做最终决定
开始之前就要说清楚:模型负责出选项、找反例、收集证据;人负责定目标、选取舍、承担风险、拍板。这条边界不清晰,流程会退化成「让模型替我决定」。
操作步骤
第一步:先写一份暂定 Spec,不要追求一次到位
设计不需要一次提示就完成。先写一版粗糙但具体的草案,把它当作假设而不是结论。一个够用的起点长这样:
目标:为订单服务增加批量取消接口
约束:
- 不修改现有单笔取消接口的签名
- 数据库表结构本轮不改
- 需要兼容仍在调用旧接口的客户端
不做什么:
- 不做异步队列化
验收条件:
- 既有测试全部通过
- 新增批量取消的契约测试
- 重复取消同一订单返回幂等结果注意「不做什么」这一栏。它比目标更能约束讨论范围,也更容易暴露模型自作主张扩展需求的问题。
第二步:让模型找矛盾与遗漏,而不是让它重写
把 Spec 交给模型时,指令要指向审查而不是生成。可以这样组织:
下面是一份接口设计草案。请不要重写它,而是:
1. 指出内部矛盾之处
2. 指出缺失的前置条件或边界情况
3. 列出你认为最危险的三个假设
4. 对每一条,说明需要什么证据才能确认
草案:
(粘贴 Spec)要求它给出「需要什么证据」,是把讨论往可验证方向推的关键。没有这一条,模型容易停在泛泛的风险提示上。
第三步:要求多个方案并比较取舍
单一方案无法比较。让它至少给出两条路线,并明确各自的适用条件:
针对上面的约束,给出两种实现路线。
对每种路线说明:
- 它成立的前提是什么
- 它在什么条件下会失效
- 它给后续演进留下什么负担
- 需要哪些测试才能确认它可行
最后说明:在什么条件下你会推荐另一种。最后那句「在什么条件下你会推荐另一种」很有用——它逼模型把推荐从立场变成条件判断,而不是给一个模糊的「视情况而定」。
第四步:从反方立场拆自己的设计
设计定下来之后,专门做一轮反向审查。不要问「这个设计好不好」,要问具体的破坏性问题:
- 这个设计在什么条件下会崩掉?
- 最危险的假设是哪一个?
- 什么条件下另一个方案反而更优?
- 安全与运维层面有哪些薄弱点?
这一步的产出是待验证清单,不是结论。模型指出的问题不能直接当成事实——重要的指摘必须回到代码、文档、测试或基准数据去确认。把每条指摘标上「已证实 / 已排除 / 待验证」,比直接采纳或直接忽略都可靠。
第五步:把设计争论转成可执行的验证
这是整套方法里最有价值的一步。当出现「这个改动可能破坏既有 API」这类担忧时,不要停在讨论,继续往下走:
- 搜索受影响的调用方,列出清单
- 运行既有测试,记录结果
- 为关键契约补一个测试
- 对比改动前后的实际响应
对应的指令可以写成:
有人担心这个改动会破坏既有调用方。
请:
1. 在代码库中搜索所有调用该接口的位置,列出文件与行号
2. 运行相关测试并贴出输出
3. 如果现有测试没有覆盖这个契约,写出一个最小契约测试
4. 对比改动前后的响应差异
不要修改实现代码,只做调查与验证。「不要修改实现代码」这句限定很重要。调查阶段一旦允许它顺手改代码,你就分不清哪些结论来自证据、哪些来自它自己刚写的实现。
第六步:把结论写回 Spec
对话里定下来的判断,必须落到文档里,否则下一个会话、下一个 Agent 会重新踩一遍。典型需要回写的条目:
| 对话中的结论 | 回写位置 |
|---|---|
| 必须保持兼容性 | Spec 的约束栏 |
| 这张表本轮不改 | Spec 的不做什么栏 |
| 这个测试作为 Done 条件 | Spec 的验收条件栏 |
| 某条指摘已被测试排除 | 设计文档的决策记录 |
| 某条指摘待验证 | 待办清单,附验证方式 |
这一步做完,对话才从一次性交流变成可复用的设计资产。
一个完整示例
下面走一遍最小可用的完整流程。场景:给一个已有服务增加批量取消接口。
1. 初始 Spec
目标:新增 POST /orders/batch-cancel
约束:不改单笔接口签名;不改表结构;兼容旧客户端
不做什么:不做异步队列
验收:既有测试通过;新增契约测试;重复取消幂等2. 第一轮:找矛盾与遗漏
把上面的 Spec 交给模型,要求它只做审查。典型产出可能包括:
- 「幂等」在批量场景下没有定义:是整批幂等还是逐条幂等?
- 没有说明单批最大条数,缺少上限约束
- 没有说明部分失败时的返回结构
- 「兼容旧客户端」与「新增接口」之间没有冲突,但缺少对旧接口行为的回归验证计划
这些指摘里,前三条是 Spec 的缺口,第四条是可执行的验证任务。
3. 第二轮:两个方案与取舍
要求给出两条路线。例如:
- 路线 A:逐条调用现有单笔取消逻辑,外层包一层循环与结果汇总
- 路线 B:新增一条批量更新语句,一次性处理
让模型说明各自的前提与失效条件。路线 A 的前提是单笔逻辑已经处理好幂等与并发;路线 B 的前提是表结构与事务边界允许批量更新。哪条更合适取决于现有实现,这需要回到代码里确认,而不是听模型判断。
4. 第三轮:反向审查
针对选定的路线,问「什么条件下它会崩」。可能的产出:并发取消同一批订单时的竞态、部分成功后的重试语义、批量上限缺失导致的超时。每条都标上待验证。
5. 第四轮:验证
让 Agent 执行调查:搜索单笔取消接口的调用方、运行既有测试、补一个契约测试、对比响应。把终端输出与测试结果作为证据保留下来。这一步的产出不是「模型说没问题」,而是「测试通过 / 测试失败 / 未覆盖」这样可核对的状态。
6. 回写 Spec
把定下来的内容补进 Spec:批量上限、部分失败的返回结构、幂等粒度、Done 条件里加上新契约测试。至此一轮闭环完成,可以进入实现阶段。
注意事项
不要以内部思考过程作为评审对象
一个常见的错误做法是:要求推理模型详细输出内部思考过程,然后去审查那段文字。对推理模型而言,原始的推理 token 通常不向使用者公开;而且在给推理模型下指令时,也不需要指定「一步一步思考」这类内部推理细节,更有效的做法是把目标、约束、Done 条件写清楚。人应该审查的是外部化的设计信息:
- Plan——按什么顺序推进
- 判断理由——为什么采用这个方案
- 前提——把什么当作事实
- 依据——参考了哪些代码或资料
- 验证结果——测试或运行确认了什么
这跟阅读思维链是两回事。重点是让判断可被审查,而不是让思考过程可见。
模型指出的问题不等于事实
反向审查很容易产出听起来严重、实际不成立的问题。重要的指摘必须用代码、文档、测试或基准数据确认。把「已证实 / 已排除 / 待验证」三态标清楚,比笼统记一条「有风险」有用得多。
不要让对话结论只留在聊天里
会话是有生命周期的。定下来的前提如果不写回 Spec 或设计文档,换一个会话、换一个 Agent 就会丢失,然后同样的讨论要重来一遍。把对话当作把隐性知识转成显性设计资产的过程。
责任边界不会因为协作而消失
模型擅长的是:短时间内给出大量选项、找矛盾与边界情况、跨代码库调查、反复执行验证。人负责的是:定目标、选择接受哪些取舍、承担哪些风险、最终采用什么。Agent 自主性再高,这个最终负责人的角色依然存在。共同设计不是把设计判断整体外包出去。
易变信息以官网为准
模型能力、上下文长度、配额、可用地区、价格等都会变化,本文不列具体数值。使用前请以所用产品官网的当前信息为准。