kitt-stt 的 Agent 驗證與效能分析平台
最值得導入的是一個完整的驗證流程:agent 能找到功能、啟動自己的服務、以正式協定驅動、保存原始證據,再讀取小型結構化結果。kitt-stt 已有大部分工具;目前的問題是這些工具分散,功能測試、效能量測與 trace 的界線沒有在同一入口說清楚。
本次已在獨立 worktree 導入專案內的 verify-kitt-stt skill、Docker 證據出口、既有硬體 profiler 的整合,以及 Perfetto SQL 查詢。圖表沿用既有工具,沒有新增 observability server 或修改 STT 推論邏輯。這是一個可執行的平台起點,尚不是自動判定效能回歸或自主調參的系統。
研究基準為本機 main 的 67160fab;分支為 research/agent-verification-20260912,目錄為 隔離研究工作樹。外部來源查閱日期為 2026-09-12。下文區分程式已提供的能力、實跑結果與尚未實作的建議。
1. pstack 哪些流程值得採用
這裡的 pstack 指 Cursor 公開的工程 workflow 集合。它的價值是把工作拆成有證據門檻的流程;不同 playbook 的交付物不同,不能用同一個「測試綠燈」代替所有結論。1
| 公開流程 | 核心做法 | 在 kitt-stt 的落點 |
|---|---|---|
| Create verification skill | 從 repo 找出介面、啟動方式、驅動方式、證據與隔離條件;至少實跑一個功能 | 專案內 skill + 五個 feature map,沿用真實 WS/WAV harness |
| Maintain verification skill | 同時核對程式與真實操作;地圖列出的功能都要覆蓋 | 後續用 $maintain-verification-skill,單個功能通過不能代表全地圖通過 |
| Perf issue | 先量 baseline,再提出由 trace 支持的修正,最後比較前後結果 | 同一輸入、環境、pacing 與暖機下重跑;不憑讀碼推算加速百分比 |
| Runtime forensics | 針對正在發生的症狀取樣,再對應到程式位置 | CPU、RSS 或 GPU 症狀分別選工具;避免同時開啟所有 profiler |
| Trace forensics | 對既有 artifact 查詢、縮小範圍、對應來源;有配對 capture 才比較 | 官方 Trace Processor + 本次 SQL;保留原始 gzip,不把大 JSON 塞進 context |
| Hillclimb | 固定工作負載與指標,一次一項變更,量測後決定保留或撤回 | 待基線具備重複樣本、品質守門與可信比較條件後,再引入逐次實驗記錄 |
上述流程分別來自建立/維護 skill 與各 playbook。234567 採用的是驗證原則;pstack 的特定編輯器安裝方式、模型配置、開 PR/合併流程不會自動成為這個 repo 的操作授權。
對 agent 最重要的介面是「給定問題,應執行哪一條命令;結果在哪裡;哪些數字能支持結論」。讓它看懂更多圖像有幫助,但若缺少輸入雜湊、量測邊界、樣本數與退出狀態,一張漂亮圖表仍然容易導致錯誤歸因。
2. 目前 repo 已經有哪些能力
flowchart TD
A[Feature map:選擇功能與證據] --> B[Docker harness:獨立 image / container / localhost port]
B --> C[Doctor:health / build / model / target]
C --> D[Live tests:真實 WAV + WS frames]
D --> E{功能是否通過}
E -->|是,且要求效能量測| F[既有 hardware_profile:bulk + realtime]
E -->|否,或不做效能量測| G[保留退出碼與證據]
F --> G
G --> H[確認 driver 結束後的新 trace 快照]
H --> I[停止並匯出 logs / config / trace]
I --> J[清除本次 container ID]
J --> K[JSON / CSV 給 agent;HTML / Perfetto 給人]
| 能力 | 現有來源 | 適用範圍與限制 |
|---|---|---|
| 自有容器啟動 | tests/docker-test.sh |
既有 health gate、CPU fallback、模型掛載與測試入口;本次補上證據保存 |
| 主要使用介面 | src/server.py:create_stt_processor、src/html_server.py |
WebSocket frame 協定;status page 並非語音體驗的主要驗證路徑 |
| 功能重播 | tests/functional/live/conftest.py:_run |
真實音訊與控制 frame,但每個 20 ms chunk 只等待 3 ms,屬加速驗證 |
| 單使用者效能 | tests/batch_decode/hardware_profile.py:run_feed_series |
bulk/realtime 分開;每個 feed 一條連線,預設三次暖機;已有 JSON、CSV 與互動 SVG 圖表 |
| 多使用者效能 | tests/batch_decode/concurrency_profile.py |
已有多座位、重疊送音量測;適合下一階段驗證 shared recognizer 的排隊成本 |
| ASR 分段成本 | src/asr/batch_asr_manager.py |
prep、rec_lock_wait、decode、get_res、itn/post,部分事件帶 final/interim phase |
| 服務端延遲 | src/kitt_stt_service.py:1679、:1747 |
final frame 的 latency、TTFT、RTF、interim 成本;trace 另有 final_tm_break 與 final_utt |
| 使用者負載 | src/kitt_stt_service.py:_emit_seat_load |
audio_lag 與 occ;後者是 handler 佔用比例,不是 GPU utilization |
| 系統資源 | utils/sys_diag.py:create_periodic_metric_recorder |
約每 2 秒 CPU/RSS/GPU 記憶體取樣;部分為整機、部分為單 process |
| Trace 留存 | utils/perfetto_trace.py:write_trace、src/server.py:298 |
原子寫入 gzip、旋轉檔案、600 秒滑動保留窗;旋轉快照可能重疊 |
| 連線 log | src/server.py:debug_logs、src/html_server.py:_handle_connection |
WS trace_id 對應 HTTP log 查詢;目前並非所有 live case 都輸出這種關聯資料 |
| TurnSense 時間 | src/turnsense/runtime.py:analyze |
log 已區分 inference 與 semaphore queue;目前沒有同等完整的 Perfetto domain spans |
因此,新增另一套 benchmark driver 或另一套 chart renderer 的收益不高。先把既有資料保存、標記邊界,再提供可查詢形式,能以較小改動讓下一個 agent 直接使用。
3. 指標必須先定義量測邊界
| 指標 | 現有定義/來源 | 可以回答 | 不能直接推論 |
|---|---|---|---|
server_latency_ms |
final latency_ms,BatchASRManager.run_offline_inference 的整段 wall time |
final ASR 路徑是否變慢,包含準備、等待與後處理 | 純 GPU kernel 耗時、整個 EOT 等待 |
decode span |
recognizer 解碼的 duration event | 解碼區段的 wall time | GPU 上所有 kernel 的獨立成本與佔用率 |
rec_lock_wait |
recognizer lock 取得前的等待 | 多使用者競爭是否拖慢 final/interim | executor queue、網路排隊或所有 event-loop stall |
server_rtf |
累計 decode time ÷ 有效音訊長度 | 推論計算成本相對語音長度的比例 | 一定能即時回應;RTF 不包含所有等待成本 |
server_ttft_ms |
服務端第一個 interim 的時間;沒有 interim 時可退回 final | 首個辨識輸出的等待 | 第一個正確可用 token;fallback 與真正 interim 應分開 |
client_final_latency_ms |
driver enqueue VAD stop 到 client 收到 final 的同一 monotonic clock 差 | 應用協定端 final 到達延遲 | 聲學說完話到 UI paint,或 authoritative EOT commit |
ttfs_ms |
服務端 wall clock 相對 VAD stop 時間與 stop_secs 補償 |
現有服務定義下的 final segment 時間 | 與 client monotonic 指標無條件相減或互換 |
max_interim_gap_s |
client 相鄰兩個 interim 的最大間距 | 使用者在中途看到文字停頓多久 | 首次 interim 前/最後 interim 後的尾端空窗 |
proc_mem (MB) |
process RSS 的週期樣本 | 長時間或多輪操作的記憶體趨勢 | 哪個 Python object retained,或 CUDA allocation 根因 |
| GPU memory | 平台可取得的整機/process counter | 記憶體容量與趨勢 | CUDA kernel 時序、是否 compute-bound;Jetson 的共享記憶體也不能照搬獨立 VRAM 解釋 |
hardware_profile.py 的 HTML 另把 server_rtf × duration_s 顯示為累計運算時間。它是由比值回推的量,不是新的端到端量測,而且報表 duration_s 與服務端包含 preroll 的有效音訊分母可能不同。需要精確分解時,讀 final metrics 或 trace 的原始時間。
EOT 尤其需要獨立處理。src/eot.py:apply 的 WAIT 可以先送 final text,再繼續等待;COMMIT 有 final 時把 EOT metadata 附在 transcription,沒有 final 時才送明確的 semantic stop。只看「收到 final」或某個 stop callback,無法證明使用者的 turn 已正確結案。
4. 需要補哪些指標與圖表
以下是後續增量的優先順序。只有表中的第一列已在本次導入;其餘是有來源落點的建議。
| 優先序 | 增量 | 建議落點 | 交付形式/判讀方式 |
|---|---|---|---|
| P0,已導入 | 保存身分、輸入與輸出、provider path、錯誤;可查詢 trace | tests/docker-test.sh、tests/verification_evidence.py |
manifest、input SHA-256、XML、JSON/CSV、原始 trace、SQL 摘要 |
| P1 | connection/turn/segment/run 關聯鍵 | live driver、UserSession/turn state、trace args |
用同一 key 串 input、final、EOT、log、trace;UID 單獨不足以區分多條連線 |
| P1 | vad_stop → final 與 vad_stop → authoritative commit 分開量 |
接收器觀察 EOT metadata;src/eot.py:_commit |
各自的樣本表及依 commit reason 分組的延遲分布;timeout 另外計數 |
| P1 | timeout/error/空 final/重複 commit 的分母 | driver collector 與 live assertions | 成功率、漏失率與錯誤率;不要只留下成功樣本的低 latency |
| P1 | 每 workload 的重複試驗與公平比較 | 既有硬體與 concurrency profiler 周邊 | 每 feed、長度、座位數的樣本數、median、spread;箱形圖或 ECDF 比單條平均線更能呈現抖動 |
| P1 | 將 TurnSense frontend、queue、inference 分成 trace spans | src/turnsense/runtime.py:analyze |
看 ASR 與 TurnSense 並行期間,critical path 到底由哪一段決定 |
| P2 | event-loop lag、executor submit→start、trace flush 等待 | event-loop 定時 probe、asyncio.to_thread 邊界、PerfettoTracer.write_trace |
延遲時間序列與 domain timeline 並排,量到後才決定要不要保留埋點 |
| P2 | 斷線後的 RSS/VRAM 基線及多輪斜率 | 既有資源 counters + 重複連線工作負載 | 以完成 session 數為 x 軸;暖機與 allocator cache plateau 要排除 |
| P2 | EOT 品質與 CER/WER 聯合評估 | 現有 bilingual/wake/EOT cases 與代表性語料 | latency–quality 對照;避免以過早結案或辨識品質下降換速度 |
建議的比較最小單位是「固定一個 workload cell」。例如同一 6 秒音訊、realtime、單座位、相同暖機、同一模型與 provider,先做多次獨立 baseline,再交錯跑 A/B。不同長度或 bulk/realtime 不可混成一個 p95。三輪可作為初步穩定性檢查,但不是統計充分性的保證;尾端指標需要更多樣本與跨輪穩定性。
圖表應由原始資料產生並保留 CSV。對 agent,先給每個 cell 的 JSON 摘要與查詢入口;對人,提供可縮放的 latency/RTF/interim 曲線,再在需要比較時補分布圖。沒有足夠樣本時,圖與文字都標成 smoke measurement。
5. 工具選擇
| 工具 | 適合的問題 | 本次決策 |
|---|---|---|
| 既有 live WS harness | 功能正確性、wake/standby/control | 保留,作為每次量測前的功能 gate |
| 既有 hardware/concurrency profiler | 即時送音、音檔長度與座位數對 latency 的影響 | 導入 hardware profile;concurrency 作為下一個 workload 維度 |
| Perfetto Trace Processor | domain durations、counter、parser diagnostics 的可程式化查詢 | 已下載官方 v58.2 CLI 到 /tmp、核對官方 manifest SHA-256,實際查詢本次 trace |
| Perfetto 官方 AI skill | 讓 agent 學習 CLI、schema discovery 與 PerfettoSQL | 有官方流程可採用;本次先用 CLI + repo SQL,未安裝全域 plugin |
| py-spy | Python CPU 熱點、執行緒堆疊;可選 native frames | 有 CPU 症狀再啟用,優先留下 speedscope/raw 格式;須能確認目標 process |
| Memray | Python/native allocation、長時間成長的配置來源 | RSS 趨勢確認後,以獨立 process 的 memray run 取證 |
| Nsight Systems | CUDA API、kernel、CPU/GPU 同步或 GPU idle gap | 明確 GPU 時序問題才使用;目前 domain trace 沒有 CUDA kernels,無法憑空分析 |
| OpenTelemetry | 跨 web-ui/gateway/STT 的因果關聯、長期觀測 | 先補服務內的 correlation;單機證據流程不足時再導入跨服務 spans |
Perfetto 官方文件已直接提供 AI workflow 與 Trace Processor 的 SQL 介面;現有 Chrome JSON trace 可由 CLI 載入,無需先自製另一套 trace database。89 本次 SQL 額外保留 track 與 final/interim phase,輸出樣本數、平均、p50、p95、最大值以及 counter 和匯入診斷。
py-spy 能輸出火焰圖、speedscope 或 raw 資料,也支援部分平台的 native stack。10 Memray 的 run 適合從 process 開始取 allocation profile;native mode 回答的問題比 RSS 數字更深入,但也會增加觀測成本。11 Nsight Systems 提供 CUDA 與系統時序,應在 GPU 假說明確時使用。12 三者都未在本次安裝或執行,不能把它們寫成已得到的診斷證據。
6. 本次實際導入
6.1 一個可重跑的入口
在專案內新增 .agents/skills/verify-kitt-stt/,有 Launch、Doctor、Drive、Evidence、Cleanup、Helpers,並列出 streaming ASR、wake、manual control、forced stop、connection logs 五個功能。skill 裡的命令可直接在這個 worktree 執行,不依賴原先 workspace 共用文件的固定 checkout。
tests/docker-test.sh 增加兩個 opt-in 參數:
EVIDENCE_DIR=/path/to/local-evidence PROFILE_LENGTHS=3,6 \
IMAGE=kitt-stt:my-verification HOST_PORT=9317 \
LIVE_TEST_TARGET=tests/functional/live/test_btn_control.py \
./tests/docker-test.sh --build -k test_btn_activate
指定全新目錄後才收集證據。PROFILE_LENGTHS 啟用既有硬體 profiler;不指定則維持功能驗證。快取音檔必須存在,避免原本的音檔 generator 在不知情時呼叫外部 TTS。skill 提供以 repository WAV 產生 3/6 秒測試片段的本機步驟。
6.2 證據與失敗語意
Manifest 記錄 Git revision、工作樹狀態、來源檔雜湊、命令、host 與參數;另保存 ASR 模型/輸入 WAV 雜湊、有效設定、實際 image ID 與 port binding。原有 profiler 即使有 timeout row 仍可能 exit 0,因此 wrapper 會讀回 JSON,拒絕錯誤列或缺失/非有限的數值。
容器由 docker create 回傳的 ID 管理。每次 provider attempt 有自己的 artifact 目錄;收集與停止之後才 remove 該 ID。測試失敗保留原 nonzero code;若功能本來成功但證據匯出失敗,整次驗證仍為 nonzero。這些契約已有獨立的可執行檢查,Docker 替身只用來測 harness 的成功/失敗/copy error 路徑;STT 功能驗收則使用真實服務。
6.3 實跑才發現的 shutdown 邊界
src/server.py:322 設定 tracing signal handler,但 src/html_server.py:89 的 serve() 會透過 event loop 重新註冊 SIGINT/SIGTERM。第一次 capture 實際只看到 shutting down ...,沒有 Final trace flushed。Tracing threads 為非 daemon;不能把 docker stop 當成已完成最後 flush 的證據。
本次保留服務端程式,讓收集器在 driver 結束後確認兩次新的週期 trace 寫入,再停止容器。第二次寫入排除第一個 snapshot 在 driver 結束前已開始序列化的可能。capture.json 分別記錄 snapshot_after_driver_exit 與 final_flush_logged;後者可以是 false,前者必須有證據。這增加約兩個 flush interval 的等待,預設最多約 20 秒,另加 Docker 停止等待。
這個做法保留的是已完成 driver 工作的證據,並沒有修好服務端 shutdown。下一個獨立 runtime 修正應讓 trace cleanup 綁在 app.serve() 的生命週期 finally,以避免 signal handlers 互相覆蓋;需要另行驗證正常停止、啟動錯誤與重複 cleanup。它也不會把「driver 提早斷線、尚未 commit 的 turn」變成完整 EOT 測試。
7. 驗證結果與解讀
第一輪已通過單一 control 功能與四個 profile rows,但缺少 final flush 確認,整輪被正確標為失敗,證據仍留在 /path/to/local-evidence。修改收集方式後的完整 skill 重跑使用 /path/to/local-evidence;該輪 exit code 為 0,capture 沒有匯出錯誤,共保留 3,778 events,snapshot_after_driver_exit=true。精確 container ID 的移除與證據留存已核對並寫入 cleanup-check.json。
最後將 health 的純文字回應命名為 health.txt 後,又以同一映像重跑單一 control 功能;證據在 /path/to/local-evidence。該輪 1 passed、5 deselected,exit code 0,317 events 保留,容器已移除。前述完整 profile run 仍保留當時名為 health.json 的原始純文字回應,未改寫歷史證據。本次建立的兩個 image tags 亦已清除;重跑 skill 會重新建置。
測試主機為 RTX 4090,driver 580.173.02。ASR startup path 為 cuda (config default),有效設定仍使用 CUDA;TurnSense 在其 runtime 中檢查實際 active provider。ASR 的 label/設定不等於逐節點 provider assignment 證據,因此這裡不宣稱每個 ASR operator 都在 GPU。主機同時有其他既存服務,本次沒有獨占 GPU;數字只驗證量測流程。
重播音訊由 say-lock-all.wav 本機重複/裁切,長度為 3 秒與 6 秒;每種 feed 先暖機三次,表中每格只有一次正式樣本。
| Feed | 音訊長度 | final ASR wall time | client final latency | RTF | interim 數 | 最大相鄰 interim 間距 | 實際送音時間 |
|---|---|---|---|---|---|---|---|
| bulk | 3 s | 32.31 ms | 38.51 ms | 0.014 | 1 | 不適用 | 0.04 s |
| bulk | 6 s | 45.02 ms | 50.50 ms | 0.009 | 1 | 不適用 | 0.04 s |
| realtime | 3 s | 42.15 ms | 45.83 ms | 0.072 | 4 | 1.026 s | 3.17 s |
| realtime | 6 s | 45.98 ms | 49.24 ms | 0.079 | 7 | 2.550 s | 6.34 s |
原始 JSON、CSV 與 互動圖表 同時保存。重點是即時模式有 interim 與可量測的停頓;即使 final latency 只有約 49 ms,6 秒樣本中間仍出現約 2.55 秒的更新間隔,因此「final 很快」不足以描述整體體驗。此現象尚未完成重複實驗與根因歸因。
這不是優化前後比較:兩輪服務端程式相同,不能把其差異歸功於效能改善。暖機、OS 排程、音訊 pacing、共享 GPU 與 callback 行為都可能影響數字。查詢整條 trace 時也會包含功能 case、暖機、bulk 和 realtime,必須保留這個混合 workload 標記。
Perfetto v58.2 能載入本次 trace 並執行 repo SQL,但也報告 trace_sorter_negative_timestamp_dropped(1)和 slice_spill_overlapping_complete_event(3)。第一輪原始資料確認負時間戳來自 server_startup:它的開始時間早於 tracer 自己的 time origin。重疊事件使用相同 logical thread track,不能把畫面上的 nesting 當成真實 CPU call stack。這些警告沒有被刪掉或自動「修正」;分析 startup 與 parent/self-time 時必須先修正輸出語意。
SQL 摘要 與 parser log 保留這些診斷。查詢各 track 後加總的 domain event 數量已與原始 JSON 核對:interim decode 48、final decode 11;這包含暖機與功能測試,並非 4 個正式 profile rows 的樣本數。核對結果在 sql-validation.json。
| 驗證 | 範圍與結果 |
|---|---|
| 新增 artifact contract 檢查 | python3 tests/test_verification_evidence.py,2 項通過;涵蓋成功、測試失敗、copy 失敗與無效 profile 數值 |
| 相關 offline checks | verification evidence、live URI、model manager、image build freshness 等,52 passed |
| 規格追溯 | tests/test_spec_traceability.py,3 passed;新測試明示屬驗證設施,沒有產品 SW_id |
| 靜態檢查 | 新增 Python 的 Ruff、shell syntax、Git whitespace 檢查通過 |
| 真實功能 | test_btn_activate,1 passed、5 deselected;不是整個 live suite |
| 效能 smoke | 2 bulk + 2 realtime rows 成功;不是品質 corpus 或統計回歸 gate |
| 全套 regression | 本次未執行;服務端推論與對外 frame 行為沒有修改 |
8. 下一階段如何讓 AI 自主評估
先以目前的入口建立少數代表性 workload:短指令、長句、wake-only、COMPLETE、INCOMPLETE、退出詞以及多座位完全重疊。每個 workload 指定它要觀察的 final/commit/cancel 契約,以及可接受的錯誤率;品質 gate 與效能數字要一起保存。
接著增加 correlation 與多次試驗。每個 observation 至少包含 run ID、connection ID、turn/segment ID、輸入雜湊、feed、長度、座位數、暖機標記、成功/錯誤狀態,以及其時間邊界。再把「前後環境是否相容」做成可檢查的比較前提。當前 manifest 提供追溯材料,但尚未自動阻擋所有不可比的比較。
最後才採用 hillclimb:固定 workload 與品質 gate,提出由 trace 支持的一個假說,每次只改一個因素,保存 before/after/spread/測試結果,決定保留或撤回。若 metric 的波動大於變更效果,就回到量測流程排除干擾,而不是讓 agent 繼續猜測微調。這是本研究建議的最小演進路線。
評估 agent 平台本身
STT latency 是產品指標;驗證平台還需要評估 agent 是否能可靠取得結論。可用固定任務與乾淨 worktree,對比導入前後的操作紀錄,先量下面四項,不把本次導入直接宣稱為 token 或工時節省。
| 平台指標 | 可執行的判定方式 |
|---|---|
| 從任務到首份有效證據的時間 | 記錄開始、Doctor 通過、drive 完成、artifact gate 通過的時間;分開建置與服務冷啟動 |
| 錯誤成功率 | 以 timeout row、缺失 metric、copy failure、錯誤 target 等已知失敗注入檢查是否仍被宣告成功;本次已有前三種的 harness 檢查 |
| 可重跑與可追溯比例 | 新 agent 只讀 skill,能否用 manifest 的輸入/設定/revision 重跑並找到原始證據 |
| 結論的 context 成本 | 比較 raw trace 全讀與 SQL 摘要加精準回查的輸入量;摘要也必須能回指相同 artifact 與 query |
這些指標比新增 agent framework 更能回答「平台是否真的讓 AI 更有效」。目前已驗證的是操作與證據流程;尚未進行盲測的 agent A/B evaluation。
來源
Repository 來源以本工作樹為準:tests/docker-test.sh、tests/verification_evidence.py、tests/verification_trace.sql、tests/functional/live/conftest.py、tests/batch_decode/hardware_profile.py、tests/batch_decode/concurrency_profile.py、src/server.py、src/html_server.py、src/asr/batch_asr_manager.py、src/kitt_stt_service.py、src/eot.py、src/turnsense/runtime.py、utils/perfetto_trace.py、utils/sys_diag.py。
-
Cursor / pstack,Create verification skill,查閱 2026-09-12;本地使用版本由同名已提供 skill 定義。 ↩
-
Cursor / pstack,Maintain verification skill,查閱 2026-09-12。 ↩
-
Cursor / pstack,Perf issue,查閱 2026-09-12。 ↩
-
Cursor / pstack,Runtime forensics,查閱 2026-09-12。 ↩
-
Cursor / pstack,Trace forensics,查閱 2026-09-12。 ↩
-
Perfetto,Trace Processor,查閱 2026-09-12;CLI 實測版本
v58.2-add693d8b。 ↩ -
Perfetto,Using AI with Perfetto,查閱 2026-09-12。 ↩
-
Bloomberg / Memray,The run subcommand,查閱 2026-09-12。 ↩
-
NVIDIA,Nsight Systems User Guide,查閱 2026-09-12。 ↩