與ChatGPT結(jié)對(duì)編程實(shí)現(xiàn)代碼詳解
以終為始
按照上一篇的架構(gòu),我們整個(gè)程序最后寫(xiě)完的運(yùn)行視圖大概是下面這個(gè)樣子的:
可以看出我們的實(shí)際結(jié)果比我們上一篇文章考慮的還要復(fù)雜:
- 因?yàn)锳I生成的API不接受批量prompt處理,所以我們需要展開(kāi)負(fù)責(zé)表達(dá)簡(jiǎn)單排列組合的DSL-b生成一批DSL-b',再由DSL-b'一對(duì)一轉(zhuǎn)成DSL-c
- 因?yàn)锳PI的限制,不是服務(wù)端生成文件讓我們?nèi)ハ螺d而,而是直接把base64給我們發(fā)回來(lái),所以不得不自己存文件還得考慮太多了怎么分分文件夾
- 因?yàn)槲覀兊哪繕?biāo)是了解哪些關(guān)鍵字和其圖片好用,還得寫(xiě)點(diǎn)信息到png里,不然光看個(gè)圖片能看出什么來(lái)?(其實(shí)AI也寫(xiě)了一些進(jìn)去,但是畢竟是混編后的,不太好讀)。而由于nodejs技術(shù)欠缺,不得不還要調(diào)用python
但總的來(lái)說(shuō),它還是上文那個(gè)架構(gòu),只是每一層都只寫(xiě)了一個(gè)節(jié)點(diǎn),畢竟個(gè)人用,也不需要那么多擴(kuò)展。而即便如此也是個(gè)挺復(fù)雜的結(jié)構(gòu),這么復(fù)雜的結(jié)構(gòu),自然不能一下生成出來(lái),我們需要分塊實(shí)現(xiàn),那么我們的每一塊要怎么實(shí)現(xiàn)呢?有什么套路呢?這將是我們接下來(lái)的內(nèi)容,內(nèi)容比較長(zhǎng),這一篇我們先講生成代碼這一步的套路。
如何編寫(xiě)一個(gè)節(jié)點(diǎn)
編寫(xiě)主干邏輯
我們以dsl_translator這個(gè)節(jié)點(diǎn)為例,一開(kāi)始,我只是這么提問(wèn)(并沒(méi)有,我只是演示一個(gè)錯(cuò)誤示范):
我想用nodeJS用下面的yaml描繪的數(shù)據(jù)結(jié)構(gòu)得到一個(gè)新的數(shù)組:
base: steps: 10 batch_size: 1 poly: - template_prompt: template: > a cat, ${ chara } ${ facial_expressions } meta: view_angle: - side_view, - front_view, faal_expressions: - (smile:1.5), - (smile:1.2), - smile,
果然,ChatGPT也不慣著我,給出了我下面的代碼:
//...... const newArray = []; data.poly.forEach(item => { const obj = { template: item.template_prompt.template, meta: item.template_prompt.meta }; newArray.push(obj); }); console.log(newArray);
我仿佛聽(tīng)到AI在說(shuō):“你就說(shuō)是不是數(shù)組吧,你要數(shù)組,我給你數(shù)組,沒(méi)毛病~”
遇到這種問(wèn)題,只能反思是自己沒(méi)說(shuō)清楚,那么該怎么說(shuō)清楚呢?
我們這個(gè)程序是服務(wù)于AI畫(huà)圖的,其實(shí)我們也可以從AI畫(huà)圖中借鑒不少經(jīng)驗(yàn),比如說(shuō)在AI畫(huà)圖中,有一個(gè)control net里的open pose技術(shù),我可以畫(huà)個(gè)骨架,就像下面這樣:
我就可以用這個(gè)骨架圖去畫(huà)個(gè)穿靴子的貓,拿著個(gè)刺劍什么的。這個(gè)技術(shù)告訴我們什么呢?我們可以通過(guò)描述一個(gè)骨架來(lái)控制AI生成的內(nèi)容。那么圖片可以,編程是不是也可以?
于是經(jīng)過(guò)一段時(shí)間的摸索,最后我寫(xiě)出了下面的描述:
我想用nodeJS用下面的yaml描繪的數(shù)據(jù)結(jié)構(gòu)得到一個(gè)新的數(shù)組:
base: steps: 10 batch_size: 1 poly: - template_prompt: template: > a cat, ${ chara } ${ facial_expressions } meta: chara: - Abyssinian, - cat_in_boots, facial_expressions: - (smile:1.5), - (smile:1.2), - smile,
要求:
假設(shè)上面的yaml轉(zhuǎn)成json的轉(zhuǎn)換代碼我已經(jīng)寫(xiě)完了
我需要遍歷poly下的所有的頂層元素
遍歷過(guò)程中,要處理template_prompt元素的子元素:
從template中讀取作為模版。
讀取meta中的屬性,因?yàn)閷傩钥赡苊看味疾灰粯?,是不確定的,所以不能硬編碼。
然后基于meta中的屬性,把template作為 string literal 解析,這個(gè)解析代碼我已經(jīng)有了,假設(shè)名為render_string_template,可以不實(shí)現(xiàn),留一個(gè)函數(shù)接口即可。
要遍歷組合meta中的屬性形成一個(gè)數(shù)組,比如chara 有兩個(gè)值,facial_expressions 有三個(gè)值,那么應(yīng)該生成2*3也就是六組屬性放入這個(gè)數(shù)組中,這個(gè)數(shù)組和template會(huì)被傳入render_string_template函數(shù),最后會(huì)獲得兩個(gè)prompt字符串
將生成的prompt字符串?dāng)?shù)組和template_prompt元素之外的其他元素合并成一個(gè)對(duì)象,要求在同一級(jí)別。prompt字符串?dāng)?shù)組有幾個(gè)元素,就會(huì)合并成幾個(gè)對(duì)象,并放入一個(gè)新數(shù)組中,我們稱之為ploys。
繼續(xù)遍歷,直到遍歷完所有頂層元素,所有元素都放入了polys中。polys是一個(gè)一維數(shù)組。
將ploys中的每一個(gè)元素與base中的屬性合成一個(gè)新的對(duì)象,base的屬性展開(kāi)與prompt屬性同級(jí),當(dāng)ploys中的每一個(gè)元素的屬性名與base中的屬性名相同時(shí),覆蓋base中的屬性。這些新對(duì)象組合出的數(shù)組就是我要的數(shù)組
可以看到,我在之前的prompt的后面加了很多的描述,其中的2-6就是用自然語(yǔ)言講了我期望這個(gè)函數(shù)實(shí)現(xiàn)整個(gè)過(guò)程的大概邏輯。這個(gè)手法就很像Open Pose。通過(guò)這個(gè)操作,我得到了我想要的代碼,畢竟這個(gè)邏輯不復(fù)雜。
邊界劃分
上面prompt的要求里除了表達(dá)了主干邏輯,其實(shí)也用了一些其他的技巧。比如“1. 假設(shè)上面的yaml轉(zhuǎn)成json的轉(zhuǎn)換代碼我已經(jīng)寫(xiě)完了”,“把template作為 string literal 解析,這個(gè)解析代碼我已經(jīng)有了,假設(shè)名為render_string_template,可以不實(shí)現(xiàn),留一個(gè)函數(shù)接口即可。”
這個(gè)呢有點(diǎn)像control net里的seg技術(shù),他就是把圖片上的東西都分區(qū)域,就像下面這樣:
然后再生成的就靠譜很多,甚至你還可以對(duì)其中特定部分進(jìn)行針對(duì)性的描述。這個(gè)技術(shù)就很適合用來(lái)控制AI生成的注意力焦點(diǎn)。
那么在我們這個(gè)例子,上面提到的那些描述就起到了seg的作用。他可以讓我們不用在一個(gè)prompt描述里編寫(xiě)所有的細(xì)節(jié),這對(duì)于復(fù)雜的邏輯很有幫助。因?yàn)榧幢闶且粋€(gè)小節(jié)點(diǎn),其實(shí)邏輯也可能有點(diǎn)復(fù)雜的,就像這里面,我們可以把meta數(shù)據(jù)與tempalte合并的功能延遲實(shí)現(xiàn),只描述與他的交互,使生成的程序先把參數(shù)扔給它。有的時(shí)候,我們想要使用特定的庫(kù),也可以用這個(gè)方式,比如我在另外一個(gè)場(chǎng)景下是這么實(shí)現(xiàn)的這個(gè)效果:
1. 讀取文件的fs,要使用const fs = require('fs/promise')引入。
2. 用js-yaml庫(kù)解析yaml。
通過(guò)這樣的技巧,我們就可以把代碼進(jìn)行進(jìn)一步的分解,讓我們更容易描述清楚我們想要部分的業(yè)務(wù)邏輯,而且也可以給我們節(jié)省點(diǎn)算力,畢竟ChatGPT生成的時(shí)候如果太長(zhǎng)也會(huì)中斷,你讓他繼續(xù)的話,有時(shí)候也續(xù)不上。這個(gè)技巧就可以讓他專注于你希望他專注的地方,從而提高表現(xiàn)力。
功能迭代
上面的寫(xiě)完之后呢,我發(fā)現(xiàn)一個(gè)問(wèn)題,這個(gè)DSL還不是我想要的最終形態(tài),對(duì)于同一套模板,我可能需要多種meta,因?yàn)橛行傩缘慕M合是沒(méi)有意義的,我也不想浪費(fèi)我寶貴的GPU。那么我們就不能太暴力的讓它窮舉所有的組合,我要針對(duì)同一個(gè)tempalte給他不同的屬性組合,所以meta的值就必須是個(gè)列表,大概如下所示:
base: steps: 10 batch_size: 1 poly: - template_prompt: template: > a cat, ${ chara } ${ facial_expressions } meta: - chara: # 這里改成了數(shù)組 - Abyssinian, - cat_in_boots, facial_expressions: - (smile:1.5), - (smile:1.2), - smile,
于是現(xiàn)在我們就面臨一個(gè)問(wèn)題:改代碼。而這個(gè)時(shí)候就體現(xiàn)出我們之前拆任務(wù)的價(jià)值了,因?yàn)楦鱾€(gè)模塊都是隔離的,那就沒(méi)有什么改代碼,重新生成一份就好了,所以我改了改要求:
要求:
假設(shè)上面的yaml轉(zhuǎn)成json的轉(zhuǎn)換代碼我已經(jīng)寫(xiě)完了
我需要遍歷poly下的所有的頂層元素
遍歷過(guò)程中,要處理template_prompt元素的子元素:
從template中讀取作為模版。
讀取meta中的屬性,因?yàn)閷傩钥赡苊看味疾灰粯?,是不確定的,所以不能硬編碼。
然后基于meta中的屬性,把template作為 string literal 解析,這個(gè)解析代碼我已經(jīng)有了,假設(shè)名為render_string_template,可以不實(shí)現(xiàn),留一個(gè)函數(shù)接口即可。
要遍歷組合meta中的每一個(gè)屬性組形成一個(gè)數(shù)組,
每一個(gè)屬性組可能只需要看做一個(gè)對(duì)象,當(dāng)且僅當(dāng)每一個(gè)屬性值都為單值
每一個(gè)屬性組可能也需要展開(kāi),當(dāng)且僅當(dāng)任何一個(gè)屬性值有多值,比如 chara 有兩個(gè)值,facial_expressions 有三個(gè)值,那么應(yīng)該生成2*3也就是六組屬性放入這個(gè)數(shù)組中,這個(gè)數(shù)組和template會(huì)被傳入render_string_template函數(shù),最后會(huì)獲得兩個(gè)prompt字符串
將生成的prompt字符串?dāng)?shù)組和template_prompt元素之外的其他元素合并成一個(gè)對(duì)象,要求在同一級(jí)別。prompt字符串?dāng)?shù)組有幾個(gè)元素,就會(huì)合并成幾個(gè)對(duì)象,并放入一個(gè)新數(shù)組中,我們稱之為ploys。
繼續(xù)遍歷,直到遍歷完所有頂層元素,所有元素都放入了polys中。polys是一個(gè)一維數(shù)組。
將ploys中的每一個(gè)元素與base中的屬性合成一個(gè)新的對(duì)象,base的屬性展開(kāi)與prompt屬性同級(jí),當(dāng)ploys中的每一個(gè)元素的屬性名與base中的屬性名相同時(shí),覆蓋base中的屬性。這些新對(duì)象組合出的數(shù)組就是我要的數(shù)組
改完要求,配上上面的DSL直接扔給了它,得到了我們想要的代碼。
所以在今天這個(gè)時(shí)刻,有些重構(gòu)工作突然變得沒(méi)有那么大價(jià)值了,因?yàn)樵趧澏ê玫倪吔缋?,重?xiě)比重構(gòu)更快,盡管兩次輸出的代碼并不一樣,但是他們的功能是一樣的。
總結(jié)一下
我們首先概述了整個(gè)程序的最終運(yùn)行視圖,以及在實(shí)現(xiàn)過(guò)程中遇到的挑戰(zhàn)。然后我們以dsl_translator節(jié)點(diǎn)為例,講解了一些與ChatGPT結(jié)對(duì)編程的一些套路:
首先是編寫(xiě)主干邏輯,通過(guò)描述我們期望函數(shù)實(shí)現(xiàn)的邏輯,類似于Open Pose中的骨架圖,以控制AI生成的內(nèi)容。我發(fā)現(xiàn)很多人讓ChatGPT工作的時(shí)候總想給一句話需求,軟件還是很復(fù)雜的,必要的話,我們還是要把主干邏輯寫(xiě)出來(lái)。程序員有多討厭一句話需求,我想我不用多說(shuō)了,到咱們自己提需求的時(shí)候,可不能忘本。
接著我們介紹了通過(guò)類似于Control Net中的seg技術(shù),我們將代碼進(jìn)行更細(xì)致的分解,這樣的技巧可以使AI專注于我們希望專注的地方,同時(shí)讓我們自己也更容易描述我們想要的業(yè)務(wù)邏輯,既節(jié)省算力,也能提升AI的表現(xiàn)。畢竟GPT-4還沒(méi)有廣泛使用,3000字還是個(gè)門(mén)檻。其實(shí)即便是廣泛使用了,我們不要忘了,AI這個(gè)東西生成總是不太穩(wěn)定的,如果你切得足夠小,成功率總是要高得多,而且你可以靠反復(fù)生成來(lái)達(dá)成某種自動(dòng)化,這個(gè)呢,我們下一篇文章討論。
接著我們面對(duì)了一個(gè)需要改代碼以加入新功能的場(chǎng)景,因?yàn)槲覀冎耙呀?jīng)進(jìn)行了設(shè)計(jì),既然已經(jīng)拆分好了模塊,重新生成一份代碼來(lái)實(shí)現(xiàn)新的功能,比改代碼更快。第一次使用這個(gè)技巧的時(shí)候,我想起了流量地球里550C接入的時(shí)候,發(fā)出的提示音:正在生成底層操作系統(tǒng)。是的,對(duì)于AI來(lái)說(shuō),重寫(xiě)比重構(gòu)更快。這個(gè)點(diǎn)極大地動(dòng)搖了軟件工程中好多具體實(shí)踐的根基,從這一刻起,面面俱到的文檔勝過(guò)了可以工作的代碼。具體到行為上的變化就是,我現(xiàn)在寫(xiě)代碼都會(huì)有意識(shí)的保存自己對(duì)AI提問(wèn)的prompt,并且我會(huì)在每個(gè)節(jié)點(diǎn)的根路徑上記錄下完善的技術(shù)文檔。
到此為止,我們把與ChatGPT結(jié)對(duì)寫(xiě)代碼中,怎么第一次把代碼寫(xiě)出來(lái)這一步的主要技巧給講的差不多了。但其實(shí)這還遠(yuǎn)遠(yuǎn)不夠,正如沒(méi)有ChatGPT的時(shí)代我們就知道的,第一次把代碼寫(xiě)出來(lái)所占軟件開(kāi)發(fā)的成本少得可憐,軟件開(kāi)發(fā)成本的絕大部分都是在后續(xù)迭代中花掉的。那么對(duì)于一個(gè)節(jié)點(diǎn)也是一樣的,盡管我們展示了一種重寫(xiě)優(yōu)于重構(gòu)的新思路,但這一篇也僅僅講了個(gè)思路,AI生成的不穩(wěn)定性依然是懸在我們心頭上的一把刀,所以我們接下來(lái)會(huì)講講更多的工程實(shí)踐來(lái)確保我們持續(xù)寫(xiě)出可信的代碼。
以上就是與ChatGPT結(jié)對(duì)編寫(xiě)實(shí)現(xiàn)代碼詳解的詳細(xì)內(nèi)容,更多關(guān)于ChatGPT編寫(xiě)代碼的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
ts?類型體操?Chainable?Options?可鏈?zhǔn)竭x項(xiàng)示例詳解
這篇文章主要為大家介紹了ts?類型體操?Chainable?Options?可鏈?zhǔn)竭x項(xiàng)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09PureScript與JavaScript中equality設(shè)計(jì)的使用對(duì)比分析
這篇文章主要為大家介紹了PureScript中的equality與JavaScript中的equality設(shè)計(jì)對(duì)比分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11TypeScript判斷對(duì)稱的二叉樹(shù)方案詳解
這篇文章主要為大家介紹了TypeScript判斷對(duì)稱的二叉樹(shù)方案實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09TypeScript使用strictnullcheck實(shí)戰(zhàn)解析
這篇文章主要為大家介紹了TypeScript使用strictnullcheck實(shí)戰(zhàn)解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08TypeScript十大排序算法插入排序?qū)崿F(xiàn)示例詳解
這篇文章主要為大家介紹了TypeScript十大排序算法插入排序?qū)崿F(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02TypeScript使用noImplicitAny實(shí)戰(zhàn)解析
這篇文章主要為大家介紹了TypeScript使用noImplicitAny實(shí)戰(zhàn)解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08