面向 ChatGPT 開發 ,我是如何被 AI 從 “逼瘋” 到 “覺悟” ,未來又如何落地

語言: CN / TW / HK

theme: smartblue

本文正在參加 ✍🏻 技術視角深入 ChatGPT 徵文活動

對於 ChatGPT 如今大家應該都不陌生,經過這麼長時間的「調戲」,相信大家應該都感受用 ChatGPT 「代替」搜尋引擎的魅力,例如寫週報、定位 Bug、翻譯文件等等,而其中不乏一些玩的很「花」的場景,例如:

  • ChatPDF :使用 ChatPDF 讀取 PDF 之後,你可以和 PDF 檔案進行「交談」,就好像它是一個完全理解內容的「人」一樣,通過它可以總結中心思想,解讀專業論文,生成內容摘要,翻譯外籍,並且還支援中文輸出等

  • BiBiGPT : 一鍵總結視訊內容,主要依賴字幕來做總結,絕對是「二創」作者的摸魚利器。

所以把 ChatGPT 理解為「搜尋引擎」其實並不正確,從上述介紹的兩個落地實現上看, ChatGPT 不是單純的統計模型,它的核心並不是完全依賴於它的「語料庫」,更多來自於臨場學習的能力「 in-context learning」,這就是 ChatGPT 不同於以往傳統 NLP「一切都從語料的統計裡學習」的原因

當然,我本身並非人工智慧領域的開發者,而作為一個普通開發者,我更關心的是 ChatGPT 可以如何提升我的開(mo)發(yu)效率,只是沒想到隨手一試,我會被 ChatGPT 的 「 in-context learning」 給「逼瘋」。

ChatGPT & UI

相信大家平時「面向」 ChatGPT 開發時,也是通過它來輸出「演算法」或者「 CURD」 等邏輯居多,因為這部分輸出看起來相對會比較直觀,而用 ChatGPT 來繪製前端 UI 的人應該不多,因為 UI 效果從程式碼上看並不直觀 ,而且 ChatGPT 對與 UI 的理解目前還處於 「人工智障」的階段。

但是我偏偏不信邪。。。。。

因為近期開發需求裡恰好需要繪製一個具有動畫效果的 ⭐️ 按鍵,面對這麼「沒有挑戰性」的工作我決定嘗試交給 ChatGPT 來完成,所以我向 ChatGPT 發起了第一個命令:

「用 Flutter 畫一個黃色的五角星」

| | | | ------------------------------------------------------------ | ------------------------------------------------------------ |

結果不負眾望,關鍵部分如下程式碼所示,Flutter 很快就提供了完整的 Dart 程式碼,並且還針對程式碼提供了程式碼相關實現的講解,不過執行之後可以看到,這時候的 ⭐️ 的樣式並不滿足我們的需求。

此時頂部的角也太「肥」了 。

| | | | ------------------------------------------------------------ | ------------------------------------------------------------ |

所以我隨著提出了調整,希望五角星的五個角能夠一樣大,只是沒想到我的描述,開始讓 ChatGPT 放飛自我

也許是我的描述並不準確?

在我滿懷期待的 cv 程式碼並執行之後,猝不及防的「五角星」差點沒讓我噴出一口老血,雖然這也有五個角,但是你管這個叫 「五角星」 ???

這難道不是某個紅白機遊戲裡的小飛機??

| | | | ------------------------------------------------------------ | ------------------------------------------------------------ |

甚至於在看到後續 ChatGPT 關於程式碼的相關講解時,我覺得它已經開始在「一本正經的胡說八道」,像極了今天早上剛給我提需求的產品經理

哪裡可以看出五個角相同了???

接著我繼續糾正我的需求,表示我要的是 「一個五個角一樣大的黃色五角星」 ,我以為這樣的描述應過比較貼切,須不知·····

如下程式碼所示,其實在看到程式碼輸出 for 迴圈時我就覺得不對了,但是秉承著「一切以實物為準」的理念,在執行後不出意外的發生了意外,確實是五個角一樣大,不過是一個等邊五邊形。

算一個發胖的 ⭐️ 能解(jiao)釋(bian)過去不?

| | | | ------------------------------------------------------------ | ------------------------------------------------------------ |

再看 ChatGPT 對於程式碼的描述,我發現我錯了,原來它像的是「理解錯需求還在嘴硬的我」,只是它在說「這是一個五角星」的時候眼皮都不會眨一下

AI:確實五個角一樣大,五個角一樣大的五邊形為什麼就不能是五角星?你這是歧視體型嗎?

所以我繼續要求:「我要的是五角星,不是五邊形」,還好 ChatGPT 的臨場學習能力不錯,他又一次「重新定義五角星」,不過我此時我也不抱希望,就是單純想看看它還能給出什麼「驚喜」

不出意外,這個「離譜」的多邊形讓我心頭一緊,就在我想著是否放棄的時候,身為人類無法馴服 AI 「既愛又恨」的複雜情緒,讓我最終堅持一定要讓 ChatGPT 給我畫出一個 ⭐️。

| | | | ------------------------------------------------------------ | ------------------------------------------------------------ |

不過心灰意冷之下,我選擇讓 ChatGPT 重新畫一個黃色五角星,沒想道這次卻有了意外的驚喜,從下面的圖片可以看到,此時的 ⭐️ 除了角度不對,形狀已經完全滿足需求。

所以一個問題我多問幾遍,也許就能接近我要的答案?

事實上這也是目前 ChatGPT 的現狀,因為「臨場學力」能力等因素影響,同一個問題它可能會給出不同的答案,而有的答案其實和我們要的根本不沾邊

| | | | ------------------------------------------------------------ | ------------------------------------------------------------ |

那麼,接下來只要讓 ChatGPT 把 ⭐️ 旋轉一下角度,應該就可以完成需求····了吧?所以我提出「幫我旋轉 180度」的要求。

結果不出意外的還是發生了意外,可能 ChatGPT 理解的 180 度和我不大一樣,如下圖所示,確實旋轉了,只是還是歪的,而基於前面的嘗試,我覺得有必要再給它一次機會。

| | | | ------------------------------------------------------------ | ------------------------------------------------------------ |

終於,在我換了以下描述之後,ChatGPT 似乎「開竅」了,終於讓我如願以償的得到了我想要的 ⭐️ 程式碼。

可以看到,其實這段程式碼並不複雜,你自己寫或者去搜索引擎扒一扒大概會比用 ChatGPT 更快,但是 ChatGPT 的魅力就在於:它也許會錯,但是總有一次它會「蒙對」,如果現在還不對,那麼未來也會有算對的時候,這就是我感受到的 AI 魅力所在

| | | | ------------------------------------------------------------ | ------------------------------------------------------------ |

既然五角星都出來,那麼接下來我們來完成最後一步的動畫效果,我們希望的是點選 ⭐️ 的時候可以有一個爆炸粒子的動畫,所以我提出了:

「希望加上一個動畫效果,就是點選的時候星星爆炸成顆粒的動畫效果」

結果還是「一口老血」,如下圖所示,「星星確實爆炸了」,只是這個爆炸動畫看起來是那麼的「一言難盡」,感興趣的可以 cv 下面的原始碼自己體驗下,不出意外接下來又是新的一輪人類和 AI 的「鬥智鬥勇」。

```dart

class YellowStar extends StatefulWidget { @override _YellowStarState createState() => _YellowStarState(); }

class _YellowStarState extends State with TickerProviderStateMixin { bool _isExploded = false; late AnimationController _animationController; late Animation _animation;

@override void initState() { super.initState(); _animationController = AnimationController( duration: const Duration(milliseconds: 500), vsync: this, ); _animation = CurvedAnimation( parent: _animationController, curve: Curves.easeInOut, ); }

@override void dispose() { _animationController.dispose(); super.dispose(); }

void _handleTapDown(TapDownDetails details) { setState(() { _isExploded = true; _animationController.forward(from: 0.0); }); }

void _resetStar() { setState(() { _isExploded = false; _animationController.reset(); }); }

@override Widget build(BuildContext context) { return GestureDetector( onTapDown: _handleTapDown, onTap: _resetStar, child: AnimatedContainer( duration: const Duration(milliseconds: 500), decoration: BoxDecoration( color: Colors.white, borderRadius: _isExploded ? BorderRadius.circular(50) : BorderRadius.zero, ), child: _isExploded ? _buildParticles() : Container( width: 100, height: 100, child: CustomPaint( painter: YellowStarPainter(), ), ), ), ); }

Widget _buildParticles() { return AnimatedBuilder( animation: _animation, builder: (context, child) { return Container( width: 100, height: 100, child: Stack( children: List.generate( 50, (index) { double radius = _animation.value * 50; double angle = 2 * pi * index / 50; double x = 50 + cos(angle) * radius; double y = 50 + sin(angle) * radius; return Positioned( left: x, top: y, child: Container( width: 4, height: 4, decoration: BoxDecoration( shape: BoxShape.circle, color: Colors.yellow, ), ), ); }, ), ), ); }, ); } }

class YellowStarPainter extends CustomPainter { final double starSizeRatio = 0.4; final double centerOffsetRatio = 0.2; final double rotationOffset = -pi / 2;

@override void paint(Canvas canvas, Size size) { double starSize = min(size.width, size.height) * starSizeRatio; double centerX = size.width / 2; double centerY = size.height / 2; double centerOffset = starSize * centerOffsetRatio;

Path path = Path();
Paint paint = Paint()
  ..color = Colors.yellow
  ..style = PaintingStyle.fill;

for (int i = 0; i < 5; i++) {
  double radians = 2 * pi / 5 * i + rotationOffset;
  double x = centerX + cos(radians) * starSize / 2;
  double y = centerY + sin(radians) * starSize / 2;
  if (i == 0) {
    path.moveTo(x, y);
  } else {
    path.lineTo(x, y);
  }

  radians += 2 * pi / 10;
  x = centerX + cos(radians) * centerOffset;
  y = centerY + sin(radians) * centerOffset;
  path.lineTo(x, y);
}

path.close();
canvas.drawPath(path, paint);

}

@override bool shouldRepaint(CustomPainter oldDelegate) => false; } ```

經過一輪「鏖戰」如下圖所示,面對同一個問題,ChatGPT 總能找到不同的實現方式來回答你,只是這些實現裡有的無法執行的,有的是執行沒有效果的,有的甚至是給你輸出了一個 //TODO 爆炸動畫 ,總之就是讓你「血壓飆升」。

當然,「勝利終將屬於人類」,如下圖所示,在經歷了無數次「血壓飆升」的場景之後,最終我還是得到了我想要的:點選 ⭐️,然後出現了💥的動畫效果,儘管還是不完美,但是基本滿足了我後續開發的需求。

這個程式碼完完全全由 ChatGPT 輸出,我一句沒改,完整程式碼貼在了後面。

```dart

class YellowStarPage extends StatefulWidget { @override _YellowStarPageState createState() => _YellowStarPageState(); }

class _YellowStarPageState extends State with SingleTickerProviderStateMixin { late AnimationController _animationController; bool _isExploded = false;

@override void initState() { super.initState(); _animationController = AnimationController(vsync: this, duration: Duration(milliseconds: 500)) ..addListener(() { setState(() {}); }); }

@override void dispose() { _animationController.dispose(); super.dispose(); }

void _handleStarTap() { if (!_isExploded) { _isExploded = true; _animationController.forward(from: 0); Future.delayed(Duration(milliseconds: 500), () { setState(() { _isExploded = false; }); }); } setState(() {}); }

@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Yellow Star')), body: Center( child: GestureDetector( onTap: _handleStarTap, child: Container( width: 300, height: 300, child: AnimatedBuilder( animation: _animationController, builder: (context, child) { return CustomPaint( painter: YellowStarPainter(_animationController.value, isExploded: _isExploded), ); }, ), ), ), ), ); } }

class YellowStarPainter extends CustomPainter { final double starSizeRatio = 0.4; final double centerOffsetRatio = 0.2; final double rotationOffset = -pi / 2;

final double animationValue; final bool isExploded;

YellowStarPainter(this.animationValue, {this.isExploded = false});

@override void paint(Canvas canvas, Size size) { double starSize = min(size.width, size.height) * starSizeRatio; double centerX = size.width / 2; double centerY = size.height / 2; double centerOffset = starSize * centerOffsetRatio;

Path path = Path();
Paint paint = Paint()
  ..color = Colors.yellow
  ..style = PaintingStyle.fill;

if (isExploded) {
  double particleSize = starSize / 30;
  paint.strokeWidth = 1;
  paint.style = PaintingStyle.fill;
  paint.color = Colors.yellow;
  Random random = Random();

  for (int i = 0; i < 30; i++) {
    double dx = random.nextDouble() * starSize - starSize / 2;
    double dy = random.nextDouble() * starSize - starSize / 2;
    double x = centerX + dx * (1 + animationValue);
    double y = centerY + dy * (1 + animationValue);

    canvas.drawCircle(Offset(x, y), particleSize, paint);
  }
} else {
  for (int i = 0; i < 5; i++) {
    double radians = 2 * pi / 5 * i + rotationOffset;
    double x = centerX + cos(radians) * starSize / 2;
    double y = centerY + sin(radians) * starSize / 2;
    if (i == 0) {
      path.moveTo(x, y);
    } else {
      path.lineTo(x, y);
    }

    radians += 2 * pi / 10;
    x = centerX + cos(radians) * centerOffset;
    y = centerY + sin(radians) * centerOffset;
    path.lineTo(x, y);
  }

  path.close();
  canvas.drawPath(path, paint);
}

}

@override bool shouldRepaint(CustomPainter oldDelegate) => true; }

```

最後,給大家欣賞一下我讓 ChatGPT 畫一隻米老鼠的「心路歷程」,很明顯這一次「人類一敗塗地」,從目前的支援上看,讓 ChatGPT 輸出複雜影象內容並不理想,因為它不的筆畫「不會拐彎」。

| | | | | | ----------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |

真的是又愛又恨。

最後

經過上面的一系列「折騰」,可以看到 ChatGPT 並沒有我們想象中智慧,如果面向 GPT 去開發,甚至可能並不靠譜,因為它並不對單一問題給出固定答案,甚至很多內容都是臨場瞎編的,這也是因為大語言模型本身如何保證「正確」是一個複雜的問題,但是 ChatGPT 的魅力也來自於此:

它並不是完全基於語料來的統計來給答案

當然這也和 ChatGPT 本身的屬性有關係, ChatGPT 目前的火爆有很大一部分屬於「意外」,目前看它不是一個被精心產品化後的 2C 產品,反而 ChatPDFBiBiGPT 這種場景化的包裝落地會是它未來的方向之一。

而現在 OpenAI 釋出了多模態預訓練大模型 CPT-4GPT-4 按照官方的說法是又得到了飛躍式提升:強大的識圖能力;文字輸入限制提升至 2.5 萬字;回答準確性顯著提高;能夠生成歌詞、創意文字,實現風格變化等等

所以我很期待 ChatGPT 可以用 Flutter 幫我畫出一隻米老鼠, 儘管 ChatGPT 現在可能會讓你因為得到 1+1=3 這樣的答案而「發瘋”」,但是 AI 的魅力在於,它終有一天能得到準確的結果