完蛋,程序员被 AI 替代了

完蛋,程序员被 AI 替代了

随谈#macOS

July 1, 20240

完蛋,我被 AI 包围了

前言

时至今日,要说什么行业最火,还得是 AI, 自 GPT3.5 破圈进入大众视野,隔三差五就会有朋友问,AI 出来了,你是不是要被取代了。

甚至有一些程序员朋友们在问我。

吓得我连夜开通开通 GPT, 一用,整个人被吓到

  • 这玩意还能读懂我的意图?
  • 这玩意还能写代码?甚至能我没有完全描述好这个功能都能考虑到
  • 写出来的代码改改就能用?

甚至在 GPT4 刚出没多久的时候,我一度产生了这种想法,这玩意是不是要取代我了?

当然,随着使用 GPT4 次数越来越多,对 GPT 也开始祛魅。要问 AI 能不能取代程序员,截止 2024 年 7 月,我的结论是:单纯从技术角度,GPT 是个好帮手,很难取代程序员。

但程序员会不会因此失业,这点却很难说。

不过,正如大家所说的 AI 不一定会取代程序员,但一定会取代不懂 AI 的程序员。

用到的工具

  1. lobechat 主要以 Claude 3.5 Sonnet 为主
  2. perplexity.ai (背后是 GPT4 / Claude 3.5 Sonnet)
  3. GitHub Copilot

AI 提升编程体验

在以下领域,AI 辅助编程表现出色:

  1. 资料搜索 查找解决方案,查找 API, 编写 POC,
  2. 开发规划 保持术语一致性和清晰性,定义文档规范
  3. 代码生成 代码补全,根据注释或要求生成代码,自动补全函数名
  4. 代码重构 代码合并、拆分、优化
  5. 文本转换 代码转换
  6. 质量保证 代码审查和测试用例

还需要补充两点

  1. 现阶段的 AI 大模型更像是对真实世界的一个压缩,这意味着无法解决前人没有解决的问题。如果你要解决的问题比较冷门,得到答案的概率会很低。 从编程角度,AI 给我带来了什么之前做不到但现在能做到的事情?很少,但以前需要进行大量调研的东西。可能问问 AI 瞬间就有一定几率瞬间解决了。
  2. AI 的发挥并不稳定,虽有时妙棋,但不乏臭手,对结果务必要进行人工检查

资料搜索

案例

以前,在没有 AI 辅助的情况下,我想使用 python + opencv 将 N 张图片拼接到一起做成一个全景图,我可能会有如下的操作

  1. 猜测这个问题可能是常见问题,搜索一下 Google, 「python opencv 全景图」
  2. 找博客/StackOverflow,阅读代码看看是否满足需求。如果不满足需求要求,则继续搜索,比如修改搜索词「python opencv panorama」。
  3. 找 opencv-python 的 github 仓库,看看 example 是否包含有类似的代码,如果还不满足需求,则看看其他相关仓库。
  4. 发现 opencv 用起来比较复杂,从微信读书之类的 app 上找本关于 opencv 相关的书籍扫盲。
  5. 拼接代码进行调试。

而现在,我只需问 AI (Claude 3.5 Sonnet)

我使用云台相机拍摄了 9 图,每张图的边缘可能和其他图有一定交集。

请使用 python + opencv 帮我拼接这九张图成为一张全景图

甚至更进一步

我使用云台相机拍摄了 10 图,其中一张图是总体的图,剩余九张图是局部更加高清的图

请使用 python + opencv 帮我堆叠这九张图到总体图上

于是,你得到了可以快速运行的代码,

import cv2
import numpy as np
import glob

def load_images(folder_path):
    """加载指定文件夹中的所有图片"""
    image_files = glob.glob(folder_path + '/*.jpg')  # 假设图片是 jpg 格式
    images = {}
    for file in image_files:
        img = cv2.imread(file)
        if img is not None:
            images[file.split('/')[-1]] = img
    return images

def find_homography(img1, img2):
    """找到两张图片之间的单应性矩阵"""
    # 初始化 SIFT 检测器
    sift = cv2.SIFT_create()

    # 找到关键点和描述符
    kp1, des1 = sift.detectAndCompute(img1, None)
    kp2, des2 = sift.detectAndCompute(img2, None)

    # FLANN 参数
    FLANN_INDEX_KDTREE = 1
    index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
    search_params = dict(checks=50)

    flann = cv2.FlannBasedMatcher(index_params, search_params)
    matches = flann.knnMatch(des1, des2, k=2)

    # 存储好的匹配点
    good = []
    for m, n in matches:
        if m.distance < 0.7 * n.distance:
            good.append(m)

    if len(good) > 10:
        src_pts = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1, 1, 2)
        dst_pts = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1, 1, 2)

        M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)
        return M
    return None

def stack_images(base_img, detail_imgs):
    """将局部高清图堆叠到总体图上"""
    result = base_img.copy()

    for img in detail_imgs:
        # 找到单应性矩阵
        M = find_homography(img, base_img)
        if M is not None:
            # 对局部图进行透视变换
            h, w = base_img.shape[:2]
            warped = cv2.warpPerspective(img, M, (w, h))

            # 创建 mask
            gray = cv2.cvtColor(warped, cv2.COLOR_BGR2GRAY)
            _, mask = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY)

            # 将 warped 图像复制到 result 上
            result = cv2.copyTo(warped, mask, result)

    return result

# 主程序
if __name__ == "__main__":
    folder_path = "path/to/your/image/folder"  # 替换为你的图片文件夹路径
    images = load_images(folder_path)

    if len(images) < 10:
        print("图片数量不足")
    else:
        # 假设总体图的文件名为 "overall.jpg"
        base_img = images.pop("overall.jpg")
        detail_imgs = list(images.values())

        # 堆叠图片
        result = stack_images(base_img, detail_imgs)

        # 保存结果
        cv2.imwrite("stacked_result.jpg", result)
        print("堆叠后的图片已保存为 stacked_result.jpg")

        # 显示结果
        cv2.imshow("Stacked Result", result)
        cv2.waitKey(0)
        cv2.destroyAllWindows()

甚至他会很贴心的告诉你「**这个方法假设局部图和总体图之间只有透视变换的差异。如果有其他类型的变形(如旋转、缩放),可能需要更复杂的配准方法 **。」

通过一番搜索,你可能也能找到答案。但在 AI 的加持下,你的效率可能远超你的预期。

只言片语,就能得到答案。

建议

当你检索一些常见的库、依赖或编程语言的基础知识时,预先查一下往往会有奇效。比如:

如果你熟悉 JS,但是此时此刻你得用 Swift UI 写一点代码,你可以问

  • swiftui 有没有 类似 js 的 a || "" 的写法
  • swift 有没有 optional chaining 的语法

如果你用 rust,但是想问问其他语言/框架中的 api,你可以

  • rust 中是否有类似 python opencv 的 drawContour 方法?
  • rust 中处理图像的常见库有哪些?

如果你熟悉中文的说法,但是不晓得英文应该怎么问。

  • 比如,我知道「划词翻译」这个功能,但是划词翻译所需 API 你让我翻译,可能我会翻译成 draw word translate api。但你直接问 GPT「如果用 mac 实现划词翻译,需要使用哪些 api」基本就能得到答案
  • 对于一些较为系统的教程或者较长的代码,LLM 并不能很好的回答。使用 Copilot Chat / ChatGPT 来提供有用的搜索词,然后查阅 Google 搜索结果中的教程和帖子以获得更全面和详细的信息。

开发规划

假设咱做的是一个 Full Stack 的项目,开发规划发生在正式编码前,把业务梳理清楚后,围绕着核心业务开始建模。

我个人的习惯是从核心业务流程开始逐步梳理出数据库表和表字段,后端组件,然后设计 API 和前端页面。

在这个阶段,可以对 AI 反复追问。

  • 「购物车 checkout 的数据库字段应该怎么设计会好点?」接着追问「在这个基础上,如何处理退款?」
  • 「我现在的数据库表是这样的,你觉得我在此基础上还要满足需求 1/2/3」应该怎么做呢?
  • 「像是以图搜图的方案一般有哪些开源替代?」接着依据回答追问「Milvus 以图搜图怎么实现?」
  • 「rest 下单接口叫什么比较好?」
    • 你可能会得到很多个备选项,POST /orders,POST /checkout,POST /cart/checkout,POST /purchase

通过反复追问,可以陆陆续续确定后端的技术选型/数据库 Schema/接口/服务层/前端路由/前端组件/业务模型的命名。

命名,命名,还是命名

命名的一致性和清晰性对于项目的可维护性和可读性至关重要。

毕竟正如 Phil Karlton 说过:There are only two hard things in Computer Science: cache invalidation and naming things. 即「 缓存失效和命名是计算机科学中最难的两件事」。

代码生成

令程序员最兴奋的环节,就是代码生成。

我这里举一个不是很合适的例子,生成注册页面和登录页面

案例 - 注册页和登录页

使用如下 Prompt

帮我生成登录页面和注册页面,以下是我的技术栈 react react-hook-form zod typescript

Note: 依赖库的 API 可能会过时,但使用 TypeScript 进行类型检查可以极大地降低修改的成本。当然,登录和注册页面的功能远非如此,但这个草稿可以作为一个简单的起点 当我询问依赖库比较老如何解决时,ChatGPT 给了我一些通用的代码修复建议,例如检查语法和检查依赖库是否过期不兼容。但是 Copilot Chat 却给了我另一个过时的版本,所以最终我使用了 Google 并过滤了最近一年的文章来解决这个问题。

使用了 Tailwind CSS 对登录页面进行了优化。在 Copilot 中由于返回结果过长,被过滤掉了,因此转而使用 ChatGPT 进行了优化,现在页面看起来虽然也就那样,但好歹能看了。(不过这明显不如直接套模板了 ahhh)

接着输入生成的代码,并且让代码仿写一个注册页面,要求添加输入密码变化时的校验功能。

为什么说不合适呢?

因为这里只是为了演示代码生成的功能,但真实开发环境中,大家都是套模板/使用 figma 编写注册/登录页。

案例 - 脚本转译

之前写个爬虫,从 github 找到一个百来行的 php 爬虫。我有如下选择。

  1. 翻阅 php 语法,调试一下。耗时 30min+(不包含环境配置)
  2. 自己写。耗时 1h+.
  3. 人肉转译。耗时 30min+.

最后,我把 php 代码投喂给 GPT3.5, 让他转译为 python。改几行代码,调试完毕。

代码补全 TODO

NOTE: 本小节使用 Copilot

记得刚写代码的时候,听过这么一句话

程序员最讨厌的两种人,一种是不写注释的人,另一种是要求自己写注释的人。

但 copilot 让我确实没有那么讨厌写注释了。

  • def 名称
  • #注释
  • #终于,爱上了写注释
  • 镜头,二指禅

代码重构

精简代码

  • 前端 html 优化「请帮我合并掉多余的 div」
  • 请帮我重写这个函数,让它更简洁

合并逻辑

程序员有一个习惯,就是复制一份代码然后自己修改。这样做的好处是,不会破坏原有的代码,但是也会导致代码重复。这时候,可以使用 GPT 来合并代码逻辑。

以聊天窗口的发送消息和接收消息为例。先调试发送消息的样式/TSX,然后复制一份调试接收消息的样式/TSX。调试完毕后,把两段代码丢给 GPT, 让 GPT 合并起来。这样在修改 receive 的样式的时候并不会有搞坏 send 样式的心智负担,只需要专注 receive 的调试,调试完毕一合并,节约了不少琐碎的时间。

样式可以这么合并,业务逻辑也可以?🧐

拆分逻辑

随着发送消息和接收消息的组件/逻辑越来越复杂,将两者合二为一的做法可能会导致代码难以维护。这时候,可以使用 GPT 将对应的代码分开。

适配版本升级

有的时候第三方库写法变了,我们的代码也要跟着变。 例如,Pinia 升级 composition 写法,需要咱手动讲 option 写法转为 setup 写法。

但有 GPT 的情况下,提交代码并提供简单示例,就能省去不少繁琐的文本编辑成本。逗号/对象属性函数转换为闭包函数/this/ref 语法。( 别忘测试)

又比如:Vue 模板语法 批量转成 tsx 语法。

但请注意:转换并非银弹,LLM 有的时候会丢东西。比如这个场景下,针对 if/click/slot 的转换效果良好,但对于 class 属性可能存在重复情况,而对于 ref 属性可能会漏掉 value。(当然,升级大模型版本会一定程度上缓解这种状况,比如升级到 gpt4o / Claude 3.5 sonnet )

文本转换

文本转换指的是一种格式的文本转换为另一种格式的文本。这种转换通常涉及到一些规则和模式的匹配,而 AI 可以帮助我们更快地完成这种转换。

Python 代码格式化

迁移笔记中的代码时经常会出现换行丢失的情况。对于像 Python 这样没有语法的语言 (插一张图,吐槽 Python 没有括号) ,手动断行来恢复原来的代码格式非常烦人。投给 GPT 格式化大大提高效率。 当然,其他语言也是可以格式化的。python 只是难度较大罢了。

import cv2 import numpy as np import glob def load_images(folder_path): """加载指定文件夹中的所有图片""" image_files = glob.glob(folder_path + '/*.jpg')  # 假设图片是 jpg 格式 images = {} for file in image_files: img = cv2.imread(file) if img is not None: images[file.split('/')[-1]] = img return images def find_homography(img1, img2): """找到两张图片之间的单应性矩阵""" # 初始化 SIFT 检测器 sift = cv2.SIFT_create() # 找到关键点和描述符 kp1, des1 = sift.detectAndCompute(img1, None) kp2, des2 = sift.detectAndCompute(img2, None) # FLANN 参数 FLANN_INDEX_KDTREE = 1 index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5) search_params = dict(checks=50) flann = cv2.FlannBasedMatcher(index_params, search_params) matches = flann.knnMatch(des1, des2, k=2) # 存储好的匹配点 good = [] for m, n in matches: if m.distance < 0.7 * n.distance: good.append(m) if len(good) > 10: src_pts = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1, 1, 2) dst_pts = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1, 1, 2) M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0) return M return None

提取信息

片段代码重构加速,如何左侧变为右侧?

"@iconify-json/heroicons": "^1.1.21",
"@iconify-json/simple-icons": "^1.1.106",
"@nuxt/content": "^2.13.0",
"@nuxt/fonts": "^0.7.0",
"@nuxt/image": "^1.7.0",
"@nuxt/kit": "^3.12.2",
"@nuxt/ui-pro": "^1.3.0",
"@vueuse/nuxt": "^10.11.0",
"nuxt-og-image": "^2.2.6"
"@nuxt/eslint": "^0.3.13",
"@nuxthq/studio": "^2.0.1",
"eslint": "^9.5.0",
"nuxt": "^3.12.2",
"vue-tsc": "^2.0.22"
  1. 人工直接编辑
  2. 使用支持正则表达式的编辑器,输入命令后补充编辑

用 GPT 来提取代码中的关键信息并且生成你需要的对应代码。而且可以对输出格式做一些要求。

  • 帮我从如下的 html 代码中提取 title,descripiton,tags 转成变量,并且提供对应的 vue 模板。

Schema 翻译

django.model 转 json/pydantic model json 转 swiftui view model/typescript model

  • model / schema
    • sqlachemy model
    • django model
    • sql table
      • create update
    • pydantic
    • zod / pojo
    • vue / react

翻译 - 转另一个

  • vue tsx 转 react tsx
  • python 逻辑转 golang
  • 多语言支持
  • PR 帮助

质量保证

检查代码

较短的但存在 bug 的函数,如果一眼看不出问题,可以尝试丢给 GPT-4, 刚修复了一个 Swift 的并发 Bug.....****

Code Review

  • 通过代码猜测功能

测试用例生成

  • 仿格式生成格式
  • 测试数据生成

其他

如果你不知道需要看什么书,你先写两本相同类型的书,接着 Copilot 就会给你补全推荐其他的书籍。🤣

  • https://x.com/hylarucoder/status/1752665100488774062
  • https://x.com/hylarucoder/status/1740384021451014176
  • 如果你使用 SwiftUI 等较为冷门的开发技术,发现 Copilot 和 GPT-3.5 在生成 View/ViewModel/Model 时提供了可用代码,但实现如 macOS 上的拖拽功能则难以实现,使用 JS/TS 则可以快速获取可用代码。所以,在训练语料不够的情况下(GPT 21 年数据对 22 年没有太多), AI 可能并不能帮助你。

一些不足

  1. 生成的代码依旧需要人工检查
  2. Swift 代码可能由于网上公开资料

大模型更像是所有世界

语料不足

在使用 AI 辅助编程的时候,也发现了

常有金句爆出,但输出不是很稳定。

  • 不同的 LLM 和 LLM 升级之后
  • prompt engineering

结尾

  • 老婆,你说以后生娃的话,我写个程序,给小孩子起名好不好。
  • 操 (哔), 这东西你都要 AI 来做,你怎么不和 AI 过日子去啊

一次性脚本

  • 估计
  • 批量命名