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?

🪊 䞀床も呌ばれおいない゚ヌゞェントを自動発芋しお敎理する

0
Posted at

月商120䞇を支えおいるのは、AIぞの指瀺の䞊手さではありたせん。「定矩した機胜が本圓に動いおいるか」を自動で確認し続ける環境蚭蚈です。

なぜこの仕組みが効くのか

Claude Codeには、~/.claude/agents/ ディレクトリに .md ファむルを眮くこずでカスタム゚ヌゞェントを定矩できたす。architectアヌキテクチャ蚭蚈、code-reviewerコヌドレビュヌ、security-reviewerセキュリティ監査のような専門゚ヌゞェントを定矩しお、Claude Codeが自埋的に䜿い分けおくれるこずを期埅する——これは自然な発想です。

ずころが、実際のログを集蚈しおみるず驚くような結果が出たす。

私の環境を䟋に挙げたす。~/.claude/agents/ には珟圚8぀の゚ヌゞェント定矩ファむルがありたす。

architect.md
code-reviewer.md
database-reviewer.md
INDEX.md
planner.md
python-reviewer.md
security-reviewer.md
typescript-reviewer.md

~/.claude/logs/agent-invocations.jsonl に蚘録されおいる2026幎5月28日から8月30日たでの682件のログを集蚈するず、盎近30日間の呌び出し内蚳はこうなりたす。

=== Agent usage (last 30d) ===
total invocations: 23  unique types: 3

Top 10:
  agent                                     calls  errors
  Explore                                      19       0
  general-purpose                               3       0
  code-reviewer                                 1       0

0-call agents (defined locally but not used in 30d): 7
  - INDEX
  - architect
  - database-reviewer
  - planner
  - python-reviewer
  - security-reviewer
  - typescript-reviewer

定矩枈み8゚ヌゞェントのうち、30日間で1回でも呌ばれたのは code-reviewer の1件のみです。残り7぀はれロ呌び出し。定矩した゚ヌゞェントの87.5%が、実際には存圚しおいないも同然の状態でした。

盎近7日間に絞るずさらに深刻で、code-reviewer も圏倖に萜ち、0回゚ヌゞェントが8぀に増えたす。

=== Agent usage (last 7d) ===
total invocations: 3  unique types: 2

0-call agents (defined locally but not used in 7d): 8
  - INDEX
  - architect
  - code-reviewer
  - database-reviewer
  - planner
  - python-reviewer
  - security-reviewer
  - typescript-reviewer

これは単なる「もったいない」の話ではありたせん。Claude Codeの゚ヌゞェント定矩はシステムプロンプトずしお垞時泚入されたす。architect.md のような倧型゚ヌゞェントを開くず220行を超える定矩が入っおいたす。䜿われもしない7぀の゚ヌゞェント定矩がトヌクンを消費し、掚論の粟床に圱響を䞎え続けおいるわけです。

「定矩した機胜しおいる」ずいう思い蟌み

゚ヌゞェントを定矩した瞬間の達成感は本物です。「これで次からコヌドを曞いたら自動でレビュヌしおもらえる」「アヌキテクチャを考えるずきに専門家が動く」——そう信じたたた数週間が経過したす。

ずころが珟実のClaudeは、゚ヌゞェントを明瀺的に指定しない限り汎甚ルヌトgeneral-purposeかExploreを遞びたす。code-reviewerの定矩説明に「MUST BE USED for all code changes」ず曞かれおいおも、それは定矩内の文章であり、Claudeが自埋的にその指瀺を読んで行動するわけではありたせん。呌び出す偎のプロンプトか、呌び出し元のロゞックが存圚しお初めお機胜したす。

未䜿甚゚ヌゞェントは2぀の意味でコストを発生させたす。

トヌクンコスト。 システムプロンプトに垞時泚入される゚ヌゞェントカタログの長さは、呌び出しのたびに消費されたす。定矩ファむルが増えるほど1リク゚ストあたりのトヌクン数が増加し、倧きなコンテキストりィンドりを食い朰したす。

認知コスト。 「どの゚ヌゞェントが実際に機胜しおいるか」を人間が手動で把握するのは難しく、定矩ファむルが増えるほど管理が耇雑になりたす。実態を反映しない定矩が積み重なるず、環境の信頌性が䞋がりたす。「この゚ヌゞェントは本圓に動いおいるのか」ずいう疑いが生たれた瞬間、自埋環境ぞの信頌は揺らぎたす。

解決の方向性実枬倀で定矩を刈り蟌む

解決策は感芚ではなく実数倀で刀断するこずです。ログに基づいお「過去30日間で䞀床も呌ばれおいない゚ヌゞェント」を自動的に掗い出し、削陀たたは敎理する運甚サむクルを䜜りたす。

この発想のポむントは䜜業ではなく環境に投資するこずです。「今日この゚ヌゞェントを削陀する」ずいう1回の䜜業ではなく、「い぀でも未䜿甚゚ヌゞェントを即座に確認できるスクリプトが存圚しおいる」ずいう状態を䜜りたす。スクリプトは月次で回す、週次で回す、あるいはcronで定期実行する——運甚の頻床はあずから決められたす。重芁なのは、実枬倀に基づいお刀断できる状態に垞時あるこずです。

私が月商120䞇の自埋環境を半幎で構築できたのも、「AIが賢くなる」こずぞの期埅ではなく、「AIが間違った方向に動いおいないか」を垞時監芖する仕組みぞの投資が䞭心にありたす。゚ヌゞェント䜿甚率の監芖は、その䞀䟋です。

党䜓の流れ

仕組みは3局に分かれおいたす。

┌─────────────────────────────────────────────────────────┐
│  Layer 1: 蚘録                                          │
│  Claude Codeのstop hookが                               │
│  ゚ヌゞェント呌び出しをJSONLぞ曞き出す                  │
│                                                         │
│  ~/.claude/logs/agent-invocations.jsonl                 │
│  → 1行1レコヌド / ts・session_id・subagent_type等       │
└────────────────────┬────────────────────────────────────┘
                     │
                     ▌
┌─────────────────────────────────────────────────────────┐
│  Layer 2: 集蚈                                          │
│  agent-usage-summary.sh が指定期間のレコヌドを集蚈      │
│                                                         │
│  - Bash倖殻匕数パヌス・環境倉数セット               │
│  - Python3ヒアドキュメントロゞック本䜓              │
│    ├ りィンドり期間でフィルタ                           │
│    ├ subagent_type別にカりント                         │
│    └ ~/.claude/agents/*.md ず突き合わせ               │
└────────────────────┬────────────────────────────────────┘
                     │
                     ▌
┌─────────────────────────────────────────────────────────┐
│  Layer 3: 出力                                          │
│  Top10呌び出しランキング  0回゚ヌゞェント䞀芧         │
│                                                         │
│  → 削陀・アヌカむブ・再蚭蚈の刀断材料になる             │
└─────────────────────────────────────────────────────────┘

Layer 1: stop hookによるJSONL蚘録

Claude Codeには、゚ヌゞェント呌び出しが完了したタむミングで任意のスクリプトを実行できるstop hookがありたす。このhookを䜿い、呌び出された゚ヌゞェントの情報をJSONLファむルぞ远蚘したす。

実際のログレコヌドはこのような圢匏です。

{"ts": "2026-08-25T01:32:02.235Z", "session_id": "d82e3fca-d397-4f40-8268-34bdeb9de46a", "cwd": "/dev/affiliate-fc2", "tool_use_id": "toolu_01HQ6HRVEgvnNjqDrmejPZ4S", "subagent_type": "general-purpose", "description": "Find CTA redirect click data for fc2 lane", "duration_ms": 3407, "status": "ok", "caller": {"type": "direct"}}
{"ts": "2026-08-30T08:55:08.361Z", "session_id": "36b40280-ff53-4f66-9582-aa09b7fbec80", "cwd": "/dev/note-autolike", "tool_use_id": "toolu_01RjC237NX1QwsWzVUMqHbvY", "subagent_type": "Explore", "description": "Survey note paid-article infra", "duration_ms": 236, "status": "ok", "caller": {"type": "direct"}}

tsタむムスタンプ、subagent_type゚ヌゞェント皮別、statusok/errorが集蚈に䜿う䞻芁フィヌルドです。duration_ms があるため、呌び出しごずの所芁時間もわかりたす。私の環境では珟圚682件が蓄積されおおり、2026幎5月28日の初回蚘録から3ヶ月分のトラッキングデヌタになっおいたす。

Layer 2: agent-usage-summary.sh の構造

集蚈スクリプトは103行です。Bashの倖殻で匕数を受け取り、ロゞックはPython3のヒアドキュメントで曞いおいたす。理由は2぀——BashだけではJSONLのパヌスが煩雑になるこず、Pythonのみでは匕数凊理のシェル統合が面倒なこずです。䞡方の埗意領域を䜿い分ける構成です。

スクリプト党䜓を瀺したす。

#!/usr/bin/env bash
# agent-usage-summary.sh — Stop hook が蚘録した agent 呌び出しを集蚈
#
# 䜿い方:
#   agent-usage-summary.sh           # デフォルト 7d
#   agent-usage-summary.sh 30d       # 30日
#   agent-usage-summary.sh 7d 30d    # 䞡方

set -uo pipefail

LOG="$HOME/.claude/logs/agent-invocations.jsonl"
AGENTS_DIR="$HOME/.claude/agents"

WINDOWS=("$@")
if [ ${#WINDOWS[@]} -eq 0 ]; then
    WINDOWS=("7d")
fi

if [ ! -f "$LOG" ]; then
    echo "no log yet: $LOG"
    exit 0
fi

export LOG_PATH="$LOG"
export AGENTS_DIR_PATH="$AGENTS_DIR"
export WINDOWS_CSV="$(IFS=,; echo "${WINDOWS[*]}")"

python3 - <<'PY'
import os, json, datetime, glob, sys
from collections import Counter

log_path = os.environ["LOG_PATH"]
agents_dir = os.environ["AGENTS_DIR_PATH"]
windows = os.environ["WINDOWS_CSV"].split(",")

def parse_window(s):
    s = s.strip().lower()
    if s.endswith("d"):
        return datetime.timedelta(days=int(s[:-1]))
    if s.endswith("h"):
        return datetime.timedelta(hours=int(s[:-1]))
    raise ValueError(f"bad window: {s}")

now = datetime.datetime.now(datetime.timezone.utc)

records = []
with open(log_path, "r", encoding="utf-8", errors="replace") as f:
    for line in f:
        try:
            r = json.loads(line)
        except Exception:
            continue
        ts = r.get("ts", "")
        try:
            dt = datetime.datetime.fromisoformat(ts.replace("Z", "+00:00"))
            if dt.tzinfo is None:
                dt = dt.replace(tzinfo=datetime.timezone.utc)
        except Exception:
            continue
        r["_dt"] = dt
        records.append(r)

# 既知 agent 䞀芧ロヌカル定矩の md ファむル名から掚定
known_agents = set()
if os.path.isdir(agents_dir):
    for fp in glob.glob(os.path.join(agents_dir, "*.md")):
        known_agents.add(os.path.splitext(os.path.basename(fp))[0])

for w in windows:
    try:
        td = parse_window(w)
    except Exception as e:
        print(f"[skip {w}]: {e}")
        continue
    cutoff = now - td
    recent = [r for r in records if r["_dt"] >= cutoff]
    counts = Counter(r.get("subagent_type", "") for r in recent if r.get("subagent_type"))
    errors = Counter(r.get("subagent_type", "") for r in recent if r.get("status") == "error")

    print(f"\n=== Agent usage (last {w}) ===")
    print(f"total invocations: {len(recent)}  unique types: {len(counts)}")
    if counts:
        print("\nTop 10:")
        print(f"  {'agent':<40} {'calls':>6}  {'errors':>6}")
        for name, n in counts.most_common(10):
            err = errors.get(name, 0)
            print(f"  {name:<40} {n:>6}  {err:>6}")

    if known_agents:
        used = set(counts.keys())
        unused = sorted(known_agents - used)
        print(f"\n0-call agents (defined locally but not used in {w}): {len(unused)}")
        for name in unused[:30]:
            print(f"  - {name}")
        if len(unused) > 30:
            print(f"  ... and {len(unused) - 30} more")
    else:
        print(f"\n(no local agents dir at {agents_dir}; cannot list 0-call agents)")
PY

蚭蚈のポむントを3぀挙げたす。

耇数りィンドりを1コマンドで比范できる。 agent-usage-summary.sh 7d 30d ず実行するず、7日間ず30日間の結果が連続しお出力されたす。「30日では䜿っおいたが7日では0回」ずいう傟向倉化も䞀目で掎めたす。

既知゚ヌゞェントの突き合わせは glob で行う。 ~/.claude/agents/*.md のファむル名から拡匵子を陀いたものを「定矩枈み゚ヌゞェント」ずしお扱いたす。新しい゚ヌゞェントを远加しおも、スクリプト本䜓を修正せずに自動で怜出察象に加わりたす。

゚ラヌカりントも同時に出力する。 status: "error" のレコヌドを別集蚈しおおり、「呌ばれおはいるが毎回倱敗しおいる」゚ヌゞェントも可芖化できたす。呌び出し数だけでなく成功率たで把握するこずで、「動いおいるが壊れおいる」ずいう別皮の問題も発芋できたす。

Layer 3: 出力の読み方ず刀断フロヌ

スクリプトの出力は2ブロックに分かれおいたす。Top 10ランキングず0-callリストです。

=== Agent usage (last 30d) ===
total invocations: 23  unique types: 3

Top 10:
  agent                                     calls  errors
  Explore                                      19       0
  general-purpose                               3       0
  code-reviewer                                 1       0

0-call agents (defined locally but not used in 30d): 7
  - INDEX
  - architect
  - database-reviewer
  - planner
  - python-reviewer
  - security-reviewer
  - typescript-reviewer

このランキングから読み取れる事実はシンプルです。23回の呌び出しのうち19回82.6%はExploreです。Exploreはファむル怜玢・コヌド調査に特化した汎甚゚ヌゞェントであり、カスタム定矩ではなくClaude Code組み蟌みの機胜です。぀たり「カスタム゚ヌゞェントを7぀も定矩したのに、実際には汎甚機胜しか䜿われおいない」ずいう状態が3ヶ月続いおいたこずが数倀で確認できたす。

0-callリストに名前が出た゚ヌゞェントに察しお、刀断は3択です。

削陀する。 明らかに䜿われおいない、か぀呌び出す仕組みも構築しおいない堎合は削陀したす。システムプロンプトのトヌクン節玄ず、環境の芋通しの良さが即座に改善されたす。

アヌカむブする。 将来䜿う可胜性があるが今は䞍芁な堎合は ~/.claude/agents/archive/ に移動したす。glob のパスを *.md で絞っおいるため、サブディレクトリに移動するだけで自動的に集蚈から陀倖されたす。

呌び出し元を䜜る。 ゚ヌゞェントの機胜自䜓は䟡倀があるが「呌ばれおいない」だけの堎合、stop hookや特定のプロンプトパタヌンでその゚ヌゞェントを明瀺的に呌び出す仕組みを远加したす。この堎合も数倀がなければ「䟡倀があるず思い蟌んでいるだけ」の状態なので、実装埌に改めお集蚈しお効果を確認したす。

実装の詳现

前半で党䜓図を瀺したした。ここからは2぀のスクリプト——stop_agent_tracker.sh蚘録ずagent-usage-summary.sh集蚈——のコヌドを深く読みたす。「なぜこう曞くのか」ず「どこが肝なのか」に絞りたす。

stop_agent_tracker.sh2パス構造が栞心

stop hookが受け取るのは、セッション終了時にClaude Codeが暙準入力ぞ流しおくる以䞋のようなJSONです。

{"session_id":"36b40280-...","transcript_path":"/.../.claude/projects/.../transcript.jsonl","cwd":"/dev/note-autolike","hook_event_name":"Stop"}

transcript_path はそのセッション党䜓の䌚話ログぞのパスです。゚ヌゞェントが䜕を呌んだかはそこに曞いおありたす。ただし、1件の゚ヌゞェント呌び出しは 2぀の別々の行 に分かれお蚘録されおいたす——呌び出した時点の tool_use皮別・匕数ず、凊理完了埌の tool_result成吊・出力です。この2行を突き合わせお初めお「䜕を、い぀、成功したか」が分かりたす。

スクリプトはそのために2パス凊理を採甚しおいたす。

# 第1パス: å…š tool_use ず tool_result をむンデックス化
uses   = {}   # tool_use_id -> (ts, name, input, caller)
results = {}  # tool_use_id -> (ts, is_error)

with open(tp, "r", encoding="utf-8", errors="replace") as f:
    for line in f:
        rec = json.loads(line)
        for b in content:
            if btype == "tool_use" and b.get("name") == "Agent":
                inp = b.get("input") or {}
                if "subagent_type" not in inp:
                    continue
                uses[uid] = (ts, b.get("name"), inp, b.get("caller"))
            elif btype == "tool_result":
                results[rid] = (ts, bool(b.get("is_error")))

1パス目でtranscript党行を走査し、usesずresultsを別々の蟞曞に積みたす。2パス目ではusesを軞にresultsを匕いおマッチングし、JSONLを曞き出したす。

ここで1぀重芁な蚭蚈刀断がありたす。マッチしなかったresultsにキヌがないtool_useは、status: "pending" ずしお蚘録したす。

res = results.get(uid)
if res:
    res_ts, is_error = res
    status = "error" if is_error else "ok"
else:
    res_ts, status = None, "pending"

Claude Codeがhookを呌ぶのはセッション終了時です。セッション途䞭で匷制終了した堎合や、tool_resultが曞き蟌たれる前にセッションが切れた堎合、pendingずしお残りたす。これがあるこずで「蚘録されおいるが完了しおいない」呌び出しを区別できたす。

duration_ms の蚈算も2パス構造の恩恵です。

t0 = parse_ts(use_ts)   # tool_use のタむムスタンプ
t1 = parse_ts(res_ts)   # tool_result のタむムスタンプ
if t0 and t1:
    duration_ms = int((t1 - t0).total_seconds() * 1000)

tool_useずtool_resultそれぞれのタむムスタンプ差が゚ヌゞェントの実行時間です。実際のログを確認するず、Exploreの236msに察しおgeneral-purposeは3407ms〜6830msかかっおいたす。この差がそのたたトヌクンコストの差に近䌌したす。

重耇防止ロゞックも芋逃せたせん。stop hookは1セッションで耇数回発火するこずがありたすClaude Codeの再起動・匷制終了埌の再接続など。同じtool_use_idを2回曞くず集蚈が狂いたす。

seen_ids = set()
if os.path.exists(out_path):
    with open(out_path, "r", encoding="utf-8", errors="replace") as f:
        for line in f:
            r = json.loads(line)
            if r.get("session_id") == sid and r.get("tool_use_id"):
                seen_ids.add(r["tool_use_id"])

曞き出し前にJSONLを党行スキャンし、同じsession_id内のtool_use_idをSetに積んでおきたす。曞き出しルヌプではseen_idsに含たれるものをスキップしたす。682件のログで䞀床も重耇が出おいないのは、この凊理が機胜しおいるからです。

agent-usage-summary.shBash倖殻Python内栞の分割理由

この蚭蚈を芋お「Pythonだけで曞けばいい」ず思う人もいるはずです。私も最初はそう思いたした。

Bash倖殻を残した理由は2぀ありたす。

1぀目匕数凊理の柔軟性。 "$@" を䜿うこずで、7d 30d のような耇数匕数を自然に扱えたす。Pythonだず sys.argv を自分でパヌスする必芁があり、配列の扱いがやや煩雑です。

2぀目環境倉数経由のデヌタ受け枡し。 ヒアドキュメント<<'PY'内のPythonは os.environ でBash偎の倉数を受け取りたす。

export LOG_PATH="$LOG"
export AGENTS_DIR_PATH="$AGENTS_DIR"
export WINDOWS_CSV="$(IFS=,; echo "${WINDOWS[*]}")"

WINDOWS_CSV の生成郚分——IFS=,; echo "${WINDOWS[*]}"——が肝です。Bash配列を,区切りの文字列に倉換しおからPython偎に枡し、Python偎で.split(",")しお戻したす。Bashの配列はヒアドキュメント内に盎接枡せないため、䞀旊文字列に゚ンコヌドする橋枡しが必芁です。

Python偎の parse_window は文字列を timedelta に倉換したす。

def parse_window(s):
    s = s.strip().lower()
    if s.endswith("d"):
        return datetime.timedelta(days=int(s[:-1]))
    if s.endswith("h"):
        return datetime.timedelta(hours=int(s[:-1]))
    raise ValueError(f"bad window: {s}")

珟時点ではd日ずh時間だけ察応しおいたす。w週やm月を远加したい堎合も、この関数だけ倉曎すれば党䜓が動きたす。

既知゚ヌゞェントの自動怜出も重芁な蚭蚈です。

known_agents = set()
if os.path.isdir(agents_dir):
    for fp in glob.glob(os.path.join(agents_dir, "*.md")):
        known_agents.add(os.path.splitext(os.path.basename(fp))[0])

~/.claude/agents/ 盎䞋の *.md ファむル名拡匵子陀くを党郚Setに入れたす。私の環境では archive/ サブディレクトリにファむルを移動するだけで、その゚ヌゞェントは集蚈察象から倖れたす。スクリプト偎の修正は䞍芁です。珟圚 archive/ ディレクトリ2024幎8月29日䜜成に以前の゚ヌゞェント定矩が移動されおいたす。

私が詰たった話

実際に動くたでの経緯を3぀曞きたす。どれも「動くはずなのになぜか蚘録されない」系の問題で、症状から原因にたどり着くたでに時間がかかりたした。

詰たり①゚ヌゞェントのツヌル名が「Task」ではなく「Agent」

最初に曞いたコヌドはtranscriptから name == "Task" を探しおいたした。Claude Codeの倖向きのAPIでは「Task」ずいう名前で゚ヌゞェント呌び出しが玹介されおいたからです。

しかし実際にtranscriptを盎接開いお䞭身を確認するず、党レコヌドに "name": "Agent" ず曞かれおいたした。

{"type": "tool_use", "name": "Agent", "input": {"subagent_type": "Explore", ...}}

name == "Task" でフィルタしおいたため、党件スルヌしお䜕も蚘録されない状態が2日続きたした。grep '"name"' ~/.claude/projects/*/transcript.jsonl | head -5 で盎接確認しお初めお気づきたす。ドキュメント衚蚘ず実ファむルの乖離です。

修正は1行でした。

# 修正前
if btype == "tool_use" and b.get("name") == "Task":
# 修正埌
if btype == "tool_use" and b.get("name") == "Agent":

教蚓は「ドキュメントを信じず実ファむルを読め」。transcript.jsonlは普通のJSONLなのでい぀でも盎接確認できたす。

詰たり②subagent_type が input.subagent_type にある

tool_useの構造を正確に把握しなかったこずで生たれた問題です。最初は b.get("subagent_type") で取ろうずしおいたした。これでは垞に None が返りたす。

実際のtranscriptレコヌドを改めお確認するず、こうなっおいたす。

{
  "type": "tool_use",
  "id": "toolu_01RjC237NX1QwsWzVUMqHbvY",
  "name": "Agent",
  "input": {
    "subagent_type": "Explore",
    "description": "Survey note paid-article infra",
    "prompt": "..."
  }
}

subagent_type は input の䞭にありたす。取り方は b.get("input", {}).get("subagent_type") が正解です。珟圚のコヌドでは inp = b.get("input") or {} ずしお先にinputを取り出し、そこから inp.get("subagent_type") で取埗しおいたす。

問題が起きたずき、682件あるはずのログが最初は0件でした。if "subagent_type" not in inp: continue ずいうガヌド節が党件を匟いおいたからです。デバッグには CC_AGENT_TRACKER_DEBUG=1 環境倉数を蚭定しおdebugログを有効化したした。

CC_AGENT_TRACKER_DEBUG=1 bash ~/.claude/hooks/stop_agent_tracker.sh <<< '...'

stop_agent_tracker.log に recorded=0 total_uses=0 が出お、「tool_useが1件も認識されおいない」こずが確認できたした。そこでtranscriptのraw JSONを盎接開いおinputの構造を確認し、1行で盎りたした。

詰たり③set -uo pipefail でWINDOWS配列が空のずき死ぬ

set -uo pipefail は未定矩倉数参照を即座に゚ラヌにしたす。これ自䜓は正しい蚭定なのですが、匕数なしで呌んだずきに ${#WINDOWS[@]} が0を返す前に ${WINDOWS[*]} を参照しようずしお゚ラヌが出たした。

具䜓的には最初のコヌドがこうでした。

WINDOWS=("$@")
export WINDOWS_CSV="$(IFS=,; echo "${WINDOWS[*]}")"  # 空配列で問題発生

匕数なしで呌ぶず WINDOWS が空配列になりたす。-u フラグがある環境では空配列の展開が゚ラヌになるこずがありzshずbashで挙動が埮劙に異なりたす、WINDOWS_CSV が空になるか、最悪スクリプトが終了したす。

修正は、WINDOWS_CSV の゚クスポヌト前にデフォルト倀を入れるこずです。

WINDOWS=("$@")
if [ ${#WINDOWS[@]} -eq 0 ]; then
    WINDOWS=("7d")
fi
export WINDOWS_CSV="$(IFS=,; echo "${WINDOWS[*]}")"

空チェックを先に枈たせ、デフォルト倀を代入しおから゚クスポヌトする。set -u 環境では「倉数を䜿う前に必ず倀を確定させる」ずいう単玔な芏則を守るだけです。

この問題はcronで自動実行したずきに初めお衚面化したした。手動実行では匕数を枡すので気づかず、cron定矩に匕数なしで曞いおいたため、毎晩サむレントに倱敗し続けおいたずいう話です。tail -20 /var/log/... を確認しお初めお「1件も蚘録されおいない倜がある」ず気づきたした。

ログに残る最初のレコヌドのセッションIDが TEST-AGENT-TRACKER-001 ずいう文字列になっおいるのも、デバッグ過皋の産物です。hookが正しく動くか手動でダミヌデヌタを流しおテストした際の蚘録がそのたた682件の先頭に居座っおいたす。本番ログずテストデヌタが混圚しおいるのは䞍栌奜ですが、集蚈ロゞックはタむムスタンプで期間フィルタするので実害はありたせん。

詰たり④code-reviewer が定矩内の「MUST BE USED」を読たない

これは技術的なバグではなく、そもそもの誀解からくる詰たりです。

code-reviewer.md の冒頭のdescriptionには MUST BE USED for all code changes ず曞いおありたす。゚ヌゞェントを定矩した圓初、「これでコヌド倉曎のたびに自動でレビュヌが走る」ず信じおいたした。

ずころが実際の30日集蚈では code-reviewer の呌び出しは1回だけです。

Claudeが゚ヌゞェントを遞ぶ仕組みを改めお確認するず、descriptionは「どの゚ヌゞェントを遞ぶか刀断するためのヒント」であり、「この゚ヌゞェントを匷制的に呌ぶ呜什」ではありたせん。呌び出し偎のプロンプトかhookで明瀺的に指定しない限り、Claudeは汎甚ルヌトを遞びたす。

MUST BE USED ずいう匷い文蚀は、゚ヌゞェントを遞んだあずにその䞭で守るべきルヌルずしお機胜したす。゚ヌゞェント倖郚からの匷制起動には効きたせん。

これを理解した䞊でやるべきこずは2択です。①「コヌド倉曎埌は code-reviewer を䜿え」ずいう指瀺をstop hookかプロンプトテンプレヌトに組み蟌む、②たたは「どうせ䜿わないなら消す」。

私は珟時点で②を遞んでいたす。code-reviewer は30日間で1回しか呌ばれおおらず、その1回は手動で明瀺した堎合です。仕組みを䜜らない限り呌び出し数は増えたせん。323行の定矩ファむルがシステムプロンプトを占有し続けるコストのほうが、「将来䜿うかも」ずいう期埅倀を䞊回るず刀断したした。

この刀断を䞋せたのも、数倀があったからです。「30日1回」ずいう事実がなければ、「もしかしたら動いおいるかもしれない」ずいう曖昧な期埅が残り続けたした。


実装ず倱敗の話をたずめたす。

スクリプト2本で完結するこの仕組みの肝は、「確認したいずきにい぀でも実数倀が出る」ずいう状態を維持するこずです。数倀があれば刀断できたす。数倀がなければ期埅で動き続け、無駄なトヌクンずシステムプロンプトの肥倧化が静かに積み重なりたす。

定矩ファむルを増やすより、呌ばれおいるかを枬る仕組みを先に䜜る——この順序が、Claude Codeを自埋環境ずしお育おるずきの基本姿勢です。

぀たずきポむント

前半・䞭段で4぀の詰たりを曞きたした。ここでは「環境固有」「運甚フェヌズ」「解釈ミス」ずいう切り口で、远加の詰たりポむントを列挙したす。実際に螏んだものだけです。


cronから実行するずPATHが死んでいる。

手動実行では動くのに、cron定矩で実行するず python3: command not found で萜ちる。cronの実行環境には ~/.zshrc や ~/.profile が読み蟌たれず、PATH は /usr/bin:/bin 皋床しかない。/usr/local/bin/python3 や nvm 配䞋の node が芋えなくなる。察策は2぀——スクリプト冒頭で PATH をハヌドコヌドするか、cron定矩の先頭行に PATH=/usr/local/bin:/usr/bin:/bin を曞く。私は埌者を採甚しおいたす。HOME も蚭定されおいないケヌスがあるため、~/ ではなく /Users/自分のアカりント/ の絶察パスをcronに曞く必芁がありたすスクリプト本䜓の $HOME は HOME 環境倉数が蚭定されおいれば動くので問題なし。

JSONL 1行が壊れおいおもスクリプト党䜓を止めない。

682件のログを蓄積しおいるず、途䞭に䞍完党なJSON行が混入するこずがありたす。Claude Codeがhookを呌び出す途䞭でセッションが切れた堎合、最埌のレコヌドが䞭途半端になるこずがありたす。json.loads をtry/exceptで囲たずに曞くず、1行の砎損でスクリプト党䜓が死にたす。集蚈スクリプトの珟行コヌドでは except Exception: continue でスキップしおおり、壊れた行を無芖しお残りを凊理したす。「こんな堎合は絶察に来ない」ず思いtry/exceptを省略するず、3ヶ月埌のログが増えた頃に初めお萜ちたす。

glob("*.md") がアヌカむブディレクトリも拟う。

glob.glob(os.path.join(agents_dir, "*.md")) は ~/.claude/agents/ 盎䞋の .md ファむルだけを察象にしたす。archive/ サブディレクトリに移動したファむルは含たれたせん——これは意図通りです。ずころが glob.glob(os.path.join(agents_dir, "**/*.md"), recursive=True) に曞き換えるず、アヌカむブ枈みの゚ヌゞェントも「定矩枈み」ずしお扱われ、0-callリストに再び名前が出たす。「アヌカむブしたのに消えおいない」ずいう状態になりたす。recursive=True は付けないのが正解です。

INDEX.md が゚ヌゞェントずしお誀怜出される。

私の ~/.claude/agents/INDEX.md ぱヌゞェント定矩ではなく、ディレクトリの玢匕ファむルです。しかし glob("*.md") は拡匵子しか芋ないため、INDEX ずいう名前が「定矩枈み゚ヌゞェント」ずしお集蚈に入りたす。結果、0-callリストに垞に INDEX が出たす。実害はないものの、リストを芋るたびに「これは䜕だっけ」ず考えるコストがかかりたす。察策はINDEXファむルをサブディレクトリぞ移すか、スクリプト偎に陀倖リストを持぀か、あるいは最初からそういうファむルを眮かないこずです。珟状はそのたたにしお、「INDEXは垞に0回で正垞」ず頭に入れおいたす。

Explore や general-purpose は 0-callリストに出ない理由がわかりにくい。

集蚈の0-callリストに出るのは「~/.claude/agents/ にファむルが存圚するが呌ばれおいないもの」だけです。Explore や general-purpose はClaude Code組み蟌みの゚ヌゞェントであり、ロヌカルに .md ファむルがありたせん。そのため known_agents に含たれず、0-callリストには出たせん。これは仕様通りですが、最初は「なぜ Explore は出ないのか」ず混乱したした。集蚈のTop 10に組み蟌み゚ヌゞェントが䞊䜍を占める構造になっおいるこずを把握した䞊でリストを読む必芁がありたす。

stop hookが走らないケヌスがある。

stop hookはセッション正垞終了時に呌ばれたす。Ctrl+Cによる匷制終了、プロセスのkill、Claude Codeのクラッシュでは発火したせん。そのため「あの長いセッションが蚘録されおいない」ずいう事態が起きたす。682件のうち䜕件が欠損しおいるかは確認できたせんが、「蚘録がある分だけ分析できる」ずいう前提で運甚しおいたす。完璧な蚘録を求めるず運甚が止たりたす。

pending ステヌタスのレコヌドが集蚈に混入する。

status: "pending" のレコヌドはセッション途䞭で蚘録されたものです。集蚈スクリプトはstatusでフィルタせず党件をカりントしたす。぀たり「開始したが完了したか䞍明」の呌び出しも呌び出し数に含たれたす。実際の682件の内蚳を確認するずpendingはごく少数ですが、粟床を䞊げたい堎合は "status" != "pending" の条件を远加する必芁がありたす。珟状は「倚少の誀差は蚱容する」運甚です。

゚ラヌ率の解釈を間違える。

Top 10の errors 列はれロが䞊んでいたすが、これぱラヌが䞀切発生しおいないずいう意味ではありたせん。errors ずしおカりントされるのは status: "error" のレコヌドのみです。゚ヌゞェントが呌ばれた結果「回答が䞍完党だった」「ファむルを芋぀けられなかった」ずいう結果はすべお ok ずしお蚘録されたす。「゚ラヌ数が少ない健党に動いおいる」は蚀いすぎで、「蚘録レベルの臎呜的゚ラヌは少ない」ず読むべきです。

耇数プロゞェクトにたたがるず cwd が混圚する。

JSONLの各レコヌドには cwd が蚘録されおいたす。私のログには /dev/affiliate-fc2、/dev/note-autolike、/dev/... が混圚しおいたす。珟圚の集蚈スクリプトはプロゞェクトを区別せず党件を合算したす。「プロゞェクトAでは Explore が倚甚されおいるが、プロゞェクトBでは䞀切䜿われおいない」ずいう分析をしたい堎合は、cwd フィヌルドでフィルタするオプションを远加する必芁がありたす。今は党瀟集蚈だけで十分なので未実装ですが、プロゞェクト数が増えたら察応する予定です。

定矩ファむルのサむズ感を軜芖しおいる。

0-call゚ヌゞェントを「たあいいか」ず攟眮しがちですが、実際に数倀を出すず芋方が倉わりたす。珟圚8ファむルの合蚈は109,852バむト玄107KB・1,221行です。内蚳で倧きいのが code-reviewer.md323行、planner.md221行、architect.md220行。これらが毎リク゚ストのシステムプロンプトに泚入されたす。107KBがどの皋床の圱響を持぀かはコンテキストりィンドり党䜓の䜿い方次第ですが、「䜿っおいない定矩を消したら回答が速くなった」ずいう䜓感は実際に埗られたす。数倀化するずやっず腰が䞊がりたす。


ベストプラクティス

実際に運甚しお効果があったもの、逆に倱敗しおから埗た教蚓をたずめたす。


1. スクリプトを䜜る前にログ圢匏を生ファむルで確認する。

grep '"name"' ~/.claude/projects/*/transcript.jsonl | head -5 を最初に叩く。"Task" か "Agent" か、subagent_type がどの階局にあるかは、ドキュメントではなく実ファむルが正です。この1コマンドが2日間の無駄を防ぎたす。

2. 「定矩した゚ヌゞェントが機胜しおいる」は実枬するたで信じない。

MUST BE USED ずいう文蚀は、その゚ヌゞェントが遞ばれたあずの内郚ルヌルです。゚ヌゞェント倖郚からの匷制呌び出しには効きたせん。定矩盎埌に agent-usage-summary.sh 7d を実行しお、翌週に0回のたたなら「呌び出し元を䜜るか、削陀するか」を即決したす。

3. 集蚈りィンドりは7日ず30日の2぀を垞に䞊べる。

agent-usage-summary.sh 7d 30d の組み合わせで、「最近䜿い始めた」「以前は䜿っおいたが最近䜿っおいない」の䞡方が1コマンドで芋えたす。30日で1回だけ呌ばれおいる゚ヌゞェントは、7日集蚈では0回になりたす。この差が「偶然1回䜿った皋床」であるこずを瀺したす。

4. 削陀を怖がらない。アヌカむブを䜿い分ける。

「将来䜿うかも」ずいう思考パタヌンが䞍芁な゚ヌゞェントを枩存したす。刀断基準をシンプルにしおください——「30日れロ回か぀呌び出す仕組みが存圚しないなら削陀、それ以倖はアヌカむブ」。アヌカむブは ~/.claude/agents/archive/ に移動するだけで、必芁になれば戻せたす。globが盎䞋しか芋ないため、移動した瞬間に集蚈から倖れたす。

5. 削陀埌にすぐ集蚈を回しお効果を確認する。

゚ヌゞェントを削陀したら agent-usage-summary.sh 30d で0-callリストが短くなったこずを確認したす。「削陀したはずなのにただ出おいる」ずきはファむルが残っおいるか、別の堎所にコピヌがありたす。倉曎の前埌で数倀を比范する習慣が、環境の信頌性を維持したす。

6. stop hookの蚘録はセッション終了埌に手動確認できるようにしおおく。

tail -5 ~/.claude/logs/agent-invocations.jsonl | python3 -m json.tool でい぀でも最新5件を確認できたす。「今日のセッションが蚘録されたか」を確認する習慣が、hookの死掻確認になりたす。1週間蚘録がなければhookが壊れおいるサむンです。

7. ゚ラヌカりントが突然増えたら優先的に調査する。

平垞時ぱラヌ数がれロです。Top 10テヌブルで errors 列に数字が出たら、その゚ヌゞェントが問題を起こしおいたす。grep '"status":"error"' ~/.claude/logs/agent-invocations.jsonl で該圓レコヌドを抜出し、session_id からtranscriptを远っお䜕が起きたかを調べたす。゚ラヌ率の監芖は䜿甚率の監芖ず同じくらい重芁です。

8. pending ステヌタスは別集蚈しお欠損率を把握する。

定期的に grep '"status":"pending"' ~/.claude/logs/agent-invocations.jsonl | wc -l を確認したす。682件䞭のpending数が増加傟向にある堎合、匷制終了が倚いセッションが䞍安定か、hookの凊理が途䞭で萜ちおいるかのどちらかです。欠損率10%未満なら蚱容、それ以䞊なら原因調査に動きたす。

9. cron定矩では HOME ず PATH を明瀺する。

0 9 * * 1 HOME=/Users/自分/ PATH=/usr/local/bin:/usr/bin:/bin bash ~/claude/scripts/agent-usage-summary.sh 7d 30d >> ~/agent-weekly.log 2>&1

HOME は $HOME の展開ができないため倀をハヌドコヌドしたす。PATH は python3 が芋える堎所たで含めたす。出力を >> ~/agent-weekly.log にリダむレクトしおおくず、倱敗したずきに理由がわかりたす。

10. 集蚈を週次レポヌトずしお蚘録に残す。

agent-usage-summary.sh 7d 30d >> ~/.claude/logs/agent-weekly-report.log を週次cronに組み蟌み、ログを蓄積したす。「2ヶ月前は python-reviewer が月5回呌ばれおいたが今は0回」ずいう倉化が、ラむブラリの倉曎や䜜業内容のシフトを反映しおいるこずがありたす。時系列で倉化を远えるず゚ヌゞェント蚭蚈の振り返りができたす。

11. 呌び出しロゞックを䜜る前に゚ヌゞェントを定矩しない。

「定矩しおから呌び出し元を考える」の順序が問題の根本です。゚ヌゞェントを定矩するずきは、同時に「これをい぀・どうやっお呌ぶか」を決めたす。hook、プロンプトテンプレヌト、特定コマンド埌の自動実行——いずれかが存圚しない限り、定矩ファむルはシステムプロンプトを肥倧化させるだけです。

12. 既存゚ヌゞェントの呌び出し数を確認しおから新しい゚ヌゞェントを远加する。

远加前に agent-usage-summary.sh 30d を実行し、0-callリストが長ければたず敎理したす。「新しい゚ヌゞェントを远加したら叀いものが䜿われなくなった」ではなく、「䜿われおいるものだけ存圚する」環境を保ちたす。定矩ファむルの総量が小さいほど、Claude Codeが゚ヌゞェントを遞ぶ刀断も明快になりたす。

13. duration_msを䜿っお重い゚ヌゞェントを把握する。

Explore の平均実行時間は236ms、general-purpose は3,407ms〜6,830msです。重い゚ヌゞェントが頻繁に呌ばれおいるなら、その甚途を Explore で代替できないか怜蚎する䟡倀がありたす。duration_msはJSONLに蚘録されおいるため、python3 -c "import json,statistics; data=[json.loads(l) for l in open('~/.claude/logs/agent-invocations.jsonl')]; ..." で平均・䞭倮倀を出せたす。


たずめ

「定矩した機胜しおいる」ずいう思い蟌みは、Claude Code環境を育おる䞊で最も静かなコストを生みたす。

今回の実枬では、8゚ヌゞェントの定矩のうち30日間で呌ばれたのは1皮類・1回だけでした。残り7぀は合蚈1,100行・玄95KBの定矩が毎リク゚ストのシステムプロンプトを占有し続けおいたした。これを可芖化したのが2本のスクリプトです——stop_agent_tracker.sh が゚ヌゞェント呌び出しをJSONLぞ蚘録し、agent-usage-summary.sh が期間・゚ヌゞェント別に集蚈しお0-callリストを出力したす。

仕組みのポむントを3぀に絞りたす。

transcript.jsonlの2パス凊理。 tool_useずtool_resultは別行に蚘録されおいたす。䞡者を突き合わせお初めお「䜕を・い぀・成吊は」が揃いたす。1パスで曞くず片方しか取れず、蚘録が䞍完党になりたす。

Bash倖殻Python内栞の分業。 匕数凊理ず環境倉数の受け枡しはBashに任せ、JSONLのパヌスず集蚈はPythonに任せたす。どちらか䞀方だけで曞こうずするず、埗意でない凊理で詰たりたす。

~/.claude/agents/*.md ずの突き合わせ。 集蚈が出す0-callリストは、ログだけでは䜜れたせん。「定矩しおあるが呌ばれおいない」を怜出するには、ファむルシステム偎の情報が必芁です。globでファむル名を取埗しおSetで差分を取る1段階が、このスクリプトの栞心です。

削陀は怖くありたせん。アヌカむブぞ移動するだけで集蚈から倖れ、必芁になれば戻せたす。「数倀がなければ刀断できない、数倀があれば迷わない」——これが自埋環境を育おるずきの基本姿勢です。

月商120䞇を支えおいるのはAIの賢さではなく、「動いおいるか枬れる環境」です。枬れるから削れる。削れるからAIが本来の䜜業に集䞭できる。そのサむクルを2本のスクリプトで回しおいたす。


仕組みの党䜓像・月120䞇の内蚳・30日手順は有料noteにたずめおいたす。
📕 Claude Code自埋環境で、実際どう皌ぐか ― 仕組み・実䟋・始め方・サポヌト


Lily@bokuwalily― 個人開発者。Claude Code で自動化基盀を組みながら、iOSアプリやWebサヌビスを量産しおいたす

  • 制䜜物・蚘事は bokuwalily.com にたずめおいたす🖥
  • AIで「寝おおも回る仕組み」を䜜っお月120䞇にした話は noteの有料蚘事 に💰
  • OSS: github.com/bokuwalily 🐙
  • 最新情報・お問い合わせは X @bokuwalily ぞ🌍
  • AI導入・自動化の盞談ず実装テンプレ7本の配垃は 公匏LINE から💬

皆さんの ❀ やシェアが励みになりたす

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?