モデルを固定したのに、品質が揺れて、勝手に安いモデルに切り替わっている気がする。あるいは統計の画面の「よく使うモデル」が実感と合わない。UIから推測する必要はありません。Claude Codeが書き出すターンごとのログには、APIが実際に応答したモデルがそのまま記録されています。その読み方を説明します。
~/.claude/projects/<project>/<session-id>.jsonl に記録されます。"type":"assistant" の各行の message.model が、そのターンで実際に提供されたモデル(APIが応答したモデル)で、同じ行に timestamp が入っています。これが地に足のついた事実で、どんなUIの要約よりも信頼できます。
「昨夜はなんだかSonnetっぽかった」という感覚を、報告にそのまま貼れる日付つきの記録に変えます。全セッションを通して、各 assistant のターンの提供モデルを時刻の順に並べます。そのまま貼り付けてください。
for f in ~/.claude/projects/*/*.jsonl; do
python3 - "$f" <<'PY'
import sys, json
for line in open(sys.argv[1]):
try: d = json.loads(line)
except: continue
if d.get("type") == "assistant":
m = d.get("message", {}).get("model")
if m and m != "<synthetic>":
print(d.get("timestamp",""), m)
PY
done | sort
1行が1つの提供ターンです(例: 2026-06-07T01:23:45.678Z claude-opus-4-8)。固定したものより下位のモデルが続く区間があれば、それが証拠です。正確なUTCの時刻があるので、自分の作業と突き合わせられます。
上の一覧を、モデルごとのターン数に畳み込みます。報告を具体的にする一番効く数字です。
cat ~/.claude/projects/*/*.jsonl | python3 -c '
import sys, json, collections
c = collections.Counter()
for line in sys.stdin:
try: d = json.loads(line)
except: continue
if d.get("type")=="assistant":
m=d.get("message",{}).get("model")
if m and m!="<synthetic>": c[m]+=1
for m,n in c.most_common(): print(f"{n:7d} {m}")
'
提供の多い順に、モデルごとのターン数が1行ずつ出ます。claude-opus-4-8 や claude-sonnet-4-6 の横に件数が並びます。
全期間の集計は、今提供されているものと正反対になりえます。最近を無視した統計の「よく使うモデル」が誤解を招くのも同じ理由です。実際のあるマシンでは、2つの窓が完全に逆転していました。
| 期間 | 最も多い提供モデル | 割合 |
|---|---|---|
| 直近30日 | claude-opus-4-7 | 80% |
| 直近7日 | claude-opus-4-8 | 73% |
そこで、最近の窓だけで集計します。DAYS は好みで変えてください。
python3 - <<'PY'
import json, glob, os, collections, datetime
DAYS = 7
cut = (datetime.date.today() - datetime.timedelta(days=DAYS)).isoformat()
c = collections.Counter()
for f in glob.glob(os.path.expanduser("~/.claude/projects/*/*.jsonl")):
for line in open(f):
try: d = json.loads(line)
except: continue
if d.get("type") == "assistant":
m = d.get("message", {}).get("model"); t = d.get("timestamp", "")
if m and m != "<synthetic>" and t[:10] >= cut:
c[m] += 1
tot = sum(c.values()) or 1
for m, n in c.most_common():
print(f"{n*100//tot:3d}% {n:6d} {m}")
PY
各行が最近の窓でのモデル別の割合です(例: 73% 24773 claude-opus-4-8)。傾向が変わった「よく使うモデル」が一目で分かります。
IDは明示されているので、別の段に移った瞬間が分かります。実在する現行の例です。
message.model のID | 意味 |
|---|---|
claude-opus-4-8 | Opus 4.8(最上位) |
claude-opus-4-7 | Opus 4.7(最上位) |
claude-sonnet-4-6 | Sonnet 4.6(中位) |
claude-haiku-4-5-20251001 | Haiku 4.5(高速・下位) |
すべての差が同じ重みではありません。見つけたものを、影響の大きさで仕分けてください。
4-7 と 4-8)の入れ替わりは弱い手がかり。 どちらも最上位で、出力の質に効くほどの差は出にくく、通常の理由でも起こりえます。claude-sonnet-4-6)へ静かに落ちたなら、それが鋭い証拠です。 段をまたぐ降格こそ、体感できる質の差を生みます。報告する価値があるのはこちらです。そして、逸話でなく再現できる形にします。
message.model はどのモデルがそのターンを提供したかを教えてくれます。それ自体は、意図や「わざと下げた」ことまでは証明しません。観測した事実(ターンごとの提供モデルと時刻)を報告し、原因の判断はルーティングを監査できる人に委ねてください。
npx cc-safe-setup) · English version
本ページは、自分のセッションのログに対するローカルな検証方法の説明であり、アカウントや法務の助言ではなく、Anthropicのルーティングの意図についての主張もしません。モデルやUsage Policyの疑問は、Anthropic自身の文書とサポートで確認してください。文脈として参照した報告: anthropics/claude-code #65889(固定したモデルの静かなルーティング)、#65899(統計の「よく使うモデル」が最近を反映しない)。