プログラミング
Show HN: Sentinel – コードを読むオープンソースQAエージェント
Show HN: Sentinel – open-source QA agent that reads your code before it clicks (blog.simbastack.com)
要約
Sentinelは、コードベースを読み込み、ビジネスフローを理解して、フロントエンドとバックエンド全体でエンドツーエンドのテストを実行するオープンソースのQAエージェントです。従来のQAエージェントとは異なり、コード構造を分析してテスト計画を自動生成し、UIだけでなくAPIレベルでのバグも検出します。設計レビュー機能も備え、開発ライフサイクル全体で品質保証を支援します。
全文翻訳
Sentinelはコードベースを読み込み、実際のビジネスフローを把握し、フロントエンドとバックエンド全体でエンドツーエンドのテストを実行します。MITライセンスでオープンソースです。
クリックと理解のギャップ
典型的なAIエージェントをアプリに適用すると、ページを開き、いくつかのボタンをクリックし、要素のずれやコンソールエラーに気づいて、実行完了とみなします。これはスモークテストが役立つ程度の有用性です。エージェントは、あなたの製品が実際に行っていることのモデルを持っていません。採用する価値のあるQAエンジニアは、あちこちクリックするだけではありません。彼らはまず製品を学び、それについて推論します。これはホテルシステムなので、シングル予約、グループ予約、部屋を解放するキャンセル、チェックイン、チェックアウト、ナイト監査をテストする必要があり、UIが正常に見えることを信頼するのではなく、それぞれがサーバーに実際に永続化されたことを確認する必要があります。
実際のアプリと指示なしで与えた場合
私たちはSentinelを、KaribuKitという自社製品である動作中のフルスタックホテルPMS(プロパティマネジメントシステム)に適用しました。Next.jsフロントエンド、独立したAPIサービス、Postgresデータベースを備えています。PMSはKaribuKit、つまり私たちの製品であり、エージェントに使い捨てテストテナントの管理者認証情報を提供できました。それ以外は何も与えませんでした。リポジトリとそれらの認証情報のみです。テスト計画も、フローのリストもありません。Sentinelはコードを読み、製品がブティックおよびサファリホテルPMSであると結論付け、独自の判断で9つの重要なビジネスフローを導き出しました。それは、完全な予約ライフサイクル、グループ予約、リードから提案へのパイプライン、料金管理、ゲストセルフサービス、滞在中部屋変更、ナイト監査、請求から返金までの支払い、そしてAIコパイロットです。キャンセルも含まれていますが、この実行ではそれらをスタンドアロンフローとして導き出すのではなく、他のフロー内のエッジケースおよびバックエンドチェックとして折り込みました。これは、人間のQAリードが初日に書くリストに近く、エージェントに何も指示されていません。
実行自体
リポジトリから導き出された計画、上位2つのフローをそれぞれ2回ずつ深くテストし、その後、訪問したすべての画面に対してビジョンパスを実行しました。その後、それらのフローの上位2つをそれぞれ2回実行し、トレースは人が作業しているのを見ているかのようでした。GET /api/availabilityを呼び出し、400エラーを受け取り、不足しているパラメータを特定して、adults=2&children=0で再試行して200エラーを取得しました。POST /api/reservations(201)で実際の予約を作成し、正しい部屋と料金が永続化されたことを確認するために再度取得しました。ステータスライフサイクルをウォークスルーし、試行錯誤でチェックインエンドポイントを見つけました(/checkinは404、/check-inは400、その後有効な呼び出しで200が返されました)、そしてフォリオを確認しました。検証されたフォリオは、1泊178ドルの部屋料金3泊、残高534ドルでした。
コーナーの赤いトーストは、ライブでキャッチされたバグです。「予約済みの部屋があるにもかかわらず、『部屋が利用できません』」というメッセージが表示されました。UIだけでは見つけられないバグです。
予約の確認でNO_AVAILABILITYが返されましたが、同じ予約はすでに部屋を確保していました。UIが誤解を招く「部屋が利用できません」というトーストに変換されたバックエンドステートマシンのバグが、ある試行では発生し、別の試行では全くフィードバックがありませんでした。カレンダーには、すでに予約が存在する部屋が利用可能として表示されていました。APIとUIは一致せず、両方のレイヤーをチェックすることでのみ検出できました。チェックインは200を返しましたが、ゲストのregistrationStatusはサーバー上でNONEのままでした。これは半ば完了した状態遷移です。実行中のカレンダーは、エージェント自身が作成した予約で埋め尽くされ、保留中と確定済みのものが並んで表示されていました。
仕組み
QAエージェントの最も深いエンジンであるflowはパイプラインです。
リポジトリを読む。決定論的な再構築パス(grepとfind、モデル呼び出しなし)が構造を抽出します。フロントエンドルート、APIルートモジュール、サービス、データベースエンティティです。モデルは、モノリポを盲目的にクロールするのではなく、このダイジェストを推論します。これは遅く、見落としがあります。
フローを導き出す。Mimo、つまりpiエージェントハーネスで実行するXiaomiモデルが、ダイジェストをUIステップ、バックエンドアサーション、エッジケースを含む優先順位付けされたエンドツーエンドビジネスフローのリストに変換します。計画はコミットごとにキャッシュされるため、コードが変更された場合にのみ再導出されます。
各フローをエージェントループとして実行する。モデルが次のアクションを決定し、Playwrightがブラウザを駆動し、ファーストクラスのapi_requestツールが各ステップでサーバー状態をチェックします。これは、ページ内でfetchを実行し、フロントエンド自体が送信したAuthorizationヘッダーをリプレイすることによって行われます。モデルはブラウザとAPIツールのみを受け取り、シェルやファイルシステムは決して受け取りません。ハードバジェットが各試行を制限します。90ツールコールを超えると、アクションツールは動作を拒否し、残された唯一の動きは終了です。
複数回実行する。自律エージェントは非決定的です。1つのフローで、ある試行ではバグが見つからず、別の試行では5つ見つかったことを測定しました。そのため、各フローはデフォルトで2回実行され(FLOW_ATTEMPTS、ノブ)、結果はユニオンされます。いずれかの試行で見つかったバグはレポートになり、各フローは試行間の最悪の判定を保持します。
デザインを評価する。エージェントが訪問する各ユニークな画面(URLで重複排除、実行あたりデフォルトで最大8つ)は、マルチモーダルモデルからのビジョンパスも受け取り、視覚的階層、スペーシング、WCAGコントラストに失敗しそうなテキスト、タイポグラフィ、および壊れた状態をスコアリングします。これは、DOMのみのチェックでは見えないレイヤーです。
再構築とレポートはプレーンコードです。モデルは判断が必要な場所にのみ存在します。フローの導出、各次のアクションの決定、画面の評価です。どれもホテルのことは何も知りません。再構築パスは、一般的なJSスタック(Next.jsルート、ExpressおよびFastifyルートモジュール、Prisma、Drizzle、またはプレーンSQLスキーマ)上の任意のレポを消化し、すべてがダイジェストを推論するため、ホテルPMSは単に手元にあったデモでした。他のスタックは再構築パッチで対応できます。すべてがスケジュールで実行されます。launchdティックが15分ごとに各レポをチェックし、必要なものを実行します。新しいコミットごとにレビューし、設定したケイデンス(12時間または24時間ごと)でQAを実行します。
QAエージェントには3つの兄弟がいます。新しい差分を読むコードレビューエージェント、Markdownをコードに合わせるドキュメント同期エージェント、および各レポで変更された内容を共有チームナレッジレポに凝縮するブレイン同期エージェントです。書き込みを行う2つは、分離されたワークツリーで動作し、PRを開きます。どちらも自分の変更をマージすることはありません。
ここまで来た経緯
私たちはMimoを最も戦略的でない理由で使い始めました。マシン上のpiエージェントハーネスがすでにそれに向けられていたからです。未解決の質問は、安価なモデルがブラウザを十分に駆動して意味のある結果を出せるかどうかでしたが、バージョン1では答えはノーでした。バージョン1は決定論的なNodeループでした。スクリプトが制御フローを所有し、Mimoをツールなしのワンショットブレインとして、ステップごとに1回呼び出しました。DOMが提供され、単一の次のアクションを名前付けし、ループがPlaywrightでそれを実行しました。単純なものでは機能しました。小規模な製品検索アプリ(写真をアップロードして視覚的に類似したアイテムを取得する)では、実際のバグをわずかなコストで見つけました。3セントの実行で、価格が値の途中で切り詰められたり(例:「₹4,19」)、商品カードが切り取られたりするバグが見つかりました。最も安価な実行は0.0044ドルでした。しかし、ループが限界でした。モデルは一度に1ステップしか見えず、前のステップを取った理由の記憶がなかったため、数回の移動以上のフローをテストするのに十分なコンテキストを蓄積できませんでした。単一ページをクリックして回る以上のことはできませんでした。
そこで、ループをモデルに与えました。pi拡張としてPlaywrightベースのブラウザツールを登録し、Mimoがpi自身のエージェントループ内でそれらを駆動できるようにしたバージョン2(pi-nativeと呼びました)は、完全なセッションメモリを備えています。それは単一の目標をより深く追求しました。しかし、それでも目標が必要であり、手動で目標を書くことは、削除しようとしていたまさにその作業でした。
バージョン3は、前述のフローエンジンです。リポジトリを読み、フローを導出し、それぞれをブラウザとバックエンドに対して実行します。
なぜMimoなのか、そして最初の実行で壊れたもの
デザインレビューを追加したとき、モデルの選択はより興味深いものになりました。UI/UXパスのためにスクリーンショットをMimoに送信しましたが、実行していたモデル(mimo-v2.5-pro)はテキスト専用だったため、ナンセンスな結果が返されました。修正は、Xiaomi APIが実際に提供するものを読むことでした。テキストモデルと並んで