欢迎光临
我们一直在努力

AI 辅助编程的 7 个误区:把模型当高级搜索引擎是对它的最大浪费

AI 辅助编程的 7 个误区:把模型当高级搜索引擎是对它的最大浪费

一、当 AI 说"这里加一个 Mutex 就好"的时候

去年十二月,我在给 dayuan 做并发缓存层。ChatGPT 很自信地建议:"在 HashMap 外层包一个 Arc<Mutex<>>,多线程访问就安全了。"

我照做了。然后发现高并发场景下吞吐量从 5000 QPS 跌到了 300 QPS——因为所有读操作都在争同一个锁。最后我用 dashmap + 分片锁的方案才把性能拉回来。

这事让我明白一个道理:AI 给你的方案永远只是"能编译通过"的方案,不是"正确"的方案,更不是"好"的方案。

作为自学编程的人,我天然依赖 AI 来弥补知识短板。但一年多下来,我发现很多人在用 AI 编程时,陷入了 7 个典型的认知误区。

二、误区全景


三、从"使用工具"到"被工具使用"

误区 1:把 AI 当高级搜索引擎

这是最常见的用法——也是最浪费的用法。

用户:tokio::spawn 怎么用?
AI: 这是文档……

你在浪费模型的推理能力。你不是在用一个代码助手,你是在用一个美化版的 grep docs.rs。

正确的用法:

用户:我在写一个 WebSocket 服务,需要在 accept 新连接时
同时维护一个全局的在线用户列表,支持广播。
现在我用 Arc<Mutex<Vec<u64>>> 存储在线用户 ID。
每次广播都要遍历一遍,5000 并发时 Mutex 变成瓶颈了。

不用给我写全代码,帮我分析三种可能的优化方向,
以及每种在什么场景下适合。

AI: 好,我来分析:

1. dashmap + 连接级别锁:
适用:读写比例均衡,需要强一致性
代价:内存占用比 Vec 高 30%

2. 分片路由 + 无锁广播:
适用:写少读多(在线状态变更少,广播多)
代价:实现复杂度高,需要设计分片策略

3. 事件总线 + 订阅模式:
适用:广播是主要操作,不需要存储状态快照
代价:需要引入消息中间件(如 tokio::broadcast)

看到了吗?你不是在问"怎么用",你是在把自己的设计困境描述给 AI,让它帮你做 trade-off 分析。

误区 2:无脑接受 AI 的代码

/// ❌ AI 写的代码,看起来没问题,直到你在生产环境看到 OOM
async fn process_all_users(user_ids: Vec<u64>) -> Vec<UserInfo> {
let mut tasks = Vec::new();

for id in user_ids {
// AI 不知道你的 user_ids 可能有 10 万个!
tasks.push(tokio::spawn(async move {
fetch_user_info(id).await
}));
}

// 10 万个 task 同时运行 → 连接池耗尽 → OOM
let mut results = Vec::new();
for task in tasks {
results.push(task.await.unwrap());
}
results
}

每次收到 AI 代码,带着这五个问题审一遍:

  • 边界条件:如果输入为空会怎样?如果输入有 10 万条会怎样?
  • 错误处理:每个 .await 点都可能出错,处理了吗?
  • 性能假设:这个操作是 O(1) 还是 O(n)?瓶颈在哪里?
  • 并发安全:多线程/多协程同时运行时,有数据竞争吗?
  • 资源泄露:文件句柄、网络连接、内存有没有正确释放?
  • 误区 3:跳过理解,直接复制粘贴

    这是我刚开始用 ChatGPT 时最大的毛病。看到代码跑通了就以为学会了,一周后再遇到同样的问题——还是不会。

    我的实践:AI 辅助学习的正确姿势

    /// 场景:AI 生成了这段代码帮你解决问题
    /// 但你没有直接粘贴,而是逐行加注释来确保自己理解了

    use std::collections::HashMap;
    use std::sync::Arc;
    use tokio::sync::RwLock;

    /// 线程安全的缓存实现
    /// 追问 AI 的问题:
    /// Q: 为什么用 RwLock 而不是 Mutex?
    /// A: 因为缓存的读操作远多于写操作,RwLock 允许多个读并发
    /// Q: 为什么外面要包 Arc?
    /// A: 因为 RwLock 需要被多个 task 共享,Arc 提供多所有权
    pub struct Cache {
    inner: Arc<RwLock<HashMap<String, String>>>,
    // ^^^ 多线程共享需要引用计数
    // ^^^^^^ 读写锁:多个读者可以同时读,写者独占
    // ^^^^^^^^^^^^^^^^^^^ 实际存储
    }

    impl Cache {
    pub fn new() -> Self {
    Self {
    inner: Arc::new(RwLock::new(HashMap::new())),
    }
    }

    /// 读取缓存(可以多个 task 同时读)
    pub async fn get(&self, key: &str) -> Option<String> {
    let guard = self.inner.read().await;
    // ^^^^^^ 获取读锁,不阻塞其他读者
    guard.get(key).cloned()
    }
    // guard 在这里被 drop → 读锁释放

    /// 写入缓存(独占访问)
    pub async fn set(&self, key: String, value: String) {
    let mut guard = self.inner.write().await;
    // ^^^^^^^ 获取写锁,阻塞所有读者和写者
    guard.insert(key, value);
    }
    }

    我的法则:AI 生成的代码,凡是不能逐行解释的,先弄懂再用。

    误区 4:觉得 AI 能替代系统学习

    AI 能告诉你"Tokio 的 runtime 有 block_in_place 这个函数"。但它不能告诉你什么时候不该用它。

    /// ❌ AI 告诉你的:
    /// 在 async 函数里调用同步代码,用 block_in_place 就行
    async fn my_async_fn() {
    let result = tokio::task::block_in_place(|| {
    // 同步阻塞代码
    std::thread::sleep(Duration::from_secs(5));
    42
    });
    }

    /// 但 AI 不会告诉你的是:
    /// 1. block_in_place 会把当前 task 移出 worker 线程
    /// 2. 如果大量 task 都用 block_in_place,worker 线程池会"漏光"
    /// 3. block_in_place 只应在 CPU 密集计算中使用
    /// 4. 对于 IO 操作,应该用 spawn_blocking 或专门的异步 IO

    系统学习 > AI 问答。对于一个新概念,我的学习路径是:

  • 读官方文档的 Overview(至少前 3 页)
  • 用 AI 回答"这个概念解决了什么问题"(不是"怎么用")
  • 写一个最小可运行的 demo
  • 故意写一个反例,看看会出什么问题
  • 最后才把 AI 生成的代码放进项目
  • 误区 5:用 AI 生成安全关键代码

    /// ❌ AI 写的用户认证代码 —— 永远不要这样用!
    #[derive(Deserialize)]
    pub struct LoginRequest {
    pub username: String,
    pub password: String,
    }

    async fn login_handler(req: LoginRequest) -> Result<impl Reply> {
    // AI 的建议:直接拼 SQL
    let query = format!(
    "SELECT * FROM users WHERE username = '{}' AND password = '{}'",
    req.username, req.password // ← SQL 注入!
    );
    // 用 req.username = "admin' –" 就能绕过密码验证

    sqlx::query(&query).fetch_one(&pool).await?;
    Ok("登录成功")
    }

    安全敏感的代码,永远不要让 AI 替你写。包括但不限于:

    类别为什么不能信 AI
    SQL 查询拼接 AI 不一定会用参数化查询
    密码哈希 AI 可能建议用 MD5/SHA1
    JWT 验证 AI 可能跳过签名校验
    权限检查 AI 可能把授权逻辑写在客户端
    加密实现 永远不要自己实现加密算法

    误区 6:高估 AI 的上下文理解能力

    // 你问 AI:
    // "这个函数有什么问题?帮我修复"

    pub async fn process_data(config: &Config, data: &Data) -> Result<Output> {
    // AI 能看到这段代码,但它不知道:
    // 1. Config 的字段中哪些是热更新的
    // 2. Data 在什么生命周期内有效
    // 3. 调用方的并发模型是什么样的
    // 4. 这个函数在整体架构中的角色

    let enriched = enrich(data)?;
    let transformed = transform(&config.rules, &enriched).await?;
    Ok(transformed)
    }

    给 AI 提供上下文的最佳实践:

    用户:我在维护一个多租户 SaaS 的数据处理管线。这个 process_data
    函数是管线的第二步,前一步是数据清洗,后一步是存储。
    当前的问题是:当租户数量从 10 增长到 1000 时,
    这个函数从 50ms 变成了 3s。

    [粘贴函数代码]

    我在怀疑 enrich 阶段的 HashMap 在租户维度做了 O(n²) 操作。
    帮我分析是否存在这个问题,以及如何修复。

    误区 7:用 AI 做架构决策

    AI 不能替你选数据库,不能替你决定微服务拆分,不能替你评估技术债。因为架构决策的核心不是技术优劣,而是你今天有多大的团队、明天要支持多少用户、三个月后的需求是什么——而这些信息都不在模型的训练数据里。


    四、我现在的 AI 编程工作流

    具体来说:

  • 需求阶段:把需求描述给 AI,让 AI 用三种不同方案各写 5 行伪代码。比较思路,不比较代码细节。
  • 设计阶段:自己画出数据流图或状态机图,让 AI 挑漏洞。
  • 实现阶段:自己写核心逻辑。AI 帮忙生成测试用例、文档注释、CLI 参数定义。
  • 审查阶段:把自己的代码给 AI,限定问题"这段代码有哪些我没考虑的边界情况?"
  • 重构阶段:功能验证通过后,让 AI 提出重构建议——这时候它已经有了完整上下文。

  • 五、总结

    AI 辅助编程一年多,我对 AI 的定位经历了三次迭代:

    • 第一阶段:AI 是代码生成器——"帮我写一个……"
    • 第二阶段:AI 是结对编程伙伴——"这里有问题,帮我看看……"
    • 第三阶段(现在):AI 是思维延伸——"我在做 X,有三种方案,帮我分析每种在 Y 场景下的 trade-off……"

    关键转变是:从"让 AI 替我想"变成了"我思考,AI 检查"。的我之所以能靠自学转行,很大程度上受益于 AI。但我也看到太多人陷入"AI 写了代码 → 跑通了 → 以为自己学会了"的循环里。这不是在学习,这是在制造技术债——只不过债主是你未来的自己。

    记住两件事:

  • AI 写代码只节省了你 20% 的时间,但如果你不理解它写的代码,未来修 bug 会多花 200% 的时间。
  • 好的开发者不是能快速写出代码的人,而是能判断"这段代码不该这么写"的人。AI 不能帮你做这个判断——只有你自己的理解能。

  • 下一篇预告:Rust 错误处理的反面教材,半年里见过的最糟糕的错误处理代码分析。

    赞(0)
    未经允许不得转载:171主机测评 » AI 辅助编程的 7 个误区:把模型当高级搜索引擎是对它的最大浪费
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址