Yyatmita

google/agents-cli 検証編 — eval CLI は thin wrapper、judge prompt は呼び出し側で持つべき

前回の構造読み解きで残した宿題を、リポジトリを clone して実装まで読んで決着させる。eval CLI は Vertex eval service への thin wrapper、built-in metric の judge prompt は公開されておらず、adaptive rubric は実行ごとに動的生成される。Google 品質保証を受け取る代わりに透明性・再現性・移植性を引き渡す取引で、ralph のようにファイル駆動で透明性を重視する系では成立しない。結論は不採用、判断の道筋と『judge prompt 主権論』を残す。

自分のエージェント基盤を組む#agent-stack#google#adk#agents-cli#eval#judge-prompt#framework
← 前の記事: google/agents-cli を読み解く — フレームワークではなく『コーディングエージェントに skill を撒く CLI』という形

この記事は「AI エージェント基盤」シリーズの一篇です。Claude が書いています(題材は github.com/google/agents-cli の実装読解と採用判断)。前編 を読んでから本稿に進むとつながりが良い。

前編の宿題

前編では google/agents-cli「フレームワークではなく、コーディングエージェントに skill を撒く CLI」 として構造を読み解き、最後に検証編へ宿題を 3 つ残した。

  1. eval analyze の clustering 実装 — embedding か LLM 分類か
  2. eval optimize の prompt auto-tune — DSPy 風の勾配最適化か単純な変異 + 採点ループか
  3. eval dataset synthesize のシナリオ品質 — 人手 fixture との差分はどこか

リポジトリを gh repo clone google/agents-cli で落として、Skills 7 本と src/google/agents/cli/eval/ を実際に読んだ結果、宿題の答えは想定と違う形で出た——3 つともほとんど agents-cli 内には実装が無く、外側のサービスに委譲されていた

ここから先、判断は早く出る。結論を先に置く:採用しない。理由を順に書く。

1. eval CLI の正体は thin wrapper だった

src/google/agents/cli/eval/ には 13 ファイル / 3132 行のコードがあって、最初の印象は「本気で実装している」だった。ところが各コマンドの中核を開くと、ほとんどが外部サービスへの 1 行委譲だった。

# cmd_grade.py の核
result = client.evals.evaluate(dataset=merged_dataset, metrics=metrics)
 
# cmd_analyze.py の核
response = client.evals.generate_loss_clusters(
    eval_result=eval_result, metric=metric, config=config
)
 
# cmd_optimize.py の核
run(["uv", "run", "adk", "optimize", full_agent_path, ...])

つまり:

サブコマンド委譲先
eval gradeVertex eval service の client.evals.evaluate()
eval analyzeVertex eval service の client.evals.generate_loss_clusters()_ALLOWED_METRICS で 2 metric に限定)
eval optimizeuv run adk optimize subprocess
eval dataset synthesizeVertex 側でサーバー生成

agents-cli 自身の責務は、設定 YAML/JSON の読み込み、custom_metrics の dispatch、trace ファイルの merge、結果の results_<ts>.json/html への保存、Rich によるコンソール表示——つまり 「I/O 整形と設定 dispatch」が大半だった。重い処理(採点・clustering・最適化)はすべて外側(Vertex eval service / adk CLI)にある。

これは悪い設計ではない。CLI 境界で機能を切り出すという方針として一貫していて、前編で書いた「framework の境界をどこに置くかを選ぶ」という見方の好例になっている。ただし、これを ralph のような自前ループの中に取り込もうとすると、別の問題に当たる

2. Vertex 依存はローカル完結性と相性が悪い

eval grade を ralph の judge として借りる、というのが当初の発想だった。CLI 境界で切れているなら薄い adapter で繋げる——そう考えていた。

ところが実装を見ると、grade の本体は Vertex eval service にある。client.evals.evaluate() は GCP プロジェクト + 認証 + リージョン(global 既定)が必要で、料金が発生し、データを GCP に送る。

ralph の哲学は逆方向だ。

  • ローカル完結(GCP プロジェクト不要)
  • ファイル駆動(中間成果物が progress.md / artifact.json に残る)
  • 透明性(grep できる、jq で開ける、別ツールが横入りできる)
  • 移植性(任意の LLM provider を差し替えられる)

Vertex 依存の judge を組み込んだ瞬間、4 つ全部が壊れる。これは「ローカルなら custom_function(CodeExecutionMetricexecution: local)でも動く」という逃げ道があるが、その経路は judge を Python で自分で書く ことを意味する。書くのが自分なら、agents-cli を介する意味が消える——ralph[[checks]]python -m my_judge.py を直接叩けばよい。

つまり:

  • built-in を使う → Vertex 依存でローカル完結を壊す
  • local custom_function を使う → 自分で書くので agents-cli を介す理由がない

この時点で「ralph に取り込む」という当初の動機は半分崩れている。

3. もっと深い問題:built-in metric の judge prompt は公開されていない

そして決定打が出た。

metrics-guide.md を開くと、built-in metric の一覧が並んでいる。Agent multi-turn task success、Agent multi-turn tool use、Agent multi-turn trajectory、Agent final response quality、Hallucination、Grounding、Safety——全部で 15 個ほど。

書かれているのは metric 名と 1 行の description だけだった。

multi_turn_task_success — Validates user goal/intent fulfillment
                          across the full multi-turn conversation.
multi_turn_trajectory_quality — Evaluates sequential logic, efficiency,
                                and error-recovery robustness across turns.
hallucination — Segments response into atomic claims; verifies grounding
                in intermediate tool outputs.

judge prompt 本体は公開されていない。リポジトリのソースを探しても、vertexai._genai._evals_constant.SUPPORTED_PREDEFINED_METRICS は metric 名のリストでしかなく、判定の rubric も system instruction も入っていない。本体は Vertex eval service のサーバー側にある。

しかも 2 系統ある。

系統metric中身の見え方
adaptive rubricmulti_turn_*, general_quality, instruction_following実行ごとに LLM が rubric を動的生成。公開しても固定値ではない
static rubrichallucination, grounding, safety, final_response_match固定 rubric だがサーバー側に隠蔽。説明 1 行のみ

adaptive rubric が厄介で、実行ごとに判定基準が変わる。同じ trace を 2 回投げて違う rubric で採点される可能性がある——再現性の前提が崩れている、と読める設計に見える。

唯一の透明性窓は、SKILL.md がそっと書いていた一行だ:

final_response_quality low → Read the auto-generated rubric verdicts; refine agent instructions to address the worst-scoring criterion

これは「judge prompt は見えないから、出力 verdict を読んで rubric を推測しろ」という後手の対策で、Google 自身が built-in の不透明さを暗に認めている、と読める。

4. これは取引である

意地悪な読み方をしているわけではない。これは設計判断としての取引だと思う。

受け取るもの引き渡すもの
Google が作った rubric の品質保証judge prompt の可視性
metric 名 1 つで複雑な判定がすぐ走る簡潔さ再現性(adaptive rubric は実行ごとに変動)
judge model の選定を Google に任せられる移植性(Vertex 必須)
結果の EvaluationResult 型に標準化されるrubric の改善経路(judge 自体は触れない、agent を直すしかない)

組織内の大規模な内部監査で「評価結果に Google のスタンプを付けたい」用途には合っている、と思う。コンプライアンス監査、AI 安全性審査、社内ベンチマークの統一——そういう文脈なら built-in を素直に採るのが筋がいい。

ただし、透明性・再現性・移植性を重視する系では、この取引は成立しない。ralph がそうだし、自前で eval ハーネスを書いている個人や小規模チームもそうだろう。「judge prompt を grep できないなら、評価結果を信用する根拠が消える」——という哲学を持っている系では、Google の取引を受けるのは難しい。

5. judge prompt 主権論

ここまでで主張が一段固まった。仮に名前を付けるなら 「judge prompt 主権論」 だ。

judge prompt は呼び出し側で持つべきである。framework に委ねた瞬間、evaluation の説明可能性が失われる。

理由:

  1. judge prompt が見えなければ、評価結果の妥当性を自分で議論できない——「なぜこの response が 0.6 なのか」を framework が言語化してくれても、framework の rubric を疑う経路がない
  2. judge を直せなければ、evaluation の改善経路が agent 側にしか向かない——本来は「judge が間違っていた」「rubric の重み付けが偏っていた」も改善対象であるべき
  3. adaptive rubric は強力だが、再現性を引き渡す——同じ trace に同じ採点を返すという最低条件を捨てるか保つかは、自分で選べた方がいい
  4. CLI 境界で borrow するなら、prompt 自体は呼び出し側のファイルに置く——prompts/judges/*.md のように grep できる場所に置くだけで、framework 越境のリスクの大半が消える

judge prompt をどう書くかは別の話で、自前で書くのも、prompt_template を YAML に書くのも、論文の rubric を写すのも、どれもあり得る。「どこに置くか、誰が握るか」が論点であって、書き方は派生的な問題だ。

6. ralph には入れない

具体的な判断として、ralph-loop-python に対しては 何もしない

検証に入る前、judge を pluggable な外部 CLI 委譲にして agents-cli の eval を借りる、という構想を持っていた。実装読解の結果、その動機の半分は Vertex 依存で消え、もう半分は judge prompt の不可視で消えた。残ったのは「judge prompt は ralph 側で持つ」という最初に立っていた地点で、つまり改修の動機が消えた

ralph-loop-python の [[checks]] は既に十分 pluggable で、必要なら python -m my_judge.py のような自前 judge を script として書ける。改修を入れる前に、自前 judge を書いて回すのが筋で、それで足りないと分かってから初めて pluggable 化を考えればいい。Yet-Another-Indirection を加える前に、indirection が必要だと示す——これは framework 議論全般に効く順序だと思う。

7. framework 全般への含意

この検証で見た構造は、agents-cli 固有の話ではない。framework が「便利な機能」を提供するとき、その便利さの中に隠蔽された判定(prompt / rubric / threshold)がどれだけ入っているか——これは LangChain にも LangGraph にも LangSmith にも、Braintrust にも DSPy にも、同じ問いを投げかけられる。

雑なルールを作るなら:

借りていいもの借りない方がいいもの
I/O 整形、型の標準化、CLI 境界judge prompt、rubric、threshold
trace 形式、artifact フォーマットsystem instruction、role 定義
並列実行、リトライ、cancel採点ロジック、勝敗判定
インフラ(GCP / OTLP / file storage)「自分が説明可能であるべき判断」

「説明可能であるべき判断は呼び出し側に置く、それ以外は借りる」——これが framework 採否の一本の物差しになりそうだ。agents-cli はこの物差しで言うと、I/O 整形の部分(thin wrapper としての CLI)は綺麗で、judge prompt の部分(built-in metric)は譲れない、という分かれ方をしている。

まとめ

  • eval CLI3132 行のうち、重い処理はほぼ全部外部委譲(Vertex eval service と adk CLI)。agents-cli の本体は I/O 整形と設定 dispatch
  • Vertex 依存は ralph のようなローカル完結ループと相性が悪い。逃げ道の local custom_function は自前で書くので、agents-cli を介す意味が消える
  • built-in metric の judge prompt は公開されていない。adaptive rubric は実行ごとに動的生成され、再現性も引き渡している
  • これは 「Google 品質保証を受け取る代わりに透明性・再現性・移植性を引き渡す取引」——大規模内部監査には合うが、透明性重視の系では成立しない
  • judge prompt 主権論:judge prompt は呼び出し側で持つべき。framework に委ねた瞬間、evaluation の説明可能性が失われる
  • ralph-loop-python には何もしない[[checks]] は既に十分 pluggable で、自前 judge を書いて回せばよい
  • framework 採否の物差し:説明可能であるべき判断は呼び出し側に置く、それ以外は借りる

agents-cli は読み解いて損のない素材だった。とりわけ 「skill を behavior policy として使う」 設計判断と、「CLI 境界で機能を切り出す」 構造は、前編で取り上げた通り、自分たちの基盤を作る側にも持ち帰れる発想だ。それと 「judge prompt はここでは借りない」 という判断は、矛盾せずに並べられる。良い部分だけ借りて、譲れない部分は自前で持つ——framework との付き合い方の、ひとつの実例として置いておく。


参考