天天看點

我用低代碼結合ChatGPT開發,每天多出1小時摸魚

作者:閃念基因

導讀

GPT 出現之後,很多人推測大量的軟體都會因為其出現而重寫。本文主要是低代碼平台與 ChatGPT 結合的一些思考以及實踐。期望與各位讀者一起搭上 AI 這列快車,為開發提提速~

目錄

1 背景

2 Demo 示範

3 思路

3.1 ChatGPT+代碼生成工具結合模式

3.2 ChatGPT 代碼生成現狀

3.3 現階段可行的思路

3.4 案例

4 設計實作

4.1 架構分層

4.2 插件化

4.3 研發調整

5 總結

01

背景

從探索模型驅動開發開始,我一直在思考一個問題:“軟體,是否可以用更簡單、更人性化的方式生成”,ChatGPT 給我了一個肯定的回答。

我們此前根據領域模型在生成代碼方面進行了一些探索,希望用模組化時間高倍率置換編碼時間。随着代碼工具的不斷完善,效率提升越來越難,因為模型是抽象的而實作是具體的,模型所承載的資訊并不足以直接生成代碼,一定需要“人”來補充資訊,這部分工作工具無法替“人”來完成。

我用低代碼結合ChatGPT開發,每天多出1小時摸魚

直到體驗了 ChatGPT,在震驚于它強大的能力同時,我們也就“如何将 ChatGPT 引入我們的代碼生成工具來提升研發效能”進行了思考,并且快速搭建了一些 Demo 驗證效果。

02

Demo 示範

這裡示範了工具基于領域模型生成代碼的流程,在第3到5步工具內建了一個基于 ChatGPT 接口實作的插件,該插件自動提取模型中的中文類名、成員變量名、成員函數中文名,然後将中文名以及翻譯用途、命名風格輸入到 ChatGPT 得到翻譯結果,并自動填充回工具,最後生成代碼。

這裡僅僅是簡單地使用了 ChatGPT 的翻譯能力,卻給我們帶來了巨大的提升,想象一下一個項目數十個類名、數百個成員變量名以及函數名需要根據中文翻譯為英文,有些詞還要使用翻譯軟體翻譯後再根據使用用途(類名使用名詞或者名詞短語、方法名使用動詞)轉換詞性,然後調整為大駝峰或者下劃線連接配接等風格,這是多麼無趣和繁瑣的工作,而現在隻需要一鍵填充,然後做微小調整即可。

僅接入了 ChatGPT 的翻譯能力就提效如此明顯,那如果将 ChatGPT 的能力封裝為一個一個插件嵌入到整個研發過程,那會達到什麼效果呢?

03

思路

3.1 ChatGPT+代碼生成工具結合模式

3.1.1 模式一:直接生成軟體

這種模式讓 ChatGPT 了解人類語言并編寫軟體,例如 ChatGPT 完全可以生成一個可運作的貪吃蛇小遊戲,當然嚴格意義上這種模式并不是 ChatGPT 和代碼生成工具結合,因為根本不需要代碼生成工具參與,這無疑是最簡單、最自然的軟體開發方式。

遺憾的是通過測試發現:ChatGPT 現階段并不能直接通過對話編寫出完整的、複雜的軟體,因為軟體有自己的核心域知識,而且不同的團隊都有自己的規範、環境等要求,例如谷歌使用 gRPC 架構、部署在谷歌雲,而亞馬遜的研發架構和部署環境與谷歌完全不同。我們不可能将這些資訊全部輸入到 ChatGPT(這些資訊太多了,通過會話描述這些資訊需要大量的工作,除了考慮性能以外也擔心敏感資訊洩露問題)。現階段該模式無法實作。我想,盡管 AutoGPT 的出現說明 AI 确實可以從0到1完成一個項目,但我想沒有人敢将它生成的項目直接應用于生産環境。

3.1.2 模式二:生成代碼片段

通過會話将代碼上下文資訊輸入到 ChatGPT,它基于這些資訊完善、編輯代碼,例如 Copilot 插件就是該模式。測試發現 ChatGPT 生成代碼片段的品質比較高且比較穩定。

該模式和模式一的差別是代碼是“工具”将 ChatGPT 生成的“代碼片段”進行組織,最終形成完整的軟體。

3.1.3 模式三:生成DSL

将自然語言轉換為 DSL ,然後基于 DSL 生成代碼或者軟體,這種模式和方案二的差別是 ChatGPT 不直接生成代碼,代碼是由工具根據 ChatGPT 生成的 DSL 生成。ChatGPT 生成 DSL 相對穩定,這種模式生成的代碼品質相對前兩種模式更加可靠。

3.2 ChatGPT 代碼生成現狀

“知彼知己,百戰不殆”,我們首先要對 ChatGPT 的能力有個清晰認識,這樣才能選擇正确的模式。我們閱讀了一些 GPT-4 能力測評論文,也做了大量的實驗驗證,說幾個有意思的點:

  • ChatGPT 是“懂”代碼的,給出一段代碼可以正确的添加注釋,甚至還可以根據上下文優化變量命名、完善代碼;
  • ChatGPT 是會“猜”代碼的,僅僅給出一個函數聲明,它可以根據函數命名、參數命名猜測函數的功能,并生成測試用例;
  • ChatGPT 生成通用代碼(例如基礎庫)比較容易,但是生成特定領域的代碼可能不符合該領域的最佳實踐;
  • ChatGPT 生成代碼品質和使用者有關,輸入越準确生成代碼品質越高,輸入内容過多或者過少都會導緻生成結果變差。

在實際場景中我們寫代碼所依賴的資訊非常多,除了目前檔案的上下文還可能跨檔案、跨系統、跨倉庫……但是由于 ChatGPT 對輸入長度的限制,将所有依賴資訊輸入到 ChatGPT 是不現實的(時間成本、敏感代碼洩露);另外一個問題是互動模式,如果代碼是離線生成還好,但如果是“和 ChatGPT 結對程式設計”對實時性要求是非常高的,想象一下如果 Copilot 每次生成提示都需要1分鐘你還會用嗎?

3.3 現階段可行的思路

結合上述資訊,我們認為盡管目前 GPT4 能力非常強大,但是并不能做到全自動生成應用,尤其是針對某個行業需要比對該行業的最佳實踐和領域知識,需要遵循團隊研發規範。這些是 ChatGPT 現階段所無法做到的。那基于 ChatGPT 現有的能力,如何嵌入到代碼生成工具中呢?我作了一些粗淺的思考:

  • 利用 ChatGPT 輔助生成 DSL,将 DSL 導入到低代碼平台生成符合團隊規範的業務代碼;
  • 将第一步生成的代碼輸入到 ChatGPT,由 ChatGPT 根據上下文補充生成代碼片段并填充到對應位置;
我用低代碼結合ChatGPT開發,每天多出1小時摸魚

讀到這裡也許會有疑問,這明顯就是模式二和模式三的結合,為什麼要分兩次讓 ChatGPT 生成代碼呢?我下面用一個案例進行詳細解釋。

3.4 案例

3.4.1 資訊提取

我用低代碼結合ChatGPT開發,每天多出1小時摸魚

如上圖所示,這是系統用例“接收車位狀态變化”的分析序列圖,通過分析序列圖我們可以得到如下資訊:

· 控制類有一個方法為“停車收費”;

· 控制類的“停車收費”方法依賴實體類“泊位”;

· 分析序列圖中的實體類的成員變量可以在類圖中得到,所有指向該實體的箭頭都映射為一個方法;

· 可以根據分析序列圖得到控制類和實體類方法的僞代碼,例如“來車”的僞代碼如下:

int 泊位::來車(){
  // 1、取值班人員
  排班(時間).取值班人員(值班人員序号);
  if(失敗){
    列印日志
    傳回失敗錯誤碼
  }
  return 0
}           

3.4.2 建構DSL生成代碼

顯然上述資訊并不足以生成代碼,以“泊位::來車”這段僞代碼為例,要想映射為一段符合C++文法的代碼,至少還需要完善下面這些資訊:

  • 翻譯,僞代碼中的單詞都要翻譯為英文;
  • 補充字段類型,時間字段是什麼類型;
  • 模式配置,例如“取值班人員”方法失敗怎麼定義?根據傳回碼判斷還是根據某個出參判斷;

做完這些工作後,我們才能将上述僞代碼使用結構化的語言描述以便生成代碼,例如:

{
"return_type":"int",
"function_name":"ArriveCar",
"param":[],
"impl":[
{
"entity":"Scheduling",
"function":"GetShiftPersonnel",
 "return_type":"int",
"param":[
{
"type":"int",
"name":"number"
}
]
}
]
}           

事實上需要配置的資訊遠遠不止這些,而且完成這些工作的知識都在“人腦”中,隻能人來完成, 建構可以生成的代碼的 DSL 并不簡單。當然我們可以通過一些方法來減少人的工作:例如将填空題修改為選擇題(大多數配置都是勾選操作而不用輸入文字)、總結最佳實踐添加預設選擇項(例如成員變量預設不生成Get函數)等等,然而始終有一部分工作是繁瑣、重複、低效且需要人來完成的,例如上述步驟中的中文根據使用情景不同翻譯為不同詞性、不同格式的英文單詞。而這部分工作就需要引入 ChatGPT 來完成,由人來翻譯500個中文詞可能需要50分鐘(10個/分鐘),而讓 ChatGPT 來翻譯僅需要幾秒鐘。

我用低代碼結合ChatGPT開發,每天多出1小時摸魚

3.4.3 完善代碼

經過第一步從模型提取資訊、第二步将資訊轉換為生成代碼所需要的 DSL ,這時候我們就可以生成代碼了,下面是我們生成的代碼目錄的一個案例:

我用低代碼結合ChatGPT開發,每天多出1小時摸魚

我們打開上述目錄中的頭檔案、PROTO 檔案不僅滿意的點了點頭。但當我們點開泊車類的 .cpp 檔案見到下面内容不僅吐槽:生成的代碼并不能直接運作!

// 來車
int ParkingSpace::ArriveCar() {
//// MDD-TAG-BEGIN:[flow][slot-ArriveCar][函數實作]
int ret = 0;
// 取值班人員
Scheduling scheduling (/*請填充參數*/);
ret = scheduling.GetShiftPersonnel(number);
if (ret != 0) {
 LOG_VERR("--->>錯誤事件名<<---", ret, "GetShiftPersonnel_ERR");
 return ret;
}
return ret;
//// MDD-TAG-END:[flow][slot-ArriveCar]
}           

是的,到這一步大部分的方法實作并沒有生成。在這裡分析一些原因,以上圖中的僞代碼為例,如果要生成代碼需要人補充什麼資訊呢?

  • “排班”實體的構造方法可能有多個,該調用哪個呢?
  • 排班實體的“取值班人員”方法中傳回0是否就意味着擷取成功?
  • 參數“number”使用哪裡的值填充?使用泊車類的成員變量還是某個全局變量填充?
  • 錯誤日志該列印哪些變量?

僅僅是這麼幾行僞代碼就需要補充如此多的資訊,假如這些資訊都是由人來配置,那和直接寫代碼有什麼差別呢?IDE 通過友好的提示,直接寫代碼甚至比在工具配置後生成代碼效率更高!

所幸的是,無需人工配置,隻要我們将僞代碼轉換為業務代碼所需要的頭檔案定義等輸入到 ChatGPT,它就可以自動推導實作,生成代碼。這就是為什麼要“分兩次讓 ChatGPT 生成代碼”。第一次是生成 DSL 以便代碼生成工具生成品質有保證的代碼架構、頭檔案定義等,第二次是根據已經生成的代碼繼續完善業務代碼。

思路已經很明确,要想落實為具體方案,那就需要代碼生成工具有一個好的設計,這裡的好是指“子產品化、低耦合、可擴充”,下面介紹代碼生成工具的設計實作。

04

設計實作

4.1 架構分層

我用低代碼結合ChatGPT開發,每天多出1小時摸魚

(圖源網絡)

上圖中标紅的部分“模型和引擎共同決定了應用的實作程度和擴充性”。本文不是講領域模組化的内容,是以模組化知識這裡不做讨論,我們重點讨論引擎的設計。補充一下系統的架構分層如下:

我用低代碼結合ChatGPT開發,每天多出1小時摸魚

協定棧: 定義代碼生成引擎輸入的格式化結構,可以的話這個結構可以作為規範标準供各種低代碼平台通用使用,當然如果能夠做到這點就能解決掉低無代碼平台的互聯互通問題(難而正确的事),是對整個行業有利的事情。

代碼生成引擎:對協定棧的實作,定義了代碼生成的模版,将輸入的資料進行處理後根據需求填入到不同的模闆中,生成 C++、TS 等代碼。

引擎插件:基于引擎進行拓展,例如使引擎更靈活,支援更多的協定。我們和 ChatGPT 結合實際上就是對 ChatGPT 的能力進行封裝,實作為一個個的插件輔助人來完成工作,提高代碼生成工具的易用性。

代碼生成工具: 對引擎以及插件的封裝,是面向使用者的産品。

4.2 插件化

明确了架構的分層,我們和 ChatGPT 內建的方案也就自然而然地明确了:将模型映射代碼的過程拆解為一個一個的任務,分析哪些任務現階段是由人完成的且 ChatGPT 已經具備相應的能力,則基于 ChatGPT 的能力分裝為一個一個的插件,協助人完成相應的任務,提升效能。下面我們講解對幾個插件的思考。

4.2.1 代碼生成插件

讓 ChatGPT 來生成代碼,Prompt 需要包含如下三部分資訊:有什麼、用什麼、做什麼。

有什麼:上文已經解釋了我們是讓 ChatGPT 生成品質相對穩定的代碼片段,即某一個函數的實作。“有什麼”實際上就是函數的入參、類的成員變量以及全局變量。入參和類的成員變量可以通過解析領域模型得到,而全局變量則定義在代碼生成的模版中,是一些固定的值(一般是全局配置)。

用什麼:即實作該方法需要哪些實體類方法、邊界類方法、基礎庫。實體類方法在3.4.2展示了,DSL 生成代碼後已經有了實體類方法的頭檔案;邊界類方法即對外系統接口的封裝,需要進行統一管理,是以也可以很友善地得到。

基礎庫分為官方庫和第三方庫,官方庫無需輸入,ChatGPT 可以推導出使用正确的方法,而第三方庫由于太多我們暫不處理,我們的目的本來也不是生成100%的代碼,無法生成的這部分就交給研發去補充吧(别忘了研發還有 Copilot 等利器)。

做什麼:需要完善的代碼邏輯,就是僞代碼,在3.4.3有做展示,這裡不再贅述。

插件将上述資訊拼接為 Prompt ,剩下的就交給 ChatGPT 吧,例如上述泊位::來車方法最終由 ChatGPT 完善為:

// 來車
int ParkingSpace::ArriveCar() {
//// MDD-TAG-BEGIN:[flow][slot-ArriveCar][函數實作]
int ret = 0;
// 取值班人員
Scheduling scheduling (time(nullptr));
ret = scheduling.GetAttendant(number_);
if (ret != 0) {
 LOG_VERR("GetAttendant", ret, number_);
 return ret;
}
return ret;
//// MDD-TAG-END:[flow][slot-ArriveCar]
}           

4.2.2 翻譯插件

正如第二章所示範的效果,這個插件我們已經實作完成了。目前大多數模組化工具支援填寫英文名稱,但是英文名稱為非必填字段且大多數人不習慣使用英文模組化,我們提取模型中未關聯英文單詞的中文,拼接為 Prompt 調用 ChatGPT 統一翻譯,一鍵填充。

當然我們所需要的插件不僅僅這兩個,我們也腦爆了單測生成插件、SQL 生成插件等等一系列的提效插件。

4.3 研發調整

我們評估如果按照這樣的思路,實作代碼生成工具可以節省90%以上的工作量,大約剩下的10%是如下工作:

代碼走查:上面已經說了 ChatGPT 生成的代碼隻是相對穩定,是以生成的代碼預設使用/*和*/包裹注釋掉,必須經過研發确認代碼正确且調整完畢後才可以删除注釋投入使用。

代碼完善:ChatGPT 無法生成100%的代碼,例如上一個接口産生了一個中間結果緩存起來,該接口會使用,考慮到 ChatGPT 的性能、插件實作的複雜性、OPENAI 接口收費價格等因素,我們不可能把上一個接口的資訊拼接到Prompt 。這部分邏輯研發直接編寫代碼所付出的成本遠遠低于使用 ChatGPT 所需成本。

單元測試:ChatGPT 寫單元測試的品質遠遠超出我的想象,它會考慮邊界條件等等各種因素,如果是針對基礎庫生成的測試用例代碼幾乎不需要修改就可以直接使用,但是如果是控制類等業務代碼的測試用例卻不能使用,例如上述接口中的 JScode 是小程式産生,毫無疑問 ChatGPT 無法構造出這樣的參數,是 Mock 還是調用可測試性接口擷取還需要研發根據實際情況做出調整。

無論如何,由于業務本身的複雜性,我們不可能寄希望于使用一個工具生成所有代碼,一定有一部分代碼是工具所無法完成的,依然需要人來參與。

但是這部分工作可以通過 Copilot 、IDE 插件等工具來輔助提效。

05

總結

各位仍需注意,如果直接使用 ChatGPT 有敏感資訊洩露風險,各團隊可以根據需要獨立部署AI模型。對于個人開發者可以使用一些開源模型,例如 Vicuna、LLaMA 等。

迄今為止 AI 工具的出現主要還是為了便利人類,而不是代替人類。加以學習利用,這些工具也許能為幫助開發提速。歡迎開發者們在評論區交流。

作者:邬俊傑

來源:微信公衆号:騰訊雲開發者

出處:https://mp.weixin.qq.com/s/3I3PHTkZ-bafYfhm7KrxTA

繼續閱讀