百度關于大模型在研發領域落地的深度思考
一、智能研發工具的發展
首先來看一下智能研發工具的發展歷程和方向。
1. 智能化的發展背景與落地訴求
早期的智能化工具,如 GitHub 的 Copilot 工具,大約在兩年半前推出。最初,Copilot 的主要功能是在開發者編寫代碼時提供自動補全建議。隨著時間的發展,其功能逐漸豐富,現在的 Copilot chat 插件支持語言轉換、單元測試等多種功能。
隨著大模型的發展,所有相關產品都在迅速升級進化。從最初的輔助編碼開始,到現在能夠與開發者協同工作,我們甚至可以將部分任務完全交給 AI 完成。由代碼補全轉變為代碼生成,不僅是在代碼基礎上生成代碼,更多是在需求的基礎上去生成代碼,從單純的寫代碼轉變為服務于整個工程。例如,最近 Copilot 推出的幫用戶搭建單元測試環境的功能,已經超越了單純的代碼編寫層面。
接下來,將從代碼生成的體量、工具職責的變化、效果的優化、能力的提升以及用戶體驗交互方式上的創新等幾個方面來介紹這一工具近期發生的變化。
2. Go Fat:更大規模的代碼補全
首先是“Go Fat”,也就是代碼補全的規模在不斷變大。代碼補全是最經典的智能開發形式。在編寫代碼過程中,自動出現一行灰色的提示,這并非通過函數定義等方式自動生成,而是基于大模型推測出一段話。這種形式對于開發者來說最容易理解和使用,因為它將敲幾個字符的時間簡化為按下 Tab 鍵的時間。在此基礎上,所有工具都希望在符合用戶習慣的前提下進一步升級。
從單行代碼的補全,逐步擴展到代碼塊,甚至基于注釋生成整個方法或函數,從而擴大補全的范圍。
然而,隨著模型生成的代碼量的增加,錯誤率也會相應上升。尤其是當一個工具或模型在不了解具體業務需求的情況下自動生成代碼時,錯誤率往往更高。一旦錯誤率達到一定比例,比如超過一半,那么人工修正這些錯誤所花費的時間將抵消甚至超過工具帶來的效率提升。因此,對于工具而言,必須通過結合模型的編程現場、項目需求以及用戶的操作歷史等多維度信息來提升準確率,使其達到一個既準確又高效的水平。
所有工具都需要解決四個核心問題:觸發時機、推理邏輯、結束位置和檢查質量。
觸發時機的核心在于判斷何時能夠補全一塊代碼,何時應僅補全一行代碼。顯然,這取決于代碼中重復性和規律性的特點。例如,在存在 if-else,for 或 switch 結構時,代碼往往具有明顯的規律性,很多 switch 的 case 中的代碼都非常相似,因此許多工具會在這些場景下觸發多行代碼的補全。在其他場景下,工具可能還是會退回到單行補全。另一種特征是注釋,當你已經寫了一行注釋時,工具可以根據該注釋進一步推理代碼,從而獲得更完整的上下文信息,提高準確性。觸發時機是通過產品層面的語法樹解析來解決的,以達到高質量、低錯誤率的目標。
第二是推理邏輯,將什么東西輸入給模型,模型才能更好的推理。最簡單的情況是將光標位置上下的兩段代碼作為輸入,模型在中間插入新代碼。除此之外,模型還可以利用文件依賴關系和 import 的內容來更好地進行推斷。例如,當你編寫代碼時,通常會打開其他文件以查看可用的功能或變量,然后返回繼續編寫。因此,你最近打開的文件在一定程度上可能會被用到。對此,在后文中還將進行更詳細的說明。
第三是結束位置。由于模型本質上是一個續寫工具,它根據前文生成后文,但若不斷續寫最后一定是錯誤的。并且代碼過長可能會無法使用,少量代碼行則可能更為實用。因此,如何適時地結束代碼生成是關鍵問題。然而,正因為代碼是多行的,沒法僅憑換行符作為 stop sequence 的結束標志,我們需要通過程序方式檢測模型輸出的內容是否應結束。這涉及多種策略,例如:當觸發時機為一個代碼塊時,我們可以在遇到匹配的右括號時結束;如果是 if 里面還有個 if,那等外層的 if 結束時結束;如果觸發時機是一個注釋,那么連續的換行或下一個注釋的出現可能是合適的結束信號;如果代碼塊之間存在空行時,這通常意味著不同的邏輯塊的開始,因此可以在此結束。這些策略與觸發時機密切相關,旨在確保代碼塊的適時結束。
接下來再看檢查質量。在使用模型時,我們偶爾會遇到一些問題,例如模型生成完全重復的代碼片段,這是一解決個顯而易見且需要的問題,但對于代碼質量而言,還存在其他明顯的不符合標準的情況。例如,在訓練過程中可能會遇到數據污染,導致模型生成包含韓文字符的注釋。顯然,無論是面向客戶還是我們自己使用,這種情況都是不可接受的。因此,我們的首要任務是通過策略來屏蔽這些錯誤內容的輸出,第二就是追加模型訓練。除此之外,還有一類模型很容易出現的問題就是代碼過度擬合,生成的代碼與現有代碼過度相似,甚至完全相同,導致無法直接使用,這類問題也要解決掉。最終,通過質量檢查和過濾措施,將高質量的代碼呈現到用戶面前。
通過上述工作,多行代碼補全的正確率或者用戶采納率,都可以達到平均值以上。
3. Be Rich:豐富的生成能力
除了協助補全代碼,我們還需探索模型在更多場景中的應用潛力。一個顯著的例子便是代碼優化。在日常業務開發中,寫面條代碼是再平常不過的事。然而,當業務已經十分繁忙時,再要求他們回頭審視代碼,進行重構與優化,無疑是一種難以接受的工作模式。如果將這類任務交給模型,能夠得到怎樣的幫助呢?
首先,模型會指出代碼中存在重復部分,并建議將這些重復的內容抽象化,通過創建一個函數來封裝它們。其次,模型會指出代碼中的硬編碼問題,如果未來需要修改這些編碼,而其他地方未同步更新,可能會導致問題。因此,它建議提取一些常量,以確保所有相關部分都能通過修改常量來同步更新。第三,它會指出代碼中存在的類型安全問題,比如某個類型應該是從其他部分獲取的,但代碼中沒有進行相應的判斷。因此,模型會建議修改語法,采用問號點等方式來增強類型安全性。第四,模型會指出代碼中的命名不規范問題,并提供一段經過優化的代碼作為示例,經過評測,這段代碼是可靠的。當然,在將這段代碼應用到原始代碼之前,我們進行了一些工程化處理,以確保它們能夠對接。這樣,你可以直接采納這段代碼,從而自然地實現一些常見的優化,而這些優化既不影響業務邏輯,又是相對可靠和正確的。因此,在編碼過程中,我們可以將一部分工作,特別是事后的優化和檢查,交給模型來完成,讓它與我們協同開發。這與以前模型中只能單純編寫一兩行代碼的情況相比,已經有了截然不同的效果。
再來看另一個場景,即函數代碼量過大的情況。當我們不小心編寫了一個 500 行的函數時,作為工程師的第一反應通常是認為這個函數需要拆分。然而,對于忙碌的工程師來說,可能會因為時間緊張而無法立即進行拆分。那么,在這個場景下,模型能做些什么呢?
以前端場景為例,假設我們有一個展示在界面上的組件或者元素。模型首先會在這個組件中抽取一些公共的功能,并將它們封裝成一個函數,使得這個函數可以在多個地方復用。其次,模型會在函數的基礎上再做一層封裝,這在 React 框架中被稱為 hook,這種封裝依然保持了代碼的可復用性。接下來,模型會進行第三層的拆分,將一個大組件拆分成多個小組件。比如,一個包含框、標題和內容的組件,模型會將其拆分成獨立的標題、內容和布局(框)組件。這樣的拆分有助于從代碼實踐的角度將組件分離成不同的層次,然后再通過組合的方式將它們重新整合在一起。當然,這并不是通過簡單的 prompt 就能實現的。這背后其實是我們平時大量實踐經驗的積累,需要將這些經驗轉化為復雜的 prompt,并轉化為一系列類似于 One-shot 或 Few-shots 的例子,放入模型的上下文中。
這是我非常期望的開發類工具所能達到的效果,將我們具有豐富經驗的工程師的實踐融入到產品中,讓所有人都能享受到這些經驗所帶來的好處。
4. Seek Deep:更深度地了解全庫
接下來,來看看產品的第三個進度,這主要體現在它對整個代碼庫的了解程度上。最初的工具對代碼庫沒有任何感知,只是根據光標前后的內容憑感覺生成代碼,這在實際業務中很容易出錯。因此,后來的工具都發展出了全庫代碼索引的概念。簡單來說,全庫代碼索引是通過向量化 embedding 技術,將代碼庫中的所有代碼進行索引,然后根據用戶的需求將其轉化為向量,并進行向量的相似度計算,從而找到相關的代碼。這個過程雖然復雜,但效果卻比較明顯。
例如,當你詢問一個方法的作用時,如果工具沒有了解整個代碼庫,它可能只會告訴你這個方法看起來像是一個用于對話的方法,并逐行解釋代碼。然而,這對于大多數工程師來說并沒有太大的價值。但如果工具了解整個代碼庫,它就能為你梳理出整條業務流程,并生成一個流程圖。這個流程圖并不是簡單的函數調用關系,而是真正業務層面上的流程。
當你需要啟動一個會話時,工具會分情況根據你調用的方法,分析出下游做了哪些復雜的流程,并分析出整個業務流程。特別是當你剛接觸一個代碼庫,還不熟悉其業務,但又急需完成一個需求或修復一個bug時,這種效果會非常明顯。
5. Create By Trust:值得信賴的工作
再審視我們當前所使用的這些工具,還存在一個問題,即其生成的結果常常會產生幻覺。一旦這種現象發生,用戶的信任度便會大幅下降。那么,我們應如何解決這一問題呢?在特定情境下,例如執行某個命令時,該命令可能會在運行過程中突然報錯。面對此類錯誤,我們可以將其提交給模型,并結合一系列工程調優手段進行修復。模型能夠精確地指出錯誤所在,具體到文件的某一行或某幾行代碼,這是因為我們擁有調用棧(step trace)信息。在修復問題后,我們可以清晰地觀察到代碼的變化。
這種做法的優勢在于,當你再次執行該命令時,若看到命令成功運行,顯示為綠色,你便能自然而然地確認問題已被解決。在這種可驗證的場合下使用模型,我們可以以極低的成本迅速判斷模型輸出的正確性,若發現錯誤,則將其撤回即可。在這種情境下,我們與模型之間的協同配合,其效率遠高于人工逐行審查模型輸出的代碼。
6. AT Your Hand:更貼合現場的交互
接下來,我們探討一下交互方面的問題。當前的眾多工具,包括我們自主開發的以及其他大模型應用,一個最顯著的特征就是在界面邊緣添加一個側邊欄,內置對話框以供聊天,這已成為大模型的主要使用方式。然而,對話框在編寫代碼時卻帶來了一個顯著問題:它會打斷編寫者的思路。當我們全神貫注于編寫代碼時,視線始終聚焦于代碼區域。如果此時需要查看對話框,就必須將視線從代碼上移開,轉至側邊欄,這無疑會打斷我們的編程節奏。對于編程人員而言,專注力至關重要。因此,現有的許多工具已開始嘗試將對話框融入代碼區域中,我們稱之為“行間對話(inline chat)”。在行間對話模式下,你可以直接在代碼光標處與工具進行交互。例如,當你需要編寫某段代碼時,只需簡單說明需求,大模型便會根據需求調用并返回相應的代碼。
此外,還可以選擇一段代碼,請求工具根據需求進行修改,如添加邊界條件檢測、判斷返回值是否正確,甚至進行代碼優化等,工具會在你的代碼基礎上進行編輯,并以紅綠塊的形式展示修改內容。若你滿意修改結果,點擊確認即可保留;若不滿意,則點擊取消,修改內容將被撤銷。這種方案能夠讓你在專注編寫代碼的同時,通過自然語言與大模型進行交互,從而更加高效地完成編程任務。
以上就是我們產品在交互方面的發展。
二、企業落地智能研發經驗
1. 在企業中踏實落地開發智能化
從企業和團隊的角度來看,大模型無疑是一個極具潛力的工具,這一點我們都普遍認同。然而,當我們將工具下放至每個個體時,卻會發現大家的接受程度參差不齊。有些人認為大模型對他們的幫助極大,工作效率有了百分之五六十甚至翻倍的提升,他們非常樂意接受并使用這一工具。而另一些人則可能覺得不習慣,偶爾的錯誤會讓他們感到困擾,覺得需要花費額外的時間去校對和修正,因此對大模型持保留態度。還有一些人可能習慣于原有的工作模式,不愿意因為引入大模型而做出改變,他們更傾向于等待,直到大模型成為主流后再考慮使用。
面對這種差異,如果團隊希望大模型能夠盡快被大家接受并發揮作用,那么我們需要做兩件事情:
第一,建立大家對大模型的心智認知。讓大家在日常工作的方方面面都能感受到大模型的存在,使他們在遇到問題時能夠自然而然地想到“或許我可以試試用大模型來解決”,這種心智的建立需要時間和持續的引導。
第二,增強大家對大模型效果的信心。雖然大模型在使用過程中可能會偶爾出現錯誤,但是不是兩次三次,實際上可能是一百次才可能出錯兩次,這種錯誤是相對較少的。只有當大家真正相信大模型即高效又準確時,大家才能愿意去接受它。
針對這兩件事情,我們的處理方式如下:首先,我們思考的是,在何種情況下能讓大家真正感受到大模型始終存在。若僅將大模型的應用局限于編寫代碼的過程中,這顯然是不充分的。因為在編寫代碼時,大模型無非充當了一個 IDE 插件的角色,一旦關閉,其存在感便蕩然無存。此后,我們也可能忘記啟動大模型以輔助其他工作。
2. 感知存在:融合 Devops 必經鏈路
對于工程師而言,有一個不可或缺的環節,即整個 Devops 鏈路。從需求提出,通過需求平臺,到編寫代碼,再到代碼評審、文檔查閱、測試、構建、編譯驗證,直至最后的部署,這些步驟都是不可或缺的。代碼提交后,必須查看流水線狀態;測試結果出來后,必須進行分析;部署上線后,還需確認線上運行情況及上線進度。我們希望在這些環節中融入大模型的應用,即便不期望大模型能帶來顯著的幫助,也希望能通過頻繁的接觸,使工程師們在面對問題時,能夠自然而然地想到大模型。
因此,我們在產品中嘗試引入了以下幾個功能。
首要的是智能化的基于大模型的代碼評審。代碼評審對我們來說是一個必不可少的環節,所有代碼必須經過評審才能入庫。在這個環節,我們引入了大模型來分析提交的代碼差異,并為大家提供一些推薦。具體推薦的內容其實并不是最重要的,關鍵在于大模型能夠發現一些典型的問題。將這一功能應用到我們公司內部后,目前的結果是,在所有的代碼評審環節的評論中,有 20% 是由大模型生成的。其中,有 15% 被大家認為確實是有用的,是一個很好的功能,而非廢話。這個數字對我們來說其實并不高,因為 15% 的正確率,如果放在正常產品中,通常會引起質疑,擔心會有錯誤。但我們的主要目標并不是追求極高的正確率,而是讓大家感受到大模型在協助他們工作。
因此,我們并未將該功能作為產品下線,而是持續保留在第二個環節,即流水線編譯失敗的階段。當編譯失敗時,大模型會自動運行,分析失敗的原因,查看日志,提出可能的問題,并建議開發者通過哪些關鍵詞搜索網絡,或者檢查代碼中可能存在的問題。這個功能并不直接產生任何代碼。
如果從大模型編寫代碼的角度來看,這個功能產生的代碼量為零,但它仍然能夠讓開發者隨時隨地感受到大模型的存在。因此,我們一直將該功能保留在線上,幫助大家進行最基礎、最簡單的分析。通過這樣不斷地向開發者推送大模型的能力,我們公司內部目前已有超過 80% 的工程師在開發階段持續使用大模型相關的工具。
3. 效果信心:利用 CICD 確保效果
接下來,要探討的是如何讓工程師對大模型生成的結果有信心。在這方面,我們有一個非常有效的做法。我們所有的正規項目都配備了 CICD 流水線,它本質上就是檢查代碼的正確性,確保代碼能夠成功編譯和運行。這個流水線為我們提供了一個最基礎、最可靠的信心保障,即我們提供的代碼至少是編譯通過的,能夠運行的。
在這個場景中,我們最深入的一個應用就是使用大模型來編寫單元測試(UT)。這并不是簡單地讓大模型根據重要代碼生成單元測試用例,而是有一系列的處理流程。首先,大模型會刪除不需要測試的代碼,因為保留這些代碼會干擾大模型。同時受限于大模型的上下文長度和成本,它會通過代碼依賴關系添加更多的相關代碼,以確保大模型能夠生成有效的單元測試。在生成單元測試后,大模型并不會直接將其交給用戶。因為此時并不知道這些代碼是否正確,所以它會運行這些單元測試,并生成一個測試報告,根據這個報告大模型會進行翻譯,從最基礎的 Json 中獲取關鍵信息,例如如果我們用的是國內的模型,它就會把英文改成中文。
這個報告通常會反映兩類問題。第一類問題是單元測試是否通過。如果單元測試失敗,可能是單元測試本身寫錯了,也可能是業務邏輯有問題。我們會通過另一個模型來判斷是哪種情況,并相應地修正單元測試或業務邏輯。修正單元測試可能包括修改斷言、刪除錯誤的單元測試或讓大模型修復錯誤的單元測試。第二類問題是覆蓋率不夠。如果大模型生成的單元測試覆蓋率太低,我們會讓模型有針對性地去提升覆蓋率。我們會從覆蓋率報告中找出哪些行沒有被覆蓋,然后分析這些行是在哪些分支條件下,讓大模型解釋這個未覆蓋的分支是什么判斷條件,再將這個解釋吐回給大模型,讓大模型注意到這個情況,再讓大模型根據情況補充更多的單元測試。
通過不斷提升單元測試的正確性和覆蓋率,我們最終會得到一組既正確又全面的單元測試。然后,我們會通過增刪用例,將這些單元測試組合起來,生成最終的單元測試代碼交給用戶。
通過使用文心的小模型來進行不同復雜度的單元測試,我們能夠在模型規模幾乎小了 10 倍的情況下,達到與 GPT-4 基本相同的覆蓋率效果。當然,這是在實驗室環境下可以多輪運行的結果。然而在實際用戶使用時,我們不能接受長時間的運行,因為那會影響用戶體驗。所以我們對時間上會有要求。
我們最終產品化的效果:首先,生成正確率要達到 100%,因為只要代碼能運行,那它一定是正確的;其次,行覆蓋率要達到 30% 以上;最后,整個過程的耗時要小于 30 秒。對于大模型一個比較大的生成任務來說,30 秒已經是一個相對較短的時間了,而且在這個時間內,我們能夠提供 30% 的覆蓋率,這是我們產品化的最終結果。
對于用戶來說,這個能力幾乎是一個純收益的事情。他們不再需要管理代碼,只需要試錯、生成并保存即可。這在我們內部提供了大量的大模型代碼生成量,也是用戶非常喜歡的一個能力。
由于該過程本就正確無誤,無需用戶手動保存文件,因此我們將其融入到代碼管理之中。當提交代碼后,大模型會自動基于你的代碼生成單元測試,并再次提交一份包含單元測試的代碼補丁。對于這份代碼,你幾乎無需進行額外的審查,直接合并即可,因為其正確性已經得到了保證。基于這樣的設計,我們可以讓用戶在全新的開發階段也無需關注單元測試的編寫,同時仍然能夠獲得具有高覆蓋率和正確性的單元測試代碼。
三、開發者智能工具使用實踐
1. 形式變革:智能時代理念
作為個體,在使用這些工具時應該采取哪些實踐方法以更有效地發揮工具的作用呢?
未來,AI 與我們的關系絕不僅僅是工具與人之間的簡單關聯,因此我們不會僅僅停留于使用 AI 的階段,而是致力于與 AI 實現真正的協同工作。所謂協同,意指在某些情況下,我們會將任務分配給 AI 完成,而在其他情況下,則親自處理。并非指在一項任務中,我僅負責前半部分而 AI 負責后半部分。基于此,我在實踐中形成了一些個人的準則。
2. 作為開發者擁抱智能化實踐
首先,關于代碼檢索這一任務,我認為讓 AI 來完成是一個極為出色的選擇。其次,當我利用 AI 來處理事務時,如何加強上下文信息,從而提升 AI 的準確率。第三,我會分享如何管理自己的知識,使其能夠與 AI 有效關聯。最后,我們將討論如何與 AI 進行協同分工。
3. 一種全新的找代碼方法
我們先來探討代碼檢索這一任務,這是我認為在代碼編寫領域中,AI 有可能真正超越人類的一項能力。人通常是如何尋找代碼的呢?我們往往會根據所需代碼的大致要求,先在腦海中構想在代碼里面的方法名或者變量名,然后利用全局搜索功能進行查找。如果搜不到就換個名字繼續搜,如果找到了,就進一步檢查該函數被哪些其他函數調用,以及函數內部調用了哪些其他函數,即我們常說的“Go to Definition,Go to Reference”。通過這一系列的步驟,我們最終在腦海中匯總這些信息,得出一個結果。
然而如果整個代碼庫都具備 embedding 的向量能力,那么我們的模型就有辦法準確地定位到代碼庫中的任意代碼片段。其次,模型天生就具備總結和解釋的能力,這使得它能夠在代碼檢索方面發揮出色。因此,我們設想了一個未來的場景:在尋找代碼時,我們可以直接使用自然語言進行搜索,而無需再糾結于具體的變量名或方法名。
在此,我有兩個實踐建議:第一,我們應該描述要找的功能或需求,而不是局限于描述自己的代碼。第二,如果我們尋找的目標不僅僅是代碼本身,而是對代碼進行整理和總結后的結論,那么我們可以直接讓模型為我們生成這個結論。
這里有兩個典型的例子來說明這一點。左邊第一個例子是要找代碼,關于在應用程序中查找創建應用表單的實現位置。如果我自己去找,可能會嘗試使用與“創建應用”相關的詞匯,如“create”等,進行搜索,但很可能無法找到。因為這個代碼庫的命名方式較為特殊,它將創建應用的功能命名為“fork”。我自己很難想到這個詞,但模型卻能夠幫我找到正確的位置。它指出在“fork”目錄下有“Info”和“UI”兩個文件,這兩個文件共同實現了創建應用的功能。整個過程模型只用了大約二十秒,而我自己可能需要花費五分鐘甚至更長時間來嘗試不同的關鍵詞進行搜索。
第二個例子是關于總結系統暴露的路由數量。我并不是在尋找具體的代碼,而是希望模型能夠幫我總結在某個目錄下系統暴露了多少個路由,并給出它們的詳細信息。模型同樣出色地完成了這項任務。它列出了所有的路由,包括它們的請求方法(如 POST、GET 等)、目標地址以及所在的文件位置。而我如果要自己去做這件事情,可能需要逐個打開 Controller 文件,查找其中的注解來識別路由,然后再將這些信息記錄下來。這同樣是一個非常耗時的過程,但模型只用了十幾秒就完成了。
因此,在代碼檢索這件事情上,模型的表現確實非常出色和可靠。
4. 讓相關代碼先走一步
接下來關注的是,我們在實際應用中如何提升模型的表現,以優化輸出結果。這主要依賴于對大模型 prompt 的編寫,因為提示詞的質量直接影響模型的表現。然而在代碼檢索或相關應用中,用戶往往無法直接輸入提示詞,而是依賴于產品內置的邏輯。這些內置邏輯主要基于以下幾個核心原則。
第一,關于編寫 import 語句,建議盡早進行。我了解到許多開發者并不習慣首先編寫 import 語句,因為 IDE(集成開發環境)通常會在使用時自動補全。然而,如果你能夠預見到將會使用到某些模塊或庫,并提前將它們寫入 import 語句中,大模型就能提前知道。因為你的開發工具會根據 import 語句自動查找并補全所需的依賴,從而增強模型的準確性。
第二,如果你正在處理與當前任務相關的文件,建議將它們打開并放置在旁邊。例如,當你在編寫一個 Controller 時,它可能會依賴于 Service 層,而 Service 層又可能依賴于 DAO(數據訪問對象)。將這些相關的文件都打開并放置在視野內,開發工具都會讀取這些當前打開的文件,并從中提取有用的信息提供給模型。
比如需要補充一個名為“APPID”的信息,如果僅依賴模型進行單獨補充,它可能會生成一個毫無意義的字符串,如“134567”,這樣的結果顯然不符合你的期望。然而,如果你已經打開了一個包含環境變量的文件,并且該文件中有關于“APPID”的環境變量設置,那么模型或開發工具就能自動讀取這個環境變量,并將其準確地補充到所需位置。這種能夠智能識別并利用當前環境中已有信息的細節處理非常出色。
關于 import 語句,在導入了某個模塊或庫后,模型或開發工具就能自動在這個文件或庫中尋找你所需的方法。例如,如果你要調用一個名為 getPrice 的方法,并且該方法接受一個參數,那么如果你已經 import 了這個方法,模型就能準確地為你補全這段代碼,包括方法名和參數傳遞。然而,如果你沒有導入相應的模塊,模型在嘗試補全代碼時可能會遇到困難,因為它無法確定你應該調用的方法的確切名稱。在這種情況下,模型可能會給出一些與你的意圖不符的建議,比如 getTotal、getAmount 或其他任何可能的方法名。因此,import 語句的使用,對于確保模型能夠準確補全代碼,效果非常好。
再來看之前提到的代碼補全問題,如果將其置于對話模式下進行考慮。這里舉一個最簡單的對話例子來說明,有一個普通的需求,即編寫一個組件,該組件的功能是向后端接口發送請求,并接收返回列表數據,同時在表格中展示這些數據。這是我們在日常工作中經常會遇到的任務。
如果我直接對模型表述這個需求,模型可能會根據當前社區中最流行的技術方案來為我生成代碼。例如,如果模型認為 React 框架很流行,且通常使用 Axios 來發送請求,那么它可能會按照這種方式來編寫代碼。然而,這樣的代碼在我們的具體業務中是否真正有價值呢?這并不一定。因為我們的業務可能會采用不同的社區框架,如果生成的代碼沒有基于我們實際使用的框架,那么這些代碼對我們來說就可能是無用的。
那我應該怎么做呢?在這個對話之前,我引用了一個在前端的項目文件 package.json,這個文件記錄了項目中安裝的所有第三方依賴的聲明。我僅僅做了這一件事情,其他的內容都沒有改變。模型似乎注意項目中使用了 antd 和 react-query 這兩個庫,因此它決定使用這兩個庫來為我編寫代碼。在發送請求時,它選擇了使用 react-query 的 useQuery,接下來是 antd 的表格,并聲明了表格的列(columns)屬性。
這樣生成的代碼與我的實際需求匹配度高,而且在我的實際業務場景中可以直接使用,幾乎不需要進行任何修改。因此我們在編寫對話時,通過引用幾個非常核心的文件作為參考,可以立即將代碼的質量從 0 分提升到七八十分的效果。這是我們在對話實踐中得出的一個經驗。
而且,這個方法不僅適用于前端 JavaScript 項目中的 package.json,還適用于其他編程語言和框架。比如 Java 項目中的 pom.xml 和 gradle 文件,PHP 項目中的 composer.json 文件,以及 Python 項目中的 requirements.txt 文件。我認為在任何需要生成有用代碼的對話中,都可以無腦地將這些文件提供給模型作為參考,這樣生成的代碼就是有用的。
5. 你就慣著她吧
最后,我們來探討一些更理論層面的內容。模型就擺在那里,它的能力如何,是否優秀,這都是既定的。那么作為使用者,應該如何去適應模型呢?適應的程度應該達到這樣的程度,即了解模型執行的時間。比如,當我輸入一個“if”時,我知道模型后續不會只生成一行代碼,而是會展開成四五行代碼。因此,我會耐心地等待 2 秒,因為我覺得生成這些代碼可能需要這個時間。同樣地,如果我知道在某個情境下,模型只會生成一行代碼,并且這個過程可能非常迅速,比如 500 毫秒,那么我就會相應地調整我的等待時間。如果模型在預定的時間內沒有給出反饋,那么我就會停止等待。這是一種適應。
另外,我還需要學會在何時給模型提供更好的推理上下文。之前也討論過,模型在某些情況下會表現得更加準確,而在其他情況下則可能不盡如人意。比如,當我在處理與業務高度相關的邏輯時,如果模型不了解具體的業務需求,那么它的預測結果很可能就不準確。在這種情況下,我就會選擇自己編寫代碼,而不是等待模型的反饋。相反,如果編寫的是一段通用的算法模型,并且模型能夠通過函數名來推斷出我的需求,那么即使模型需要 3 秒、5 秒,我也會耐心地等待,因為模型生成的代碼通常會比我自己編寫的更加高效和準確。
此外,還需要認識到模型并不是完美的,因此我們需要充分發揮模型的優勢,同時避免它的缺點。有些任務,比如找代碼、解釋代碼、讀代碼以及局部的重構和改寫等,模型通常能夠比我更加高效地完成。而有些任務,模型則可能無法勝任,這時就需要親自上陣。通過與模型的長期磨合,我們逐漸積累了這種經驗。
總結起來,人和模型都有自己的擅長領域,而人擅長技術選型、任務規劃以及創造型的工作。
6. Focus, Let AI Run The Errands
那 AI 擅長什么呢?AI 模型擅長記憶和對細節的處理。它們能夠快速地記住大量的信息,幾個 T 的 Token 都是模型的記憶。此外,AI 還擅長處理一些瑣碎且細節豐富的工作,比如局部的代碼優化、探索型的數據搜索、互聯網信息的搜集以及歸納總結等。
所以人類并不應該與 AI 在這些方面競爭。相反,我們應該充分發揮自己的優勢,比如做規劃、進行創造性思考等,將重復勞動、瑣碎的事情交給 AI,這個稱之為 Focus,專注。“Let AI run the errands”,即讓 AI 處理那些雜七雜八的事情。這是我認為當真正改變了工作流以后,與AI協作的最好的關系。
7. 內容總結
上圖中對我們的工具、企業和個人在 AI 發展上做的事情進行了總結,在此不做贅述。
從未來的視角來看,我認為將無人駕駛作為一個比較對象是十分恰當的。目前,無人駕駛技術處于 L2 與 L3 之間的發展階段,而我們的智能研發則尚處于 L0 與 L1 之間,未來一定會繼續發展,越往后“Transfer of responsibility”即它的職責將愈發向 AI 領域偏移。AI 將承擔越來越多的工作,而人類所需完成的事務則會相應減少。我認為這是一個極為美好的前景,人類將得以投身于更多富有創新性的工作之中。因此,我們可以滿懷期待地憧憬未來,隨著我們不斷向右前行,最終人類將能夠專注于更具價值的事業。
四、問答環節
Q1:分享中提到的了解全庫,將代碼邏輯繪制成圖形是純粹依賴于代碼生成,還是結合了業務上的需求文檔等?按案例中解讀的效果還是比較好的,如果單從代碼的邏輯流出發,很可能得不出那樣比較細致的關系。
A1:我們現在的實現不需要依賴需求文檔,因為大部分產品的需求文檔質量的不太高,還有可能是過時的。所以,首先我們是通過代碼之間的固定調用關系,通過語法解析生成代碼的知識圖譜,其次在代碼層面之上用大模型做代碼的解釋和增加注釋等工作。這些工作將建立自然語言與代碼之間的聯系,將兩者融合后,運用一系列綜合算法進行處理。例如,當找到一個代碼塊時,會提取該代碼塊的解釋,并同時獲取其調用關系的圖譜以及圖譜的解釋。將這些信息整合后,再借助大型模型來生成該代碼塊整體流程的摘要。最終,將生成一個類似于之前所展示的圖,實際上這是一個通過 Mermaid 方法生成的圖,它將以一段 Mermaid 代碼的形式展現。
Q2:代碼關系抽取等工作是實時做的,還是已經基于代碼庫離線處理好了?
A2:這些工作肯定是提前處理好的,代碼庫第一次打開時可能需要幾分鐘處理一遍,后續代碼修改則增量更新。
Q3:全項目進行向量化是在云端做的嗎?如何保障代碼的安全?
A3:這一環節確實是在云端完成的,首先它是安全的。我們將其分為兩個部分:第一部分是本地代碼傳輸到云端過程中的網絡安全性,這一安全主要通過 https 協議來保障。若需進一步增強安全性,我們可以實施 https 協議的證書 Auth 校驗,以此防止通過私有證書進行劫持的風險。這是傳輸部分的安全性。第二部分則是存儲環節的安全性。我們的方案是不直接存儲任何源代碼內容,存下來的是 embedding 向量對應的源代碼文件名和文件對應的范圍,即代碼起始和結束的行號與列號,具體的代碼內容我們并不存儲。在實際使用時,我們需要這段代碼時,會向客戶端發送一個請求,讓客戶端根據提供的信息找到相應的代碼段。并且我們會通過一個哈希值進行校驗,以確認代碼內容是否已被修改。如果校驗通過,再將這段代碼取回并供給模型使用。因此在存儲方面,我們通過不執行任何落盤存儲操作來確保這些代碼本身不會因我們的安全風險而泄露到外部。