與LLM共享協作的3個月(1)--混亂和秩序同時發生
魔幻寫實的體驗
從今年7月開始,我利用LLM協作大約完成了3個專案。事實上,從今年初開始,我就有嘗試使用LLM編寫的程式碼,但之前的使用方式多半是限制在某個非常小的步驟,例如使用一個沒用過的套件裡的函式來操作。這類的程式區塊相當簡便,通常3-4行,程式語法、檢入參數、預期輸出,都一目了然。唯一差別,只在我過去是搜尋google,或幾乎等同於搜尋stack overflow,來找到答案,然後可能複製整段程式碼回來,再自己改動關鍵節點;而利用LLM,他可以幫我整段依照想要的設定直接改好。即使如此,這也很難定義成是跟LLM協作,因為大部分的程式碼,還是自己一鍵一鍵敲下完成的。
上半年之前,雖然我對LLM在程式設計上的進步時有耳聞,也看到不少範例,但本質上還是把他當成一個下位的輔助工具,例如:可以詢問他一些自己不清楚的概念(類似搜尋引擎),或者讓他整理文章、論文,簡單來說,就是發揮原本作為語言模型,本來應該擅長的事。
那為什麼,我會開始使用LLM來協作,並進一步承認,或願意改變腳色,讓他跟我處於平等地位,甚至在某方面佔據主導位置,一直到現在,在某方面來說已經需要依賴他呢?
一切從7月自己決定開始做的一個專案開始。
由後往前
由於一些因素,我認為在短期內,已經無法找到適合保留自己文章的網路平台。同時,我也對於「把自己的文字長期保存在他人的平台」這件事,長期感到不安全。這種不安感,並不是因為資訊安全或隱私、版權受到侵犯等等所引發的憂慮,而是「總有一天,平台會關閉,或經營方向、策略改變,不再符合當初找到的需求」的擔心。誠然,目前線上的許多文字平台,都已長期穩定經營。但,也有可能出現會員訂閱,或把某些關鍵功能改成作者付費才能使用,造成先前對文章、或專欄規劃的設定受到影響。這樣一來,就迫使我必須尋找一個,能持續存放文字,保有一定和外界溝通、但主控權完全在自己的平台。顯然,除了自己從頭打造,別無其他更好的辦法。
然而,這段過程必然痛苦。雖然架設部落格,從技術來說並不應該是很大的工程。然而,我完全沒接觸過相關的程式語言,也對整個資料串流的順序、邏輯不熟悉。過去,我一直把重心放在資料科學,如果硬要分前後端,是比較偏後端的工作(實際上也不算是,如果從網站架設的腳色分工來看,工作性質不太一樣)。先看看整個工程的架構:

過去的實作經驗,只能讓我對資料庫的操作比較熟悉。至於API、業務邏輯、前端介面設計、版面等等,我都沒有任何經驗。在規劃時,我只有對網站的一些內容有想法,這之中大部分是集中在編輯文章的功能上。但編輯文章,僅僅只是網頁跟介面一部份的功能而已。實際上,光是這部分,就可以架設一個小網站出來了:編輯文章儲存在資料庫讀取呈現。這樣已經是一個小小的循環了,但顯然,只對編輯文章的頁面有想法,是還不足以建立這個小網站的。必須還要知道要儲存什麼資料、要編輯什麼資料、這些資料表的關聯是什麼、資料表要儲存那些欄位等等。接著,是將應用程式介面(application programming interface,API)具體實作出來,依託著資料庫,和其互動,可以儲存、編輯、讀取,甚至刪除資料(create, read, update, read,CRUD)。
然後,是網頁的介面端:是希望編輯文章的頁面,呈現出和到時呈現給讀者的介面完全一樣的版面?還是保有一部份的差異,但讓編輯文章的頁面更有彈性?對編輯文章的功能有想法,具體有哪些?那些可以做出來,而那些可能需要更複雜的檔案功能?
坦白說,一開始我真的沒想這麼細。但LLM引導我思考,幫我確立不少需求。有一些是他建議,而我沒想到的;還有一些是我有想到,但他把想法補充得更完整,更具實踐性。認真說起來,即使是在專案完成的現在回顧,當時確實還有許多不成熟的地方,事實上到現在也還是一樣。不過,透過這個專案協作,我更了解LLM作為輔助工具,在程式設計上能提供的好處、限制,以及該注意的地方。可以說,因為基礎不足而找上LLM協作,但最後卻找到屬於自己,使用LLM協作的方法,當然,對於前端的開發和認識也更進一步。整個過程沒有產生實質的收益,但對於自己的能力是很有收穫的。
從文章編輯開始
儘管當時對於整個網站的架構還沒有十分明朗,但我知道這項專案沒辦法一蹴而及。因此,我選擇從當時認為最核心的部分--文章--開始嘗試起。我的想法是,如果核心功能可以建立,那後續的擴展就比較容易;反之,如果失敗了,那也沒關係,表示必須用別的方法。至少,有嘗試過了。
但文章的核心功能,CRUD,也是好幾項。還有顯示的介面,應該怎麼設計?要先設計顯示介面,還是先建立後端的資料庫和邏輯?我當時還不夠理解,選擇了先設計顯示介面,也就是文章編輯的介面。當時的想法是,這個比較直觀,可以看到程式碼和效果間的轉換,要迭代修改也快。殊不知,文章沒存進後面資料庫,怎麼在修改之後呈現?前端介面的呈現只是船過水無痕。一旦關掉瀏覽器,在打開的時候就是空的一頁。照理來說,我碰過資料庫,也寫過line機器人,應該對這種依託資料庫行動的程式本質不陌生,但因為對網站運作太不理解,所以選了一個感覺非常菜的角度,切入網站設計中。
當時為了快速上手設計概念,還去惡補了CSS和html的相關課程影片,但後來發現,重點不在於語法,而在於想法。而且,在文字編輯上,LLM推薦給我使用的是tiptap,他又是另外一種套件,裡面有自己的一套,並不是根據CSS或html語法直接寫的。
儘管選擇的方向不正確,但我也享受到快速迭代程式碼,看到呈現結果的樂趣。從這時候開始,LLM正式接管我的專案庫,大部分的程式碼都不再是我寫的,而是LLM。因為選擇只看前端渲染結果,LLM在程式碼上不太有漏洞,改動也精確,主要的開發限制是在token--當時我都還沒有付費使用GPT--而很快就來到流量上限,必須等待一段時間的重置。這個問題在我買了pro之後改善不少,但那時進到下個階段,又有新的問題要處理。

總之,我花了一週多的時間,便建立好符合我想要的文章編輯功能。這裡面結合了圖片自由排版、程式碼區塊、方程式獨立和行內區塊、以及項目符號及編號、註解等等功能。此外,為了確保標題的階層性足夠,我一口氣設定了markdown格式裡面,H1-H6的標題,這也讓我在編輯文章時,可以確保有更小的子標題使用,從而保障文章的閱讀結構和體驗。

但就像我說的,沒有依託資料庫和API的單純前端網頁,只能作被動的靜態渲染網頁程式碼內已有的資料,而沒有辦法依據情況替換不同的資料。因此,我很快就遇上這個需求:至少要能把編輯好的文章儲存,並在下次開啟時能讀取。
前端?後端?前台?後台?
這個觀念其實蠻重要的,我並沒有從書籍或其他網頁學到,而是自己在摸索開發line機器人的時候,建立這個想法,後來在其他網頁上看到、得到驗證。算是網站架構非常基本的觀念啦,但如果是菜雞互啄的程度,我相信在這邊提一下也無可厚非。
一開始,我以為網站架構就是只分所謂前端後端,這些名詞因為搭配工程師的職位,對於即使不是資訊相關產業的人員,應該也不陌生。就像律師和法助、醫師和護理師這樣,即使不在法律或醫學領域工作的人,也不會沒聽過這些職位名字。準確來說,前端是指處理和使用者互動介面的程式,即網頁呈現,所以包括網頁的版面設計、網頁中不同部件的功能,功能背後的程式邏輯,都算是前端的範疇。後端相對於前端,指的就是處理和資料庫互動的程式,即API,以及資料庫管理、維護、更新等等。在某些網頁,會把前端=前台,後端=後台,這樣的說法是建立在管理者本身精通程式語言和資料庫,不需要另外使用介面來管理的前提下。
前台,在我的想法,是指完全針對使用者互動處理的內容。通常,這必定包含前端,因為總要有個網頁介面讓使用者操作,對吧?但,操作之後,這些相關的資訊和更動,仍然要提交到資料庫,或至少暫時處理的伺服器上面。這時候,就需要API或資料庫來承接,由前端從使用者那邊得到的資料。舉個例子:一個飲料店的點單平台,前台就是接受點單,例如A紅茶去冰半糖2杯,B綠茶正常甜度冰塊3杯。使用者可以在網頁上輕易的選到品項,標記甜度冰塊,以及杯數。接著,使用者提交的資料,就會從網站的後端,處理儲存到資料庫。這部分的資料處理,並不會暴露在使用者(理論上)前面。
後台,在我的想法,是指針對管理者互動處理的內容。如果管理者本身是工程師,他了解程式語言,也會操作資料庫,那如果他要做這個月的業務報表,只要從資料庫下指令(例如SQL),就可以知道這個月紅茶賣了幾杯,綠茶賣了幾杯,消費者比較喜歡甜度冰塊如何。但很多時候,管理者不是工程師吧?可能是業務經理、店長,甚至執行長。如果要教他們程式語言,才能看懂查找這些資料,我猜建立系統的工程師大概別想拿到專案尾款了。「給我設計一個管理的系統介面出來!」是的,這時後台也必須要有前端介面,這時的使用者是管理者,不是網頁名義上的使用對象。他負責提供一些管理員有需要的功能,並在互動之後,代替管理者下程式或資料庫指令,把資料撈出來,甚至還能做一些排版美化。
對我來說,我的角色或能力剛好介於專職的後端工程師和一般程式小白的管理者之間。我看得懂程式碼,大略知道意義,可能會用一些指令,但並不是精通,而且使用頻率沒那麼高。如果要我每次都要下指令撈資料,也不是一件輕鬆的事。而且,最重要的是,文章功能本身並不開放給前台閱讀者啊!這個功能一開始就註定是後台前端的。所以,我以此為基礎,先建立後台是完全合理的事情。後台平台建立好,發展前台會比較順暢。現在回過頭看,這部分的決策是對的。
API?APP?AP...?
第一次接觸API,是在開發line機器人的時候。line提供各式各樣的功能和效果,只要呼叫對應的函式,並輸入符合其格式要求的資料,就能做到。由於函式已經被line包裹好,如果不是去github查看程式原碼,是不知道實際上line怎麼處理輸入的資料的。因此,我知道這些函式是API,但並不清楚裡面的內容。輸入的資料格式一般都是json,這是個被廣泛使用的規格,我不陌生。但這次,我必須自己設定API,因為資料庫的操作和輸入輸出,全部都掌握在手上了。這是權力,是自由,也是挑戰。
API的關鍵在於操作對象、需要的資料,和測試。所以,其實跟對應資料表欄位的設定有關係。如果一開始沒有規劃好,就會像我這樣,在開發過程中又必須重構,必須花更多的時間才能把整個專案骨架做好。回顧開發過程,我覺得應該要想到的至少有三方面:
- 想要寫入、讀取、修改,或刪除那些資料?某些欄位,是只有寫入和刪除,而不需要讀取,也不能修改的,例如primary key。其他欄位,要不要開放修改?讀取的時候需要嗎?例如發表時間,需要刻意讀取嗎?如果前台有「最近文章」,可能需要依照發表文章的時間,由進到遠排序,那就需要讀取。反之,如果只是為了記錄某段時間發表多少文章,純粹作為後端紀錄,那步一定要寫在讀取的範圍裡。
- 資料的格式必須嚴格設定,並且對齊。例如時間欄位,寫入的時候就要限制是時間格式,讀取的時候也要宣告。前端的程式語言不同於Python,對於資料型別的要求很確實,這雖然對我這種只習慣Python的人來說,在適應上很痛苦,但卻是必須的,因為他可以某種程度上,保護整個專案。
- 有沒有批次操作資料庫的要求?如果有,那應該跟單篇的操作邏輯分開處理。
這些還只是基本的想定。除此之外,還有對應的測試和資安問題需要考慮。到專案尾聲,我總共有六種API類型,每種類型都有其特質,有些是必須直接操作資料庫的,有些是檢視資料庫串接操作的,也有一些比較接近單元測試的。此外,不同的API,對應的限制不同,例如前端使用的API,其開放的對象比較多,因此偏讀取,而盡量不開放寫入。
資料庫
從開發後台開始,我的開發流程就都是從資料表開始設計了。先把資料表的欄位和資料格式定義好,之後利用API操作資料庫,然後才是前端的函式去包裹特定API。這部分牽涉到需求、以及明確的定義業務流程,如果對於需求不夠清楚,很難轉或為具體的流程,結果就是不符需求、太過寬鬆,或者到時因為需求增加或改變,而必須重構。重構資料庫其實是有風險的。如果只是新增欄位,那倒影響還小;如果是改變某些欄位,例如更改名字,或改變其功能,甚至刪除欄位,那是必要再修改對應的API,如此就會花上許多時間,出錯的機會也會增加。這都是最早開發的時候,我沒想清楚的地方。
和LLM的互動
前面寫的落落長一段,我猜讀的人或大概沒興趣吧!因為如果是有經驗的開發者,這些基礎都很熟悉了。而對完全不了解程式的人而言,這些也不重要。最想知道的,大概是怎麼詠唱出適合的魔法,讓LLM幫忙完工就好。
如果是問我的意見,我會覺得這兩個族群的人是沒有交集的。不想花時間研究程式的人,就不會願意多花時間。而有開發經驗的人,你的提示方式一定自成一格。坊間這兩類人,分享各自提示方法的都大有人在,我想後面邏輯應該也大相逕庭。不過,這次的開發經驗,讓我體會到一點:不學程式語言,或語法,至少也要學程式處理邏輯。這個邏輯包括順序、包括條件判斷、包括迴圈,這些是跨程式語言都不變的,很少有程式語言沒有這些原則。使用未知或不熟悉的程式語言來進行開發,至少還可以從這些邏輯,去判斷LLM寫的對不對,或符不符合需求。我的體會也只是片面的,並不是很深入,但整個開發過程有給我這樣的感想。
在專案初期,我下給LLM的指示,是蠻籠統的。可能只告訴它,我要什麼功能;最開始的時候,我甚至利用它來調查適合的程式語言,以及套件。同樣是javascript,有Node.js,有Next.js,還有Next.js,以及react。我們的專案,有前端,有後端,如果框架都沒決定好,連測試的設定都會有問題。這些在之前主要使用Python來作資料分析的時候,是很難想像的。但事實上就是如此,這邊的領域廣到,怎麼會覺得什麼都不需要懂,只憑白話的指令,就能讓LLM從頭到尾正確地完成專案呢?
回顧一下我認為對於開發專案,至少需和LLM確定的事情:
需求
這部分越詳細越好。整個專案,最好一開始就把各個環節都想好。例如我是在主要對於文章編輯功能有想法的時候就進場,這樣還是太早了。事後回顧,應該把整個網站想要做的功能都盡量列清楚。在跟LLM討論的過程中,會進一步釐清需求的,輪廓會越來越完整,所以不用擔心想得不夠仔細。
舉例來說,我在建立後台的時候,想到要有文章列表,可以一次看到目前有哪些文章,也可以操作發布或刪除。這個時候,LLM就會問我:你想要批次發布或刪除,還是一篇篇處理?我才知道,之前的API設計是給單篇用的,多篇,傳入的資料不符合。所以要另外設計API。這個案例幸運的是本來就要分開設計,否則就要重構了。但我在資安的地方就沒這麼好了,整個大重構至少三次,推遲進度至少兩周以上。如果能把各方面的環節需求都建立好,對於後續開發會輕鬆許多,這點無論是否依靠LLM都是如此,但這很吃經驗。
變數和資料表欄位
這部分最好也是設定清楚,一開始就要列出主要變數的功能,我認為可以從資料表的欄位出發,確保設計的時候不會錯亂。如果全部交給LLM,開發到最後,會有非常多的變數問題,而專案裡面使用的變數實在太多,有時連自己都找不到在哪(還好IDE有工具)。
拆解專案
需求和架構要是整體的,但開發的時候還是要拆解。可以把核心功能先做好,再把其他同時和核心有關,但彼此不一定相關的部分拆解,然後用subagent來進行平行處理。當然,這樣對token的消耗非常巨大,如果沒有買到一個月200美金,而是只用20美金以下的話,很難負荷每時段、每周的需求量,常常就會卡住,要等下一時段。這要看你對專安開發的時間迫切性、和常規使用量決定。
測試
我仍然採用傳統的TDD(test-driven development),利用測試導向來寫API。LLM通常不習慣這樣做,如果你只跟它說「我需要API都有測試」,99.99%它會先寫API,再寫測試。理想狀況是,它先根據需求寫測試碼,然後再根據測試碼寫API,目標是通過測試。不過在開發過程中,會遇到兩個問題:
測試環境
這部分我到現在都還不夠熟練,常常還是要去查,才知道需要準備什麼。開發過程中,LLM幫我重構測試至少5次。不同類型的測試,需要的套件和環境設定不同,如果全部硬兜在一起,彼此會起衝突,導致測試不通過,而個別測試又可以通過,讓你不確定到底問題在哪裡。環境包括套件、要模擬的項目、是否需要操作真實資料庫(當然,是指開發的資料庫,而非佈署)、保持測試環境的乾淨等等。這邊非常花功夫。LLM花功夫不說,像我這種開發菜鳥要知道它做了什麼,往往還要額外花一倍的時間。有經驗的開發者,必然會在這裡也建立需求、相關變數資料,明白交代具體環境,因此讓建立測試需要的時間事半功倍,而開發人員也只需要快速檢視重點,是否符合討論需求即可。
修正測試
我在開發過程中,遇到兩種狀況:一是測試環境的問題,導致測試失敗,所以修的是測試程式,而非API。二是API迭代更新快,測試環境有時候沒跟上,也要修正。雖然乍看之下不是大問題,但其實和TDD的精神是矛盾的。我也還在思考,要怎麼做才能讓這個流程更穩定。
以上就是我用LLM協作開發網站的經驗,如同副標題所說,「混亂和秩序並存」。混亂的是自己的思緒、過程、開發順序,而在手忙腳亂、兵荒馬亂的同時,找到一些規則,建立秩序,才達到開發目標。下一篇,我想分享一些仍然是圍繞在網站專案,但跟佈署有關的議題,這部份對於有經驗的人來說,一樣是菜,不過值得讓我回顧。