本文へ移動
cccskills
無料GitHub で公開

opt-improve

ボトルネックに合わせた改善策を設計・実行・検証する

インストール方法を見る

含まれるファイル(1)

  • SKILL.md24.1 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Skill: opt-improve(改善策の設計と検証)

/opt-improve [data_path] で実行。 ベースラインのエラー分析結果に基づき、改善策を設計して検証する。

いつ使うか

  • /opt-baseline でボトルネックが特定された後
  • 「どうすればスコアが上がるか」を知りたい時

Opus 4.7 ヒント

  • 拡張思考は最初に1回: Step 0 のレイヤー1/2/3 判定の直前に深く考える。「固定として扱っているもののうち、本当に動かせないものはないか」を3つ以上洗い出す。レイヤー1の発見はレイヤー3の10倍効く
  • 改善策3案は並列実行: 案A/B/C を別スクリプトに分け、run_in_background=true で同時投入。Before/After 表を一気に作る(探索の局所最適化を避ける)
  • 大きな再定式化は Plan サブエージェント: 変数粒度を変える・分解軸を変える等、構造を変える改善は subagent_type=Plan で骨格設計してから実装
  • LLM ヒューリスティクス進化は背景: ReEvo / EoH / FunSearch は時間がかかる。背景投入し、その間に目的関数⇄評価関数の精密一致(+15-27%実績)を先に進める
  • QA チェックは並列 Read: spec.md / scripts/*.py / evaluator を 1 メッセージで Read してから整合性チェック
  • 間違った分割を回避: 「いつ」と「何を」を分離してスキル制約を破壊する失敗例あり。分割の方向は 違反原因を解消する方向 にだけ採る

詳細は reference/opus47_collaboration.md。


実行内容

0. 改善のレイヤーを見極める(最重要 — 必ず最初に実行)

改善には3つのレイヤーがある。上のレイヤーほど効果が大きい。 下のレイヤーで頭打ちになったとき、同じレイヤーで粘るのではなく、一つ上に戻れ。

レイヤー1: 問題を変える(解の空間そのものを広げる/変える)
  ├── 暗黙の前提を疑う(「これは本当に動かせないのか?」)
  ├── 入力データの使い方を変える(同じデータから別の解釈を引き出す)
  └── 運用ルールの変更を提案する(制約を緩和ではなく、現実を変える)

レイヤー2: モデルを変える(同じ問題を別の定式化で解く)
  ├── 制約のハード/ソフトの切り分けを見直す
  ├── 変数の粒度を変える(1人単位→チーム単位、1日単位→時間帯単位)
  └── 分解の軸を変える(時間/空間/段階)

レイヤー3: パラメータを変える(同じモデルの中で調整する)
  ├── 目的関数の重みチューニング
  ├── ソルバーのパラメータ調整
  └── ヒューリスティクスの設計改善

レイヤー1の思考法:

「ソルバーが OPTIMAL を返してこれが精一杯」は「このモデルの中での最適」でしかない。 ソルバーの外側に目を向けるために、以下を自問する:

Q1. 「固定」として扱っているものの中に、実は動かせるものはないか?
    - リソースの割当状態(既にアサイン済みだが、入れ替えてよいかもしれない)
    - リソースの利用可否(使えないとされているが、条件次第で使えるかもしれない)
    - 需要の形(分割・統合・延期・委譲ができるかもしれない)

Q2. 制約として扱っているものの中に、実はコストで扱えるものはないか?
    - 「できない」のか「やりたくない」のか
    - 「不可能」なのか「高くつく」のか

Q3. モデルに含めていない資源や選択肢はないか?
    - 使っていないがアクセス可能なリソース
    - 現在の計画期間の外にあるが、前倒し/後ろ倒しで使える時間
    - 問題のスコープ外だが、協力を依頼できる主体

判定フロー:

改善の余地があるか?
  │
  ├── 解の品質が悪い → レイヤー3(パラメータ調整)から試す
  │     └── 頭打ち → レイヤー2へ
  │
  ├── 割当件数/カバレッジが頭打ち → まずレイヤー1を疑う
  │     「容量が足りない」「リソースがない」は結論ではなく出発点。
  │     容量を増やす打ち手、リソースを捻出する方法を3つ以上列挙してから
  │     「本当に不可能」と判断する。
  │
  └── 制約違反が解消しない → レイヤー2で定式化を見直す
        └── それでも無理 → レイヤー1で制約の前提を疑う

レイヤー1の改善は、レイヤー3の10倍以上効くことがある。 目的関数の重みを延々チューニングする前に、5分で「問題の外側」を見渡せ。


1. ボトルネックの種類に応じた対策選択(レイヤー2-3)

エラー類型A: 特定の制約に違反が集中

まず違反の中身を深く分析する。「何が違反しているか」だけでなく「なぜ違反するか」を掘る。 分割の観点は最初から決め打ちできない。実験と分析を繰り返して見つけるもの。

分析の手順:
  1. 違反の内訳を集計する
     「31件の違反が全てHC6」→ HC6に集中

  2. 「なぜその制約が破れるか」を調べる
     「AM便の荷物がPMまで車内に滞留して2時間を超える」
     → 原因: AM便とPM便の混載

  3. 原因から分割の観点が見える
     「AMとPMを分ければ混載がなくなる」
     → 時間で分割すればよいとわかる

  4. 試す → 結果を確認 → 効果があれば採用
     AM/PM分割 → 違反31件→0件 → 採用

  ※ 最初から「時間で分割」が正解だとは限らない
  ※ 空間で分割、段階で分割も候補として試す
  ※ 間違った分割は逆効果になる(下記参照)

分割の候補と選び方:
  ○ 時間で分割(時間制約がきつい場合)
    → 違反の原因が「異なる時間帯の混在」なら有効
  ○ 空間で分割(地理的制約がきつい場合)
    → 違反の原因が「遠い場所の組合せ」なら有効
  ○ 段階で分割(複数の決定が絡んでいる場合)
    → まず大きな決定(割当)→ 次に小さな決定(順序)
  × 分割してはいけない方向もある
    → 「いつ」と「何を」を分離 → スキル制約を破壊して69違反の大失敗例あり

  判断基準: 「違反の原因を解消する方向で分割する」

対策2: ボトルネック制約に特化したムーブを設計する
  例: 訓練ペアリングが壁 → 熟練者を先に配置する構築法
  例: 時間枠制約が壁 → 時間帯ごとに分離してルート構築

エラー類型B: 何をやっても解けない(feasibility未達)

→ 手法の構造的限界を疑う

  判定基準: ソルバーで解けるか?
  ├── ソルバーで解ける → 貪欲法/ヒューリスティクスの限界
  │     → ソルバーを使うか、グローバル推論を持つ手法に切替え
  │
  └── ソルバーでも解けない → 問題そのものが不可能かもしれない
        → 思考回路⑦(不可能性の証明)へ

  feasible達成に必要な条件(実験で確認済み):
    ○ ソルバー(CP-SAT, OR-Tools)→ 制約伝播+バックトラッキング
    ○ SWO + IFS → 反復フィードバック+バックトラック(ソルバー不要)
    ○ SWO + Ejection Chain → 反復フィードバック+連鎖交換(ソルバー不要)
    × 単純な貪欲法 → 900回試して0回feasible(地平線効果)
    × SA/GA単体 → 局所探索だけでは制約の相互作用を解消できない
    × FunSearch/EoH → スコアは上がるがfeasibilityには不向き

  「ソルバーなしで解きたい」場合:
    SWO(問題箇所の反復修正)+ IFS(バックトラック)の組合せが有効。
    ドメイン知識を組み込むとさらに効果的。
    (例: 訓練ペアリング制約 → 熟練者を先に配置するルール)

エラー類型C: 解けるがスコアが低い

→ 目的関数と評価関数のズレを疑う
  1. 評価関数のコードを精読
  2. ソルバーの目的関数と評価関数の差分を特定
  3. 目的関数を評価関数に精密一致させる
  → これだけで+15-27%改善した実績あり

エラー類型D: 解けるが一部が極端に悪い

→ 悪い部分を特定して局所的に再最適化
  1. 解の品質分布を可視化(ルートごと/日ごと/人ごと)
  2. 最悪の部分を特定
  3. その部分だけ固定解除して再最適化

2. 改善策の実装と実行

- 改善策をコードに落とす
- ベースラインと同じ評価器で測定
- Before/After比較テーブルを生成

3. スコアをさらに上げたい場合(LLMヒューリスティック進化)

ソルバーで解けてfeasibleだが、もっとスコアを上げたい場合に使う。

Step 1: 目的関数の精密一致(最優先、これだけで+15-27%)

評価関数のコードを精読 → ソルバーの目的関数との差分を特定
→ 目的関数を評価関数と完全に一致させる
→ これだけで大幅改善する。他の手法より先にやること。

Step 2: LLMにカスタムヒューリスティクスを進化させる

ソルバーの解をベースに、LLMがスコア関数やムーブを自動設計する。

  RoCo方式: 4つの役割(探索者/改良者/批評者/統合者)で協調設計
    → 多様なアイデアが出やすい

  EoH方式: 複数の戦略を同時に進化させ、最良を選択
    → 「どの順序で埋めるか」等の戦略レベルの改善に有効

  ReEvo方式: 実行→失敗分析→改善を3ラウンド繰り返す
    → 「なぜダメだったか」の反省が的確な改善を生む

  FunSearch方式: スコア関数の集団を生成→評価→選択→進化
    → スコア関数の微調整に向いている

使い分け:
  ├── スコア関数を改善したい → FunSearch / RoCo
  ├── 割当の戦略そのものを変えたい → EoH
  └── 何が悪いか分析してから直したい → ReEvo

注意:
  - ソフト制約のスコア改善には強い
  - Hard制約のfeasibility達成には不向き(ソルバーを使うべき)
  - 最も効果的な使い方: ソルバーの解をベースにLLMが微調整

4. ソフト制約のバリエーション生成とスコア比較

ソフト制約の重み配分を変えた複数のバリエーションを生成し、数値スコアで比較する。 顧客に「どの方針で行くか」を選んでもらうための材料。

手順:
  1. ソフト制約ごとに「重視するバリエーション」を作る
     例: A=バランス型、B=公平性重視、C=夜勤制限重視、D=最低時間確保重視

  2. 各バリエーションを同じソルバーで解く(HC は共通、SC の重みだけ変える)

  3. 全ソフト制約を 0-100 のスコアで評価する
     - 100 = 完全遵守
     - スコアの計算方法は問題に応じて定義(遵守率、標準偏差ベース等)

  4. 比較テーブルを出力する

     | バリエーション | 充足率 | 公平性 | 夜勤制限 | 最低時間 | 総合 |
     |--------------|--------|--------|---------|---------|------|
     | A バランス型  | 95.8   | 69.6   | 100.0   | 100.0   | 92.7 |
     | B 公平性重視  | 95.8   | 82.1   | 80.0    | 90.0    | 89.3 |
     | C 夜勤制限重視 | 95.8   | 55.0   | 100.0   | 100.0   | 90.1 |

  5. トレードオフを説明する
     「公平性を上げると夜勤制限が犠牲になる」等

注意:
  - 問題が小さい/制約がきつい場合、全バリエーションが同じ解になることがある
    → これ自体が発見:「トレードオフの余地がないほど余裕がない」
  - バリエーションは 3-5 個が適切。多すぎると選べない
  - 総合スコアの重みは顧客と合意して決める

出力ファイル: scripts/variants.py + results/variant_results.json

5. 追加データの必要性判断

改善策を試した結果:
  ├── 解けた → /opt-report で報告書作成
  ├── 改善したがまだ不十分 → もう1周回す
  └── 行き詰まった → 追加データが必要
        │
        何が足りないか:
        ├── 制約の詳細(「本当にこの制約は必須?緩和可能?」)
        ├── 実績データ(「実際にどれくらい時間かかってる?」)
        ├── 優先度(「どの指標が一番大事?」)
        └── ドメイン知識(「現場の人はどうやってる?」)

        → 追加依頼の文面を生成

4. 仕様書(spec.md)の更新

改善の過程で制約や仮定に変更があった場合、必ずバージョンフォルダ内の spec.md を更新すること。

  • 仮定が実データで置き換わった → 仮定テーブルの値と検証状況を更新
  • 制約がハード→ソフトに変わった → 制約テーブルを移動
  • 新しい制約が判明した → 追加
  • 「前バージョンからの変更点」セクションに記録する
  • 大きな変更(追加データ・制約の根本変更)があった場合は新バージョンフォルダを作る

5. QA — spec.md とコードの整合性チェック

spec.md を更新したら、必ずこのチェックを実行すること。 仕様変更がコードに反映されていないと、結果が仕様と矛盾する。

最重要: 独立な HC 検証器を必ず書く

ソルバーの FEASIBLE フラグを信じてはいけない。 ソフト化した制約(penalty 化や 5→4 緩和など)を入れると、ソルバーは「緩和後のモデルで解を見つけた」を FEASIBLE と報告するが、元の spec.md の HC を満たしているとは限らない。

必ず以下の独立検証器を実装すること:

def verify_hard_constraints(solver, vars_d, data):
    """Re-check HC1..HCN independently from the raw assignment.
    
    Does NOT rely on the model's internal satisfiability.
    Reads solver.Value(x[...]) and checks each HC from scratch.
    """
    violations = {f"HC{i}": [] for i in range(1, N+1)}
    # ... HC1: demand met? HC2: max hours? ... HCn: pair constraints?
    return {
        "all_satisfied": all(len(v) == 0 for v in violations.values()),
        "total_violations": sum(len(v) for v in violations.values()),
        "by_constraint": {hc: len(lst) for hc, lst in violations.items()},
    }

各シナリオの結果には必ず以下を記録する:

  • solver_status (ソルバーの内部ステータス)
  • hc_all_satisfied (独立検証の結果)
  • hc_total_violations (違反総数)
  • hc_violations_by_constraint (HC別の違反内訳)

レポートには hc_all_satisfied を主として表示し、「ソルバーが解を返した」と「元の HC を全て満たした」を区別すること。 ソフト化シナリオ(HC1 soft, HC8 soft など)は、原則として hc_all_satisfied = false になる(緩和した HC が違反する)。

■ チェック1: ハード制約の完全一致
  spec.md の「ハード制約」テーブルの全項目が、ソルバーコードに制約として実装されているか?
  
  手順:
    1. spec.md から HC1, HC2, ... を列挙する
    2. scripts/ 内の全 .py ファイルを検索し、各 HC が model.Add() で実装されているか確認
    3. コードにあるが spec にない制約がないか(逆方向チェック)
  
  よくあるミス:
    - spec で HC→SC に変更したのに、コードがまだ model.Add() のまま(ペナルティになっていない)
    - 新しい HC を spec に追加したのに、コードに未反映

■ チェック2: ソフト制約と目的関数の対応
  spec.md の「ソフト制約」テーブルの全項目が、目的関数のペナルティ項に含まれているか?
  
  手順:
    1. spec.md から SC1, SC2, ... を列挙する
    2. model.Minimize() の中身を確認し、各 SC に対応するペナルティ項があるか
    3. 重みの大小関係が spec の「重み」列と矛盾していないか
  
  よくあるミス:
    - SC を追加したのに目的関数に項がない
    - 「重み: 高」なのに実際の係数が小さい

■ チェック3: 仮定の値の一致
  spec.md の「仮定」テーブルの値が、コード内の定数と一致しているか?
  
  手順:
    1. spec.md から仮定(A1, A2, ...)の値を列挙する
    2. コード内の対応する定数・パラメータと突合する
  
  よくあるミス:
    - spec で移動速度を 30→22 km/h に更新したのに、コードが 30 のまま
    - 作業時間の仮定を変えたのに評価関数の定数が古い

■ チェック4: 評価関数と目的関数の一致
  evaluate() 関数が計算するスコアと、ソルバーの目的関数が最適化する対象が一致しているか?
  
  手順:
    1. evaluate() が使う項目を列挙
    2. model.Minimize() が使う項目を列挙
    3. 差分がないか確認
  
  よくあるミス:
    - evaluate() に新しい SC を追加したのに、ソルバー側に未反映(逆も同様)
    - ペナルティの計算式が evaluate() と model で異なる

■ チェック5: 仕様書内の矛盾検出 spec.md 内で制約同士が矛盾していないか?情報源が古くなっていないか?

手順: 1. ハード制約同士の矛盾: HC-A と HC-B が同時に満たせない場合がないか 例: 「全員週40h以上」と「1日1シフトのみ」が10人×21シフトで矛盾 2. ハード制約とソフト制約の矛盾: HC が SC を無意味にしていないか 例: HC「夜勤禁止」と SC「夜勤週2回以下」が共存(SCが不要) 3. 仮定と制約の矛盾: 仮定の値が制約と整合しているか 例: 「作業時間10分」の仮定なのに「1件30分」の制約がある 4. 情報源の鮮度: Ref の取得日が古い項目はないか 例: 3ヶ月前のヒアリング結果を根拠にした制約が、その後変わっていないか

矛盾や古い情報が見つかった場合: → 確認型質問書(/opt-request 形式)を生成して依頼者に確認する → 「R3(2026-04-01 ヒアリング)で『夜勤2名必須』とありましたが、 R5(2026-04-03 メール)では『1名でも可』と読めます。 現在の正はどちらですか? A) 2名必須(R3 が正) B) 1名でも可(R5 が正) C) 条件による(___)」


**チェック結果は `reports/improve_report.md` の QA セクションに記録すること。**
**矛盾や確認が必要な項目があれば、確認型質問書を `reports/data_request.md` に追記すること。**

### 6. 出力

**以下のMarkdownをバージョンフォルダ内の `reports/improve_report.md` に保存すること。** 改善スクリプトは `scripts/` に、数値結果は `results/` に保存する。

```markdown
## 改善結果

| 手法 | Feasible | 違反数 | スコア | vs ベースライン |
|------|----------|--------|--------|----------------|
| ベースライン(ソルバー) | ... | ... | ... | 基準 |
| 改善策A | ... | ... | ... | +XX% |
| 改善策B | ... | ... | ... | +XX% |

## 何が効いたか
- [最も効果的だった改善と、なぜ効いたかの説明]

## ソフト制約バリエーション比較

| バリエーション | 充足率 | SC1 | SC2 | SC3 | SC4 | SC5 | 総合 |
|--------------|--------|-----|-----|-----|-----|-----|------|
| A バランス型  | ... | ... | ... | ... | ... | ... | ... |
| B ○○重視     | ... | ... | ... | ... | ... | ... | ... |
| C ○○重視     | ... | ... | ... | ... | ... | ... | ... |

(各スコアは 0-100。100 = 完全遵守)

### トレードオフの説明
- [「公平性を上げると夜勤制限が犠牲になる」等]
- [全バリエーションが同じ場合: 「トレードオフの余地がないほど制約がきつい」]

## まだ解決していない課題
- [残っている違反/スコア不足の原因]

## 追加で必要な情報
- [ ] [具体的に何が欲しいか]

## QA チェック結果

| チェック項目 | 結果 | 備考 |
|-------------|------|------|
| HC の spec↔コード一致 | ✓ / ✗ | [不一致があれば詳細] |
| SC の spec↔目的関数一致 | ✓ / ✗ | |
| 仮定値の spec↔コード一致 | ✓ / ✗ | |
| 評価関数↔目的関数一致 | ✓ / ✗ | |
| 仕様書内の矛盾 | ✓ / ✗ | [矛盾があれば詳細] |
| 情報源の鮮度 | ✓ / ✗ | [古い Ref があれば詳細] |

## 次のステップ
→ もう1周改善する or /opt-report で報告書

状態管理

読み込み

  • バージョンフォルダ内の .opt_state.yaml の baseline セクションを読み込む
  • ボトルネック、改善余地、ベースラインスコアを引き継ぐ

書き込み

  • 実行完了時にバージョンフォルダ内の .opt_state.yaml の improve セクションを書き込む
  • iterations に各改善試行の結果を追記(上書きではなく追記)
  • best に最良結果を記録
  • スキーマは reference/state_schema.md を参照

トラブルシューティング

改善策を試しても全くスコアが上がらない

原因と対策:
  ├── 目的関数と評価関数のズレ → パターン1(最優先で確認)
  │   evaluator のコードを精読し、ソルバー目的関数との差分を特定
  ├── ボトルネックの特定が間違っている → /opt-baseline に戻って再分析
  ├── 問題の分解方向が間違っている
  │   → 悪い分解の例: 「いつ」と「何を」を分離してスキル制約を破壊
  │   → 違反の原因を分析して、原因を解消する方向で分解する
  └── 下界に近い → 思考回路⑥で改善余地を確認。余地がなければこれが限界

改善でスコアは上がるが制約違反が増える

原因と対策:
  ├── 目的関数にハード制約のペナルティが不足
  │   → ハード制約違反のペナルティを10倍にする
  ├── ソフト制約の改善がハード制約を破壊している
  │   → パターン6: まず feasibility だけ追求 → 次に品質改善
  └── 制約間の相互作用が複雑
      → SWO+IFS(パターン4)やソルバー修復(パターン5)を検討

LLMヒューリスティック進化が収束しない

原因と対策:
  ├── 評価関数にノイズがある → 同じ入力で複数回評価して平均を取る
  ├── 探索空間が広すぎる → 1つのパラメータだけ変えて効果を確認
  ├── 進化の方向がランダム → ReEvo方式で「なぜダメだったか」の分析を挟む
  └── そもそもLLM進化が向かないケース
      → feasibility問題にはソルバーを使う(LLM進化はソフト制約向き)

計算時間が長すぎる

原因と対策:
  ├── 問題規模が大きい → 分解する(パターン3: Cluster+ソルバー)
  ├── 制約が多すぎる → 不要な制約を削減(本当に必要か再確認)
  ├── 対称性がある → 対称性除去制約を追加(ソルバーの探索を効率化)
  └── 時間制限内に良い解が欲しい
      → add_hint() で初期解を与える(warm start)
      → まず短時間で feasible を見つけ、残り時間で改善

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

opt-assess

無料日本語概要

受け取ったデータから最適化問題の構造を把握し、仮説を立てる。最も重要なスキル。

sugupoko/mathematical_optimizer_skill42026年6月7日 更新

opt-baseline

無料日本語概要

実行可能性(feasibility)を確認する。問題の複雑度に応じて一発解きor段階的解法を選ぶ

sugupoko/mathematical_optimizer_skill42026年6月7日 更新

opt-deploy

無料日本語概要

最適化モデルの運用設計。自動化・監視・フォールバックを定義する

sugupoko/mathematical_optimizer_skill42026年6月7日 更新

opt-report

無料日本語概要

最適化結果を非技術者にも伝わる改善提案書にまとめる

sugupoko/mathematical_optimizer_skill42026年6月7日 更新

opt-request

無料日本語概要

追加データの依頼書を生成する。何がなぜ必要かを非技術者向けに説明

sugupoko/mathematical_optimizer_skill42026年6月7日 更新

sugupoko のスキルをすべて見る

このスキルの問題を報告する