AI幫幫忙-2:如何Web化
上一篇《我如何用AI快速生成考古題練習工具》做出了一個「題庫與工具分離」的本地小工具。這一篇記錄它走上網站的過程:寫入怎麼解決、為什麼 cookie 只放身分碼,以及這一輪和 AI 協作的新心得。
前言:放上網站之後,問題變成「寫入」
工具在自己的電腦上用得很順:題庫放在 json/,交卷後把紀錄寫回資料夾。但當我想把它放上網站分享給一起備考的人,馬上遇到一個在本機從沒想過的問題:網頁上的 JavaScript 沒辦法替我把檔案寫到伺服器上。
這件事值得先講清楚,因為它決定了後面所有的設計:
- 瀏覽器只能寫「使用者自己的裝置」:瀏覽器儲存(localStorage、IndexedDB),或是在 Chrome、Edge 讓使用者授權一個本機資料夾。Firefox 和 Safari 目前沒有選資料夾的功能。
- 伺服器上的檔案,只有伺服器端的程式才能改。靜態網頁伺服器只負責「讀」。
所以 Web 化的核心就是兩個問題:紀錄存在哪裡?誰有權限讀寫?
我原本的想法,被 AI 踩了剎車
我一開始的直覺很單純:「那就把作答紀錄寫進 cookie,不就好了?」
AI 沒有直接照做,而是指出兩個限制:
- 容量: 單一 cookie 大約只有 4KB。作答次數一多、再加上備註,很快就爆了。
- 安全: 由 JS 寫入的 cookie 無法設成 HttpOnly,內容放愈多愈不適合。
於是設計改成:cookie 只放一組匿名身分碼,紀錄本體由 PHP 存在伺服器。 這個修正省了我很多彎路,也讓我意識到:跟 AI 討論設計時,要把自己的想法講出來讓它挑毛病,而不是只下指令。
設計:同一份 HTML,兩種模式
我的要求很明確:本地端維持原樣,Web 端才走新路,而且改動愈小愈好。 最後的設計是:
| 本地模式 | Web 模式 | |
|---|---|---|
| 怎麼判斷 | 網址是 file: 或 localhost |
其他網址,且 API 探測成功 |
| 紀錄存哪 | 授權的資料夾,或下載檔案 | 伺服器 json/records/<身分碼>/ |
| 啟動時 | 依題庫名稱逐檔讀取紀錄 | 一次向 API 取回全部紀錄 |
| 交卷與備註 | 寫回資料夾 | 送到 PHP API,失敗再退回本地備援 |
幾個我覺得值得記下來的細節:
- API 探測失敗會自動退回本地模式。 如果網站還沒部署 PHP,或 API 壞了,使用者不會被卡住,畫面會提示「找不到 API,已改用本地模式」。
- 備援永遠存在。 就算 API 回應失敗,紀錄仍會備份在瀏覽器,並退回原本的資料夾或下載流程,交卷的成績不會憑空消失。
- 本地程式幾乎沒動。 對原有 JS 的改動,集中在三個地方:模式偵測、啟動時載入紀錄、儲存紀錄的函式開頭。這也是我一直要求 AI「在原有程式碼上做最小修改」的原因:舊的題庫、舊的紀錄、舊的操作習慣,都不會被破壞。
身分碼:換裝置也能接上做題紀錄
匿名身分碼是一組 32 碼隨機字串,第一次進網站時由 JS 產生並存在 cookie。畫面上會顯示它,可以複製。
我想要的效果是:在另一支手機或另一台電腦,貼上同一組身分碼,就能還原先前的做題紀錄與備註。 所以加了「用身分碼還原紀錄」的按鈕:輸入身分碼、確認後,工具會改寫 cookie、清掉本機暫存的舊備份(避免兩個身分的紀錄混在一起),再向伺服器重新載入。
這個設計也有代價,要老實說:身分碼等同鑰匙,知道的人就能讀寫那份紀錄;清除 cookie 或換瀏覽器,就必須靠身分碼找回。如果之後需要更嚴謹的帳號登入,是另一個層級的工程。
PHP API:小而保守
後端只有一支 api/records.php,用檔案存放,不需要資料庫。它做的事情很單純:
- 讀取: 依 cookie 的身分碼,回傳該身分的所有紀錄。
- 寫入: 收到前端送來的紀錄後,和伺服器上已有的資料「合併」,以作答時間編號去重、重算累計錯題與備註,再加上檔案鎖寫入。這樣兩個裝置同時交卷,也不會互相覆蓋。
- 驗證: 身分碼、題庫編號都限定格式,請求大小有上限,每個身分的檔案數量與保留的作答次數也有上限。
部署在寶塔面板時,我整理了幾個容易踩到的地方:
- 目錄權限:
json/要讓 PHP 的執行身分(通常是www)可寫入,API 才能自己建立json/records/<身分碼>/。 - open_basedir: 站點若開了「防跨站攻擊」,資料目錄必須在允許範圍內,否則會出現
open_basedir restriction錯誤。 - 別讓紀錄被直接下載: 紀錄放在
json/底下,用 Nginx 規則禁止直接存取json/records/,PHP 讀寫則不受影響。
順手做的整理
Web 化的過程中,我還改了兩個原本讓我不太舒服的地方:
- 啟動時讀取紀錄的方式。 原本程式裡寫死了某一份紀錄檔名,其他題庫的紀錄根本不會自動載入。現在改為:本地模式依「已載入的題庫」推算每份紀錄檔名;Web 模式一次向 API 取回全部。
- 綜合題庫的選單。 之前每個科目一排按鈕,科目一多就亂成一串。現在是「先選考試與科目、再選題數」的兩個下拉選單,加一顆重抽按鈕;而且名稱會顯示成「中級AI應用規劃師・科目一」這種看得出考什麼的格式。題庫 JSON 也多了兩個選填欄位
abilityName、subjectName,新考試不用改工具就能顯示中文名稱。
這一輪和 AI 協作的三個新心得
1. 重大改動,先要「設計方案」,再逐項確認需求。 這次我明確要求 AI 先給設計、有不清楚的地方一個一個問我,確認之後才動手。結果是:cookie 放什麼、紀錄放哪個資料夾、哪些按鈕要隱藏,都在寫第一行程式之前就講定了,少掉很多返工。
2. 「最小修改」要講得很明確,而且要不斷重申。 AI 很容易順手把整個檔案重寫一遍,效果看似更完整,卻可能悄悄改掉你已經調好的行為。我的做法是每次都強調「在原有程式碼上修改、不要全部重新生成、沒要求就不要動別的檔案」,並要求它說明改了哪幾處。
3. 看清楚 AI 做不到的事。 AI 能做語法檢查,也能用模擬資料測試前端流程,但它的環境沒辦法執行 PHP,更沒辦法在我的寶塔上驗證權限設定。PHP API 只能靠結構檢查和人工審視,必須由我自己在伺服器上實測。AI 的回覆如果沒有講明「哪些沒測過」,就要主動追問。
上線前的檢查清單
請在自己的網站依序確認:
api/records.php?ping=1能回傳{"ok":true}。- 開啟工具後,畫面顯示 Web 模式與身分碼。
- 交卷後,伺服器的
json/records/<身分碼>/出現對應的紀錄檔。 - 寫下備註、再次整理頁面,備註仍會帶出來。
- 開兩個瀏覽器分頁同時交卷,紀錄沒有互相覆蓋。
- 清除 cookie 後,用身分碼可以還原紀錄與備註。
- 直接用網址存取
json/records/…的檔案會被拒絕。 - 暫時關閉 API(或改名),工具會自動退回本地模式,且交卷成績仍能下載保存。
新增與更新的檔案
api/records.php:Web 模式的紀錄 API(PHP 8)。info-test.html:加入本地/Web 雙模式、身分碼與還原、啟動載入紀錄、綜合題庫的新選單。- 《生成規則-重啟對話V2.md》:新增 Web 模式與 API 規格、部署注意事項,以及適用於修改既有程式的提示詞。
結語
把工具從本機搬上網站,最大的收穫不是學會寫 PHP,而是想清楚「誰能寫哪裡」。程式碼只是結果,真正決定成敗的是前面那幾個問題:紀錄放在哪裡?身分怎麼認?壞掉時怎麼退?AI 可以幫我把這些問題拆開、把程式寫出來,但每一個決定,仍需要我自己點頭。
如果你也打算把自己的小工具放上網站,歡迎拿這套做法去用,也歡迎把踩到的坑告訴我。