作者 | 茹炳晟,騰訊 Tech Lead
1 LLM 在軟件開發(fā)過程中的單點(diǎn)提效
LLM 對軟件研發(fā)的單點(diǎn)提效,我之前錄制過一段視頻,大家可以直接觀看,里面有詳細(xì)的演示,我在這里就不再贅述了。
除此以外,我這里羅列一些更多的可能用途:
智能代碼提示
代碼片段智能生成
SQL 語句的智能生成與調(diào)優(yōu)
更高效更精準(zhǔn)的靜態(tài)代碼檢查與自動修復(fù)(非 rule-based)
智能輔助的代碼評審與代碼重構(gòu)
單元測試和接口測試代碼的自動生成
更高級的重復(fù)代碼檢查(語義重復(fù)檢查)
失敗用例的自動分析與歸因
更精準(zhǔn)的技術(shù)問答
…
看到這里,你有可能會得出一個結(jié)論:完蛋了,程序員要大面積失業(yè)了。真的是這樣嗎?要回答這個問題,我們需要從全局來看問題,首先我們要搞清楚,LLM 對于軟件研發(fā),什么變了?什么沒有變?
2 LLM 對于軟件研發(fā),什么變了?
看了上面的案例,你應(yīng)該已經(jīng)能夠體會到 LLM 對于軟件研發(fā)單點(diǎn)效率提升的各種可能性,這些能力讓我們看到了軟件研發(fā)的變化,我把這些變化總結(jié)為:基礎(chǔ)編碼能力的知識平權(quán),進(jìn)而帶來局部效率的提升。
以前工程師個體學(xué)習(xí)掌握一門計算機(jī)語言以及相應(yīng)的數(shù)據(jù)結(jié)構(gòu)和算法,需要較長的學(xué)習(xí)周期,很多經(jīng)驗和模式還需要工程師個體在大量實(shí)踐中進(jìn)行總結(jié),每個工程師個體都在重復(fù)著這個過程,現(xiàn)在 LLM 讓一個沒有接受過系統(tǒng)培訓(xùn)的個體也能擁有同樣的能力,個體和個體之間的能力差異被 LLM 拉平了,這就是知識平權(quán)。
如果說 ChatGPT 實(shí)現(xiàn)了數(shù)字時代知識的平權(quán),codex 類的代碼語言大模型實(shí)現(xiàn)了基礎(chǔ)編碼能力的知識平權(quán),進(jìn)而帶來軟件研發(fā)的局部效率提升。
可以說,LLM 降低了軟件開發(fā)的門檻,可以讓更多對軟件開發(fā)感興趣的人更加輕松地參與到軟件開發(fā)工作中,同時,LLM 提高了編程的效率和質(zhì)量,使我們可以在更短的時間內(nèi)完成更多的工作,因而能留出更多的時間讓我們思考。
前段時間,前哈佛大學(xué)計算機(jī)科學(xué)教授,曾在 google 和 Apple 擔(dān)任高級工程職位的 Matt Welsh 發(fā)表了一個視頻,其中的主要觀點(diǎn)是“LLM 將會代表著編程的終結(jié)”,他認(rèn)為程序員會被淘汰,未來只有產(chǎn)品經(jīng)理和代碼審查員。我不知道大家對這個怎么看?
我的觀點(diǎn)是,在抱有敬畏之心的同時,我們不要輕易下結(jié)論。為什么,因為軟件研發(fā)還有很多東西是沒有變的,而這些沒有變的才是軟件工程中的核心問題和主要矛盾。
3 LLM 對于軟件研發(fā),什么沒有變?
我們面對的是軟件工程的問題,編程不等于軟件工程,編程只是軟件工程的一部分。軟件工程的四大內(nèi)在特性(復(fù)雜度、不一致性、可變性、不可見性)并沒有因為 LLM 的出現(xiàn)而發(fā)生本質(zhì)上的變化,這才是軟件工程面臨的主要矛盾。
從復(fù)雜度的角度來看,問題域本身的復(fù)雜度并沒有變,本質(zhì)復(fù)雜度也沒有變,變的可能只是一部分的隨機(jī)復(fù)雜度。雖然說局部編碼變簡單,或者說更高效了,但是需求分析和軟件設(shè)計并沒有因為 LLM 而變得簡單,這個稍后我們來詳聊。
從一致性的角度來看,由于軟件研發(fā)的本質(zhì)依然是“知識手工業(yè)者的大規(guī)模協(xié)作”,所以我們非常需要一致性。如果系統(tǒng)是一致的,則意味著相似的事情以相似的方式完成,錯并不可怕,怕的是錯的千變?nèi)f化。LLM 的出現(xiàn)并沒有提升軟件研發(fā)的一致性,甚至由于 LLM 本身的概率屬性,使用 LLM 實(shí)現(xiàn)代碼生成的不一致性問題反而是被放大了,這點(diǎn)我們后面展開講。
從可變性的角度來看,軟件會隨著需求不斷演進(jìn)和變化,所以架構(gòu)設(shè)計和模塊抽象只能面向當(dāng)下,它天然是短視的,或者說是有局限性的,這種局限性即使是最優(yōu)秀的架構(gòu)師也是不可逾越的。
在敏捷開發(fā)模式下這個問題更被凸顯了出來,而且需求本身就是零散的,目標(biāo)也是模糊的,在沒有全局視圖的情況下,架構(gòu)自然就是有局限性,所以需要不斷迭代變更。每個迭代,你能拿到的信息僅僅是宏大視圖中的小小一角,根本沒有全貌,LLM 對此也是無能為力的。
從不可見性的角度來看,軟件的客觀存在不具有空間的形體特征,不同的關(guān)注點(diǎn),會有不同的圖。綜合疊加這些圖是困難的,而且強(qiáng)行可視化的效果會造成圖的異常復(fù)雜,反而失去了可視化的價值。設(shè)計無法可視化就限制了有效的溝通和交流。
如果以上四點(diǎn)再疊加上大型軟件的規(guī)模效應(yīng),其中包含軟件系統(tǒng)本身的規(guī)模和軟件研發(fā)團(tuán)隊的規(guī)模,問題就更嚴(yán)重了,即會顯著提升軟件研發(fā)過程中的溝通成本、決策成本、認(rèn)知成本和試錯成本,而這些才是軟件工程問題的本質(zhì),這些本質(zhì)問題自始至終都沒有變過,LLM 對此也基本無能為力。
基于上述的分析,我們可以看到,軟件工程的核心矛盾并沒有改變,現(xiàn)代軟件工程應(yīng)對的是規(guī)模化場景下的各種問題,基于 LLM 實(shí)現(xiàn)的編程提效只是其中的一小部分,而其中最重要的需求和代碼演進(jìn)模式都沒有發(fā)生本質(zhì)變化,我們接下里分別展開討論一下。
4 需求的重要性沒有變,在 LLM 時代還被放大了
只有我們的需求足夠的清楚,那么生成的代碼才會準(zhǔn)確。如何準(zhǔn)確全面描述需求成為了關(guān)鍵。面向自然語言編程,首先你要有能力把話講清楚。但是問題是:你能講清楚嗎?
我們通過一些實(shí)踐發(fā)現(xiàn),要把需求描述到讓它能正確寫出來代碼,需要的工作量似乎已經(jīng)接近甚至超過編碼了。為什么會這樣,有兩個方面的原因。
一是因為大多數(shù)的代碼實(shí)現(xiàn)是 imperative 的,而需求描述是 declarative 的,這兩者對人的要求完全不一樣。我們程序員群體接受的教育是編程,而不是需求描述,也就是說程序員本來更擅長寫代碼,而不是描述需求。
二是因為在當(dāng)前的開發(fā)模式下,程序員直接用代碼默認(rèn)幫需求(產(chǎn)品經(jīng)理)做了很多代償。很多在需求中沒有明確提及的內(nèi)容被程序員用代碼直接實(shí)現(xiàn)了(代償)。而現(xiàn)在要倒過來先把需求的細(xì)節(jié)完全理清楚這個可能不是程序員當(dāng)前的工作習(xí)慣。而且代碼的信息熵其實(shí)要大于自然語言,程序員更善于用代碼而非自然語言來描述事務(wù)。
舉個例子:如何清楚地描述一個排序函數(shù) sort 的需求?sort 輸出的數(shù)字必須是從小到大排列的,這樣描述需求就夠了嗎?其實(shí)遠(yuǎn)遠(yuǎn)不夠,重復(fù)數(shù)字怎么處理?排序數(shù)據(jù)的數(shù)量有沒有上限?有的話如何提示?排序時長需要有超時設(shè)計嗎?是預(yù)先判定還是中途判斷?算法復(fù)雜度有明確的要求嗎?算法需要應(yīng)對并發(fā)嗎?并發(fā)的規(guī)模怎么樣?等等。
一個軟件的需求,不僅僅是功能性的,還有很多非功能的需求,這些都是需要描述清楚的。另外代碼實(shí)現(xiàn)的時候,還要考慮為可測試而設(shè)計,為可擴(kuò)展而設(shè)計,為可運(yùn)維而設(shè)計,為可觀測而設(shè)計等等。原本這些很多是開發(fā)代償了,現(xiàn)在要從需求生成代碼,你必須要提前講清楚。
所以,我們的總結(jié)是:“軟件從業(yè)者高估了編程的復(fù)雜度,但是卻低估了功能和設(shè)計的深刻度”。
5 代碼是持續(xù)“生長”出來的,需要持續(xù)更新
對于現(xiàn)行的軟件開發(fā)范式,當(dāng)需求發(fā)生變動后,一般是會在原有代碼基礎(chǔ)上改動,而不是直接從頭全量生成全部代碼,這個時候,LLM 本質(zhì)上做的是局部編程的輔助(pAIr programming)。局部編程輔助過程中,經(jīng)常需要對代碼做局部修改,而這個往往并不容易。
我們知道,代碼的信息熵大于自然語言,用信息熵更低的自然語言去描述代碼,尤其是準(zhǔn)確描述大段代碼中的若干個位置往往是困難的。想象一下,如果只用在線聊天的方式跟別人講在代碼的什么地方做修改的效率是何其低下,相比指著屏幕,或者使用專門的 CR 工具,效率的差距是巨大的。
如果需要進(jìn)一步描述如何修改就會更困難,因為大概率需要用到很多代碼上下文的相關(guān)描述,所以對于 prompt 的表述要求以及長度要求都很高。
另外,LLM 接納修改意見(prompt)后的輸出本身也是不穩(wěn)定和不收斂的,同時也具有不可解釋性,LLM 本質(zhì)上不是基于修改意見(prompt)進(jìn)行改寫,而是基于修改意見(prompt)重新寫了一份。輸出的代碼需要人重復(fù)的閱讀和理解,使得認(rèn)知成本變高了。
同時,LLM 的原理決定了其會“一本正經(jīng)的胡說八道”的本質(zhì),會混合捏造一些不存在的東西,可以說人工智能的混合捏造是人工智能在無知情況下的“自信”反應(yīng),而這個點(diǎn)在代碼生成上是災(zāi)難性的,比如會將不同類型的 SQL 語句混在一起使用,會分不清 Go 語言的 os.Kill 和 Python/ target=_blank class=infotextkey>Python 語言的 os.kill()。這個問題可能需要使用 AI 審計 AI 的方式來緩解了。
剛才提到,要在原有代碼基礎(chǔ)上修改,就需要利用已有的代碼上下文,而不是從 0 開始。要實(shí)現(xiàn)這一點(diǎn),一個最樸素的做法就是把整個項目的代碼都貼到 prompt 里,但這樣并不現(xiàn)實(shí)。因為 GPT-3.5 限制最多只能 4096 個 tokens,GPT-4 最多 8192 個,除非項目非常小,否則根本放不下。這個問題可能需要用 langchain 來解決了。
LangChain 是一個鏈接面向用戶程序和 LLM 之間的一個中間層,通過輸入自己的知識庫來“定制化”自己的 LLM。langchain 使用 embedding 建立基于項目特定的向量知識庫,實(shí)現(xiàn)“基于特定文檔的問答”。
6 LLM 時代,對軟件研發(fā)更多的思考
思考 1:替代的是碼農(nóng),共生的是工程師
在軟件開發(fā)過程中,當(dāng)偽代碼級別的設(shè)計完成后,最后一公里的編碼實(shí)現(xiàn)會被 LLM 替代,因為基于記憶的簡單重復(fù)編碼不是人的優(yōu)勢,而是機(jī)器的優(yōu)勢。
這部分工作現(xiàn)在屬于碼農(nóng),也就是我們俗稱的 CRUD 仔和 API Boy,所以很多不涉及設(shè)計的碼農(nóng)可能會被大模型替代。
而工程師需要關(guān)注業(yè)務(wù)理解、需求拆分、架構(gòu)設(shè)計、設(shè)計取舍,并在此基礎(chǔ)上通過 prompt 學(xué)會與 AI 合作,從而實(shí)現(xiàn)“工程師 + LLM”形成 1+1 >2 的效果。這就是共生。
需要注意的是,這種共生必須始終保持人的主觀能動性,機(jī)器必須是 Copilot,也就是智能副駕駛,主駕駛位置必須是人,這樣的人 - 機(jī)關(guān)系才能長期健康發(fā)展。這也就是為什么說微軟現(xiàn)任 CEO 薩提亞強(qiáng)調(diào) Copilot(智能副駕駛)是比 Autopilot(自動駕駛)還先進(jìn)的根本原因。
另外,特別要提的是:短期內(nèi)率先學(xué)會使用 LLM 的工程師必將獲益,但是很快大家都會掌握,這個時候能力水平就再次被拉平了,這個很像之前“外賣騎手困在系統(tǒng)里”那篇文章中的觀點(diǎn),所以作為共生的工程師,我們更需要從以下三個方面來強(qiáng)化自己的能力:
需求理解、需求分析、需求拆解的能力
架構(gòu)設(shè)計、架構(gòu)分析、設(shè)計取舍的能力,并推動設(shè)計的文檔化和規(guī)范化
理解問題本質(zhì),而不是單純學(xué)習(xí)應(yīng)用(授人以魚不如授人以漁)
思考 2:有利于控制研發(fā)團(tuán)隊規(guī)模,保持小團(tuán)隊的優(yōu)勢
隨著一個軟件規(guī)模的擴(kuò)展,軟件項目參與的人越來越多,分工越來越細(xì),人和人之間需要的溝通量,也指數(shù)增長。很快你會發(fā)現(xiàn),溝通花費(fèi)的時間,漸漸地就比分工省下來的時間還要多。說白了,過了一個臨界點(diǎn),人越多不是越幫忙,而是人越多越添亂。一個人 12 個月能完成的事,不見得上 12 個人 1 個月就能完成,甚至 12 個月也未必能完成。
《人月神話》里建議了一種組織方式,叫“外科手術(shù)式的隊伍”。就像一臺外科手術(shù)一樣,有一個主刀大夫,軟件項目也應(yīng)該有一個首席程序員,其他人都是給他提供支持的。這樣,就既能獲得由少數(shù)頭腦產(chǎn)生的產(chǎn)品完整性,又能得到多位協(xié)助人員的總體生產(chǎn)率,還徹底地減少了溝通的工作量。
但是軟件規(guī)模大了之后,需要的程序員數(shù)量必然會更多,團(tuán)隊規(guī)模一定會加速擴(kuò)展。但是 LLM 的出現(xiàn),讓基礎(chǔ)編程工作一定程度上實(shí)現(xiàn)了自動化,這樣非常有利于控制研發(fā)團(tuán)隊規(guī)模,保持小團(tuán)隊的效率優(yōu)勢。
思考 3:暗知識
大模型的成功很大程度上來自于對已有的互聯(lián)網(wǎng)文本語料和專業(yè)書籍等資料的學(xué)習(xí)。相對應(yīng),在軟件工程領(lǐng)域,需要學(xué)習(xí)的不僅僅是代碼,還應(yīng)該包括需求,設(shè)計。
但是,很多需求和設(shè)計并不以文檔的形式存在,往往會存在于程序員和架構(gòu)師的腦子里,或者在討論的過程中。就算有文檔,文檔和代碼大概率不同步。就算文檔同步,文檔(需求和設(shè)計)背后經(jīng)常有大量的方案對比和推敲,甚至有很多要在原有債務(wù)基礎(chǔ)上的設(shè)計妥協(xié),這些決策過程一般都不會明確地被記錄下來。這些沒有被文檔化下來的知識,我們稱其為“暗知識”。
雖然我們說只要有足夠的數(shù)據(jù),大模型就可以學(xué)到需求和設(shè)計知識。但這些“暗知識”本身就很難被捕捉到,“足夠的數(shù)據(jù)”這一前提在需求分析和軟件設(shè)計可能難以滿足。
另外,在實(shí)際軟件開發(fā)中,需求可能一次不能表達(dá)得很清楚,需要一邊開發(fā)一邊逐步寫清楚需求。尤其是敏捷開發(fā)更是如此。所以一些通用的,不需要特定領(lǐng)域知識的問題,LLM 的表現(xiàn)會比較好,但是那些專用的,需要特定領(lǐng)域知識(私域知識)的問題,LLM 就可能不是很擅長。
總結(jié)來看:“你能想到的多過你能說出來的,你能說出來的多過你能寫下來的。”所以這就天然限制了 LLM 能力的上限。
思考 4:Prompt 即代碼,代碼不再是代碼
讓我們做個大膽的設(shè)想,如果當(dāng)軟件需求發(fā)生變化的時候,我們不再是去改代碼,而是直接修改需求對應(yīng)的 prompt,然后基于 prompt 直接生成完整的代碼,這個將是軟件開發(fā)范式的改變。
在這種范式下,我們需要確保代碼不能有人為修改,必須都是由 prompt 直接生成,此時我們還需要對 prompt 做版本管理,或許會出現(xiàn)類似今天 git 的 prompt 版本管理的新物種。
此時,從本質(zhì)上來看 prompt 即是代碼,而原本的代碼不再是代碼了,這就真正實(shí)現(xiàn)了基于自然語言(prompt)的編程,此時的編程范式將從 prompt to code 轉(zhuǎn)變?yōu)?prompt as code。
更進(jìn)一步思考,當(dāng)實(shí)現(xiàn)了 prompt as code,我們是否還需要 code,關(guān)于代碼的很多工程化實(shí)踐還重要嗎?現(xiàn)在我們之所以認(rèn)為代碼工程化很重要,因為代碼是人來編寫,代碼是人來維護(hù)的。而當(dāng)代碼由 LLM 來編寫,由 LLM 來維護(hù)的話,那么現(xiàn)有的軟件架構(gòu)體系還適用嗎?這個時候或許才真正實(shí)現(xiàn)了軟件研發(fā)范式的進(jìn)化。
思考 5:直接可運(yùn)行,prompt to executable 軟件開發(fā)范式的可能性
在深入一步思考,直接可運(yùn)行,prompt to executable 的基礎(chǔ)設(shè)施會出現(xiàn)嗎?
代碼只是軟件工程中的一部分,遠(yuǎn)遠(yuǎn)不是軟件工程的全部,你想想你有多少時間占比是在編碼的。一般來講,編碼完成后往往要經(jīng)歷 CI 和 CD 等一些列的工程實(shí)踐才能向終端用戶交付價值。
所以全新的軟件范式是否可以實(shí)現(xiàn)從 prompt 直接到可運(yùn)行的程序?qū)嵗磕壳皝砜矗蛟S Serverless 是可能的架構(gòu)之一。
思考 6:計算機(jī)教育的反思
LLM 出現(xiàn)后,關(guān)于對計算機(jī)教育的反思我認(rèn)為有兩個層面的思考:
首先是,計算機(jī)學(xué)科研究方向的變化,之前 NLP、知識圖譜、代碼理解,代碼缺陷發(fā)現(xiàn)、test oracle 生成等都是獨(dú)立的研究方向,但是 LLM 表現(xiàn)出的 AGI 能力似乎讓這些垂直領(lǐng)域的研究失去的意義,因為 LLM 的 AGI 能力都能解決,或許效果還更好。
所以這些研究方向?qū)⒑稳ズ螐氖切枰覀兯伎嫉摹S腥苏f LLM 是 NLP 的新里程碑,但也有人認(rèn)為其更像是 NLP 的墓志銘,這句話很好的表達(dá)了我的觀點(diǎn)。
其次,LLM 一次次地證明了通過“死記硬背 + 簡單推理”就能通過大多數(shù)人類的考試和技術(shù)面試。那教育的終極目標(biāo)又是什么?先進(jìn)的人工智能嘗試把機(jī)器培養(yǎng)成人,而落后的教育試圖把人培養(yǎng)成機(jī)器。計算機(jī)教育,其實(shí)我們整個教育到了需要徹底反思的時刻了。
或者我們都錯了?!
彼得德魯克說過“動蕩時代的最大風(fēng)險不是動蕩本身,而是企圖以昨天的邏輯來應(yīng)對動蕩”,今天 LLM 對軟件工程的影響,我還是在以以往的邏輯在做分析,這個基礎(chǔ)可能本來就是錯誤,全新的時代需要全新的思維模式,然后我們拭目以待。