前言
ChatGPT 最近一直都處于技術圈的討論焦點。它除了可作為普通用戶的日常 AI 助手,還可以幫助開發者加速開發進度。聲網社區的一位開發者 "小猿" 就基于 ChatGPT 做了一場實驗。僅 40 分鐘就實現了一個互動直播 Demo。他是怎么做的呢?他將整個過程記錄了下來。 (文章轉載自開發者的個人博客,以下為正文) “遇事不決,AI 力學” ~ ChatGPT 可以說是 2023 開年最熱門的話題, 它不僅在極短時間內風靡了整個技術圈,更是病毒式地席卷了圈外的各個行業,并對各大企業都起到了實質性影響:
谷歌緊急推出 “Bard” 對抗 ChatGPT
微軟發布新 Bing 集成 ChatGPT
復旦發布首個類 ChatGPT 模型 MOSS
國內阿里、百度、昆侖萬維、網易、京東都開始新一輪 AI 軍備
那 ChatGPT 究竟有什么魔力能讓 “群雄折腰”?這和 ChatGPT 的實現有很大關系:
與以往的統計模型不行,ChatGPT 不是那種「一切都從語料統計里學習」的 AI,相反 ChatGPT 具備有臨場學習的能力,業內稱之為 in-context learning ,這也是為什么 ChatGPT 可以在上下文中學習的原因。
ChatGPT 屬于 AI 領域在商用技術上的重大突破,當然,本篇我們不是要討論 ChatGPT 的實現邏輯,而是 ChatGPT 會怎么樣加速我們的開發?
PS:在此之前有人通過指示在 ChatGPT 界面下實現了一個虛擬機,雖然這是一個極端的例子,但是可以很直觀地感受到:「ChatGPT 對我們開發的影響是肉眼可見」。
那 ChatGPT 在實際工作中是如何影響我們的開發?為了更直觀,下面我們用一個開發場景來模擬這個流程。
基于 ChatGPT 開發
01 開發之前
假設我們現在有一個開發「直播」的需求,那我們可以直接求助 ChatGPT:
「開發一個直播 app,是使用第三方 SDK 好還是自己從 0 開發好」?
如下圖所示,從回答上可以看到,AI 建議我們根據團隊實際情況去選擇,而在知曉「我的團隊只有 5 個人」的情況后,它建議我選擇采用 “接入第三方 SDK” 的方式更合理。
那么選擇 “接入第三方 SDK” ,接下來的問題就是:「選擇做直播,在中國推薦使用哪些廠家的 SDK」? 如下圖所示,這個問題 ChatGPT 同樣提供了多個選項,從選項里看 *聲網、騰訊云和阿里云 * 好像都符合我們要求,而在接著的「優勢問題」對比上看,這三個選項都 “不相伯仲”,那我們就在再細化問題。
假設我們希望直播可以有更多 “互動能力”,那么把問題修改為「做互動直播,更推薦使用哪一個廠家的 SDK」,截圖如下圖所示,這次我們得到了更明確的答復,看來聲網的 SDK 會更貼合我們的需求。
為了更放心這個選擇,我們通過「聲網 SDK的優勢」和「什么產品使用了聲網 SDK」兩個問題進行提問,如下圖所示,從回復上看聲網作為一個全球化的廠家,在音視頻領域還是值得相信。同時,還有包括小米、陌陌等產品都使用了聲網的服務。那么就按照 AI 的建議選擇聲網 SDK 吧。
有沒有發現,在獲取資料的檢索方式上,ChatGPT 確實比搜索引擎更直觀且高效。
那么敲定完 SDK ,接下來我們需要選擇應用的開發框架,我們把需求限定在 Android 和 iOS,更好是能兼容 Web,覆蓋整個移動端 ,因為團隊人數不多,所以我們希望采用跨平臺開發來節約成本,那么問題就是:
「移動端哪個跨平臺框架更適合做直播」?
如下圖所示,得到的答案有 React Native 和 Flutter ,而恰好在 Flutter 回復里可以看到聲網 SDK 的存在,所以我們可以敲定 App 開發框架就選 Flutter 了。
最后,在開發之前,我們還需要繼續提問「如何獲取聲網 SDK」和「使用聲網 SDK 需要做什么」,這樣我們就可以在開始開發之前提前準備好需要的東西。
關于注冊獲取 App ID 等步驟這里就省略了,畢竟目前這部分 ChatGPT 也無能為力。
02 開始開發
那么到這里我們就假定大家已經準備好了開發環境,接下來可以直接進行開發。 我們還是繼續面向 ChatGPT 開發,首先我們的提問是:「用聲網的 Flutter SDK agora_rtc_engine 6.1.0寫一個視頻通話頁面,給我 dart 代碼」,結果如下 GIF 所示,可以看到 ChatGPT 開始了瘋狂的輸出:
為什么關鍵詞是「視頻通話」?因為它比直播場景更精準簡單,生成的代碼更靠譜(經過提問測試),而基于視頻通話部分,后面我們可以快速拓展為互動直播場景;而指定版本是為了避免 AI 使用舊版本 API。
從上門的代碼生成可以看到,ChatGPT 生產的代碼是自帶中文注釋,更貼心的是,如下圖所示,在生成的代碼末尾還給你解釋了這段代碼的實現邏輯,就像一個 “知心大姐姐”。
從這里也可以感覺到 ,ChatGPT 不是一個單純的完全只會基于語料答復整合的 AI 。
當然,直接復制生成的代碼后會發現這段代碼會報錯,這和 ChatGPT 目前的模型數據版本有一定關系,所以針對生成的代碼我們需要做一定手動調整,比如:
采用createAgoraRtcEngine和initialize創建和初始化RtcEngine
將setEventHandler修改為最新的registerEventHandler
將AgoraRenderWidget修改為AgoraVideoView
最后修改代碼如下,其中 80% 以上的邏輯都來自 ChatGPT 的自動生成,雖然沒辦法做到 “直出”,這無疑大大提高了開發的生產力。
class VideoCallPage extends StatefulWidget { final String channelName; const VideoCallPage({Key? key, required this.channelName}) : super(key: key); @override _VideoCallPageState createState() => _VideoCallPageState(); } class _VideoCallPageState extends State接下來,如下圖所示,在將項目運行到手機和 PC 端之后,可以看到我們就完成了最簡單的直播視頻場景,而基于我們打算做直播的念頭僅僅過去了 40 分鐘,這其中還包含了注冊聲網賬號和申請 App ID 的過程,我們通過簡單的提問、復制、粘貼、修改,就完成了一個直播需求的 demo。{ late RtcEngine _engine; bool _localUserJoined = false; bool _remoteUserJoined = false; int? rUid; @override void initState() { super.initState(); initAgora(); } @override void dispose() { _engine.leaveChannel(); super.dispose(); } Future initAgora() async { await [Permission.microphone, Permission.camera].request(); _engine = createAgoraRtcEngine(); await _engine.initialize(RtcEngineContext( appId: config.appId, channelProfile: ChannelProfileType.channelProfileLiveBroadcasting, )); _engine.registerEventHandler(RtcEngineEventHandler( onJoinChannelSuccess: (RtcConnection connection, int elapsed) { setState(() { _localUserJoined = true; }); }, onUserJoined: (connection, remoteUid, elapsed) { setState(() { _remoteUserJoined = true; rUid = remoteUid; }); }, onUserOffline: (RtcConnection connection, int remoteUid, UserOfflineReasonType reason) { setState(() { _remoteUserJoined = false; rUid = null; }); }, )); await _engine.enableVideo(); await _engine.startPreview(); await _engine.joinChannel( token: config.token, channelId: widget.channelName, uid: config.uid, options: const ChannelMediaOptions( channelProfile: ChannelProfileType.channelProfileLiveBroadcasting, clientRoleType: ClientRoleType.clientRoleBroadcaster, ), ); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("VideoCallPage"),), body: Center( child: Stack( children: [ _remoteUserJoined ? _remoteVideoView(rUid) : _placeholderView(), _localUserJoined ? _localVideoView() : _placeholderView(), ], ), ), ); } Widget _placeholderView() { return Container( color: Colors.black, ); } Widget _remoteVideoView(id) { return AgoraVideoView( controller: VideoViewController.remote( rtcEngine: _engine, canvas: VideoCanvas(uid: id), connection: RtcConnection(channelId: widget.channelName), ), ); } Widget _localVideoView() { return Positioned( right: 16, bottom: 16, width: 100, height: 160, child: AgoraVideoView( controller: VideoViewController( rtcEngine: _engine, canvas: const VideoCanvas(uid: 0), ), ), ); } }
紅色方塊是后期加上的打碼~
那么到這里,雖然目前為止 demo 項目還不是互動直播,但是基于這個 demo 實現互動直播場景不會太難,因為你已經跑通了整個 SDK 的鏈路流程了。
03 進階開發
那假設我們需要繼續往互動直播的方向開發,那么我們肯定會遇到 “互動” 這個需求,比如「收到用戶發送的一段內容后畫面彈出一個動畫」 這樣的需求。 那么首先我們要知道聲網 SDK 如何監聽用戶發送的內容,所以接下來我們繼續提問:「如何使用聲網的 agora_rtc_engine 6.1.0 監聽別人發送的文本消息」 ?
這里為什么還強制寫 agora_rtc_engine 6.1.0 ?因為如果不寫,默認可能會輸出 4.x 版本的老 API。
盡管得到的答案并不是 Dart 代碼而是 OC ,但是關鍵詞registerEventHandler和Message我們捕抓到了,簡單對比一下,就是Flutter SDK里的registerEventHandler對象,可以發現平替的接口就是onStreamMessage回調。
那么接著就是彈出什么內容,因為我們沒有素材,假設還沒有設計師,那不如就讓 ChatGPT 幫我們畫一只兔子吧,不過測試結果并不好,如下圖所示,從輸出結果上看 ,這并不是我們想要的。
這里是我自己加的粉色,不然都是白色會糊成一坨,不得不說 ChatGPT 在繪制能力上 “很抽象”。
所以 ChatGPT 有時候也不是很智能,可能目前在繪畫理解上它還沒那么成熟, 但是沒問題, ChatGPT 是可以通過上下文學習 “調教” 的,比如我們覺得兔子的耳朵形狀太離譜,那么我們可以讓 ChatGPT 給我們調整。 如下所示,雖然調整之后依然不對,但是比起一開始是不是好很多了?
這就是 ChatGPT 在每次會話上下文里學習的表現。
然后我們在兔子耳朵的基礎上再讓 ChatGPT 補全兔子頭,雖然最終的效果依然不理想,但是比起一開始已經進步了很多。
同時我們還讓 ChatGPT 給我們畫了一個 “星星”,然后結合這兩個 Canvas 繪制的素材,我們在代碼里設置接收到 "兔子" 和 星星 文本的時候,就彈出一個放大動畫效果。 最終運行后效果如下 GIF 所示,看起來很簡陋,但是要知道,我們只是經過了簡單的復制 / 粘貼就完成了這樣的效果,這難道不是開發效率的極大提高?
源碼在后面。
來自 ChatGPT 的兔子頭代碼:
class StarPaint extends StatelessWidget { @override Widget build(BuildContext context) { return CustomPaint( painter: HeartPainter(), size: Size(50, 50), ); } } class StarPaint extends CustomPainter { @override void paint(Canvas canvas, Size size) { final paint = Paint() ..color = Colors.red ..style = PaintingStyle.fill; final path = Path(); final halfWidth = size.width / 2; final halfHeight = size.height / 2; final radius = halfWidth; path.moveTo(halfWidth, halfHeight + radius); path.arcToPoint( Offset(halfWidth + radius, halfHeight), radius: Radius.circular(radius), clockwise: true, ); path.arcToPoint( Offset(halfWidth, halfHeight - radius), radius: Radius.circular(radius), clockwise: true, ); path.arcToPoint( Offset(halfWidth - radius, halfHeight), radius: Radius.circular(radius), clockwise: true, ); path.arcToPoint( Offset(halfWidth, halfHeight + radius), radius: Radius.circular(radius), clockwise: true, ); canvas.drawPath(path, paint); } @override bool shouldRepaint(covariant StarPaint oldDelegate) { return false; } }來自 ChatGPT 的星星代碼:
class StarPaint extends StatelessWidget { @override Widget build(BuildContext context) { return CustomPaint( painter: HeartPainter(), size: Size(50, 50), ); } } class StarPaint extends CustomPainter { @override void paint(Canvas canvas, Size size) { final paint = Paint() ..color = Colors.red ..style = PaintingStyle.fill; final path = Path(); final halfWidth = size.width / 2; final halfHeight = size.height / 2; final radius = halfWidth; path.moveTo(halfWidth, halfHeight + radius); path.arcToPoint( Offset(halfWidth + radius, halfHeight), radius: Radius.circular(radius), clockwise: true, ); path.arcToPoint( Offset(halfWidth, halfHeight - radius), radius: Radius.circular(radius), clockwise: true, ); path.arcToPoint( Offset(halfWidth - radius, halfHeight), radius: Radius.circular(radius), clockwise: true, ); path.arcToPoint( Offset(halfWidth, halfHeight + radius), radius: Radius.circular(radius), clockwise: true, ); canvas.drawPath(path, paint); } @override bool shouldRepaint(covariant StarPaint oldDelegate) { return false; } }自己補充的監聽文本、發送文本和動畫效果代碼:
onStreamMessage: (RtcConnection connection, int remoteUid, int streamId, Uint8List data, int length, int sentTs) { var message = utf8.decode(data); if (message == "兔子") { showDialog( context: context, builder: (context) { return AnimaWidget(Rabbit()); }); } else if (message == "星星") { showDialog( context: context, builder: (context) { return Center( child: AnimaWidget(StarPaint()), ); }); } Future.delayed(Duration(seconds: 3), () { Navigator.pop(context); }); }, Future相信到這里大家應該可以感受到 ChatGPT 提高開發效率的魅力,甚至你還可以把 ChatGPT 集成到你的直播場景里,通過 Flutter 上的chatgpt_api_client插件,你可以在 App 里直接向 ChatGPT 提問,比如通過 OpenAI 的 API 實現一個可以互動的虛擬主播。_onPressSend() async { try { final streamId = await _engine.createDataStream( const DataStreamConfig(syncWithAudio: false, ordered: false)); var txt = (Random().nextInt(10) % 2 == 0) ? "星星" : "兔子"; final data = Uint8List.fromList(utf8.encode(txt)); await _engine.sendStreamMessage( streamId: streamId, data: data, length: data.length); } catch (e) { print(e); } } class AnimaWidget extends StatefulWidget { final Widget child; const AnimaWidget(this.child); @override State createState() => _AnimaWidgetState(); } class _AnimaWidgetState extends State { double animaScale = 1; @override void initState() { super.initState(); Future.delayed(Duration(seconds: 1), () { animaScale = 5; setState(() {}); }); } @override Widget build(BuildContext context) { return AnimatedScale( scale: animaScale, duration: Duration(seconds: 1), curve: Curves.bounceIn, child: Container(child: widget.child)); } }
我怎么知道這個插件?肯定也是問 ChatGPT 的啊~
04 最后
到這里,相信大家應該能感受到,在使用 ChatGPT 之后,** 整個開發效率能夠得到很大的提升,特別是內容檢索的高效和準確上比搜索引擎更加靠譜,** 另外也能幫我們完成一些 “體力活” 形式的代碼。 當然我們也看到了目前 ChatGPT 并不能完全替代人工,因為它在很多方面生成的內容并不完美,特別是很多代碼還是需要我們人工調整,但是這并不影響 ChatGPT 的價值。 最后引用我曾經看到過的關于 ChatGPT 的一些評價:
「當你抱怨 ChatGPT 鬼話連篇滿嘴跑火車的時候,這可能有點像你看到一只猴子在沙灘上用石頭寫下 1+1=3。它確實算錯了,但這不是重點。它有一天會算對的。」
我相信 AI 并不是直接取代人類的方式,因為它對社會的擠壓不是從水平上碾壓,而是劣幣驅逐良幣,比如有位大佬就說過:「乙方最討厭甲方什么都不懂還 bb,但乙方的議價權恰恰來源于甲方什么都不懂還 bb」 ,而現在 ChatGPT 在慢慢消磨掉整個議價權。 總的來說「ChatGPT 只是一個產品,它不代表的整個技術的 “上限” ,它代表的是技術已經到達商用的臨界點」。 現在,它在慢慢成為開發圈子里的習慣,和曾經的 Copilot 一樣,而同時它在其他領域如文字編排等的能力,甚至遠超它在開發領域的價值。
審核編輯 :李倩
-
AI
+關注
關注
87文章
30172瀏覽量
268439 -
SDK
+關注
關注
3文章
1026瀏覽量
45780 -
ChatGPT
+關注
關注
29文章
1548瀏覽量
7504
原文標題:把ChatGPT加入Flutter開發,會有怎樣的體驗?
文章出處:【微信號:OSC開源社區,微信公眾號:OSC開源社區】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論