Skip to content

AI 辅助编程的正确姿势

协作模型的常见误解

流行的说法把 AI 辅助编程描述为"AI 写代码,人来审查"。这个模型本身就是错的——它把人机协作当成流水线上的两道工序,一个负责生产,一个负责质检。

实际上高效协作是分层控制。人定义结构和约束,AI 在约束空间内填充实现。人负责"做什么"和"为什么",AI 负责"怎么做"。简单说,人做决策,AI 做执行。

这个分层模型和第四章讨论的命令式到声明式跃迁是同一个结构——开发者声明意图和约束,由 AI 生成符合约束的实现。

三种协作模式及其适用边界

实践中有三种协作模式,适用场景和信任边界截然不同。

补全模式。 AI 在已有代码上下文中预测接下来的若干行。这是信任度最高的模式,因为约束空间最小——函数签名、类型注解、上下文变量已经大幅收窄了可能的输出。补全模式让想法更快变成代码。当函数签名和类型已经声明完毕,补全出函数体是一个高度约束的任务,这种场景下 AI 的准确率远高于开放式生成。

生成模式。 AI 根据自然语言描述从零生成代码。这是信任度最低的模式,因为约束空间最大——一段自然语言描述可以对应无数种实现。生成模式应该这么用:"描述需求、审查输出、提供约束、再次生成"的迭代循环。关键是逐步加约束,引导 AI 收敛到正确的实现。

重构模式。 AI 在保持行为不变的前提下改善代码结构。这是风险最可控的模式,因为"行为不变"提供了一个可验证的硬约束——重构前后跑同一组测试,通过就接受,不通过就拒绝。重构模式解决的是开发者最不愿意手动做的事情:把一段能跑但结构乱的代码整理干净。

三种模式的共同规律是:约束越强,AI 输出的可信度越高。 道理很简单——约束缩小了输出空间,输出空间越小,错误输出的占比越低。

规格先行的工作流

理解了约束与可信度的关系,正确的工作流就呼之欲出:先写规格,再让 AI 填充实现。

"规格先行"落到 AI 辅助编程中,具体怎么做:

python
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 写的,应该没问题"而降低标准。知道该审查和真正去审查之间,隔着"图省事"三个字。