起案日: 2026-05-29 起案者: [email protected] ステータス: active (調査未着手) 背景: ADR 起案テキスト tmp/adr-pipeline-prompt-caching-draft.json が Pipeline Cross-Validation Gate (ADR-0076) で差し戻し。critical 盲点「5m TTL 下で cache hit 率実質ゼロ + docs-search-worker の read ≒ 0 観察 → コスト試算崩壊」を解消するため、調査タスクに格下げ。

0. 経緯

2026-05-28 に Anthropic Console (bizlp ワークスペース) で「プロンプトキャッシュ未使用」警告 + 月 USD 202.54 のコスト データを確認。drp の 4 Opus 4.7 ノードを caller と特定し、cache_control 適用を ADR 起案 (sessionId: d62fcf0a-64e9-46b7-930a-4b9ac637a551, draft id adr-pipeline-prompt-caching) として Pipeline に投入。

結果 (2026-05-28 終了):

項目
Score47/50 (Standard 閾値 40 超え)
Cross-Validation Gaterejected (Critical blind-spot が #efficient Must 軸を毀損)
Blind-spot findings8 件 (critical 1 / high 3 / medium 3 / low 1)
差戻し理由要約コスト試算の根拠未検証、docs-search-worker の実測値 (read≒0) と矛盾

Pipeline の指摘は妥当。検証データ不足のまま本番投入提案だったため、ADR 起案は中止し本 RQ に格下げ。

1. 調査設問

1.1 P1 設問 (実装可否判断に必須)

  1. docs-search-worker で write が発生しつつ read ≒ 0 なのはなぜか?

    • 候補仮説 A: 5m TTL 内に同一 prefix が再呼び出しされていない (検索クエリの prefix が毎回異なる)
    • 候補仮説 B: cache_control の付与位置が誤っている
    • 候補仮説 C: Anthropic Console の集計が write/read を別画面で表示しており、実は read もある (今回見たグラフは Opus 4.7 のみ、Sonnet 4.6 は別途確認要)
    • 候補仮説 D: Cloudflare Workers の cold start で SDK が新規 cache key を生成している
  2. drp の Pipeline 呼び出し頻度の実測

    • 5/25-26 のスパイク日: 1 時間あたり何本の Pipeline run が走ったか
    • 散発日: 1 日 1-2 本程度か、ゼロか
    • データ source: Cloudflare Worker logs / D1 (ADR-0077 telemetry) / Anthropic Console グラフ
  3. 各 Opus 4.7 ノードの systemPrompt token サイズ計測

    • body_generation / scoring / consistency / reviewClaude の各 systemPrompt が Opus 4.5+ の最小 cacheable トークン 4,096 を超えているか
    • 計測方法: prompts/production/<id>/prompt.meta.yaml から系統的に集計、または実 KV から取得して tokenizer 実行
    • 4K 未満のノードは cache_control 付与しても効かないため対象外

1.2 P2 設問 (実装最適化のため)

  1. LiteLLM v1.40+ の cache_control passthrough 仕様

    • 公式ドキュメントでの passthrough 保証範囲
    • messages[].content[].cache_control 形式と extra_body 形式のどちらが推奨か
    • bizlp の LiteLLM version (litellm/config.prod.yaml 参照) で対応済か
  2. TTL 戦略の最適化

    • 5m vs 1h の break-even hit rate (write 1.25x vs 2x のコスト構造)
    • bizlp の集中投入パターン (連続 4-8 Opus 呼び出し/起案) でどちらが有利か
    • 複数 cache_control breakpoint (tools / system / messages) を組み合わせる必要があるか
  3. 代替案: モデル選択最適化との比較

    • Pipeline の Opus 4.7 ノード (body/scoring/consistency/reviewClaude) の一部を Sonnet 4.6 にダウングレードした場合のコスト削減効果
    • 品質劣化リスク (ADR-0033 で Opus にアップグレードした経緯あり)
    • cache_control 適用と比較して、どちらが ROI が高いか

1.3 P3 設問 (中長期最適化)

  1. 複数 breakpoint 戦略

    • tools / system / 会話履歴 の階層キャッシュ (Anthropic docs §"Multiple cache breakpoints")
    • 4 breakpoint まで使えるが、Pipeline 構造に適合するか
  2. Pre-warming の有効性

    • max_tokens: 0 でのキャッシュ事前ロード (Anthropic docs §"Pre-warming the cache")
    • Pipeline 起動前 (CI / 定期 trigger) で warming する設計

2. 検証手順 (推奨実行順序)

Step 1: docs-search-worker の cache 実測 (P1-Q1)

# Anthropic Console で Sonnet 4.6 のグラフを切り替え、read/write 比率確認
# wrangler tail で実時間のリクエスト patterns 観察 (1-2 日)
npx wrangler tail docs-search-worker --format pretty

期待アウトプット: read ≒ 0 の原因仮説 (A-D) のうちどれが当てはまるか確定。

Step 2: Pipeline 呼び出し頻度の集計 (P1-Q2)

# D1 telemetry から起動間隔を集計
npx wrangler d1 execute pipeline-telemetry \
  --command "SELECT DATE(created_at) AS day, COUNT(*) AS runs, MIN(created_at) AS first, MAX(created_at) AS last FROM runs GROUP BY day ORDER BY day DESC LIMIT 30"

期待アウトプット: 同一日内の連続実行間隔ヒストグラム。5m 以内に次の Pipeline が走る確率を算出。

Step 3: systemPrompt token サイズ計測 (P1-Q3)

# 各ノードの prompt.meta.yaml + system prompt から tokenizer で計測
# 簡易計算: 日本語は 1 token ≒ 1.5 chars 想定
# 精密: @anthropic-ai/sdk の count_tokens API

期待アウトプット: 4 ノードのうち何個が 4K tokens 以上か (caching 対象候補数)。

Step 4: LiteLLM passthrough の検証 (P2-Q4)

# dev 環境で 1 Pipeline run を仕掛けて、Anthropic Console で
# cache_creation_input_tokens > 0 が出るか確認
# 出なければ LiteLLM 側で stripping している

Step 5: 代替案比較 (P2-Q6)

各ノードを Sonnet 4.6 に変えた場合のコスト試算 (Opus $5/MTok → Sonnet $3/MTok = 40% 削減 vs caching の 90% 削減)。

3. 完了条件 (Definition of Done)

  • P1-Q1: read ≒ 0 の原因が特定され、ADR 案で対処可能か判定
  • P1-Q2: Pipeline 呼び出しパターンが定量化され、5m hit 確率が算出
  • P1-Q3: 各 Opus ノードの systemPrompt token サイズ確定、4K 超のノード数判明 (2026-05-29 計測完了、§5 参照)
  • 上記 3 設問の結果から ADR 再起案の可否を判断
  • 「効果あり」と判定された場合のみ ADR 再起案、結果は本 RQ にも追記

4. 関連リソース

  • 元 ADR 起案テキスト: tmp/adr-pipeline-prompt-caching-draft.json (KV からは削除済、ローカルのみ保持)
  • Pipeline session: d62fcf0a-64e9-46b7-930a-4b9ac637a551 (2026-05-28 rejected)
  • TODO バックログ: docs/_internal/02_project/TODO_future.md DOC-OPS-12 (ADR 路線から RQ 路線に格下げ要更新)
  • 主要 caller: drp/src/nodes/{body_generation,scoring,consistency,parallel_review}.ts
  • 参照実装 (read≒0 の調査対象): docs-search-worker/src/anthropic.ts:91-96
  • 関連 ADR: ADR-0033 (multi-provider 抽象を LiteLLM Gateway に集約)、ADR-0076 (Cross-Validation Gate)、ADR-0077 (TelemetryRecord D1)
  • Anthropic 公式ドキュメント: https://docs.anthropic.com/en/docs/build-with-claude/prompt-caching
  • LiteLLM Anthropic caching: https://docs.litellm.ai/docs/providers/anthropic

5. P1-Q3 計測結果 (2026-05-29)

5.1 計測対象と方法

  • 対象: prompts/production/{body-generation,gate2-consistency,gate3-parallel-review,gate4-scoring}/prompt.md (ファイル実体)
  • 計測方法: Anthropic 公式 POST /v1/messages/count_tokens API、model claude-opus-4-5 指定 (4.5 / 4.6 / 4.7 は同一 4,096 minimum)
  • 計測スクリプト: for node in ...; do curl POST /v1/messages/count_tokens; done (本 RQ commit に詳細)
  • : KV (PROMPTS_KV) で上書きされている可能性は残るが、prompt.meta.yaml の semver と運用慣行 (file 起点で KV push) からファイル実体と大きく乖離する可能性は低い

5.2 結果

Nodemodel_pin (meta)gateway.ts での実モデルsystem tokensvs 4,096 minimumキャッシュ可否
body-generationclaude-opus-4-7claude-opus3,599-497 (87.8%)
gate2-consistencyclaude-sonnet-4-6 (meta) ※claude-opus690-3,406 (16.8%)
gate3-parallel-review (claude)claude-sonnet-4-6 (meta) ※claude-opus323-3,773 (7.9%)
gate4-scoringclaude-sonnet-4-6 (meta) ※claude-opus775-3,321 (18.9%)

prompt.meta.yamlmodel_pindrp/src/llm/gateway.ts:96-101MODELS が乖離している。本番呼び出しは gateway.ts の claude-opus (LiteLLM alias → claude-opus-4-7) が優先。meta 側の model_pin はテスト用または更新漏れと思われる (別途確認推奨)。

結論: 全 4 ノードが Opus 4.5+ の 4,096 token 最小キャッシュ可能要件を下回るcache_control: { type: 'ephemeral' } を system に付けても キャッシュは発生せず、警告も返らない (Anthropic 仕様)。元 ADR 起案の前提が構造的に成立しないことが確定。

5.3 implications (帰結)

  1. 元 ADR 起案 (DOC-OPS-12) は技術的に無効 — LiteLLM passthrough の検証以前の問題として、Opus 4.7 の最小要件で弾かれる
  2. Pipeline Cross-Validation Gate の差戻しは更に強い根拠で正当化 — 「未検証」どころか「検証したら明確に NG」
  3. docs-search-worker の read ≒ 0 は同じ理由の可能性 — Sonnet 4.6 系も最小要件は 1,024 tokens (Opus 4.5+ より低い) だが、もし system が 1K 未満なら同じ罠
  4. コスト削減 ($30-70/月) のシナリオは完全に消滅 — caching write 課金もそもそも発生しないため、純粋にゼロ効果

5.4 次の検討方向 (caching に固執する場合)

内容必要作業評価
αsystemPrompt を膨らませて 4K 超過用語集 / ADR スタイルガイド / few-shot example を system に追加 (各 +1-3K tokens)質改善も期待できるが「caching のために膨らませる」は本末転倒
βmessage 構造を変えて adrBody を user 先頭 → cache_control 配置scoring/consistency/reviewClaude で共通の adrBody (2-5K tokens) を user の prefix に固定、cache_control をその後ろに1 Pipeline run 内の 3 ノードで cache hit 可能、ただし system は依然 cache 外
γモデルダウングレード (Opus → Sonnet)gateway.ts の MODELS で claude-sonnet に変更input $5→$3 で 40% 削減、品質劣化リスクあり (ADR-0033 で逆方向の決定済)
δcaching 案を完全放棄、ノード別 prompt slim down現在の prompt 内の冗長部分を削減、トークン使用量を絶対量で削減output token も同時削減可能、最も保守的

仮の推奨: γ (モデルダウングレード) または δ (slim down) を P1-Q1/Q2 完了後に検討。α/β は変更コストが大きく ROI 低い。

5.5 P1-Q3 の波及効果

  • P1-Q1 (docs-search-worker の read ≒ 0) は Sonnet 4.6 系で最小 1,024 tokens なので「systemPrompt が 1K 未満」が原因の可能性を加える
  • P1-Q2 (Pipeline 呼び出し頻度) は本 caching 案では不要に。代わりに γ/δ 案のコスト試算根拠として活用
  • 本 RQ の DoD §3 の P1-Q3 は ✅ 完了。P1-Q1/Q2 の続行可否は γ/δ 案に組み替えるべきか要判断

5.6 案 δ 詳細: body-generation prompt slim down 提案 (2026-05-29)

§5.4 で δ (prompt slim down) を採用方針として確定。優先対象は body-generation 一択 (system 3,599 tokens で他 3 ノード合計の約 3 倍、output も最大)。本節で具体的削減候補を特定。

5.6.1 削減対象ブロック (現状 3,599 tokens → 目標 ~2,500 tokens, -30%)

#場所 (L 番号)現状 (推定)提案 (推定)節約種別
1L1-2 役割定義~50~500維持
2L4-13 テンプレート placeholder 列~150~100-50軽量化
3L15-16 Implementation Status 説明~300~80-220大幅圧縮
4L18-24 Kruchten Type 説明~400~150-250大幅圧縮
5L26-48 コンテキスト §1.1-1.5 説明~600~430-170軽量化
6L50-51 決定~30~300維持
7L53-82 判断基準 (Q42/WSM/K.O.)~700~530-170軽量化
8L84-86 検討した代替案~60~600維持
9L88-104 影響 (Good/Bad/Neutral)~300~220-80軽量化
10L106-112 撤退条件 / 参照~80~800維持
11L114-139 生成ルール 1-10~900~700-200圧縮
12L117-120 Mode 別構造~150~70-80表化
合計~3,720 ※実測 3,599~2,500-1,220 (-32.7%)

5.6.2 主要削減ブロックの置換案

ブロック #4 Kruchten Type 説明 (250 tokens 節約)

現状 (L18-24, 400 tokens):

[Kruchten Type 必須化 (ADR-0030 で採用)]
本フィールドには Existence / Property / Executive のうち該当する 1-3 個を `/` 区切りで列挙する (例: `Existence` または `Existence/Property` または `Existence/Property/Executive`)。判定基準:
- **Existence**: 新しい要素・コンポーネント・データ構造を導入する決定
- **Property**: システム全体に適用される横断ルール・規約・制約を定める決定
- **Executive**: 技術・プラットフォーム・プロセス・組織選定の決定
複数該当する場合は §5.1 (Existence 影響) / §5.2 (Property 影響) / §5.3 (Executive 影響) で分解記述する。判定の詳細は `docs/adr/README.md` の「Kruchten 3 分類ガイド」を参照。
裁定優先順位: ① 起案者明示があればそれを採用 → ② 過半数一致 (Parallel Review 2/3 以上) → ③ 過半数不一致時は起案者裁定 (Pipeline は INFO 通過、ラベル候補列挙)。

提案 (150 tokens):

[Kruchten Type] Existence / Property / Executive のうち該当を `/` 区切りで列挙 (例: `Existence/Property`)。判定基準と裁定ルールは docs/adr/README.md の「Kruchten 3 分類ガイド」を参照。複数該当時は §5.1/§5.2/§5.3 で分解。

根拠: Existence/Property/Executive の定義は docs/adr/README.md に正本がある。Opus 4.7 ならこのサイズの分類タスクで 1 文の指示で十分判定可能 (few-shot 不要)。裁定優先順位は Pipeline の Parallel Review 合議ロジックと被るため prompt 側で説明不要。

ブロック #3 Implementation Status 説明 (220 tokens 節約)

現状 (L15-16, 300 tokens)提案 (80 tokens):

[Implementation Status] 新規 ADR は `Not Started` 固定で起案する。値の定義は docs/adr/README.md の「Implementation Status ガイド」を参照。

根拠: 「マージ時に Done に手動更新」等は起案 prompt の責務外 (実装後の運用ルール)。生成時には不要。

ブロック #11 生成ルール 1-10 のうち Rule 4 Confirmation (170 tokens 節約)

現状 (L122-123, 300 tokens):

[Confirmation 必須化 (ADR-0036 で採用)]
§6.5 Confirmation (準拠確認 / Fitness Function) セクションには「決定が実装で守られているかを継続的に検証する fitness function」を 3 要素 (検証手段 / 実行頻度 / 違反時の対応) で記述する。既存検証メカニズムを最大活用 (scripts/adr-lint.mjs / scripts/4_review_specs_by_gemini.js / Pipeline Gate 2 / 手動 QA など)。自動検証不可なら手動レビューチェックリストを明示。Light Mode + 純ドキュメント ADR は N/A 許容 (理由必須)、Standard / Critical Mode は N/A 不可。

提案 (130 tokens):

4. **Confirmation セクション** (§6.5): fitness function を 3 要素 (検証手段 / 実行頻度 / 違反時対応) で記述。既存メカニズム (adr-lint / Gate 2 / 手動 QA) を活用。Light + ドキュメント ADR のみ N/A 許容、それ以外は N/A 不可。詳細: docs/adr/README.md。
ブロック #12 Mode 別構造 (80 tokens 節約)

現状 (L117-120, 150 tokens) の箇条書きを表化:

提案 (70 tokens):

3. **Mode 別構造**:

| Mode | §1.x サブ見出し | §3 判断基準 | §5.x サブ見出し |
|------|----------------|------------|----------------|
| Light | 単一節 | 省略可 (推奨記載) | 単一節 |
| Standard | 推奨 | 必須 | 推奨 |
| Critical | 必須 | 必須 | 必須 |

5.6.3 削減してはいけない部分

以下は 絶対に残す:

内容残す理由
L126-132 (Rule 7)「数値・日付・評価マトリクス等は原文転記」これがないと Pipeline 出力で起案者の数値が消失する (実害大)
L133-138 (Rule 8)「Markdown 表・コードブロック・引用はそのまま転記」同上
L139 (Rule 10)「セクション帰属が曖昧な箇所も削除しない」起案テキストの欠落防止
L88-104 §5 構造Light vs Standard/Critical の影響分割Fairy Tale アンチパターン回避 (Bad を必ず書く)

5.6.4 期待効果

指標現状slim 後 (見込)改善
system tokens3,599~2,500-1,099 (-30.6%)
input コスト/call$0.018$0.0125-$0.0055
output tokens (副次効果)5,000-8,0004,500-7,200 (10% 減仮定)-500-800
output コスト/call (副次)$0.125-0.20$0.113-0.18-$0.012-0.025
月次 (30 call)$4.3-6.5$3.3-5.1-$1-1.5/月
TTFT (体感)baseline~30% 短縮体感差あり
品質baselinepromptfoo eval で検証不変 ~ 改善 (冗長性除去で安定性向上)

5.6.5 実装段取り (main ワークスペース)

  1. 本 RQ §5.6 を slim 化仕様として参照 → main で prompts/production/body-generation/prompt.md を編集
  2. semver bump (prompt.meta.yaml 1.1.0 → 1.2.0)
  3. promptfoo eval 実行 (prompts/production/body-generation/promptfoo.yaml) で品質回帰チェック
  4. eval 通過 → scripts/prompt-kv-push.mjs で KV 投入 + active pointer 切替
  5. 1-2 週間運用観測:
    • Telemetry (ADR-0077, per-node token/cost) で input/output 削減実績確認
    • Cross-Validation Gate / Gate 4 採点で品質劣化兆候の有無確認
  6. 効果確認後、他 3 ノード (gate2-consistency / gate3-parallel-review / gate4-scoring) の slim 化要否を判断
    • gate4-scoring は Self-Consistency samplingN > 1 設定なら優先度上昇
    • 他 2 ノードは system が元から小さく ROI 低い可能性大

5.6.6 リスク

  • 過剰削減で品質劣化: Rule 4 (Confirmation) 圧縮で N/A 判定が雑になる可能性 → promptfoo eval で検出
  • docs/adr/README.md 参照に依存: 「詳細は README 参照」と書くが Opus は README を読めない。判定に必要な最小情報は prompt 内に残す (本提案は最小情報を保持)
  • template placeholder 維持: L4-13 の {{タイトル}} 等は body_generation.ts で substitution されないが、テンプレ構造の「形」を示すために残す。削除すると model が出力構造を想像で書く

5.6.7 完了条件

  • main 側で prompts/production/body-generation/prompt.md slim 化 PR 提出
  • promptfoo eval 全件 pass
  • semver bump + KV active 切替
  • 1-2 週間 Telemetry 観測、input 削減 25%+ / output 削減 5%+ 確認
  • 確認結果を本 RQ に追記、他 3 ノード slim 化の要否を判断

6. Pipeline 検出の 8 盲点 (転記、調査時の参考)

#重要度内容対応 RQ 設問
1critical5m TTL 下で cache hit 率実質ゼロ + docs-search-worker の read≒0Q1, Q2, Q5
2highLiteLLM passthrough 未検証 + ADR-0033 との暗黙的矛盾Q4
3high4K トークン最小要件未計測 → 対象ノード数の不確実性Q3
4high撤退条件 (1 週間観測) の誤判定リスク(RQ 完了後の ADR で再設計)
5mediumコスト削減試算の根拠不透明Q1-Q3 (実測ベース)
6mediumKV systemPrompt 更新時の invalidation 不可視(運用設計時)
7mediumCloudflare Workers での LiteLLM デプロイ形態の cache_control 消失リスクQ4
8lowモデル選択最適化など根本対策の検討阻害Q6