blog.takurinton.dev

AI agentの共通設定と評価基盤

2026-05-04

こんにちは

どうもー。

最近、AIエージェントに作業を任せるための設定を repository として管理するようにしました。

これです。 https://github.com/takurinton/agentops

名前は agentops にしています。中身としては、プロジェクト横断で使う AI エージェント向けの設定、スキル、hook、評価基盤をまとめた repository です。

いわゆる application code ではありません。何かの server が立つわけでもないし、library として import するものでもないです。どちらかというと、開発のやり方そのものを repository にした、という感じです。

今までも各 repository に CLAUDE.md や skill を置いていました。ただ、複数の repository で同じような運用をし始めると、だんだん差分が出てきます。

例えばこういうやつ。

  • PR を作るときの手順
  • code review の観点
  • どの条件なら自動マージしてよいか
  • session の振り返りをどう skill 改善に繋げるか
  • 設定変更をどう評価するか
  • project ごとの変数だけをどう差し替えるか
  • これらは repository ごとに完全に独立しているように見えて、実はかなり共通です。

    逆に、ここが repository ごとに微妙にズレると、AIエージェントの振る舞いが揃わなくなります。

    人間でも、チームごとにレビュー観点やPRの出し方が違うと認知負荷が上がります。それと同じで、AIエージェントに渡す開発プロセスも、ある程度は共通化したほうがよい。

    ということで、共通部分を agentops repo に集約して、各利用 repo はそれを取り込む形にしました。

    何を置いているか

    今の構成はだいたいこんな感じです。

    text
    agentops/
    ├── CLAUDE.md
    ├── AGENTS.md
    ├── .claude/
    │   ├── skills/
    │   │   ├── code-review/
    │   │   ├── create-pr/
    │   │   ├── improve-skill/
    │   │   ├── prompt-guide/
    │   │   └── weekly-review/
    │   ├── evals/
    │   │   ├── run.mjs
    │   │   ├── compare.mjs
    │   │   ├── judge-prompt.md
    │   │   └── cases/
    │   └── hooks/
    ├── .codex/
    │   ├── config.toml
    │   ├── hooks.json
    │   └── rules/
    └── scripts/
        ├── sync.sh
        └── sync-claude-to-codex.py
    

    大きく分けると、以下の4つです。

    1. 共通の行動ルール

    2. よく使う作業をまとめた skill

    3. hook や permission などの実行環境設定

    4. それらが期待通り動くかを見る eval

    CLAUDE.mdAGENTS.md は、その repository でエージェントが守るべき基本ルールです。

    skill は、特定の作業をするときの手順書です。例えば /create-pr ならブランチを切って、変更して、commit して、push して、PR を作るところまでを定義します。

    code-review は PR の diff を見て review comment を投稿する skill です。重要度を criticalhighmediumlow に分けて、low 以下なら自動マージする、という運用まで含めています。

    improve-skill はちょっとメタな skill で、ユーザーからのフィードバックや eval の失敗を受けて、skill 自体を改善するためのものです。

    つまり、AIエージェントに作業をさせるだけではなく、AIエージェントに渡す手順自体を改善するサイクルも repository の中に入れています。

    なぜ共通 repo にするのか

    最初は各 project に直接 .claude/skills を置けばいいと思っていました。

    実際、1つの repository だけを見ているならそれで十分です。

    その repository の事情に合わせた instructions を書いて、必要な skill を足して、うまくいかなかったところを直せばよい。

    ただ、複数の repository で同じことをやり始めると、すぐに面倒になります。

    例えば create-pr の手順を少し改善したとします。

  • main に戻って pull してから branch を切る
  • PR 作成前に重複 PR を確認する
  • CI workflow を手動起動する
  • PR 作成後に session 振り返りを走らせる
  • こういう改善は、1つの repository だけで有効なものではありません。だいたい全部の repository に効く。

    でも各 repository にコピーしていると、全部に同じ修正を入れる必要があります。普通に忘れます。

    あと、レビュー観点も同じです。

    エラーハンドリング、N+1、API互換性、migration の安全性、fallback のUXみたいな観点は、project によって濃淡はあっても共通しています。ここを毎回書くのはだるいし、毎回ちょっとずつ違う表現になると、エージェントの判断も揺れます。

    なので、共通化できるものは agentops に寄せる。

    project 固有のものだけを各 repository に置く。

    この分け方にしました。

    同期の考え方

    利用する repository 側では、agentops を submodule として持つ想定です。

    そして、その repository の root で以下を実行します。

    bash
    ./agentops/scripts/sync.sh
    

    これで agentops 側の共通設定から、利用 repo の .agents/.codex/AGENTS.md などを生成・更新します。

    ポイントは、利用 repo 側に wrapper script を置かないことです。

    最初は各 repository に sync-agentops.sh みたいな wrapper を置いてもよいかなと思っていました。ただ、それをやると wrapper 自体がまた各 repository に散らばります。

    共通ロジックを集約したいのに、共通ロジックを呼び出すための薄い script が各 repo に増えるのは微妙です。

    なので、利用 repo は ./agentops/scripts/sync.sh を直接叩く。

    更新したいときは submodule の commit を上げて sync する。

    これだけにしています。

    template 変数が必要な skill もあります。例えば code-review なら対象 repository や build command が必要です。

    text
    REPO=owner/repo
    BUILD_COMMAND=cargo test
    CI_WORKFLOW=ci.yaml
    

    こういう project 固有値は、利用 repo 側の設定ファイルから読み、共通 skill の {{REPO}}{{BUILD_COMMAND}} を置換して生成します。

    共通の手順は agentops に置く。

    project 固有の値だけ利用 repo に置く。

    これで、手順の改善と project 固有設定を分けられます。

    Codex 用の生成

    この repo では、.claude/skills を source of truth にして、Codex 用の .agents/skills を生成しています。

    text
    .claude/skills/* -> .agents/skills/*
    CLAUDE.md        -> AGENTS.md
    .claude/settings -> .codex/config.toml / hooks.json / rules
    

    この変換を scripts/sync-claude-to-codex.py でやっています。

    ここでやりたいのは、ツールごとの設定ファイル形式の違いを吸収することです。

    運用として大事なのは「PRレビューはこうする」「skill改善はこうする」「危ない操作はこう扱う」というルールであって、それをどのファイル名に置くかは本質ではありません。

    もちろん、ツールごとにできることや設定形式は違います。なので完全に同じにはなりません。ただ、少なくとも共通の skill 本文や repository の行動ルールは、なるべく同じ source から生成したい。

    このへんを手作業で同期し始めると、かなり早い段階で破綻します。「こっちの skill は直したけど、もう片方の skill は古い」みたいな状態になると、何が正なのかわからなくなる。

    なので、生成できるものは生成する、という方針にしています。

    評価の仕組み

    この repo で一番やりたかったのは eval です。

    AIエージェント向けの設定は、普通の code と違って壊れ方がわかりにくいです。

    TypeScript の型エラーや Rust の compile error みたいに、明確に赤くなるわけではありません。

    でも実際には壊れます。

  • 呼ぶべき skill を呼ばない
  • 関係ない skill を呼ぶ
  • 指示の意図を取り違える
  • 危険な操作を止めない
  • 1箇所だけ直して、同じ問題の他の箇所を見落とす
  • review の重要度判断が甘い
  • こういうのは全部、instructions の品質問題です。

    そこで .claude/evals にテストケースと runner を置いています。

    テストケースは YAML です。

    yaml
    - id: sr-example-001
      input: "PRのレビューをして #42"
      tags: [code-review, basic]
      expected:
        skill_called: code-review
        args_contain: "42"
    

    カテゴリは今のところこういうものがあります。

  • skill-routing: 正しい skill が呼ばれるか
  • intent: 意図を正しく解釈して、必要なファイルを読めるか
  • compliance: ルールを守れるか
  • fix-completeness: 同じパターンの問題を全箇所直せるか
  • issue-based-workflow: issue と口頭補足から実装フローに乗れるか
  • run.mjs はこの YAML を読み、各 input をエージェントに投げます。

    そのとき、読み取り系の tool だけを許可して、勝手にファイルを変更したり command を実行しないようにしています。

    実行結果からは、だいたい以下を取り出します。

  • 呼ばれた tool
  • 呼ばれた skill
  • 読まれた file
  • text response
  • cost
  • 実行時間
  • それを judge prompt に渡して、LLM-as-Judge で 1〜5 点を付けます。

    基本的には 4 点以上を pass にしています。

    例えば

  • skill-routing なら、正しい skill が正しい引数で呼ばれたら 5 点。
  • 正しい skill は呼ばれたけど引数が微妙なら 4 点。
  • 関連 skill だけど最適ではないなら 3 点。
  • 全然違う skill なら 1〜2 点、という感じです。
  • 採点結果は JSON として保存されます。

    さらに --auto-fix を付けると、fail したケースをまとめて improve-skill に渡します。

    bash
    node .claude/evals/run.mjs --auto-fix
    

    この流れはかなり重要です。

    ただ「今回うまくいかなかったね」で終わらせるのではなく、その失敗を再現する eval case にして、skill を直して、また eval を回す。

    つまり、AIエージェントの失敗を regression test にする。

    これは普通の software engineering と同じです。bug が出たら test を足す。test が落ちることを確認する。実装を直す。test が通ることを確認する。

    AIエージェントの instructions も同じように扱いたい、というのがこの repo の考え方です。

    自己改善サイクル

    create-pr skill には、PR 作成後に session を振り返る流れも入れています。

    やりたいことは、作業中の手戻りを放置しないことです。

    例えば、ユーザーに「その方針じゃない」と言われてやり直したとします。

    これは単なる会話上のミスではなく、skill や instruction が足りていない可能性があります。

    あるいは、特定の種類の issue を読んだときに毎回同じ component を見落とすなら、それは intent 解釈の eval にしたほうがよいです。

    なので、PR を作った後に以下を見ます。

  • ユーザーから指摘されて方針変更した箇所
  • 手戻りが発生した箇所
  • 外部サービス選定や architecture 判断を間違えた箇所
  • skill の呼び出し順が悪かった箇所
  • 改善点があれば eval case を追加し、run.mjs --auto-fix を回す。

    fail すれば improve-skill が skill を直す。

    その変更は PR として出す。

    ここまでやると、少なくとも「同じ失敗をしたときに検知できる」状態になります。

    もちろん、これで全部が自動的によくなるわけではありません。

    LLM-as-Judge も万能ではないし、eval case の書き方が雑なら普通に意味のない評価になります。

    ただ、何もないよりはずっとよいです。少なくとも「AIの挙動がなんとなく悪い」という曖昧な話を、「この input に対してこの skill が呼ばれなかった」という具体的な話にできます。

    複数人で運用するなら

    個人で使う分には、けっこう雑でも回ってて、今自分が個人で開発してるものの大半にこれを組み込んでいるわけだけど、そこそこ改善を回せている。

    ただ、複数人で使うなら、まず、共通設定と個人設定を分ける必要があるし、そもそも今の状態だと結構雑です。

    agentops repo におくべきものとローカルにおくべきものの区別が今はできていなくて、自分が共通で使う少々強い権限を普通に置いている。

    それから、skill の変更は code review したほうがよいです。

    skill はただの markdown ですが、実質的には開発 process の code です。

    code-review skill の重要度判定を少し変えるだけで、自動マージされる PR の範囲が変わります。

    create-pr skill の手順を変えるだけで、branch の切り方や CI の回し方が変わります。

    なので、skill 変更も普通に PR で review する。 できれば eval case も一緒に追加する。「なぜこの instruction を足したのか」が後から追えるようにする。

    あとは rollout の単位です。

    利用 repo は agentops を submodule として持つので、どの commit の設定を取り込んでいるかが明確になります。

    これは地味に大事です。

    共通 repo の main を常に直接参照する形にすると、ある日突然全 project のエージェント挙動が変わります。

    submodule の commit を上げる方式なら、project ごとに取り込むタイミングを制御できます。

    複数人でやるなら、最低限このへんを決めたいです。

  • 共通設定に入れてよいもの
  • project 固有設定に置くもの
  • 個人ローカル設定に逃がすもの
  • skill 変更の review ルール
  • eval が落ちたときに merge を止めるか
  • 自動マージを許可する repository と禁止する repository
  • permission のデフォルト
  • 特に permission はちゃんと見たほうがよいです。

    個人開発では danger-full-access 的な設定でも、まあ自分が責任を持てばよいです。

    でもチームで共通配布するなら話が変わります。

    共通設定としては conservative にして、必要な人だけ local で強くするほうが安全です。

    自動マージも同じで、全 repository に一律で入れるのではなく、対象を絞ったほうがよい。

    現状の課題

    まだ作り始めなので、課題は普通にあります。

    eval case が少ない

    runner や judge prompt はありますが、case が少ないと評価基盤としては意味がないです。今ある example はあくまで雛形なので、実際の運用で起きた失敗をどんどん追加していく必要があります。

    特に増やしたいのは、曖昧な実装依頼のケースで、実際の依頼は「この issue やって」「さっきのやついい感じに直して」「ここだけ変えて」みたいな雑なものが多いです。

    そういう input から正しく repository を読んで、適切な skill に入れるかを見ていきたいです。

    LLM-as-Judge の安定性

    judge は便利ですが、judge 自体も LLM なので揺れます。なので、なるべく actual data を構造化して、採点基準を明確にする必要があります。

    今は tool call、skill call、files read、text summary を渡しています。今後は「どの instruction に従ったか」「どの instruction に違反したか」ももう少し構造化して見たいです。

    たぶん組織でちゃんとやるなら、このへんはもっと詳細に取れるはずです。今は eval runner の実行結果から取れる情報を JSON にしているだけですが、実際には AI エージェントの操作ログ、tool call、承認履歴、PR の結果、CI の失敗、review comment、ユーザーが途中で割り込んだ箇所などをまとめて見るほうがよい。Datadog や OpenTelemetry みたいな monitoring の考え方に近くて、エージェントが何を見て、何を判断して、どこで手戻ったのかを trace として追えると、eval case に落とすべき失敗もかなり見つけやすくなると思う。

    個人開発だと、そこまではやらずに session を見返して雑に case を足すくらいで十分です。ただ、複数人で同じ agentops を使うなら、「よく失敗している prompt」「特定の repository でだけ落ちる skill」「権限確認で詰まる操作」「レビューで人間が毎回止めている種類の変更」みたいなものを dashboard 的に見られるとよさそうです。そうすると、skill を勘で直すのではなく、実際に詰まっているところから改善できる。

    あと、sync script は便利ですが、削除や衝突の扱いは慎重にしたいです。

    共通 repo から生成された file と、project 側で手で足した file が混ざるとややこしい。

    生成物には banner を付けていますが、運用として「生成されたものは直接編集しない」を徹底する必要があります。

    このへんは codegen と同じ。

    まとめ

    AIエージェントをちゃんと使おうとすると、結局 software engineering の話になります。

    手順を共通化する。project 固有値を分離する。生成できるものは生成する。失敗を eval case にする。設定変更を review する。rollout の単位を管理する。

    やっていることは地味ですが、こういう地味な部分をちゃんと repository にしておくと、AIエージェントの運用がかなり楽になります。

    AIに「いい感じにやって」と言うだけだと、普通にダメだけど、「この作業はこの skill」「このケースは fail」「この重要度なら merge しない」という形に落としていけば、ズレ方を少しずつ小さくできます。

    まだ完成形ではないですが、個人開発でも複数人開発でも、AIエージェントの設定をちゃんと versioning して eval するのはかなり効くと思っています。