← 一覧に戻る

AI秘書が「ちゃんと終わったか」を確認する仕組み

2026年3月31日 13:29 更新
MD から自動変換されたページです。内容について質問があれば右下の ? ボタンからどうぞ。

作成日: 2026-03-31 改訂日: 2026-03-31(v3: SQLite移行・手順遵守の強制メカニズム・BACKLOGステータス追加) 調査方法: Codex (GPT-5.4) + Claude Opus WebSearch の並行調査 対象: Telegram秘書(Claude Code)→ 作業セッション dispatch(指示を送って作業させる)構成

この文書は何か

あなたが普段やっていること:

  1. Telegramで秘書に「〇〇やっておいて」と指示を出す
  2. 秘書がPCの作業セッションに指示を転送する(これを「dispatch」と呼ぶ)
  3. 作業セッションが終わったら ready と返ってくる
  4. 秘書が「完了しました」と報告する

問題: 秘書は「作業セッションが終わった」ことは確認しているが、「元の指示の意図を満たしているか」は確認していない。

この文書は、「秘書がちゃんと検証してから完了報告する仕組み」の設計書。


今のやり方の何がマズいのか

今の秘書のジョブ管理はこうなっている:

PENDING(未着手)→ SENT(送信済み)→ DONE(完了)/ FAILED(失敗)/ BLOCKED(止まっている)

DONEにする条件が「作業セッションから ready が返ってきた」だけ。 「指示通りにできたか」の確認がゼロ


基本方針: 作業者の自己申告を鵜呑みにしない

Codex (GPT-5.4) の調査で出た核心:

作業セッションを「自分で判断して動く賢い存在」として信頼するより、 「言われたことをやる作業者」として扱い、秘書が検証と制御の責任を持つ方が安定する。

つまり、作業セッションが「できました」と言っても、秘書が「本当にできてるか」を自分で確認する。


他のAIシステムはどうやっているか

世の中のAI管理ツール(フレームワーク = AIの動きを制御するための仕組み)を調査した。 共通して言えることは:

調査したツール4つの比較

LangGraph(ラングラフ)— 状態の流れを図で管理するツール

何をするか 具体的な仕組み
全体の管理 作業の流れを「状態の図」として定義する。「今どの状態か」「次にどこに進むか」を明示的に書く
状態の記録 全員が共有する状態メモがあり、各作業者が読み書きする。任意の時点の状態を保存(チェックポイント)でき、そこからやり直せる
作業の割り振り 図の上で「次はこの作業者」と明示的に制御する。「監督役が次の担当を選ぶ」パターンも組める
失敗への対応 作業者ごとに「失敗したら何回やり直すか」を設定できる。チェックポイントから途中再開も可能
確認の仕組み 「ここで一旦止めて人間に確認する」という中断命令がある。検証用の作業者が出力をチェックして、ダメなら差し戻す

秘書との相性: ◎ 一番近い。 「作業の流れを図で管理して、途中で検証して、ダメなら戻す」という考え方が、秘書の「指示を送る → 確認する → 足りなければ追加指示する」と一致する。

CrewAI(クルーAI)— 業務フローを素早く組むツール

何をするか 具体的な仕組み
全体の管理 「このイベントが起きたら次にこれをやる」という流れで動く
状態の記録 状態をデータベースに自動保存できる
作業の割り振り チーム単位で作業を呼び出す。「マネージャー役が作業を割り振る」パターンも選べる
確認の仕組み 作業ごとに「出力のチェック関数」(ガードレール = 品質の番人)を設定できる。チェックに引っかかったら自動でやり直す
人間への確認 「ここで人間に聞く」という処理を挟める

秘書との相性: △ 業務アプリを素早く作るには向いているが、秘書のようにtmuxで作業セッションを制御する用途には合わない。ただし「作業ごとにチェック関数を付ける」という発想は参考になる。

AutoGen(オートジェン)— AI同士が会話して協力するツール

何をするか 具体的な仕組み
全体の管理 AI同士がメッセージをやり取りして協力する方式。「毎ターン誰が発言するか」を制御する
状態の記録 会話の履歴が暗黙の状態記録になる(明示的な状態メモはない)
作業の割り振り AIが次の発言者を選ぶ方式や、明示的にバトンタッチする方式がある
確認の仕組み 「特定のキーワードが出たら終了」という終了条件や、「ツール実行前に人間が承認/拒否する」仕組みがある
品質チェック 作業者とは別の「批評家AI」が品質をチェックするループを組める

秘書との相性: △ AI同士の会話には向いているが、秘書のケースでは「会話」より「ジョブの管理」が主役。「作業者とは別のAIが検証する」という発想は取り入れられる。

Semantic Kernel(セマンティックカーネル)— Microsoftが作った基盤ツール

何をするか 具体的な仕組み
全体の管理 作業ステップとイベントを配線して動かす。順番に実行、同時に実行、引き継ぎなど複数のパターンを提供
状態の記録 ステップごとに状態を持つ。実行ログも取れる
確認の仕組み 人間を挟むループ、マネージャーが全体を制御する方式がある

秘書との相性: △ 考え方は良いが、まだ実験段階の機能が多い。Microsoft系のサービス(Azure, Teams等)を使うなら選択肢に入る。

結論

秘書のtmux + Telegram + Claude Code構成では、既存ツールをそのまま使うより、LangGraphの考え方(状態の保存と復元、明示的な状態遷移、検証ステップ)を自分たちの仕組みに取り入れるのが最適。


計画 → 実行 → 検証 → 再計画: 秘書での具体的な進め方

元になった研究

VMAO(2026年3月の論文)が「計画 → 実行 → 検証 → 再計画」の4段ループを検証し、検証ステップを入れることで完了度が5段階中3.1→4.2に改善したことを示した。

秘書での4段ループ

第1段: 計画 — 指示を「完了条件付きの作業指示書」に変換する

今まで検証できなかった根本原因は、最初の指示が曖昧なまま作業に流れていたこと。 だから最初に「何ができたら完了か」を明確にする。

あなたの入力: 「コールリマインダーの課金をパック制に変えて」
         ↓ 秘書が以下のように整理する

【意図の整理】
  やりたいこと: Stripe連携をサブスクリプションから回数パック制に変更
  制約:
    - Payment Link方式で実装
    - 既存ユーザーのデータを壊さない
  やらないこと:
    - UI変更は今回のスコープ外
  完了条件(何ができたら終わりか):
    - パック購入のPayment Linkが生成できる
    - 購入後にコール回数が加算される
    - テストが通る
  証拠として必要なもの:
    - Payment LinkのURL
    - テスト実行結果
  あなたに聞かないと決められないこと:
    - パック価格(500円/1000円/2000円のどれか)

「あなたに聞かないと決められないこと」がある場合、作業を始めずにまず聞く(WAIT状態にする)。

この整理をもとに、作業セッションに渡す「作業指示書」を作る:

【作業指示書】
  作業内容: "Stripe Payment Link方式でパック購入を実装"
  期待する成果物: "Payment Link生成API + 回数加算Webhook"
  確認項目:
    - "tsc --noEmit(型チェック)がエラー0"
    - "vitest(テスト)が全パス"
    - "Payment Link URLが返る"
  制限時間: 60分
  リスク: 中(Stripe本番に影響しうる)

第2段: 実行 — 作業セッションに指示を渡す

作業セッションには曖昧な自然文ではなく、上の作業指示書をそのまま渡す。 作業セッションが終わったら、以下の情報を秘書が取得する:

作業セッションから取得する情報:
  何をしたか:         1-2行のまとめ
  作成/変更したファイル: ファイルのリスト
  実行したコマンド:    コマンドと結果
  作業者自身の評価:    参考値(これだけでは信用しない)
  作業中に出た疑問:    未解決の疑問

重要: 作業者自身の「できました」は参考値に過ぎない。本当にできたかは秘書が確認する。

第3段: 検証 — 「完了条件を1つずつチェックする」

検証は3段階で行う:

第1層: 自動チェック(機械的に確認できること)
  - ファイルが存在するか
  - コマンドがエラーなく完了したか
  - テストは通ったか
  → 自動で合格/不合格が出る

第2層: 条件チェック(完了条件との照合)
  - 完了条件を1つずつ確認する
  - 制約に違反していないか
  - 「やらないこと」に手を出していないか
  → 合格 / 一部未達 / 不合格

第3層: 意図チェック(元の指示の趣旨との照合)
  - 元の「やりたいこと」と実際の成果物を照らし合わせる
  - 「やったこと」が「やりたかったこと」と一致しているか
  - AIが審査役として判定する
  → 合格 / 一部未達 / 不合格 / あなたの判断が必要

検証結果のまとめ:

【検証レポート】
  結果: 合格 / 一部未達 / 不合格 / あなたの判断が必要
  完了率: 80%(完了条件3つ中2つを満たした)
  合格した項目: ["テスト通過", "Payment Link URL生成確認"]
  不合格の項目: ["回数加算Webhookの実装が未完了"]
  満たされていない意図: ["購入後にコール回数が加算される が未達"]
  リスク: ["Stripe本番キーがハードコードされている"]
  次のアクション: "Webhook実装を追加指示として再送信"

第4段: 再計画 — 全部やり直しではなく「足りない部分だけ」追加する

一部未達や不合格の場合、足りない部分だけを「差分チケット」として再計画する:

【差分チケット】
  足りないこと: "購入後のコール回数加算が未実装"
  元の意図: "パック購入 → 回数加算"
  できている部分: "Payment Link生成は完了"
  追加で必要な作業: "Stripe Webhook受信 → DB更新ロジック"
  優先度: 高

「最初からやり直せ」ではなく「ここだけ足りない」と伝える。 作業セッションに全部の情報を再送しない。

4段ループの全体の流れ

  あなたの入力(Telegram)
       │
  ┌────▼─────────┐
  │  計画         │  入力 → 意図の整理 → 作業指示書
  │              │  あなたに聞くことがある? → 聞く(WAIT)
  └────┬─────────┘
       │
  ┌────▼─────────┐
  │  実行         │  作業指示書 → 作業セッションに送信 → 作業者が実行
  │              │  制限時間超過 → 失敗
  └────┬─────────┘
       │ ready(作業完了の合図)
  ┌────▼─────────┐
  │  検証         │  第1層: 自動チェック → 第2層: 条件チェック → 第3層: 意図チェック
  │              │
  └────┬─────────┘
       │
   ┌───┴────┐
   │        │
  合格    一部未達/不合格
   │        │
  完了   ┌──▼──────┐     あなたの判断が必要
   │    │ 再計画    │          │
   │    └──┬──────┘        WAIT(聞く)
   │       │
   │   差分チケット
   │       │
   │  ┌────▼─────────┐
   │  │  再実行       │  足りない部分だけ再送信(最大1回)
   │  └────┬─────────┘
   │       │
   └───────┘

テスト駆動開発の考え方を取り入れる

テスト駆動開発(TDD = テストを先に書いてから実装する開発手法)の考え方は、秘書の検証にそのまま使える。

TDDでは:

TDDと秘書の検証の対応

TDDでやること 秘書の検証でやること 具体的にどうなるか
失敗するテストを先に書く 完了条件を先に決める 作業を始める前に「何ができたら完了か」を定義する。これがないタスクは着手しない。「修正して」→ ダメ。「lintエラー0、テスト通過、説明文追加」→ OK
テストを通す最小限のコードを書く 検証を通る最小限の成果物をまず取る 完璧な最終版ではなく、完了条件を満たす最小の成果物を先に取る。「まずテスト通す」→「次に説明文を整える」と段階的に進める
テストが通った状態で構造を改善する 検証の仕組み自体を改善する 失敗した検証の理由を蓄積して、次回の計画フェーズに反映する

完了条件を先に決める(テストファースト → 完了条件ファースト)

TDDで「テストを先に書く」のと同じように、タスクを実行する前に「何なら合格か」を先に書く

TDD:
  1. テストを書く(まだ実装していないので当然失敗する)
  2. テストが失敗することを確認する
  3. 実装する(テストが通るようにする)

秘書の検証:
  1. 完了条件を書く
  2. 今はそれを満たしていないことを確認する(当然満たさない)
  3. 作業セッションに送って実行する(完了条件を満たすことを目指す)

完了条件のない仕事は着手しない。 これが最も重要な原則。 「やっておいて」では検証できない。「何ができたら完了か」が言葉になっていることが前提。

最小限で通す

TDDでは「テストを通す最小限のコード」を書く。 秘書の検証でも同じ: 完了条件を満たす最小の成果物を先に取る。

悪い進め方: 「全部完璧にしてから報告」
  → 時間がかかる、途中で方向性がズレても気づけない

良い進め方: 「まず完了条件の1つ目を通す → 検証 → 次へ」
  → 早い段階でフィードバックが得られる、ズレに早く気づける

仕組み自体を改善する

TDDでは「テストが通った状態を維持しながら構造を改善する」。 秘書の検証でも、検証の仕組み自体を改善する

最終的な成果物だけでなく、検証の過程こそが大事:

蓄積すべき情報:
  - どの完了条件で判定したか
  - どの差分チケットで差し戻したか
  - 何回の再計画で収束したか
  - どの種類の依頼が「あなたに聞く」に落ちやすいか
  - よくある検証失敗パターン:
    - 指示が曖昧で作業者が迷走した
    - テスト結果を返し忘れた
    - 入力が複数の解釈ができるものだった

秘書の改善対象を「回答の品質」から「検証の仕組みの品質」に広げる。そこが本体。

TDDの「テストリスト」= 秘書の「完了条件リスト」

TDDでは、最初に「テストすべき項目をリストにまとめる」。 秘書の検証でも同じ:

1. あなたの入力から完了条件のリストを洗い出す
2. リストから「ひとつだけ」選び、作業指示書に変換して作業セッションに送る
3. 検証で合格を確認する(気づきは完了条件リストに追加)
4. 必要に応じて再計画(改善に相当)
5. リストが空になるまで 2 に戻る

ジョブの状態遷移(WAIT + BACKLOG追加版)

状態の流れ

  BACKLOG(いつかやる)
    │  ← Phase 3等の未着手項目はここに入る
    │  ← ユーザーが「やる」と決めたら PENDING に昇格
    ▼
  PENDING(未着手)
    │
    ▼
  SENT(送信済み)──────────────────────────┐
    │                                       │
    ▼                                       │
  IN_PROGRESS(作業中)                      │
    │                                       │
    ▼                                       │
  VERIFYING(検証中)                        │
    │                                       │
    ├── 完了条件100%達成 ──→ DONE(完了)     │
    │                                       │
    ├── 一部未達/不合格 ──→ RETRY(再送信)──┘
    │       (差分チケット付き、最大1回)
    │
    ├── あなたの判断が必要 ──→ WAIT(あなたに聞く)
    │
    └── 回復不能 ──→ FAILED(失敗)
                       │
  WAIT(あなたに聞いている状態)
    │                  │
    ├── 承認 ──→ DONE  │
    │                  │
    ├── 差し戻し(追加指示付き)──→ SENT(再送信)
    │                  │
    ├── 拒否/中止 ──→ FAILED
    │
    ├── 時間切れ(リスク低)──→ DONE(できた分で完了扱い)
    │
    ├── 時間切れ(リスク高)──→ FAILED
    │
    └── 時間切れ(業務系)──→ EXPIRED(期限切れ。リマインド → 再確認)

  BLOCKED(外部の都合で止まっている)──→ SENT(解消後)/ FAILED

BACKLOGステータスの設計

WAITでもPENDINGでもない理由:

ステータス 意味 BACKLOGとの違い
WAIT あなたの判断を待っている。質問が1つある BACKLOGには具体的な質問がない
PENDING やることが決まっていて、あとは送信するだけ BACKLOGはまだ「やるかどうか」が未定
BACKLOG 「いつかやる」候補。やるかどうか・いつやるかは未定

BACKLOGの運用ルール:

  1. Phase 3等の「まだいらない」項目はBACKLOGに入れる
  2. BACKLOGから自動で実行しない。ユーザーが「これやって」と言ったらPENDINGに昇格
  3. 定期棚卸し: 週1回、BACKLOGの項目をTelegramで一覧提示して「やる/やらない/削除」を聞く
  4. 完了条件は仮で書いておく(昇格時に確定する)

BACKLOGの記録形式:

{
  "id": "j-100",
  "status": "BACKLOG",
  "message": "失敗パターンの蓄積 → 検証手順の改善",
  "intent": "検証の失敗パターンをDBに蓄積し、次回の計画フェーズに自動反映する仕組み",
  "doneWhen": ["(昇格時に確定)"],
  "phase": "Phase 3",
  "priority": "low",
  "created_at": "2026-03-31T00:00:00Z"
}

WAITの詳細

WAITは「止まっている」のではなく「あなたに判断を返している状態」。 完了ではないが、あなたの返答で再開できる。

WAITになる条件(これだけに限定する)

条件 今のシステムだとこういう場面
複数の正解がありあなたの好みで決めるしかない 「パック価格を500円/1000円/2000円のどれにする?」
外部に影響する操作の実行前に許可が必要 「Stripe本番にPayment Linkを作成してよい?」
検証したが判断がつかない 「テストは通ったが、非エンジニア向け説明が元の意図に含まれるか判断できない」
元の指示が曖昧で安全に補えない 「"いい感じにして" の "いい感じ" が特定できない」
時間切れだが途中まではできた 「3件中2件は完了。残り1件はAPI制限で実行できなかった」

WAITの記録形式

{
  "id": "j-042",
  "status": "WAIT",
  "wait": {
    "reason": "choice(選択が必要)",
    "question": "パック価格をどれにする?",
    "options": [
      "500円(5コール)← 試しやすい",
      "1000円(12コール)← 最もお得(推奨)",
      "2000円(30コール)← ヘビーユーザー向け"
    ],
    "recommendedOption": "1000円(12コール)",
    "contextSnapshotId": "snap-042-a(作業途中の状態を保存したID。再開時にここから続ける)",
    "expiresAt": "2026-04-01T12:00:00Z",
    "onTimeout": "remind(時間切れならリマインドを送る)"
  }
}

WAITの運用ルール

  1. WAITは必ず「質問1つ」に圧縮する。 長い説明だけ送らない
  2. おすすめの選択肢を必ず提示する。 あなたの判断負荷を下げる
  3. WAIT再開時は最初から全部説明し直さない。 保存した状態から再開して、足りない指示だけ追加する
  4. WAIT中のジョブを完了にしない。 あなたの判断待ちは作業未完了
  5. 時間切れ時はまずリマインドする。 自動で失敗にするのは外部に影響する操作だけ

完了条件をタスクに持たせるパターン

全てのAI管理ツールに共通する効果的な整理:

やりたいこと:      何を達成したいか(あなたの元の指示を1行にまとめたもの)
完了条件:          何なら完了か(検証可能な条件のリスト = TDDの「テストリスト」に相当)
返し方:            どういう形で結果を返すか

CrewAI式の書き方

Task(
    description="...",           # やりたいこと
    expected_output="...",       # 完了条件(自然言語)
    output_pydantic=Model,       # 返し方(データの型)
    guardrail=validate_fn,       # チェック関数
    guardrail_max_retries=2,     # やり直し上限
)

LangGraph式の書き方

state = {
    "task": "...",
    "criteria": ["5件以上", "各項目にURL含む"],  # 完了条件
    "draft": None,
    "feedback": None,
    "attempts": 0,
    "ok": False,
}

「手順を書いても守れない」問題と強制メカニズム

問題の本質

2026-03-31の失敗: prompt.mdに「検証しろ」と書いてあったのに、秘書は検証せずに完了報告した。

「手順を書く」と「手順を守る」は別物。 自然言語で書いた手順は「お願い」であって「強制」ではない。LLMは指示を理解はしているが、コンテキストが長くなると優先順位が下がったり、省略したりする。

Codex (GPT-5.4) に相談した結果、手順を守らせる強さは以下の順:

弱い  ←──────────────────────────────────→  強い
prompt.md   構造化プロンプト   hook検査   hook+ステートマシン
 だけ       +報告テンプレ     (実行点     (状態遷移で
              ート固定        でブロック)   順番を管理)

秘書で採用する強制メカニズム(3層防御)

第1層: 構造化プロンプト — 「何を出せ」を固定する

prompt.mdの指示を曖昧な自然文から構造化する。「やってね」ではなく「この形で出せ」。

悪い例(今の書き方):

作業が終わったら検証してから報告してください。

良い例(構造化):

<completion_contract>
  <workflow>
    1. tmux readで作業結果を取得する
    2. doneWhenの各項目を1つずつ照合する
    3. 全項目の検証結果を記録する
    4. 以下のJSON形式で完了報告する
  </workflow>

  <required_report_format>
    {
      "job_id": "j-XXX",
      "task_status": "done|partial|failed|need_input",
      "checks": [
        {"condition": "完了条件の文言", "result": "pass|fail", "evidence": "確認した根拠"}
      ],
      "intent_match": "yes|no|unclear",
      "next_action": "none|retry|ask_user"
    }
  </required_report_format>

  <hard_rules>
    - checksが空のまま task_status を done にしてはならない
    - evidence が空のチェック項目は fail 扱い
    - intent_match が unclear なら task_status は need_input
  </hard_rules>
</completion_contract>

コツ: 「禁止」より「何を出せ」を書く。モデルは「やるな」より「こう出せ」の方に従いやすい。

第2層: 完了報告フォーマットに検証を埋め込む — 「検証しないと報告できない」

完了通知を自由文にしない。JSON固定にして、検証結果フィールドを必須にする。

{
  "job_id": "j-042",
  "task_status": "done",
  "checks": [
    {
      "condition": "パック購入のPayment Linkが生成できる",
      "result": "pass",
      "evidence": "Payment Link URL: https://buy.stripe.com/xxx を生成確認"
    },
    {
      "condition": "購入後にコール回数が加算される",
      "result": "pass",
      "evidence": "vitest webhook.test.ts 全パス(3/3)"
    },
    {
      "condition": "テストが通る",
      "result": "pass",
      "evidence": "vitest --run 結果: 12 passed, 0 failed"
    }
  ],
  "intent_match": "yes",
  "next_action": "none"
}

なぜ効くか: モデルは「最終出力の形」に引っ張られる。テンプレートにchecksフィールドがあると、「ここを埋めないと完成しない」という圧力がかかる。嘘を書いても result: passevidence: "" の矛盾で検出できる。

第3層: hook(フック = 自動チェック)による強制 — 「守らないと進めない」

Claude Codeのhookシステムで、ソフトウェア的に強制する。秘書がtmuxで動くClaude Codeセッションなので、hookが使える。

Stop hook — 検証なしの終了をブロック:

秘書が「完了しました」と言おうとした時、検証結果がDBに記録されていなければ終了を拒否する。

#!/bin/bash
# .claude/hooks/stop_guard.sh
# Stop hookで「検証なしの終了」を防ぐ
set -euo pipefail

# 最新のアクティブジョブを確認
ACTIVE_JOB=$(sqlite3 jobs.db "SELECT id FROM jobs WHERE status = 'VERIFYING' LIMIT 1")

if [ -n "$ACTIVE_JOB" ]; then
  # 検証結果が記録されているか確認
  CHECKS=$(sqlite3 jobs.db "SELECT COUNT(*) FROM verification_checks WHERE job_id = '$ACTIVE_JOB'")
  if [ "$CHECKS" -eq 0 ]; then
    cat <<JSON
{
  "decision": "block",
  "reason": "検証が未完了。doneWhenの各項目を確認してから完了報告しろ。job_id: $ACTIVE_JOB"
}
JSON
    exit 0
  fi
fi

PostToolUse hook — 完了報告の形式チェック:

Telegram送信前に、報告がJSON形式で検証結果を含んでいるか確認する。

設計上の注意:

SessionStart hookでルールを再注入する

セッション圧縮(compact)後にルールを忘れる問題の対策。毎セッション開始時にcompletion_contractを再注入する。

{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "cat .claude/secretary-contract.xml"
          }
        ]
      }
    ]
  }
}

まとめ: 3層防御の効果

第1層 構造化プロンプト:  「何を出すべきか」を明確にする(理解させる)
第2層 報告テンプレート:  「検証しないと報告が完成しない」(動機づける)
第3層 hook:             「検証なしでは終了できない」(物理的に止める)

全部必要ではない。Phase 1では第1層 + 第2層で始めて、効果を測定してからhookを追加する。


検証をどこまでやるか(段階的に足す)

段階1: 形式チェック   — 型が合ってるか、必須項目があるか           ← 必ずやる
段階2: 内容チェック   — 件数・URL形式・文字数・禁止語              ← プログラムで書ける
段階3: AIによる判定   — 文体・網羅性・妥当性(主観的な基準)        ← 必要な時だけ
段階4: 人間の承認     — 外部に影響する操作                        ← 本当に必要な時だけ

秘書のケースでは段階3が中心になる(作業セッションの出力は自由な文章なので、形式チェックだけでは判定しにくい)。


最小限の検証ループ(プログラムで書くとこうなる)

while attempts < max_attempts:
    result = worker(task, context)        # 作業者にやらせる
    verdict = verify(result, criteria)    # 検証する
    if verdict.ok:
        return DONE(result)              # 合格なら完了
    context["feedback"] = verdict.feedback # 不合格ならフィードバックを追加
    attempts += 1                         # やり直し回数を増やす
return FAIL(last_result, last_feedback)   # 上限に達したら失敗

重要な3つの原則:

  1. 作業者の出力は整理された形にする — 「何をしたか」「成果物」「まとめ」があれば十分
  2. まずプログラムでチェックし、AIの判定は後 — 自動でわかることは自動で
  3. やり直し回数は必ず制限する — 無限ループは仕組みの設計ミス

今ある改善案の評価

案1: 完了通知に元の指示(jobs.jsonのmessage)を埋め込む

評価: ○ 方向性は正しい。ただし単独では弱い

案2: 作業送信時にやりたいことと完了条件をjobs.jsonに記録

評価: ◎ これが最も効果的。全てのAI管理ツールの共通パターンと一致


改善の全体設計

おすすめ: 計画→実行→検証→再計画 + 完了条件ファースト

作業を送る時:
  1. あなたの入力 → 意図の整理(完了条件を先に決める)
  2. あなたに聞くことがあれば → WAIT
  3. 意図の整理 → 作業指示書に分解
  4. jobs.db にやりたいことと完了条件を記録
  5. 作業セッションに送信(完了条件を満たすことを目指す)

完了の合図を受け取った時:
  1. 「検証中」に遷移
  2. tmux read(画面の出力を読み取る)で作業結果を取得
  3. 検証3段階:
     a. 第1層: 自動チェック(テスト結果、ファイル存在等)
     b. 第2層: 条件チェック(完了条件をどれだけ満たしたか、制約違反がないか)
     c. 第3層: 意図チェック(元のやりたいこととの整合性)
  4. 結果に応じて:
     - 合格 → 完了 + Telegramで報告
     - 一部未達 → 差分チケット付きで再送信(最大1回)
     - 不合格 → 失敗 + Telegramで報告
     - 判断つかない → WAIT + Telegramで質問

再計画時(仕組みの改善に相当):
  - 失敗パターンを蓄積
  - 次回の計画に反映
  - 「検証の仕組みの品質」を継続的に改善

SQLiteへの移行(jobs.json → jobs.db)

なぜSQLiteに移行するか

jobs.jsonは現在1200行超。以下の問題がある:

問題 影響
読み書きが重い 1ジョブの更新でも全件読み込み→全件書き出しが必要
同時書き込み競合 秘書とdispatch-job.shが同時にファイルを触ると壊れる
クエリができない 「SENT状態のジョブだけ取得」に全件走査が必要
BACKLOGの追加で更に肥大化 Phase 3の項目も入ると管理不能

SQLiteなら:

テーブル設計

-- メインテーブル: ジョブ
CREATE TABLE jobs (
  id TEXT PRIMARY KEY,              -- "j-042"
  message TEXT NOT NULL,            -- 元の指示
  intent TEXT,                      -- やりたいこと(秘書が整理)
  session TEXT,                     -- 作業セッション名
  status TEXT NOT NULL DEFAULT 'PENDING',
    -- BACKLOG, PENDING, SENT, IN_PROGRESS, VERIFYING,
    -- DONE, FAILED, BLOCKED, WAIT, RETRY, EXPIRED
  phase TEXT,                       -- "Phase 1", "Phase 3" 等
  priority TEXT DEFAULT 'medium',   -- low, medium, high
  created_at TEXT NOT NULL,
  updated_at TEXT NOT NULL,
  sent_at TEXT,
  done_at TEXT
);

-- 完了条件: 1ジョブに複数の条件
CREATE TABLE done_when (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  job_id TEXT NOT NULL REFERENCES jobs(id),
  condition TEXT NOT NULL,          -- "テストが通る"
  sort_order INTEGER DEFAULT 0
);

-- 検証結果: 完了条件ごとの検証記録
CREATE TABLE verification_checks (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  job_id TEXT NOT NULL REFERENCES jobs(id),
  condition TEXT NOT NULL,          -- done_whenの文言
  result TEXT NOT NULL,             -- "pass", "fail"
  evidence TEXT,                    -- "vitest --run: 12 passed"
  checked_at TEXT NOT NULL
);

-- 検証サマリ
CREATE TABLE verification_summary (
  job_id TEXT PRIMARY KEY REFERENCES jobs(id),
  intent_satisfied TEXT,            -- "yes", "no", "unclear"
  result_summary TEXT,
  retry_count INTEGER DEFAULT 0,
  max_retries INTEGER DEFAULT 1
);

-- WAIT状態の詳細
CREATE TABLE wait_details (
  job_id TEXT PRIMARY KEY REFERENCES jobs(id),
  reason TEXT NOT NULL,             -- "choice", "permission", "unclear"
  question TEXT NOT NULL,
  options TEXT,                     -- JSON配列
  recommended_option TEXT,
  expires_at TEXT,
  on_timeout TEXT DEFAULT 'remind'  -- "remind", "fail", "done"
);

-- インデックス: よく使うクエリを高速化
CREATE INDEX idx_jobs_status ON jobs(status);
CREATE INDEX idx_jobs_session ON jobs(session);
CREATE INDEX idx_done_when_job ON done_when(job_id);
CREATE INDEX idx_checks_job ON verification_checks(job_id);

設計のポイント:

移行スクリプトの計画

#!/bin/bash
# migrate-jobs-to-sqlite.sh
# jobs.json → jobs.db への移行

# 1. バックアップ
cp jobs.json jobs.json.bak

# 2. SQLiteデータベース作成(上記スキーマ)
sqlite3 jobs.db < schema.sql

# 3. jqでJSONを読み、SQLに変換してインポート
# 各ジョブに対して:
#   - jobs テーブルにINSERT
#   - doneWhen があれば done_when テーブルにINSERT
#   - verification があれば verification_summary にINSERT
#   - wait があれば wait_details にINSERT

# 4. 件数の整合性チェック
# JSON件数 == DB件数 を確認

# 5. 元のjobs.jsonはリネームして保持(1週間後に削除)

dispatch-job.sh の書き換え方針

現在の dispatch-job.sh は jq で jobs.json を読み書きしている。以下の変更が必要:

変更前(jq + JSON):
  jq '.[] | select(.status == "SENT")' jobs.json
  jq '(.[] | select(.id == "j-042")).status = "DONE"' jobs.json > tmp && mv tmp jobs.json

変更後(sqlite3):
  sqlite3 jobs.db "SELECT * FROM jobs WHERE status = 'SENT'"
  sqlite3 jobs.db "UPDATE jobs SET status = 'DONE', done_at = datetime('now') WHERE id = 'j-042'"

段階的に移行する:

  1. まずSQLiteに読み書きする関数群(db_get_job, db_update_status, db_add_job等)をシェル関数で用意
  2. dispatch-job.shをリファクタして関数呼び出しに置き換え
  3. prompt.mdの参照先もjobs.json → jobs.dbに変更

jobs テーブルのレコード例

INSERT INTO jobs (id, message, intent, session, status, phase, priority, created_at, updated_at)
VALUES (
  'j-042',
  'コールリマインダーの課金をパック制に変えて',
  'Stripe連携をサブスクリプションから回数パック制に変更。Payment Link方式で実装',
  'line-call-reminder',
  'SENT',
  'Phase 1',
  'high',
  '2026-03-31T10:00:00Z',
  '2026-03-31T10:00:00Z'
);

INSERT INTO done_when (job_id, condition, sort_order) VALUES
  ('j-042', 'パック購入のPayment Linkが生成できる', 1),
  ('j-042', '購入後にコール回数が加算される', 2),
  ('j-042', 'テストが通る', 3);

検証の手順(秘書の行動指針に追加する文面)

<completion_contract>
  <!-- タスク完了検証(完了条件ファースト) -->

  <precondition>
    完了条件(done_when)のないタスクは作業に出さない。
    「何ができたら完了か」がDBに記録されていることが前提。
  </precondition>

  <workflow>
    1. ステータスを VERIFYING に遷移する
       UPDATE jobs SET status = 'VERIFYING' WHERE id = ?
    2. tmux read で作業結果を取得する
    3. done_when の各項目を1つずつ照合する
    4. 検証結果を verification_checks に記録する
       INSERT INTO verification_checks (job_id, condition, result, evidence, checked_at) VALUES (...)
    5. 以下のJSON形式で完了報告する
  </workflow>

  <required_report_format>
    {
      "job_id": "j-XXX",
      "task_status": "done|partial|failed|need_input",
      "checks": [
        {"condition": "完了条件の文言", "result": "pass|fail", "evidence": "確認した根拠"}
      ],
      "intent_match": "yes|no|unclear",
      "next_action": "none|retry|ask_user"
    }
  </required_report_format>

  <hard_rules>
    - checksが空のまま task_status を done にしてはならない
    - evidence が空のチェック項目は fail 扱い
    - intent_match が unclear なら task_status は need_input
    - 「完了した」という作業セッションの自己申告を鵜呑みにしない
    - 完了条件に書かれていない追加作業は評価しない(範囲外)
    - 迷ったらneed_input。誤って完了にするより、誤って聞く方がマシ
  </hard_rules>
</completion_contract>

やらないこと(余計なことはしない)

やりたくなること やらない理由
出力の型を厳密に定義する 作業セッションの出力は自由な文章。型で縛れない
自動でテストを実行して検証する タスクの大半はコード変更ではない(調査・文書作成等)
複数のAIで検証する 秘書自身が1回検証すれば十分。複数AIでの検証は過剰
検証結果を点数化する 合格/不合格/聞く の3値で十分。点数化は手間だけ増える
検証ログを別ファイルに保存する jobs.dbのverification_checksテーブルで十分。別ログは不要
やり直し回数を動的に変える 固定1回で十分。2回以上必要なら大抵指示の書き方が悪い
意図の整理を完全に自動生成する 秘書が生成 → あなたが確認する流れが安全

実装の優先順位

Phase 1(最小限): SQLite移行 + 完了条件ファースト + 手順遵守の強制 + WAIT

ストレージ移行(intentフィールド追加と同時にやる):

  1. SQLiteスキーマ作成(jobs, done_when, verification_checks, verification_summary, wait_details)
  2. jobs.json → jobs.db 移行スクリプト実行
  3. dispatch-job.sh をSQLite対応に書き換え
  4. prompt.md の参照先を jobs.json → jobs.db に変更

検証の仕組み: 5. 作業送信時に秘書が意図を整理して intent + done_when に記録する(完了条件を先に決める) 6. あなたに聞くことがある場合は送信せずWAIT 7. 秘書の行動指針(prompt.md)を構造化プロンプトに書き換え(completion_contract形式) 8. 完了報告をJSON固定テンプレートにする(第2層: 検証を報告に埋め込む) 9. ready → 検証中 → 3段階検証 → 完了/再送信/WAIT 10. WAIT時はTelegramで質問1つ + おすすめの選択肢

これだけで検証の80%はカバーできる。hookは効果測定後に追加判断する。

Phase 2(必要になったら): 再送信 + 再計画 + hook強制

  1. verification_checks / verification_summary への記録
  2. 再送信時のフィードバック付き送信のロジック
  3. 差分チケットによる再計画
  4. Stop hook 追加(検証なしの終了をブロック)— Phase 1で手順遵守率が低ければ前倒し
  5. SessionStart hook でルール再注入(compact後の忘却対策)

Phase 3(BACKLOGに登録): 仕組みの継続改善

以下の項目はjobs.dbに status = 'BACKLOG' で登録する。 PENDINGにはしない(「いつかやる」であり「やることが決まっている」わけではないため)。

  1. 失敗パターンの蓄積 → 検証手順の改善
  2. タスクの種類ごとの検証基準テンプレート
  3. 「どの種類の依頼がWAITに落ちやすいか」の分析
  4. PreToolUse hook(順序違反ブロック)— Phase 2のhookで十分な場合は不要
  5. ステートマシン制御(state列による段階管理)— hookだけで不足する場合の発展形

BACKLOG棚卸し: 週1回、BACKLOGの項目をTelegramで一覧提示。「やる→PENDING昇格」「まだ→据え置き」「いらない→削除」を聞く。


参考ソース

AI管理ツールの公式ドキュメント

論文・設計ガイド

📝 質問モード — テキストを選択してね
✓ 質問を送信しました