Lab:Kiro CLI + Langfuse 評估整合

把一個真實的 ACP agent CLI 接進真實的可觀測性後台,用 LLM 評審打分,並在過程中抓出兩個貼近實務的整合失敗案例。

Setup mode:real-tool Time box:90–120 分鐘 複雜度:advanced 涵蓋概念:3 個 English

這個 Lab 在示範什麼問題

Kiro CLI(openab 用來執行 Kiro 的 ACP agent 後端)本質上是一個很單純的命令列工具:你把 prompt 用 pipe 傳進去,它印出一個回答,然後結束。這件事本身完全無法告訴你這個回答「好不好」、花了多少「credit」,或是「換了 prompt 之後是不是真的變好」。Langfuse 存在的目的正是回答這些關於 LLM 應用程式的問題——但 Kiro CLI 並沒有原生支援把資料送進 Langfuse。這中間的橋樑,需要有人自己搭。

這個 lab 就是在搭這座橋,然後拿它做一次真正的評估流程:把一小組客服 FAQ 資料集,分別用兩種不同的 system prompt 餵給 Kiro CLI,每個回答都用 LLM 評審打分,再比較兩次執行結果。另外還有一個較小的附加任務:中途切換不同模型,看看比較貴的模型是不是真的物有所值。

最少需要理解的概念:

系統架構與資料流

Harness(Python)dataset/eval-items.jsonl → task 函式
→
Kiro CLIkiro-cli chat --no-interactive
(ACP agent,每次呼叫各自獨立的 subprocess)
Stop hook.kiro/hooks/*.json → log_usage.sh → usage-log.jsonl
→
評審呼叫獨立的第三方 provider(不同模型家族)+ 評分規則 prompt
Langfuse SDKdataset.run_experiment(task, evaluators)
→
Langfuse(叢集內)trace + generation + score,
歸類到一個具名的 dataset run
Langfuse UIbaseline-v1 vs improved-v1
並排比較

每一筆資料集項目會同時跑兩條平行的資料路徑:回答路徑(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 只是把那次呼叫的輸出被解析出來的分數存起來而已。

學習者的步驟與檢查點

  1. 先讀過整個 scaffold,從 Kiro CLI wrapper 和 Langfuse glue module 開始看起。
  2. 在動手執行之前,先把 5 個檢查點的預測寫下來。
  3. 執行 setup / smoke-test 腳本——它會確認 Kiro CLI 可連線、Langfuse 可連線,以及 harness 所依賴的確切 stdin 呼叫格式是否成立。
  4. 實作那個唯一被留空的核心函式——一個 LLM-as-judge 評分器。
  5. 先跑 baseline 變體,再跑 improved 變體,觀察兩者之間是否出現資料缺口。
  6. 執行比較檢查腳本,並閱讀它印出的結果。
  7. 對 2–3 個模型執行模型切換任務。
  8. 在 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 之後再對答案。

驗收標準

前置需求、時間、成本與備援方案

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 的初衷。