|
📣 工商時間 這期示範的法律關係圖網站,是用 Embabel 框架開發的系統,細節都在鐵人賽 30+1 天的文章裡。但能看懂它、審查它、改動它,靠的不是哪一個框架,是基本功。 《AI 賦能全端開發:從零打造企業級智慧應用》教的就是這套基本功:用 Spring Boot + React 建好後端 API、資料庫、認證授權與前端介面,再加上 Spring AI、RAG 檢索、tool calling 與 MCP,親手完成一套企業級 AI CRM。 基本功打穩之後,換成 Embabel 也好、換成別的框架也好,你都能讓 AI 用不同的工具做出你需要的功能,交出一套完整的系統,而不是只會跟著某一個框架的範例走。 |
把 Agent 的「技能包」搬上網頁,中間到底要補什麼?
嗨,我是凱文大叔。
寫完 30 天鐵人賽之後,我又補了第 31 篇。因為有個問題一直沒回答:
技能(Skill)只能在 CLI 裡用嗎?不會用 CLI 的人怎麼辦?
我拿自己的台灣法律技能包 law-powers 做了實驗,把它整包搬進一個 Spring Boot 4.1 + Embabel 1.5.1 的網站,跑在這裡:
貼上案情,網站會照技能的流程做:腦力激盪 → 追問 → 檢索法條判決 → 要件涵攝 → 抗辯評估 → 起草書狀 → 畫出 3D 法律關係圖。
順帶一提,這個網站本身也是用技能蓋出來的:後端的 Embabel 寫法、前端把能力開給 Agent 的 WebMCP 寫法,都來自我在鐵人賽整理的技能套件 ai-agent-dev-guide。文末會告訴你怎麼一行指令裝進 Claude Code。
以下是搬家過程中,我覺得最值得分享的幾件事

① 技能本體其實不用改寫
這是最少人知道的一招:embabel-agent-skills 可以直接讀 Claude Code 格式的技能目錄,變成能掛進 prompt 的 reference。
// 直接載入 Claude Code 格式的技能目錄,不需要改寫成別的格式
Skills skills = new Skills("law-powers", ..., new DefaultDirectorySkillDefinitionLoader(false))
.withLocalSkill("skills/legal-research");
llm(context).withReference(skills) // ← 技能包在這裡進 prompt
.createObject(prompt, BrainstormResult.class);
路徑就是:CLI 技能 → withLocalSkill() → withReference() → 網頁服務。
技能包還是原本那個 repo,還能在 Claude Code 裡用,兩邊不會走鐘。
順帶一提:我的技能包有 10 個技能,網站只載入 5 個。技能不是載滿就好,每一份都佔 prompt 空間,跟工具白名單是同一個道理。
② 技能寫的是「敘述」,網頁要的是「不可跳過的骨架」
技能說「先腦力激盪,再檢索,再涵攝」。
在 CLI 裡,這是 Agent 自己決定要照做;在網頁上,使用者可能拿到一個跳過檢索的結果,那就完了。
所以我把每個步驟寫成一個 @Action,用 Java record 的型別串起來。analyze 要 ResearchResult,而 ResearchResult 只有 research 產得出來——Embabel 的 GOAP 會自己排出順序,程式裡沒有任何一行 if (step == 3)。
技能的章節標題,通常就是你的 Action 清單。
③ 流程要能停下來等人
法律案件第一次貼進來的案情,幾乎不可能夠用。有沒有簽書面契約?對方是自然人還是公司?這些會直接改變結論。
Embabel 一行解決:
@Action
public UserAnswers askUser(BrainstormResult brainstorm) {
// 沒有問題就直接回空物件往下走,不為了對稱硬停一次
if (brainstorm.questions().isEmpty()) return new UserAnswers(List.of());
// 有問題才暫停流程,等使用者作答後再繼續
return WaitFor.awaitable(new QuestionsAwaitable(brainstorm.questions()));
}
兩個細節:這個 Action 完全不呼叫 LLM(零 token);沒問題就直接回空物件往下衝,不為了對稱硬停一次。
④ 最容易漏掉的一層:把技能裡的「請務必」變成程式擋得住的規則
技能文件寫「請務必引用真實存在的法條」。
這句話在 CLI 靠 Agent 自律;在公開網站上,它必須是程式。
所以建圖那步 LLM 產出之後,一定會再過一層純函式 GraphRules.apply():
- 法條節點的條號必須真的出現在檢索結果裡,否則整個節點移除。
- 要件是否該當,一律以涵攝結果覆寫,不採信建圖那步的說法。
- 被移除的還會寫進 notes,不默默吃掉。

👉 搬家時請對技能裡每一句「務必/禁止/只能」問一次:如果模型沒照做,我的程式擋得住嗎? 擋不住的,就補一段後處理。
⑤ 還有一層技能完全沒寫:治理
因為 CLI 是你自己的機器。上線就全部要補:
- 每日 token 預算
- 每人每日配額
- 登入身分
- 用量統計
- 個資告知與刪除
- 授權排除條款
這層不做完,不要公開網址。
最後一個有趣的迴圈
網站再透過 WebMCP 把 22 個工具暴露給 ChatGPT/Chrome Agent,讓 Agent 也能操作這個頁面。
但我刻意沒有做 submitQuestions 這個工具——Agent 只能把建議答案填進欄位,送出永遠要人按。
技能從 Agent 手上搬到網頁給人用,網頁又把能力還給 Agent,只是這次在人的監督之下。
搬家前先自問的五句話
把這期壓成一張清單,下次要把任何技能搬上網頁時拿出來對一次:
| 層 | 技能裡長什麼樣 | 網頁上必須變成什麼 |
|---|---|---|
| ① 技能本體 | Claude Code 格式目錄 | 原封不動,用 withReference() 掛進 prompt;只載入用得到的 |
| ② 流程 | 章節敘述「先 A 再 B」 | 每章一個 @Action,用型別串起順序 |
| ③ 追問 | 「資訊不足時請追問」 | 零 token 的 WaitFor Action,沒問題就直接往下 |
| ④ 規則 | 「請務必/禁止/只能」 | LLM 產出後的純函式後處理,擋不住的補一段 |
| ⑤ 治理 | (完全沒寫) | 預算、配額、身分、用量、個資、授權——做完才公開 |
蓋這個網站用的技能套件,也開源了
鐵人賽 30 天寫完後,我把整個系列整理成一套 Claude Code 技能,放在 GitHub:
👉 github.com/kevintsai1202/ai-agent-dev-guide
裡面有四個技能,互相交叉引用:
| 技能 | 負責哪一層 | 這期用在哪 |
|---|---|---|
ai-agent-dev-guide |
入口/路由 | 不確定該用哪個技能時從這裡進;含「值不值得用 Agent 框架」判準 |
embabel-agent-backend |
後端(Embabel) | ②③④ 的 @Action、GOAP、WaitFor、@Condition 寫法 |
json-render-ui |
前端(不綁後端) | LLM 產出 UI 規格、SSE 漸進渲染 |
webmcp-development-guide |
對外給 Agent(選用) | 「最後一個迴圈」那 22 個 WebMCP 工具 |
裝法只要兩行,在 Claude Code 裡輸入:
/plugin marketplace add kevintsai1202/ai-agent-dev-guide
/plugin install ai-agent-dev-guide@ai-agent-dev-guide
或者用 skills CLI 一次裝進所有支援的 Agent:
npx skills add kevintsai1202/ai-agent-dev-guide
基準版本是 Spring Boot 4.1 + Embabel 1.5.1 + Spring AI 2.0。還在 Spring Boot 3.5 的專案請留在 Embabel 1.0 那條線,技能裡的版本相容章節有寫清楚兩條線不能混。
完整文章(含四項「值不值得用 Agent 框架」的判準、@Condition 踩坑實錄)在鐵人賽 Day 31:
—— 凱文大叔
延伸閱讀:
- 示範網站:law-graph-webmcp
- 技能套件:ai-agent-dev-guide(GitHub)
- Embabel Agent Framework(GitHub)
- 004 期:鐵人賽預告——用 Embabel 讓 Java 也能寫出企業級 AI Agent
