2025年10月30日 星期四

Spec Kit實作記錄

Spec Kit實作記錄

hackmd連結

Spec Kit的出現似乎讓vibe coding的未來出現一絲光明,即使這名詞的發明者,AI大神Andrej Karpathy,最近也出來說明這並不可行,但是對於一些簡單的專案來說應該還是有幫助才是。

我個人仍然是認為,寫程式重視底韻,你必需要有一定的知識才能有辦法去驅動人工智慧,不然你根本連AI在幹嘛都不知道,仔細想想,這是很恐怖的一件事。

也許你認為在什麼都不懂的情況下可以用自然語言的方式生成一個網頁或是應用程式非常潮,但是在沒有一定知識的情況下你也只知道就是生成一個你想像中的結果,這個結果有沒有什麼安全性的問題可能連自己都是一問三不知了。

步驟

官方連結

在Spec-Kit的官方說明中明確的列出幾個指令,簡單易懂,就跟叫叫ABC一樣,其中Essential Commands是過程中必需執行的指令,而Optional Commands是過程中你可以選擇要不要執行的指令。

我看保哥在一個公開談話中的demo是在specify階段中直接給定PRD文件,這部份當然也是可以另外利用LLM來生成。

🧭 Essential Commands

指令 說明
/speckit.constitution 建立或更新專案的治理原則與開發指導方針
/speckit.specify 定義你想要建構的內容(需求與使用者故事)
/speckit.plan 依據選定的技術棧建立技術實作計畫
/speckit.tasks 產生可執行的實作任務清單
/speckit.implement 依照計畫執行所有任務,完成功能建置

🧩 Optional Commands

指令 說明
/speckit.clarify 釐清未明確定義的部分(建議在 /speckit.plan 之前執行;原名 /quizme
/speckit.analyze 進行跨產物一致性與覆蓋度分析(建議在 /speckit.tasks 之後、/speckit.implement 之前執行)
/speckit.checklist 生成自訂品質檢查清單,驗證需求的完整性、清晰度與一致性(類似「英文單元測試」)

⚙️ Environment Variables

變數 說明
SPECIFY_FEATURE 用於非 Git 儲存庫的功能偵測覆蓋。設定為功能目錄名稱(例如:001-photo-albums)以指定當前工作的功能。
注意: 在使用 /speckit.plan 或後續指令前,必須於代理環境中設定此變數。

指令簡單說明

/speckit.constitution

在這階段主要定義整個專案過程中不可違逆,必需完全遵守的規則,像我會要求所有文件皆以正體中文書寫,開發過程必需遵守MVP(最小開發原則)

實作記錄

開發工具

我採用的測試開發工具是KILO CODE,然後首次儲值是10美金送20美金,搭配的模型是deepseek 3.2claude haiku 4.5,一個便宜一個好用,主力是deepseek

文件導讀工具

目前對於新的技術我的作法都是直接將所有文件利用notebookllm整理,自己會快速的看過一次,後續有問題就直接在notebookllm中詢問。

這樣做的好處在於問題不會發散也不會有所謂的幻覺出現,因為notebookllm的問題回應始終是根據你所給定的文件來回應。

問題記錄

:::warning
這邊的問題都是根據我收集在notebookllm中的資源回應所得,並不保證是正確的
:::

  • 第一次的迭代完成之後,第二次執行/speckit.specify並沒有同步建立002-xxx分支,導致後續的執行皆在001-xx分支中執行,雖然發現這問題之後有嚐試設置SPECIFY_FEATURE,但是似乎沒有效果
    • 後續的處理方式是直接利用git branch建立對應的分支,然後git checkout過去該分支之後測試執行,確定相關文件的導讀都是從002-xxx

:::info
理論上在第二次執行/speckit.specify的時候會生成002-xxx的資料結構以及分支,這次的問題在於資料結構有生成,但是分支沒有生成,後續第三次執行的時候要特別確認。
:::

  • 001-xxx執行完畢之後,如果要第二次迭代執行/speckit.specify,是否需要切回分支master
    • 建議是需要切回再做執行,並且記得要做合併分支

:::info

  • 猜測第一次就是因為沒有切回master才會造成分支沒有成功切換為002-xxx

  • 確認即使切回master再執行仍然不會生成分支,所以這部份必需要自己建立分支之後自己切換,不過不確定是不是kilo code才有這問題,後續可以再測試github copilot

  • 調整為github copilot之後確認,使用copilot會自動切新分支,也許在kilo code上要有更明確的提示詞才會執行
    :::

  • 第2次之後執行/speckit.specify,即使技術棧沒有調整仍然需要執行/speckit.plan

  • /speckit.implement之後如果發現與task.mdplan.md上所述不同,那就要再重新執行一次/speckit.implement,並於提示詞中明確說明

開發工具從kilo code轉copilot

uvx --from git+https://github.com/github/spec-kit.git specify init --here --ai copilot --script ps

:::warning

  • 執行之後constitution.md也被重置,這部份要特別注意,也許是我的指令錯誤了
    :::

現有專案導入speckit

:::info
下面給出的是notebookllm的說明,後續再用現有的專案來測試看看
:::

這是一個非常好的問題,Spec Kit 的設計就是為了能夠導入既有的專案(在 SDD 術語中稱為 Brownfield 專案,即「遺留系統現代化」或「疊代增強」)。

將 Spec Kit 導入現有專案的關鍵在於使用正確的初始化命令,並確保您為 AI Agent 提供了足夠的專案上下文(Context)原則(Constitution),讓它能理解既有程式碼庫的結構和約束。

以下是導入 Spec Kit 到現有專案的詳細步驟和注意事項:


步驟一:導入 Spec Kit 套件

Spec Kit 提供兩種主要方式在現有專案中初始化其骨架:

方法一:使用 Specify CLI 導入(推薦)

您需要使用 specify init 命令並加上 --here 旗標,這會指示 CLI 在當前目錄下初始化 Spec Kit 必要的檔案和資料夾,而不是創建一個新的專案資料夾。

  1. 安裝 Specify CLI:如果您尚未安裝 uvx,請先安裝。

  2. 在現有專案根目錄執行

    uvx --from git+https://github.com/github/spec-kit.git specify init --here
    # 或使用句點:
    # uvx --from git+https://github.com/github/spec-kit.git specify init .
    

    您將被提示選擇要使用的 AI Agent(例如 Claude Code、GitHub Copilot 等)和腳本類型(shps)。

方法二:手動下載與複製檔案

您可以直接從 Spec Kit 的 GitHub 儲存庫的 Release 頁面 下載針對特定 Agent 的模板壓縮包,然後手動解壓縮並將相關檔案放入您的專案根目錄。

  • 複製內容:將包含 Agent 指令(例如 .github 提示或 .claude 提示)的資料夾和 .specify 資料夾(包含記憶體、腳本和模板)拖曳到您的專案根目錄中。

步驟二:建立專案憲章(Constitution)

這是導入現有專案中最關鍵的一步。由於 AI Agent 擅長模式補全,但不擅長讀心術,它需要明確的最高原則來理解現有專案的技術棧和架構。

您必須使用 /speckit.constitution 命令,來定義這些**不可協商(non-negotiable)**的原則。

  • 用途:憲章(Constitution)會將 AI 模型鎖定在現有的技術棧、代碼品質標準或架構決策上。
  • 內容範例:由於是現有專案,憲章應包含現有技術的約束,例如:「網站使用 Hugo 建構,不使用其他前端框架」、「所有 Web App 必須使用 Next.js 和 Postgress 資料庫」。
  • Agent 參考:Agent 在規劃 (/plan) 階段會參考此憲章,以確保其技術計畫符合您已設定的約定。

步驟三:提供既有程式碼庫的上下文(Context Engineering)

您必須主動確保 Agent 能夠獲取足夠的上下文,以便在不破壞現有程式碼的情況下新增功能。

  1. Agent 指令文件:許多 Agent(如 Copilot CLI)會讀取 agents.md 或類似的 Agent 檔案,您可以將專案的結構、元件定義和操作方式編碼到這些文件中,以便 LLM 理解組件關係和應在哪裡進行變更。
  2. 動態上下文分析:現代 LLM 具備動態解析程式碼樹的能力,但提供主動上下文(Proactive Context)通常效果更好。Agent 在後續的 /speckit.plan 步驟中,也會嘗試解析專案結構(例如尋找 Hugo 配置文件或 Next.js 的檔案結構),並將其應用於新功能的開發。

步驟四:新增功能 (Iterative Enhancement)

一旦基礎設置完成,您就可以像對待全新(Greenfield)專案一樣,開始為現有專案新增功能。Spec Kit 在處理現有程式碼庫的功能新增時,展現了強大的威力。

  1. 隔離開發:當您使用 /speckit.specify 命令時,Spec Kit 會自動在您的現有 Git 儲存庫中建立一個新的功能分支,將新功能的工作隔離起來,防止影響主分支的穩定性。
  2. 規格化新增功能:針對您要新增的特定功能,運行標準的 SDD 流程:
    • /speckit.specify:定義要新增什麼 (What) 和為什麼 (Why),專注於新功能如何與現有系統交互,不包含技術細節。
    • /speckit.clarify(可選):幫助您發現關於新功能要求的盲點或定義不足(underspecification),尤其在複雜的既有程式碼庫中非常有用。
    • /speckit.plan:定義如何實作 (How), Agent 會參考您在憲章中定義的既有技術棧(例如「使用 Next.js」),並根據新功能生成專屬的技術工件(如新的 API 介面或資料模型)。
    • /speckit.tasks:將計畫分解為可執行的任務,然後使用 /speckit.implement 進行實作。

總結:導入 Spec Kit 到現有專案的流程就是利用 specify init --here 進行初始化,然後利用 /speckit.constitution約束 AI Agent,使其遵循您現有專案的技術規則,最後透過結構化的 SDD 流程來疊代新增功能

相關資源

2025年4月8日 星期二

MSC 地中海郵輪與名勝世界郵輪

MSC 地中海郵輪與名勝世界郵輪

MSC 地中海郵輪與名勝世界郵輪

前言

說起我的郵輪初體驗,也就是我的「處女航」,就獻給了 MSC。而且非常「幸運」地,首航即遇上引擎故障——沒錯,我就是搭上去年那班讓不少人(可能吧?)有點羨慕的「引擎故障升等體驗」航班。必須說,雖然行程受影響,但 MSC 後續的處理確實展現了大企業該有的氣度:該趟航程直接免費,還給了一個相當大方的未來航次抵用金 (FCC),大概是你這次花五萬,下次就直接折你兩萬五的概念。也因為這樣,讓我對他們處理危機的方式留下不錯的印象,所以年底的 MSC 航程,我還是毫不猶豫地報名下去了。

至於最近的名勝世界郵輪體驗,則要感謝我老婆的妹妹熱情邀約。考量到整體花費不高,需要的請假天數也不多,回來後甚至還有兩天緩衝休息,就決定一起出門體驗看看。我們搭上的是「賞櫻首航」,非常幸運地正好遇上櫻花滿開,尤其到了岸上觀光的立山公園,那盛開的美景真的讓人心曠神怡,非常享受。後來聽說我們下一班(4月6日出發)的旅客更幸運,因為人很少(好像才九百多人),船公司更是龍蝦、生魚片大方供應,聽起來也是夠大氣!

這兩趟郵輪搭下來,從吃的、住的、玩的到服務,真的是各有千秋,有讓人想豎起大拇指按讚的地方,當然也有讓人默默搖頭的小細節。所以,這篇文章的重點,就是要來比較一下 MSC vs. 名勝世界郵輪。

在開始之前,請容我強調一下:這個比較非常主觀,沒有絕對的客觀,因為這完全是基於我個人的真實感受。你可以不同意我的看法,畢竟每個人的標準和在意的點都不同,但這就是我最直接的體驗分享。

接下來,我會用表格的方式,盡量把我還記得住的各個項目,做一個詳細的比較。如果硬要先給個簡單結論,我會說:名勝世界郵輪的 CP 值真的高到破表,而且「中文優先」的環境非常加分;但我個人實在無法接受它五樓公共區域那瀰漫的菸味。

那麼,就讓我們開始這次的比較吧!

比較總表

比較面向 MSC 地中海郵輪 名勝世界郵輪 (Resorts World Cruises) 你的觀察/備註
整體印象/氛圍 奢華感、歐式風情、國際化 親民、亞洲熟悉感、"中文優先" RWC 可能更貼近台灣旅客習慣
價格定位/CP值 感覺較高價 CP值高、價格相對親民 RWC CP值勝出
語言服務 國際語言為主 "中文優先" (服務人員、廣播) RWC 對中文旅客極友善
自助餐 品項多、變化少、口味偏西式 品項相對少、每日變化、口味合台灣胃 RWC 口味/變化勝,MSC 品項多
主餐廳 傳聞出餐慢、菜色與自助餐相似? 可能有龍蝦夜 早餐與自助餐有區隔 (佳) RWC 主餐廳感覺較用心?
飲水提供 付費瓶裝水 / 自助餐水 (口感差) 免費飲水機 (水有味需過濾) RWC 提供免費飲水是優點
娛樂活動 強調視覺 (天幕/名店街),互動性可能較弱 大廳活動多 (電影/遊戲/參與度高),感覺更吸引人 RWC 活動感覺更親民、互動性高
賭場 感覺是重點設施,吸引不少客人 (視讀者是否在意賭場)
客艙 (內艙) 基本體驗差異不大 基本體驗差異不大 (平手)
服務品質 主觀感覺較佳 中文溝通便利 MSC 服務感受好,RWC 語言無礙
兒童設施 有<2歲空間 (玩具舊)、有室內泳池 僅2-12歲空間,無室內泳池 MSC 對帶小小孩 & 壞天氣時較友善
游泳池 有室內泳池 無室內泳池 MSC 勝
吸菸政策/執行 (未遇問題) 嚴重缺點: 5樓室內吸菸嚴重,氣味擴散,管理待加強 RWC 的重大負面問題 (MSC 在此項因無負面描述而勝出)
岸上觀光 通常不包含 優點:包含免費岸上觀光 RWC 包含岸上觀光是優勢
WiFi 日計費 日計費 (平手 - 計費方式類似)
下船效率 排超久 可能因人較少排隊較快? (主觀感受) (RWC 略佔優勢?)

優缺點總結

附帶一提,在MSC的所有廣播都是先英文、日文、韓文,最後才是中文;在名勝世界的時候絕對都是先中文再英文,那種感覺是完全不一樣的。

MSC 地中海郵輪

優點:
  • 歐式奢華體驗
  • 服務品質感受佳
  • 有室內泳池
  • 有<2歲兒童空間
缺點:
  • 價格可能較高
  • 餐飲口味/變化較不合台灣胃
  • 飲水不便
  • 中文服務可能較少 (廣播順序可見一斑)

名勝世界郵輪 (Resorts World Cruises)

優點:
  • CP值高
  • "中文優先"環境
  • 餐飲口味/變化適合台灣人
  • 免費飲水機
  • 含岸上觀光
  • 活動互動性高
缺點:
  • 室內吸菸問題嚴重
  • 無室內泳池
  • 無<2歲兒童空間

給讀者的選擇建議

推薦「名勝世界郵輪」給:

  • 追求高 CP 值、預算考量者。
  • 重視中文溝通便利性。
  • 偏好符合亞洲/台灣口味餐飲。
  • 希望行程包含岸上觀光。
  • 能接受(或本身是)吸菸環境的旅客

推薦「MSC 地中海郵輪」給:

  • 偏好歐式氛圍與較高服務質感。
  • 需要室內泳池或帶<2歲幼兒同行。
  • 對室內菸味零容忍的旅客
  • 對餐飲口味接受度較廣,或願意嘗試付費餐廳。

2025年3月4日 星期二

Dify + Whisper Asr Webservice

Dify + Whisper Asr Webservice

這邊假設你已經成功啟動 dify 的服務

Whisper Asr Webservice

Whisper Asr Webservice 是一個很方便的工具,因為作者已經幫忙把語音轉文字的服務 API 化,對於應用端來說只要做常規的 API 呼叫就可以滿足應用上的需求。

啟動 Whisper Asr Webservice

啟動服務最快的方法就是使用 Docker:

docker run -d -p 9000:9000 -e ASR_MODEL=base -e ASR_ENGINE=openai_whisper \
onerahmet/openai-whisper-asr-webservice:latest
docker run -d --gpus all -p 9000:9000 -e ASR_MODEL=base -e ASR_ENGINE=openai_whisper \
onerahmet/openai-whisper-asr-webservice:latest-gpu

不過要注意自己的 CUDA 版本是否有對應,如果最新版的無法啟動的話就要降版,這取決於你的硬體設備。

OpenAPI 設置

成功啟動 docker container 之後,可以直接連接到 http://你的ip:9000/docs ,沒意外的話你會看到下圖:

openapi image

點擊紅框處的 openapi.json,系統會開啟一個新的視窗顯示一堆 JSON 格式的字串,複製出來後進行以下處理:

  1. infopaths 中間加入 servers
  2. 修正 requestBody 中的 content-type,改為 multipart/form-data; boundary=----WebKitFormBoundarydzemBAPhdeDfTCfR
更多關於模型的選擇與細部配置請參考 Whisper Asr Webservice 官方文件說明。

Dify - 自定義工具設置

進入 Dify 後,點擊「工具」→「建立自定義工具」,貼上剛剛修改的 OpenAPI json 即可建立成功。

custom tool

Dify - 應用流程開發

現在可以在 workflow 中取用自定義的語音轉文字工具:

workflow

設定參數:

params

測試應用:

test

將文字結果轉成問答集:

問題:客戶在聯絡退稅服務時需要提供哪些信息? 答案:...(省略部分內容)... 問題:退稅作業需要多久時間? 答案:退稅大約需要一個星期。 ...(更多問答內容)...

若有興趣,可以將問答轉為主持人與來賓對話,發展成 Podcast 節目;若是公司會議紀錄,也能每日播放當作內部溝通素材。

2025年1月2日 星期四

告別2024~迎接2025

告別2024~迎接2025

2024年總計讀了八篇的論文:

  1. Unpaired Image-to-Image Translation using Cycle-Consistent Adversarial Networks(CycleGAN)
  2. ArcFace: Additive Angular Margin Loss for Deep Face Recognition(翻譯)
  3. Perceptual Losses for Real-Time Style Transfer and Super-Resolution(翻譯)
  4. Image-to-Image Translation with Conditional Adversarial Networks(翻譯)
  5. Analyzing and Improving the Image Quality of StyleGAN
  6. Instance Normalization: The Missing Ingredient for Fast Stylization
  7. Distilling the knowledge in a neural network
  8. Attention Is All You Need
    以及三個系列課程:
  9. Applied LLMs Mastery 2024
  10. 李宏毅_生成式導論 2024
  11. Mathematics for Machine Learning and Data Science

不過在git上的實作記錄就偏少了,這是2025年必需要改進的部份。

當然除了讀書之外在公司內也帶著同仁一起做了幾個案子,也確認自己的方向是正確的,人工智慧對於企業有絕對的幫助。 所謂的學習是在學習一種模式(pattern),企業的資料是不斷重覆的生成,一定也存在著某種的模式可以學習。

沒有達成的部份就是Rust、Vue、Pytorch都沒有動靜,這也是要檢討的部份。

所以,我要再立下2025年新的目標:

  1. 讀10篇論文(將yolov5~v11的論文補完,擴散模型、llm相關關鍵議題論文)
  2. 三個系列課程
  3. 放棄Rust,改學C++(參考C++ Primer Plus自學)
  4. Vue3
  5. 重新復習Hello演算法中的所有演算法
  6. 公司內成功建置人臉辨識考勤系統
  7. 讀完Build a Large Language Model (From Scratch),並且自己手刻一發
  8. 熟悉幾個llm框架(dify)
  9. 補完強化學習聖經第二部中文翻譯
    滾動式增加,要讓2025更充實。

2024年4月9日 星期二

Oracle的預儲程序參數與資料表名稱相同時的異常

Oracle的預儲程序參數與資料表名稱相同時的異常

最近踩了一個坑,主要是一個預儲程序利用給定的參數來做查詢,再將查詢到的資料寫入變數,原本都還很正常,但是突然失效,偏偏單獨的查詢都很正常,遍尋不著,後來同事協助之後終於脫坑。

create or replace procedure test_proc(p_a varchar2) is
begin
	select column into v_column from table where column_a = p_a;	
end

上面是一個示意的範例,test_proc這個預儲程序在得到p_a之後就會去資料表查詢,然後將資料寫入v_column,但不知道什麼時候,資料表中被加入一個同名欄位,也就是p_a,這導致了參數中的p_a就失效了。

這可能就代表著在oracle的預儲程序中,整個上下文還是會以查詢資料表為主,查詢資料表沒有對應欄位名稱的時候才會以變數做為條件?

這很頭痛的是,這是無聲異常,而且還可能造成大量異常,不得不防,以後預儲函數的參數必需要設置的非常怪異,才不會有機會跟未來人命名欄位名稱相同。

2024年3月26日 星期二

Roland F701開箱

ROLAND F701(白色)電鋼琴

自從搬出老家之後,喵小姐一直心心唸唸著想要一台電鋼琴,終於在今年的年初,也就是過年的時候,趁著活動帶著她去一趟竹北鴻韻,雖然這已經是第n次去了。

原本預算是20k,打算買台基本的能在家過過乾癮就好,不過實際到現場按壓幾次之後,喵小姐還是決定把預算突破天際,就買了F701,常言道,捏一下就上去了,這一捏簡直是蛋破小鳥死。但是為了喵小姐的夢想我還是捏上去了。

其實最一開始是希望老泡先生可以學鋼琴,但是他從小就愛敲敲打打,所以最後選擇學習爵士鼓。現在多了一個安咕咕先生,希望他會愛上鋼琴。

購買的時候因為是要安置在三樓,多了樓層費,來的師父在安裝上也非常熟門熟路的,很辛苦,到我家已經是晚上八點多,離開的時候說他還有另外兩家要送,看來在台灣熱愛學習音樂的人真的不少。

F701在DEMO的時候有特別提到可以搭配APP,算是蠻方便的,壓下去的聲音也會隨著手指的力度而有所不同,這是當初喵小姐捨棄初階機的最主要原因。搭配耳機就可以在不擾人清夢的情況下好好練習。而且架子本身也提供一個吊掛耳機的地方,非常方便。

總之,好機,白色更是美的西哩花拉的,搭配活動送了一個升降椅,值得入手。



2024年2月27日 星期二

利用tailwind css設計一個簡易側導覽

最近在嚐試利用前端搭配flask寫網頁,幾個小功能,但是需要一個連結給使用者,所以就想要做一個側導覽功能,初始結構是20美元小助理給的,自己微調一下,符合使用比較重要,大致就是拆成html跟javascript,css的部份直接採用tailwind套版,直觀又快速。

首先是html,版面的部份要切出左側一個區域來給側導覽,然後一個按鈕,側導覽的初始狀態是整個隱藏,經過按鈕點擊來做顯示與隱藏的切換:

<div id="sidebar" class="fixed top-0 left-0 h-full bg-gray-800 transition-transform duration-300 ease-in-out -translate-x-full z-10">  
 <div class="mt-16">  
 <ul class="text-white">  
 <li class="p-4 hover:bg-gray-700"><a href="{{ url_for('Router1') }}">Router1</a></li>  
 <li class="p-4 hover:bg-gray-700"><a href="{{ url_for('Router2') }}">Router2</a></li>  
 <li class="p-4 hover:bg-gray-700"><a href="{{ url_for('Router3') }}">Router3</a></li>  
 <li class="p-4 hover:bg-gray-700"><a href="{{ url_for('Router4') }}">Router4</a></li>  
 </ul> </div></div>  
<button id="toggleNav" class="fixed top-0 left-0 mt-10 p-2 text-white bg-blue-500 rounded z-20 transition-transform duration-300 ease-in-out">  
  MenuBar  
</button>

接著在按扭地方加入一個監聽器,主要是偵測到點擊的時候就做sidebar屬性-translate-x-full的切換,然後按鈕的屬性再隨著sidebar的屬性做變化的判斷

document.addEventListener('DOMContentLoaded', function() {  
    const toggleNavButton = document.getElementById('toggleNav');  
  toggleNavButton.addEventListener('click', function() {  
        const sidebar = document.getElementById('sidebar');  
  sidebar.classList.toggle('-translate-x-full');  
  
 if(sidebar.classList.contains('-translate-x-full')){  
            toggleNavButton.style.transform = 'translateX(0px)';  
  } else {  
            toggleNavButton.style.transform = `translateX(${sidebar.offsetWidth}px)`;  
  }  
    });  
});