一、AI 助手真正改变的,是「输入端」

用了一年多 Copilot、Cursor、Claude Code 之后,我最大的体会是:AI 并没有让我"写代码的手速"变快多少,它改变的是我把需求表达出来的方式。以前我们是在脑子里把需求翻译成语法,现在是把需求讲清楚,让 AI 先出一版草稿,我负责判断和修正。

这带来的直接收益是启动成本下降。最难的往往不是写中间那 100 行,而是面对空文件的那 5 分钟。AI 能瞬间给出一个"能跑起来的骨架",哪怕结构不完美,我也有了可以删改的对象。对人来说,修改永远比创作容易,这是我认为 AI 提效最底层的原理。

但也别神化它。AI 生成的代码在"局部正确、整体平庸"这件事上非常稳定——它会给你一份标准答案,而工程里真正值钱的往往是那些非标准的取舍。所以我的定位一直是:AI 是高级实习生,我是负责人。

二、上下文工程:把需求讲清楚,比会写提示词更重要

很多人用 AI 效果不好,问题不在模型,而在喂给它的上下文太少或太杂。模型看不到你的项目结构、你的团队规范、你上周踩过的坑,只能靠"通用最佳实践"脑补。所以现在我花时间最多的不是写 prompt,而是整理 context。

我的几个习惯:

提示:在 Cursor 这类工具里,用 @file / @codebase 精确圈定上下文,比在对话里粘贴几十行代码效果更好——既省 token,也减少模型被无关信息干扰的概率。

三、真正省时间的四个场景

用下来,我认为收益最高、风险最低的场景是这几个:

  1. 样板代码与胶水层:dataclass、序列化、schema 校验、CLI 参数解析、配置文件读取。这类代码规则明确、几乎没有创造性,AI 又快又准。
  2. 单元测试的初稿:让我给一个 200 行的函数手写 15 个测试用例我会拖延,让 AI 先生成我再删改,半小时的活五分钟解决。测试是 AI 最被低估的用武之地。
  3. 读代码与解释报错:把一段看不懂的正则、一个冗长的堆栈丢给它,让它逐行解释。这属于"白嫖"它的知识面,几乎零风险。
  4. 跨语言/跨库的翻译:我知道怎么用 pandas 做,但要换成 polars 语法就卡壳,让 AI 直接翻译过来再微调,比查文档快得多。

反过来,核心业务规则、并发与锁、数据库迁移、安全相关代码,我基本不直接采纳 AI 的第一版。这些地方一个隐蔽 bug 的代价,远超它省下的十分钟。

四、代码示例:从一句模糊需求到可用实现

下面是我实际的一次交互记录。我只说了一句"给请求加个重试",第一版 AI 给的是全网都有的 time.sleep 循环,我追问"要支持指数退避、只对可重试异常生效、要能注入 sleep 方便测试",才拿到能用的版本:

import random
import time
from functools import wraps


class RetryableError(Exception):
    """业务上可重试的异常,例如网络超时、5xx。"""


def retry(max_attempts=3, base_delay=0.5, exceptions=(RetryableError,), sleeper=time.sleep):
    """指数退避重试装饰器。sleeper 可注入,方便测试时替换。"""

    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(1, max_attempts + 1):
                try:
                    return func(*args, **kwargs)
                except exceptions as exc:
                    if attempt == max_attempts:
                        raise
                    delay = base_delay * (2 ** (attempt - 1))
                    # 加一点抖动,避免多个客户端同时重试造成惊群
                    sleeper(delay + random.uniform(0, delay * 0.1))

        return wrapper

    return decorator

第二轮我让它补测试,它生成的用例里漏掉了"最后一次失败要抛出原异常"这一条,我手动补上。这说明AI 写测试也有盲区,尤其是边界和失败路径,还是得自己过一遍。

import pytest


def test_retry_succeeds_after_transient_failures():
    calls = {"n": 0}

    @retry(max_attempts=3, base_delay=0.1, sleeper=lambda _: None)
    def flaky():
        calls["n"] += 1
        if calls["n"] < 3:
            raise RetryableError("boom")
        return "ok"

    assert flaky() == "ok"
    assert calls["n"] == 3


def test_retry_raises_original_error_after_exhausting_attempts():
    @retry(max_attempts=2, base_delay=0.1, sleeper=lambda _: None)
    def always_fail():
        raise RetryableError("permanent")

    with pytest.raises(RetryableError, match="permanent"):
        always_fail()

五、验收与兜底:AI 代码的审查清单

AI 产出的代码,我默认它是"待评审的外部 PR"。过一遍下面这个清单,能挡掉大部分坑:

提示:养成"AI 写、我跑测试"的习惯。测试通过不代表逻辑正确,但测试都跑不过,那一定是错的——这是最便宜的一道防线。

六、边界、心法与我的日常工作流

最后聊点"道"层面的东西。AI 助手会放大你的工程习惯:本来就有测试、有规范、有模块化的项目,AI 用起来如虎添翼;本来就是一坨面条代码的项目,AI 只会更快地生产面条。所以提升 AI 效率的最佳投资,往往是先把项目整理成"AI 友好"的形态:清晰的目录、完整的类型标注、可一键运行的测试命令。

我目前的日常流程大概是这样的:

  1. 先用自然语言写清楚需求和验收标准(给自己看的,也是给 AI 看的)。
  2. 让 AI 给出实现草稿,只看结构不看细节,结构不对就推倒重来。
  3. 让 AI 补测试初稿,我手动补失败路径和边界用例。
  4. 跑测试 + linter,把报错丢回给 AI 让它改,通常一两轮收敛。
  5. 自己通读一遍,重点看错误处理、并发和依赖,然后提交。

一句话总结这段时间的心得:AI 把"从 0 到 0.7"的成本压到了接近零,但"从 0.7 到 1"的那部分工程判断,依然是开发者的核心价值。用它省下的时间,我更愿意花在架构设计和读源码上——那才是越用越值钱的部分。