這個 Lab 在示範什麼問題
Kiro CLI(openab 用來執行 Kiro 的 ACP agent 後端)本質上是一個很單純的命令列工具:你把 prompt 用 pipe 傳進去,它印出一個回答,然後結束。這件事本身完全無法告訴你這個回答「好不好」、花了多少「credit」,或是「換了 prompt 之後是不是真的變好」。Langfuse 存在的目的正是回答這些關於 LLM 應用程式的問題——但 Kiro CLI 並沒有原生支援把資料送進 Langfuse。這中間的橋樑,需要有人自己搭。
這個 lab 就是在搭這座橋,然後拿它做一次真正的評估流程:把一小組客服 FAQ 資料集,分別用兩種不同的 system prompt 餵給 Kiro CLI,每個回答都用 LLM 評審打分,再比較兩次執行結果。另外還有一個較小的附加任務:中途切換不同模型,看看比較貴的模型是不是真的物有所值。
最少需要理解的概念:
- LLM Observability(LLM 可觀測性)——trace → observation → score,是記錄一次 Kiro CLI 呼叫的基本單位。
- LLM-as-Judge Evaluation(LLM 評審式評估)——用第二次、獨立的模型呼叫,依照評分規則(rubric)替第一次的輸出打分。
- ACP Agent Backend(ACP agent 後端)——為什麼 Kiro CLI 的行為比較像是一個「有 session 概念的服務」,而不是單純的一次性函式呼叫。
系統架構與資料流
(ACP agent,每次呼叫各自獨立的 subprocess)
歸類到一個具名的 dataset run
並排比較
每一筆資料集項目會同時跑兩條平行的資料路徑:回答路徑(task → Kiro CLI → output)與評分路徑(output + expected_output → 一個獨立的評審 provider,刻意不是 Kiro CLI → Evaluation)。第三條獨立的路徑——成本路徑——完全跑在 Kiro CLI 自己的行程裡,透過 Stop hook 寫下用量快照,之後再由一個 run 層級的 evaluator 統整;因為評審永遠不會碰到 Kiro CLI,這條成本路徑只反映任務執行本身,不包含評估的額外成本。三條路徑最終都會落在同一個 Langfuse dataset run 裡,這也是為什麼「有沒有變好、花了多少錢」這兩個問題可以在同一個畫面上得到答案。
每個工具負責什麼——責任的交界在哪裡
| 工具 | 負責的事 | 不負責的事 |
|---|---|---|
| Kiro CLI | 執行 agent、產生回答、觸發生命週期 hooks(Stop 等) | 結構化追蹤、評分、成本彙總——它只負責印出文字然後結束 |
| Harness(這個 lab 的 Python 程式) | 把 subprocess 呼叫轉成 Langfuse observation;擁有評審的評分規則 | 作為「Kiro CLI 是否真的可連線」的判斷依據——那是 Setup 階段的事 |
Stop hook | 在每一次回應之後觸發用量檢查,不需要 harness 主動去問 | 如果 trigger 欄位對不上 Kiro 真正的 trigger 名稱,就什麼都不會發生——見失敗案例二 |
| Langfuse | 儲存 trace/score,把它們歸類成可比較的 dataset run,並提供 UI | 自己判斷回答品質好壞——那是評審呼叫在做的事,而且刻意跑在跟 task 不同的 provider 上 |
最值得留意的一條介面:「評審」本身既不是 Langfuse 的功能,也不是 Kiro CLI。它是一次普通、獨立的呼叫,打到一個不同模型家族的獨立 provider,只是剛好跑在一個叫做「evaluator」的函式裡面。刻意不讓評審碰 Kiro CLI 有兩個原因:如果評審也走同一個 CLI,會透過同一個 Stop hook 把成本重複算進 task 的用量裡;而且用同一個模型家族評自己的輸出,容易出現偏袒自己家族的系統性偏差。Langfuse 只是把那次呼叫的輸出被解析出來的分數存起來而已。
學習者的步驟與檢查點
- 先讀過整個 scaffold,從 Kiro CLI wrapper 和 Langfuse glue module 開始看起。
- 在動手執行之前,先把 5 個檢查點的預測寫下來。
- 執行 setup / smoke-test 腳本——它會確認 Kiro CLI 可連線、Langfuse 可連線,以及 harness 所依賴的確切 stdin 呼叫格式是否成立。
- 實作那個唯一被留空的核心函式——一個 LLM-as-judge 評分器。
- 先跑 baseline 變體,再跑 improved 變體,觀察兩者之間是否出現資料缺口。
- 執行比較檢查腳本,並閱讀它印出的結果。
- 對 2–3 個模型執行模型切換任務。
- 在 Langfuse UI 打開兩個 dataset run,使用內建的 run 比較畫面。
每個階段可觀察的訊號:Kiro CLI 的 stdout(單一 piped input 是否真的只產生單一乾淨的回覆?)、usage-log.jsonl(每次回應後是否真的多了一行?)、Langfuse UI(每個項目是否都出現了 trace、generation、score?),以及比較腳本本身印出的成功/失敗結果。
失敗案例——該檢查什麼(不是答案本身)
案例一——一個不能照字面相信的評審分數
資料集中有一筆項目是刻意設計成「沒有唯一正確答案」的——這個問題本身就依賴一些沒有被提供的政策資訊。你的評審還是會照跑不誤,請仔細看它的推理內容,而不只是看分數本身。該檢查什麼:你的評分規則有沒有任何方式表達「這一題不該用跟事實查核題一樣的方式打分」,以及如果沒有的話,你的彙總指標會發生什麼事。
案例二——一個看起來已設定好、實際上沒生效的成本追蹤 hook
理論上,一個綁在 Stop 上的 hook 應該要在每次 Kiro CLI 回應之後記錄一筆用量快照,這樣就不需要有人手動盯著用量指令。但在跑完其中一個資料集變體之後,那次執行的用量記錄會出乎意料地是空的。Kiro CLI 並不會對此報錯或警告——這個 hook 檔案是合法的 JSON,只是永遠對不上任何觸發條件,安靜地什麼都沒發生。該檢查什麼:把 hook 的 trigger 欄位,對照 Kiro 目前有效的 trigger 名稱清單,並想清楚:為什麼一個語法完全正確的 hook 設定,還是可能什麼作用都沒有。
確切的根本原因與修法,刻意不寫在這個公開頁面上——那是這個 lab 的私有解答。跑完 lab-review.md 之後再對答案。
驗收標準
- Langfuse 裡存在兩個具名的 dataset run,每一個都有對應資料集中每一筆項目的分數。
- 兩個 run 都健康之後,比較腳本能夠成功執行完畢。
- improved prompt 變體在大部分(不必是全部)項目上分數高於 baseline;那筆刻意設計成模糊的項目可以是例外。
- 模型切換任務對你跑過的每一組(模型 × 項目)都要產生一個分數與一個延遲時間。
- 你能用自己的話解釋:為什麼那筆模糊項目不會推翻你的評審設計,以及你是怎麼診斷出用量資料遺失的——而不只是「我把它修好了」。
前置需求、時間、成本與備援方案
| Kiro CLI | 需要已安裝並完成驗證(API key)。不是每個環境都預裝,開始前請先確認。 |
| 評審 provider | 跟 Kiro CLI 完全獨立的憑證(Anthropic、OpenAI 或 Gemini,看你手邊有哪個),且模型家族要跟你的 Kiro CLI task 端不同——永遠不會透過 Kiro CLI 發送。 |
| Langfuse | 一個可連線的 Langfuse 專案(叢集內既有的實例,或 cloud.langfuse.com),並使用你自己的 API key。 |
| 預估時間 | 90–120 分鐘,包含閱讀 scaffold 以及兩個失敗案例的調查時間。 |
| 成本風險 | 兩個獨立的計費來源:兩個資料集變體加上模型比較任務,大約會產生 25 次 Kiro CLI 呼叫(只有 task 執行);另外大約 25 次評審 provider 呼叫則另外計費。兩邊都建議前後各檢查一次用量。 |
| 備援方案(mock fallback) | 沒有提供。這個 lab 刻意採用 real-tool 模式——它想教的失敗模式,只有在真正的 CLI 行程與真正的 hook 比對邏輯介入時才會出現。 |
為什麼這個 lab 沒有 local-mock 備援方案
要模擬 Kiro CLI,就得同時模擬這個 lab 想教的兩件事:真實 CLI 在 non-interactive stdin/stdout 上的確切行為(不同版本差異很大),以及一個會在觸發名稱對不上時安靜不作動的 hook 比對引擎。把這兩者都模擬出來,等於要重新寫一份(並且默默信任)你原本應該去驗證的那個東西——這樣就違背了 real-tool lab 的初衷。