0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

その「できました」、本当に?——AIエージェントの仕事をラン単位で計測する横断ダッシュボード【テンプレ全公開】

0
Last updated at Posted at 2026-07-17

🧭 本記事は Claude Code実務運用シリーズ の STEP 8「Fable 5で運用を棚卸しする」です。
STEP 8 の完結編。8-5 の「点検」を、ラン単位の記録と横断ダッシュボードによる「常設の計測」に昇格させます。
シリーズ全体の地図と読む順は 親記事 にまとめています。

ChatGPT Image Jul 17, 2026, 01_36_52 PM.png

はじめに:AIエージェントは、必ず「できました」と言う

自律開発ループも、レビューチームも、企画レビューも、最後は必ず「できました」と報告してきます。問題は、その報告を信用する根拠を、私たちがほとんど持っていないことです。

人間のチームなら、レビュー指摘の的中率や差し戻し回数で信頼を管理します。AIエージェントにも同じことをすればいい——そう思って作ったのが、この記事で全公開するラン単位の実行記録(RUNLOG)と工程横断ダッシュボードです。

前回(8-5)は、Fable 5 に運用を棚卸しさせたら記録の索引漏れが見つかった、という「点検」の話でした。今回はそれを恒久化します。点検は年に数回、計測は毎ランです。

先に結論:信用は3つの数字で管理する

数字 何が分かるか 解釈の注意
最終判定の分布 エージェントが「止まるべきときに止まっているか」 NEEDS_HUMAN_DECISION(人間判断待ち)は失敗ではなく規律。全ラン承認の方をむしろ疑う
指摘の検証生存率 レビュー指摘のうち、独立検証を生き残った割合 100% が続くなら検証が甘い可能性。落ちる指摘があるのは検証が機能している証拠
人手介入回数 「自律」ループが実際どれだけ人間を呼んだか 0 が理想ではない。承認ゲートでの介入は設計どおりの挙動

私の直近の実測では、レビュー指摘の検証生存率は 92%(39/42) でした。この数字は「8% の指摘は検証で棄却された」という意味で、これが見えることに価値があります(全体の実出力は後述)。

全体像:3層の記録構造

各スキル(自律開発ループ / レビューチーム / 企画レビュー ...)
  │ ランごとに STATE(詳細記録)を書く
  ▼
~/Documents/agent-ops/{スキル名}/
  ├── RUNLOG.md      ← 1ラン=1行の索引(追記のみ)
  ├── runs/STATE_*.md ← ラン単位の詳細(Step Log・試行・介入)
  ├── LEARNINGS.md    ← 学習候補(人間承認制)
  └── PROPOSALS.md    ← 改善提案カード
  │
  ▼
ダッシュボード(本記事のスクリプト)
  ├── スキルごとの集計(判定分布・生存率・介入)
  ├── チケット番号を横串にした工程横断マトリクス
  └── 索引漏れ検出(STATE 突合+出力フォルダ突合の2経路)

STATE の書き方と自己改善ループ(学習候補・人間承認)は 8-4 で公開しています。本記事はその上に載る索引(RUNLOG)と集計(ダッシュボード)の層です。

設計契約:なぜこの形なのか(5箇条)

テンプレを配る前に、設計判断を先に書きます。ここが本体です。

1. RUNLOG は「1ラン=1行・追記のみ」
過去行の書き換え・削除を禁止します。これで RUNLOG は改竄されない監査ログになり、「あのランは無かったことに」ができなくなります。エージェント自身にも追記しか許しません。

2. 記録はリポジトリの外の耐久ストレージに置く
ランの出力フォルダ(.claude/output/ 配下)は gitignore かつ作業ディレクトリローカルで、フォルダ掃除と一緒に実行履歴ごと消えます。8-5 で見つかった索引漏れは、まさに「記録が untracked フォルダにしかない」状態でした。索引と詳細記録はホームディレクトリ配下(~/Documents/agent-ops/)に集約します。

3. スキルごとにデータを分離する
レビュー系の学習と開発ループの学習を混ぜません。フォルダごと分けることで、「レビューの教訓が実装エージェントの挙動を歪める」汚染を構造的に防ぎます。

4. エージェントが書けるのは「事実と候補」だけ
実行時の挙動を変えられるのは「人間が承認したもの」だけ、という原則(詳細は 8-4)。RUNLOG・STATE は事実の記録なのでエージェントが書き、学習の本番反映は人間のゲートを通します。

5. チケット番号を横串キーにする
企画レビュー・実装ループ・コードレビューの全記録に同じチケット番号を入れておくと、「この案件は企画から実装まで AI 工程を何回通ったか」が後から突合できます。ダッシュボードの横断マトリクスはこのキーで作ります。

RUNLOG テンプレート(コピペ可)

# RUNLOG: {スキル名} ラン索引(1 ラン = 1 行。追記のみ)

| run_id | ticket | final_state | first_review_approved | fix_loops | ng_reasons | build | test | interventions | wall_min | STATE |
|---|---|---|---|---|---|---|---|---|---|---|
| 20260717-1130_dev-loop_XXXXX | #XXXXX | NEEDS_HUMAN_DECISION | no(修正必須0) | 0 | ENV_ERROR | 2回目PASS | 未実施(対象未整備) | 1 | 17 | ~/Documents/agent-ops/dev-loop/runs/STATE_20260717-1130_dev-loop_XXXXX.md |

運用ルール(このファイルの憲法として冒頭に書いておきます):

  • エージェントができるのはラン1行の追記のみ。既存行の変更・削除は人間でも原則しない
  • 列はスキルごとに変えてよい(企画レビューなら stance / verification_survival、レビューチームなら sc_survival など)。ticket か run_id 末尾にチケット番号を入れることだけ揃える
  • wall_min は数値のみにする(注記は別の列か STATE 側へ)。セル内に | を書かない——集計と表の生命線です
  • 未実施の項目は空欄ではなく「未実施(理由)」と書く。やっていないことを消さないのが計測の生命線です

横断ダッシュボード(スクリプト全公開)

冒頭の設定ブロック(記録置き場・STATE の場所・スキャン対象リポジトリなど)を自分の環境に合わせるだけで動きます。標準ライブラリのみ・1ファイルです。

書き換えるのはここだけです:

# ===== 設定(自分の環境に合わせて書き換える) =====
AGENT_OPS_DIR = "~/Documents/agent-ops"      # スキルごとの RUNLOG.md 置き場
STATE_GLOBS = [                               # STATE ファイルの置き場(RUNLOG との突合に使う)
    "~/Documents/agent-ops/*/runs/STATE_*.md",
    # "~/Documents/plan-reviews/*/STATE*.md",  # STATE を成果物の隣に置くスキルがあれば追加
]
SCAN_REPOS = [                                # 出力フォルダスキャンの対象リポジトリ(なければ空でよい)
    # "~/workspace/your-repo-a",
]
SCAN_SKILL = "dev-loop"                       # 出力フォルダをどのスキルの索引と突合するか
TICKET_DIR_PREFIX = "redmine-"                # ランごとの出力フォルダ名の接頭辞
ARCHIVED_MARK = "-archived-"                  # 退避済みフォルダは現役ランとして数えない
# ==================================================
スクリプト全文(約200行・クリックで展開)
#!/usr/bin/env python3
"""agent-ops dashboard: AIエージェント各スキルの実行記録(RUNLOG)を横断集計する。

前提とする記録構造:
  {AGENT_OPS_DIR}/{skill名}/RUNLOG.md   ... 1ラン=1行・追記のみのMarkdown表
  {リポジトリ}/.claude/output/{PREFIX}{チケット番号}/ ... ランごとの出力フォルダ

やること:
  1. スキルごとの RUNLOG を読み、ラン一覧と集計(判定分布・生存率・人手介入)を表示
  2. チケット番号を横串キーに、工程横断マトリクスを表示(同一チケット複数ランは ×N 表示)
  3. 索引漏れを2経路で検出する(安全網):
     a. STATE ファイル ↔ RUNLOG の突合(全スキル対象。出力フォルダの規約に依存しない)
     b. リポジトリの出力フォルダ ↔ RUNLOG の突合(SCAN_SKILL の索引とだけ照合)

使い方:
  python3 agent_ops_dashboard.py
  python3 agent_ops_dashboard.py --agent-ops ~/Documents/agent-ops --repo ~/workspace/repo-a
"""
import argparse
import re
from glob import glob as fglob
from pathlib import Path

# ===== 設定(自分の環境に合わせて書き換える) =====
AGENT_OPS_DIR = "~/Documents/agent-ops"      # スキルごとの RUNLOG.md 置き場
STATE_GLOBS = [                               # STATE ファイルの置き場(RUNLOG との突合に使う)
    "~/Documents/agent-ops/*/runs/STATE_*.md",
    # "~/Documents/plan-reviews/*/STATE*.md",  # STATE を成果物の隣に置くスキルがあれば追加
]
SCAN_REPOS = [                                # 出力フォルダスキャンの対象リポジトリ(なければ空でよい)
    # "~/workspace/your-repo-a",
]
SCAN_SKILL = "dev-loop"                       # 出力フォルダをどのスキルの索引と突合するか
TICKET_DIR_PREFIX = "redmine-"                # ランごとの出力フォルダ名の接頭辞
ARCHIVED_MARK = "-archived-"                  # 退避済みフォルダは現役ランとして数えない
# ==================================================


def read_text(path: Path) -> str:
    try:
        return path.read_text(encoding="utf-8")
    except (OSError, UnicodeDecodeError):
        return ""


def parse_md_table(text: str):
    """最初のMarkdown表を行dictのリストにする(列構成はスキルごとに違ってよい)"""
    rows, header = [], None
    for line in text.splitlines():
        line = line.strip()
        if not line.startswith("|"):
            continue
        cells = [c.strip() for c in line.strip("|").split("|")]
        if set("".join(cells)) <= set("-: "):  # 区切り行
            continue
        if header is None:
            header = cells
        elif len(cells) == len(header):
            rows.append(dict(zip(header, cells)))
    return header or [], rows


def ticket_of(row: dict) -> str:
    """行からチケット番号(横串キー)を拾う。ticket 列 → plan 列 → run_id 末尾の順。
    数字5桁以上に限定して年(2026等)の誤検出を防ぎ、run_id は日付部分を拾わないよう
    最後のアンダースコア以降だけを見る"""
    for key in ("ticket", "plan"):
        m = re.search(r"(\d{5,})", row.get(key, ""))
        if m:
            return m.group(1)
    tail = row.get("run_id", "").rsplit("_", 1)[-1]
    m = re.search(r"(\d{5,})", tail)
    return m.group(1) if m else "?"


def tlabel(t: str) -> str:
    return f"#{t}" if t != "?" else "(チケットなし)"


def pick(row: dict, *keys: str) -> str:
    """列名の候補から最初に見つかった値を返す(スキルごとの列名差を吸収)"""
    for k in keys:
        if row.get(k):
            return row[k]
    return ""


def survival_pair(raw: str):
    m = re.search(r"(\d+)\s*/\s*(\d+)", raw or "")
    return (int(m.group(1)), int(m.group(2))) if m else (None, None)


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--agent-ops", default=AGENT_OPS_DIR)
    ap.add_argument("--repo", action="append", default=None)
    args = ap.parse_args()

    ops_dir = Path(args.agent_ops).expanduser()
    repos = [Path(r).expanduser() for r in (args.repo or SCAN_REPOS)]

    print("# agent-ops ダッシュボード(工程横断)\n")

    # ---- 1. スキルごとの RUNLOG を集計 ----
    matrix = {}            # ticket -> {skill: 代表値(最新ラン)}
    run_counts = {}        # (ticket, skill) -> ラン数
    indexed_by_skill = {}  # skill -> そのスキルの索引済みチケット集合
    referenced_states = set()  # RUNLOG から参照されている STATE パス
    for skill_dir in sorted(p for p in ops_dir.glob("*") if p.is_dir()):
        runlog = skill_dir / "RUNLOG.md"
        if not runlog.exists():
            continue
        header, rows = parse_md_table(read_text(runlog))
        skill = skill_dir.name
        indexed_by_skill[skill] = set()
        print(f"## {skill}({len(rows)} ラン)\n")
        if not rows:
            print("(記録なし)\n")
            continue

        states, surv, tot, interventions = {}, 0, 0, 0
        for r in rows:
            t = ticket_of(r)
            indexed_by_skill[skill].add(t)
            verdict = pick(r, "final_state", "stance", "sc_survival") or "実行"
            matrix.setdefault(t, {})[skill] = verdict  # 複数ランは最新行が代表(×N は別掲)
            run_counts[(t, skill)] = run_counts.get((t, skill), 0) + 1
            states[verdict] = states.get(verdict, 0) + 1
            s, n = survival_pair(pick(r, "verification_survival", "sc_survival"))
            if n:
                surv += s
                tot += n
            m = re.search(r"\d+", pick(r, "interventions"))
            if m:
                interventions += int(m.group())
            for v in r.values():  # STATE パスの参照を収集(突合用)
                m = re.search(r"[~/][^\s)]+\.md", v)
                if m:
                    referenced_states.add(str(Path(m.group()).expanduser().resolve()))
            wall = pick(r, "wall_min")
            wall_disp = f"{wall}" if wall.isdigit() else wall  # 数値以外(注記つき等)はそのまま
            print(f"- {tlabel(t)}: {verdict}"
                  f"{' / 介入 ' + pick(r, 'interventions') if pick(r, 'interventions') else ''}"
                  f"{' / ' + wall_disp if wall else ''}")
        print(f"\n- 判定内訳: " + " / ".join(f"{k}: {v}" for k, v in sorted(states.items())))
        if tot:
            print(f"- 指摘の検証生存率: {surv}/{tot}({surv * 100 // tot}%)")
        print(f"- 人手介入 合計: {interventions}\n")

    # ---- 2. チケット横断マトリクス(複数ランは ×N を付す) ----
    if matrix:
        skills = sorted({s for row in matrix.values() for s in row})
        print("## チケット横断マトリクス\n")
        print("| チケット | " + " | ".join(skills) + " |")
        print("|---|" + "---|" * len(skills))
        for t in sorted(matrix, key=lambda x: (not x.isdigit(), x)):
            cells = []
            for s in skills:
                v = matrix[t].get(s, "")
                n = run_counts.get((t, s), 0)
                cells.append(f"{v} ×{n}" if n > 1 else v)
            print(f"| {tlabel(t)} | " + " | ".join(cells) + " |")
        print()

    # ---- 3a. STATE ↔ RUNLOG 突合(全スキル対象の安全網) ----
    print("## 索引漏れチェック\n")
    orphan_states = []
    for pattern in STATE_GLOBS:
        for p in sorted(fglob(str(Path(pattern).expanduser()))):
            if str(Path(p).resolve()) not in referenced_states:
                orphan_states.append(p)
    if orphan_states:
        for p in orphan_states:
            print(f"- ⚠️ RUNLOG 未登録の STATE: {p}")
    else:
        print("- ✅ STATE 突合: すべての STATE が RUNLOG から参照されています")

    # ---- 3b. 出力フォルダ ↔ RUNLOG 突合(SCAN_SKILL の索引とだけ照合) ----
    scan_index = indexed_by_skill.get(SCAN_SKILL, set())
    unregistered = []
    for repo in repos:
        outs = [repo / ".claude" / "output",
                *sorted((repo / ".claude" / "worktrees").glob("*/.claude/output"))]
        for out in outs:
            if not out.is_dir():
                continue
            for d in sorted(out.glob(f"{TICKET_DIR_PREFIX}*")):
                if not d.is_dir() or ARCHIVED_MARK in d.name:
                    continue
                t = d.name.replace(TICKET_DIR_PREFIX, "")
                if t not in scan_index:
                    unregistered.append((t, d))
    if unregistered:
        for t, d in unregistered:
            print(f"- ⚠️ #{t}: {SCAN_SKILL} の RUNLOG に無い実行痕跡 → {d}")
        print("\n実行記録が索引の外にあります。RUNLOG への登録(または記録プロトコルの組み込み)を検討してください。")
    elif repos:
        print(f"- ✅ 出力フォルダ突合: {SCAN_SKILL} の RUNLOG 未登録ランはありません")


if __name__ == "__main__":
    main()

実際の出力(チケット番号はマスク)

私の環境(3スキル・16ラン)での実出力です。

# agent-ops ダッシュボード(工程横断)

## dev-loop(4 ラン)

- #AAAAA: NEEDS_HUMAN_DECISION / 介入 0 / 34分
- #BBBBB: NEEDS_HUMAN_DECISION / 介入 8 / 90分
- #CCCCC: APPROVED_FOR_HUMAN_REVIEW / 介入 1 / 11分
- #CCCCC: NEEDS_HUMAN_DECISION / 介入 1 / 17分

- 判定内訳: APPROVED_FOR_HUMAN_REVIEW: 1 / NEEDS_HUMAN_DECISION: 3
- 人手介入 合計: 10 回

## plan-review(4 ラン)

- #DDDDD: 条件付き賛成 / 介入 0 / 21分
- (チケットなし): 条件付き賛成 / 介入 1 / 25分
- (チケットなし): 条件付き賛成 / 介入 0 / 37分
- (チケットなし): 条件付き賛成(必達3点) / 介入 0 / 40分

- 判定内訳: 条件付き賛成: 3 / 条件付き賛成(必達3点): 1
- 指摘の検証生存率: 65/65(100%)
- 人手介入 合計: 1 回

## review(8 ラン)

- #EEEEE: 9/9 / 介入 1 / 34(中断160除外)
- #FFFFF: 対象外 / 介入 0 / 11分
- #GGGGG: 対象外 / 介入 1 / 16分
- #HHHHH: 9/9 / 介入 0 / 未計測(推定約25分)
- #IIIII: 8/8 / 介入 0 / 27分
- #JJJJJ: 7/9 / 介入 0 / ~27
- #KKKKK: 6/7 / 介入 0 / 28分
- #LLLLL: - / 介入 0 / -(中断・ended未記入。クローズ未了ランの遡及登録)

- 判定内訳: -: 1 / 6/7: 1 / 7/9: 1 / 8/8: 1 / 9/9: 2 / 対象外: 2
- 指摘の検証生存率: 39/42(92%)
- 人手介入 合計: 2 回

## 索引漏れチェック

- ✅ STATE 突合: すべての STATE が RUNLOG から参照されています
- ✅ 出力フォルダ突合: dev-loop の RUNLOG 未登録ランはありません

この1枚から読み取れることを、そのまま書きます。

  • 自律開発ループは4ラン中3ランで人間判断待ちで止まっています。 低い「完走率」に見えますが、止まった理由はすべて仕様未確定(受け入れ基準が曖昧など)で、コード品質起因ではありません。「勝手に進まず止まる」が機能している、と読みます
  • レビュー系の検証生存率は 92%。 8% の指摘は独立検証(Self-Critique)で棄却されました。100% が続いていた初期より、落ちる指摘が出てきた今の方が検証を信用できます
  • 同じチケット(#CCCCC)に2つのランがあるのは、実装ラン(承認)と、後日の検証ラン(人間判断待ち)。追記のみの索引なので、履歴として両方残ります
  • 黙って中断したランも1行残っています(#LLLLL)。ended 未記入のまま消えていたランを後述の突合が検出し、「中断」と注記して遡及登録したものです

安全網が実際に効いた話(3つ)

1. 索引漏れの検出。 別リポジトリで実行した1ランが中央 RUNLOG に登録されておらず、しかも記録は git 管理外のフォルダにしかない状態でした。ダッシュボードのスキャンが「未登録」としてフラグし、遡及登録して塞ぎました(発見の経緯は 8-5 参照)。

2. ダッシュボード自身の考慮漏れの検出。 完了済みランの出力フォルダを退避する運用(-archived- リネーム)を後から導入したところ、ダッシュボードが退避フォルダを「未登録ラン」と誤検出しました。誤検出も含めて画面に出るので、その日のうちに気づいて除外1行を足して直せました。計測の仕組み自体も、運用で育ちます。

3. 黙って中断したランの検出。 STATE ↔ RUNLOG 突合を追加した初回実行で、半月前に開始したまま ended 未記入で消えていたラン(クローズ未了)が1件見つかりました。完了したランは自分で RUNLOG に追記できますが、途中で死んだランは自分を索引できません。だからこそ、索引(RUNLOG)と実在(STATE)を突き合わせる外部の安全網が要ります。

運用:誰が、いつ見るか

実行は機械、判断は人間——ここを分けておくと運用が続きます。

  • 回すのは機械: ダッシュボードの実行と索引漏れ検出は定期実行に任せ、「⚠️ が出たときだけ通知」にします(私の環境では launchd で週次実行+⚠️検出時のみ macOS 通知。配線の全文は次の節に置きます)。人間が「回すのを覚えておく」運用は、それ自体が索引漏れと同じ種類の穴になります
  • 読むのは人間: 数字の解釈(この判定分布は健全か)・例外への対応(未登録を遡及登録するか、記録プロトコル側を直すか)・学習を本番反映するかの承認は、機械に渡せません。閾値でアラートを自動化するには、正直、母数がまだ足りません
  • 毎日眺める必要はありません。ただし**「判断の前に見ない」は禁止**です。この計測基盤の存在理由は人間の承認判断に材料を渡すことなので、誰も読まないダッシュボードはただの飾りです

定期実行の配線(コピペ可・macOS)

「⚠️ が出たときだけ通知」の実物です。ラッパースクリプト+launchd の2ファイルで、毎週月曜 9:30 に無人実行されます。

① ラッパースクリプト~/Documents/agent-ops/dashboard_check.sh

#!/bin/bash
# ダッシュボードを実行して保存し、⚠️(要対応)を検出したときだけ macOS 通知を出す。
# 実行失敗時も通知する(黙って死なない)
set -u

DIR="$HOME/Documents/agent-ops"
OUT="$DIR/dashboard_latest.md"

/usr/bin/python3 "$DIR/agent_ops_dashboard.py" > "$OUT" 2>&1
status=$?

warn_count=$(grep -c '⚠️' "$OUT" 2>/dev/null || true)

if [ "$status" -ne 0 ]; then
  /usr/bin/osascript -e 'display notification "実行に失敗しました。dashboard_latest.md を確認してください" with title "agent-ops ダッシュボード"'
elif [ "${warn_count:-0}" -gt 0 ]; then
  /usr/bin/osascript -e "display notification \"要対応 ${warn_count} 件(索引漏れ等)。dashboard_latest.md を確認してください\" with title \"agent-ops ダッシュボード\""
fi

exit 0

② launchd 定義~/Library/LaunchAgents/com.yourname.agentops-dashboard.plist((あなたのユーザー名) を2箇所書き換え)

plist 全文(クリックで展開)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.yourname.agentops-dashboard</string>
    <key>ProgramArguments</key>
    <array>
        <string>/bin/bash</string>
        <string>/Users/(あなたのユーザー名)/Documents/agent-ops/dashboard_check.sh</string>
    </array>
    <key>StartCalendarInterval</key>
    <dict>
        <key>Weekday</key>
        <integer>1</integer>
        <key>Hour</key>
        <integer>9</integer>
        <key>Minute</key>
        <integer>30</integer>
    </dict>
    <key>RunAtLoad</key>
    <false/>
    <key>StandardOutPath</key>
    <string>/Users/(あなたのユーザー名)/Documents/agent-ops/logs/launchd.log</string>
    <key>StandardErrorPath</key>
    <string>/Users/(あなたのユーザー名)/Documents/agent-ops/logs/launchd.log</string>
</dict>
</plist>

③ 登録と動作確認

chmod +x ~/Documents/agent-ops/dashboard_check.sh
mkdir -p ~/Documents/agent-ops/logs
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.yourname.agentops-dashboard.plist

# 即時実行して end-to-end を確認(dashboard_latest.md が更新されればOK)
launchctl kickstart -k gui/$(id -u)/com.yourname.agentops-dashboard

# 登録状態の確認(PID が「-」・終了コード 0 が正常)
launchctl list | grep agentops

補足:

  • ⚠️ が無い週は何も通知されません。無音が正常動作です
  • launchd の user agent なので、ログイン中の Mac でのみ動きます
  • 解除は launchctl bootout gui/$(id -u)/com.yourname.agentops-dashboard
  • Linux なら crontab 1行で同等です: 30 9 * * 1 ~/Documents/agent-ops/dashboard_check.sh(通知部分は notify-send 等に置換)

正直な限界

  • 母数は 16 ランです。生存率 92% は統計的な性能値ではありません。ここで測っているのは性能ではなく規律——「止まるべきで止まったか」「やっていないことを未実施と書いたか」——です
  • 数字は記録の正直さに依存します。「テスト未実施」を「問題なし」と書くエージェントが1体いれば計測は崩れます。だから記録ルール(追記のみ・未実施の明記)の方がスクリプトより大事です
  • ダッシュボードは「見える化」までです。学習の本番反映(記録から挙動を変える)は人間承認制で、まだ先の段階として運用しています

まとめ:「できました」を信用する根拠を、言葉ではなく記録にする

  • AIエージェントの報告は、**ラン単位の記録(RUNLOG)と3つの数字(判定分布・検証生存率・介入回数)**で裏を取る
  • 記録は「追記のみ・リポジトリ外・スキル分離・人間承認ゲート・チケット横串」の5箇条で設計する
  • ダッシュボードは1ファイルで足りる。大事なのはスクリプトではなく、消えない記録を残す規律の方

STEP 8 はこれで完結です。棚卸し(8-2)で始まった「増えた運用を疑う」流れが、記録を生む仕組み(8-4)→記録の点検(8-5)→**常設の計測(本記事)**まで来ました。次の STEP 9 では、こうして固まった運用を Skill として再利用可能にしていきます。


このシリーズの歩き方

Claude Code実務運用シリーズ ― 暴走させない、から仕組みにするまで。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?