一、AI 助手真正改变的,是「输入端」
用了一年多 Copilot、Cursor、Claude Code 之后,我最大的体会是:AI 并没有让我"写代码的手速"变快多少,它改变的是我把需求表达出来的方式。以前我们是在脑子里把需求翻译成语法,现在是把需求讲清楚,让 AI 先出一版草稿,我负责判断和修正。
这带来的直接收益是启动成本下降。最难的往往不是写中间那 100 行,而是面对空文件的那 5 分钟。AI 能瞬间给出一个"能跑起来的骨架",哪怕结构不完美,我也有了可以删改的对象。对人来说,修改永远比创作容易,这是我认为 AI 提效最底层的原理。
但也别神化它。AI 生成的代码在"局部正确、整体平庸"这件事上非常稳定——它会给你一份标准答案,而工程里真正值钱的往往是那些非标准的取舍。所以我的定位一直是:AI 是高级实习生,我是负责人。
二、上下文工程:把需求讲清楚,比会写提示词更重要
很多人用 AI 效果不好,问题不在模型,而在喂给它的上下文太少或太杂。模型看不到你的项目结构、你的团队规范、你上周踩过的坑,只能靠"通用最佳实践"脑补。所以现在我花时间最多的不是写 prompt,而是整理 context。
我的几个习惯:
- 先给约束,再给需求:技术栈版本、禁止使用的库、错误处理风格、命名约定,这些写在前面,返工率能降一半。
- 给例子而不是给形容词:说"照着
utils/retry.py里那个装饰器的风格写",远好过说"写得优雅一点"。 - 一次只做一件事:让 AI 同时写实现、写测试、写文档、改重构,结果是每样都做 70 分。拆开提问,可控性高得多。
提示:在Cursor这类工具里,用@file/@codebase精确圈定上下文,比在对话里粘贴几十行代码效果更好——既省 token,也减少模型被无关信息干扰的概率。
三、真正省时间的四个场景
用下来,我认为收益最高、风险最低的场景是这几个:
- 样板代码与胶水层:
dataclass、序列化、schema校验、CLI参数解析、配置文件读取。这类代码规则明确、几乎没有创造性,AI 又快又准。 - 单元测试的初稿:让我给一个 200 行的函数手写 15 个测试用例我会拖延,让 AI 先生成我再删改,半小时的活五分钟解决。测试是 AI 最被低估的用武之地。
- 读代码与解释报错:把一段看不懂的正则、一个冗长的堆栈丢给它,让它逐行解释。这属于"白嫖"它的知识面,几乎零风险。
- 跨语言/跨库的翻译:我知道怎么用
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"。过一遍下面这个清单,能挡掉大部分坑:
- 能否通过项目现有的 0 / 1 / 类型检查:很多 AI 代码风格是"标准 Python",不是"你的项目 Python"。
- 依赖是否被偷偷引入:它很喜欢顺手
import一个更好的库,而那个库可能根本没在pyproject.toml里。 - 错误处理是否被吞掉:警惕空的
except Exception: pass,以及被静默降级的失败分支。 - 边界条件:空列表、
None、并发、0和负数、编码问题,AI 经常只写 happy path。 - 是否真的理解了它写的每一行:不理解就让它解释,解释不清的代码一律不合并。
提示:养成"AI 写、我跑测试"的习惯。测试通过不代表逻辑正确,但测试都跑不过,那一定是错的——这是最便宜的一道防线。
六、边界、心法与我的日常工作流
最后聊点"道"层面的东西。AI 助手会放大你的工程习惯:本来就有测试、有规范、有模块化的项目,AI 用起来如虎添翼;本来就是一坨面条代码的项目,AI 只会更快地生产面条。所以提升 AI 效率的最佳投资,往往是先把项目整理成"AI 友好"的形态:清晰的目录、完整的类型标注、可一键运行的测试命令。
我目前的日常流程大概是这样的:
- 先用自然语言写清楚需求和验收标准(给自己看的,也是给 AI 看的)。
- 让 AI 给出实现草稿,只看结构不看细节,结构不对就推倒重来。
- 让 AI 补测试初稿,我手动补失败路径和边界用例。
- 跑测试 +
linter,把报错丢回给 AI 让它改,通常一两轮收敛。 - 自己通读一遍,重点看错误处理、并发和依赖,然后提交。
一句话总结这段时间的心得:AI 把"从 0 到 0.7"的成本压到了接近零,但"从 0.7 到 1"的那部分工程判断,依然是开发者的核心价值。用它省下的时间,我更愿意花在架构设计和读源码上——那才是越用越值钱的部分。