AI 辅助编程的正确姿势
协作模型的常见误解
流行的说法把 AI 辅助编程描述为"AI 写代码,人来审查"。这个模型本身就是错的——它把人机协作当成流水线上的两道工序,一个负责生产,一个负责质检。
实际上高效协作是分层控制。人定义结构和约束,AI 在约束空间内填充实现。人负责"做什么"和"为什么",AI 负责"怎么做"。简单说,人做决策,AI 做执行。
这个分层模型和第四章讨论的命令式到声明式跃迁是同一个结构——开发者声明意图和约束,由 AI 生成符合约束的实现。
三种协作模式及其适用边界
实践中有三种协作模式,适用场景和信任边界截然不同。
补全模式。 AI 在已有代码上下文中预测接下来的若干行。这是信任度最高的模式,因为约束空间最小——函数签名、类型注解、上下文变量已经大幅收窄了可能的输出。补全模式让想法更快变成代码。当函数签名和类型已经声明完毕,补全出函数体是一个高度约束的任务,这种场景下 AI 的准确率远高于开放式生成。
生成模式。 AI 根据自然语言描述从零生成代码。这是信任度最低的模式,因为约束空间最大——一段自然语言描述可以对应无数种实现。生成模式应该这么用:"描述需求、审查输出、提供约束、再次生成"的迭代循环。关键是逐步加约束,引导 AI 收敛到正确的实现。
重构模式。 AI 在保持行为不变的前提下改善代码结构。这是风险最可控的模式,因为"行为不变"提供了一个可验证的硬约束——重构前后跑同一组测试,通过就接受,不通过就拒绝。重构模式解决的是开发者最不愿意手动做的事情:把一段能跑但结构乱的代码整理干净。
三种模式的共同规律是:约束越强,AI 输出的可信度越高。 道理很简单——约束缩小了输出空间,输出空间越小,错误输出的占比越低。
规格先行的工作流
理解了约束与可信度的关系,正确的工作流就呼之欲出:先写规格,再让 AI 填充实现。
"规格先行"落到 AI 辅助编程中,具体怎么做:
from pydantic import BaseModel, Field
class OrderProcessor:
"""处理电商订单的核心服务。
职责边界:
- 验证订单数据完整性
- 计算价格(含折扣和税费)
- 生成订单确认
不负责:
- 支付处理(由 PaymentService 负责)
- 库存扣减(由 InventoryService 负责)
"""
def calculate_total(
self,
items: list["OrderItem"],
discount: "Discount | None",
tax_rate: float,
) -> "OrderTotal":
"""计算订单总价。
规则:
1. 先计算商品小计(单价 * 数量)
2. 应用折扣(如有)
3. 在折扣后金额上计算税费
4. 折扣不得使总价低于 0
"""
... # AI 填充此处
class OrderItem(BaseModel):
product_id: str
unit_price: float = Field(gt=0)
quantity: int = Field(gt=0, le=999)
class Discount(BaseModel):
type: str = Field(pattern="^(percentage|fixed)$")
value: float = Field(gt=0)
class OrderTotal(BaseModel):
subtotal: float = Field(ge=0)
discount_amount: float = Field(ge=0)
tax_amount: float = Field(ge=0)
total: float = Field(ge=0)这段代码在调用 AI 之前已经做了大量的决策:类的职责边界、方法签名、输入输出的类型定义、业务规则的文档化、数值约束的声明。留给 AI 的只是在这些约束内实现计算逻辑——这是一个高度约束的任务,类型系统和业务规则文档把 AI 出错的空间压缩到了最小。
类型注解和文档字符串同时是人和 AI 的上下文。它们就是一种隐式的 prompt——告诉 AI 输出应该满足什么约束。第四章对这种"约束传播"有更详细的讨论。
上下文工程:被低估的关键变量
AI 辅助编程的质量瓶颈在于上下文质量。同一个模型,给它一个没有类型注解、没有文档、命名含糊的代码库,和给它一个类型完整、文档清晰、命名精确的代码库,产出质量天差地别。
上下文工程的核心操作:
类型注解是最高效的上下文。 一个 def process(data: list[dict]) -> dict 给 AI 提供的信息量远低于 def process(orders: list[OrderItem]) -> ProcessingResult。类型注解的信息密度远高于自然语言注释,因为类型注解是形式化的,没有歧义。
项目级指引文件是全局上下文。 CLAUDE.md 这类文件的作用是传递设计哲学和决策偏好。"偏好组合而非继承"这条规则,可以让 AI 在生成代码时自动选择组合模式,而不需要在每次交互中重复说明。
依赖关系是隐式约束。 当 AI 能看到 import 语句和已有的工具函数时,它会复用已有的抽象。让相关文件留在上下文窗口里,是减少 AI 生成冗余代码最管用的办法。
何时怀疑 AI 的输出
该信任 AI 的输出到什么程度,取决于任务本身。下面几条经验规则背后的原理很简单——AI 在模式匹配上强大,在推理上脆弱:
高信任场景: 输出有明确的验证手段(可以编译、可以跑测试);任务是常见模式的实例化(CRUD 操作、标准算法实现);约束空间小(类型签名已定义、接口契约已声明)。
低信任场景: 涉及业务逻辑的边界条件(AI 缺乏业务上下文);涉及并发、安全、分布式一致性等需要全局推理的领域;实现方案的选择需要代码库之外的知识(性能特征、部署环境、组织约束)。
应当立即审查的信号: AI 生成的代码引入了新的依赖;AI 修改了它不应该接触的文件;AI 的实现方案与代码库的既有风格明显不一致。
前提与代价
规格先行的工作流有个前提:开发者得有能力写出好的规格——清晰的类型定义、准确的职责划分、完整的约束声明。这本身就是一项高技能要求。初学者可能连"该定义什么类型"都不知道,这时 AI 辅助编程就只能当探索工具用。
另一个局限是认知惰性。当 AI 输出"看起来对"的代码时,人类审查者倾向于接受而非深究。这是人的认知弱点,光靠技术手段解决不了。唯一的缓解手段是纪律:对 AI 生成的代码执行与人写的代码相同标准的审查流程,不因为"它是 AI 写的,应该没问题"而降低标准。知道该审查和真正去审查之间,隔着"图省事"三个字。