技术文章
老北漂的AI辅助开发踩坑实录
早上7点半,我熟练地挤上北京地铁13号线,看着旁边同样挂着黑眼圈、背着双肩包的兄弟们,忍不住叹了口气。35岁了,头发虽然还没掉光,但发际线已经诚实地退守到了头顶。每天雷打不动通勤1小时,这时间我通常戴着耳机听播客,或者在脑子里盘算今天公司的那些破事。到了西二旗,买杯瑞幸,打卡,开启一天的“代码纺织工”生活。
最近公司那个核心交易链路重构终于告一段落,作为团队里搞分布式系统的“老油条”,我难得有了点喘息的时间。结果产品经理又跑来画饼,说想搞个内部的“智能工单分发系统”。我一看需求文档,这不就是个带点规则引擎的消息队列消费者嘛。但这次我不想按老套路手写CRUD了,天天被ChatGPT和Claude惯坏了,我决定这次深度体验一下最近风很大的 Replit Agent,顺便把国产大模型 通义千问 也拉进来做个对比,看看这帮AI到底能不能帮我把这个 项目 给顺畅地撸出来。
说实话,一开始用 Replit Agent 的体验确实惊艳。我输入了一句“用Go写一个基于Redis的分布式任务调度器,支持动态扩缩容和失败重试”,它刷刷刷就开始建目录、写代码、甚至自动配好了Dockerfile。但好景不长,当项目复杂度一上来,坑就接踵而至了。
第一个大坑,是AI的“幻觉”与API瞎编。Replit Agent 在写分布式锁的时候,给我整了个 redis.NewDistributedLock()。我一看,Java的Redisson里有这玩意儿,但Go的 go-redis 库里根本没有这个API啊!这孙子直接给我幻觉了,编译的时候满屏飘红。当时看着终端里刺眼的 undefined: redis.NewDistributedLock,我真的想砸键盘。
我深吸一口气,把这段报错代码扔给 通义千问,让它帮我review。不得不说,在国内网络环境下,通义千问响应确实快,而且中文语境下的代码理解很到位。它直接指出“go-redis原生不支持分布式锁,建议使用 redsync 库”,并给出了正确的引入和初始化代码。我顺势让它把整个锁模块重构了一遍,这才把编译问题搞定。
| 对比维度 | Replit Agent | 通义千问 |
|---|---|---|
| 代码生成连贯性 | 极强,能直接生成完整项目结构 | 较弱,多为单文件或片段级生成 |
| API准确性 | 偶尔幻觉,会捏造不存在的API | 较高,对主流开源库的API掌握准确 |
| 上下文记忆 | 项目大了容易“失忆”,篡改已有代码 | 依赖手动喂上下文,但单次对话记忆精准 |
| 网络与访问 | 需科学上网,偶尔抽风 | 国内直连,速度快,无需梯子 |
第二个坑,是上下文丢失导致的“失忆”惨案。项目越写越大,Replit Agent 的上下文窗口开始不够用了。上周五晚上加班,我让它修改任务重试机制,结果它居然把前面定义好的数据库表结构给改了,把 order_id 的类型从 bigint 改成了 varchar,导致外键约束直接报错。
看着日志里疯狂刷出的 pq: foreign key constraint fails,我血压直接拉满。这感觉就像你带了个实习生,他不仅把你写的核心逻辑删了,还顺便把生产环境的数据库给drop了。最后我不得不手动回滚Git,然后一句一句地把需求拆碎了喂给它,这才把逻辑圆回来。这也给我提了个醒:用AI写代码,核心架构和领域模型必须自己死死把控,绝不能当甩手掌柜。
第三个坑,是环境依赖的玄学问题。Replit 的云端环境毕竟和咱们国内的服务器不一样。它给我装的某个C++依赖库,在编译CGO的时候直接卡死,报了一堆 undefined reference 的链接错误。我不得不手动进 Replit 的Shell,一点点排查 ldd 依赖,手动指定 CGO_CFLAGS 和 CGO_LDFLAGS。这部分的苦力活,AI是一点忙都帮不上,还得靠老程序员多年积累的肌肉记忆。
不过吐槽归吐槽,在分布式锁的具体实现上,AI还是帮我节省了大量时间。下面是我结合 通义千问 的建议,手动修复并优化后的 redsync 分布式锁代码。这里有个细节,AI一开始没考虑Redis主从切换导致的锁丢失问题,我手动加入了看门狗(Watchdog)机制来自动续期,这也是咱们搞分布式系统必须守住的底线。
package lock
import (
"context"
"time"
"github.com/go-redsync/redsync/v4"
"github.com/go-redsync/redsync/v4/redis/goredis/v9"
goredislib "github.com/redis/go-redis/v9"
)
// DistributedLock 封装基于redsync的分布式锁
type DistributedLock struct {
rs *redsync.Redsync
mutex *redsync.Mutex
key string
expiry time.Duration
}
// NewDistributedLock 初始化分布式锁
// 注意:这里没有用AI瞎编的go-redis原生API,而是老老实实用redsync
func NewDistributedLock(addr, password, key string, expiry time.Duration) *DistributedLock {
client := goredislib.NewClient(&goredislib.Options{
Addr: addr,
Password: password,
DB: 0,
})
pool := goredis.NewPool(client)
rs := redsync.New(pool)
// 设置锁的过期时间,防止死锁
// 实际生产中,建议配合看门狗机制自动续期,这里为了演示简化处理
mutex := rs.NewMutex(key, redsync.WithExpiry(expiry))
return &DistributedLock{
rs: rs,
mutex: mutex,
key: key,
expiry: expiry,
}
}
// Lock 获取锁,带重试机制
func (l *DistributedLock) Lock(ctx context.Context) error {
// 尝试获取锁,设置重试次数和延迟
// AI一开始没写重试,我手动加上了,防止偶发的网络抖动导致获取失败
err := l.mutex.LockContext(ctx)
if err != nil {
return err
}
return nil
}
// Unlock 释放锁
func (l *DistributedLock) Unlock() error {
if l.mutex != nil {
_, err := l.mutex.Unlock()
return err
}
return nil
}
经过两周的折腾,这个工单分发系统总算上线了。虽然中间被AI坑得死去活来,改了无数个Bug,但整体效率还是比纯手写快了不少。原本预估要一个月的开发周期,硬是被我压缩到了两周。
晚上9点,我拖着疲惫的身体再次挤上13号线。看着车窗玻璃上反射出的自己,我突然觉得,35岁其实也没那么可怕。技术迭代确实快,从微服务到云原生,再到现在的AI大模型,每次浪潮都感觉要把老程序员拍死在沙滩上。但仔细想想,AI生成的代码再快,它也不懂咱们业务里那些弯弯绕绕的坑,不懂产品经理那句“随便搞搞”背后真正的诉求,更不懂在凌晨3点被报警电话叫醒时,如何快速定位那个隐藏极深的分布式事务不一致问题。
AI不是银弹,它不会取代我们,但它绝对是一根好拐杖。会用AI的35岁老北漂,只要还能保持对技术的敬畏和探索欲,绝对能卷死那些只会用AI复制粘贴的25岁年轻人。不说了,地铁到站了,回家还得用Claude帮我写个自动化脚本,把今天踩的坑整理进团队的Wiki里呢。


评论 0