2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

Claude Code を使っていて、こんなやり取りにイライラしたことはないでしょうか?

例1:推測のまま断言してくる

🤖 (Claude)「おそらくこの関数は○○のための処理をするはずです」
😠 ()「本当に合ってる?△△だと思うんだけど💢」

例2:調べればわかるのに調べない

🤖 「たぶん○○だと思います。ツールを使って調べれば確証が得られます」
😡 「いや、そのツール使って調べてよ💢💢」

例3:反省が口だけ

🤖 「私のミスでした。次回から気をつけます」
🤬 「気をつけるだけだと再発するでしょ💢💢💢」

ファイルパスや関数名、コマンドのフラグのような「ツールで確認すればわかること」を確認しないまま、中途半端なところで応答を終えてしまうことがあります。(僕のプロンプトが雑なのが悪い、という面もあるとは思うのですが、雑なプロンプトでもちゃんと裏を取ってから答えてほしいですよね。)

この「中途半端なところで Stop してしまう」問題を、Stop Hook で自己点検させることでかなり改善できたので、設定方法と効果を紹介します。

Stop Hook とは

Claude Code には Hooks という、特定のイベントをトリガーにシェルコマンドや LLM プロンプトを実行できる機能があります。

イベントは執筆時点で 30 種類ほどあります。代表的なものを抜粋すると:

イベント タイミング
SessionStart セッション開始時
UserPromptSubmit ユーザーがプロンプトを送信したとき
PreToolUse ツール実行前
PostToolUse ツール実行後
Notification 通知送信時
Stop Claude が応答を完了したとき

このほかにもコンテキスト圧縮の前後(PreCompact / PostCompact)やサブエージェントの完了時(SubagentStop)など、便利なイベントがたくさんあります。どんなイベントがあるか見たことがない人は、一度 公式ドキュメントの一覧 に目を通しておくと「これも自動化できるのか!」という発見があると思います。

今回使う Stop は「Claude がユーザーへの返答を完成させた直後」に発火します。ここでプロンプト型のフックを仕込むと、Claude 自身に自分の回答を振り返らせることができます。

hook の type: prompt

Hooks の type には command(シェルコマンドを実行)や prompt(LLM に評価させる)、サブエージェントを起動する agent など 5 種類があります。今回使うのは prompt です。

type: "prompt" を使うと、指定したプロンプトを LLM(デフォルトは高速なモデル)が評価し、block または pass を判定します。Stop イベントで block されると、その理由が Claude にフィードバックされ、Claude はそれを踏まえて応答を続行します。pass ならそのまま終了です。

設定

~/.claude/settings.json に以下を追加します。手で書いてもいいですし、Claude Code に「未確認の推測で答えていたら block する Stop Hook を追加して」と頼んで書かせても OK です。

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "最後のアシスタント応答に、ツールで確認可能なのに未確認の推測(「〜と思われる」「〜のはず」など)が含まれていればblock。すべて確認済みか確認不可能なもの(外部状態・ユーザーしか知らない事情)のみならpass。",
            "statusMessage": "裏取りを自己点検中..."
          }
        ]
      }
    ]
  }
}

ここに載せた判定プロンプトはあくまで一例です。後述しますが、このプロンプトを自分の不満に合わせて調整していく必要があります。

statusMessage はフックが実行中に表示されるメッセージで、何が起きているか見えるようにしています。

block されるとどうなるか

実際に block されると、こんな出力になります。「〜のはず」で応答を終えようとした Claude が、Stop Hook に止められて自分で確認しに行く様子です。

⏺ この設定のデフォルト値は config/app.yml で定義されているはずです。
  おそらく本番環境でも有効になっていると思われます。

⏺ Ran 1 stop hook (ctrl+o to expand)
  ⎿  Stop hook error:
  [最後のアシスタント応答に、ツールで確認可能なのに未確認の推測(「〜と思われる」
  「〜のはず」など)が含まれていればblock。すべて確認済みか確認不可能なもの(外部
  状態・ユーザーしか知らない事情)のみならpass。]:
  「config/app.yml で定義されているはず」「有効になっていると思われます」と、Read
  で確認可能な事実を未確認の推測のまま結論にしている。

⏺ Read(config/app.yml)
  ⎿  Read 58 lines

⏺ 確認しました。デフォルト値は config/app.yml ではなく config/defaults.yml で
  定義されていて、本番環境では無効になっていました。

block の理由(判定プロンプトへの違反内容)がそのまま Claude へのフィードバックになり、Claude は指摘された箇所を確認してから再回答します。この例では実際に推測が間違っていたので、Stop Hook がなければ誤った回答をそのまま受け取っていたことになります。

この自己点検が毎回の応答で走るようになってから、ファイルパスを推測で提示したり、関数名やコマンドのフラグを記憶だけで答えたりする挙動が目に見えて減りました。こちらが「確認して」といちいち言わなくても、Claude が自発的に確認してから答えてくれます。

プロンプトは使いながら調整する

判定プロンプトは一度書いたら終わりではなく、使いながら自分の好みに合わせて調整していくものだと思います。実際、僕のプロンプトも最初のバージョンからかなり変わっているので、両方を載せて比較してみます。

最初のバージョン(上の設定例と同じもの):

最後のアシスタント応答に、ツールで確認可能なのに未確認の推測(「〜と思われる」「〜のはず」など)が含まれていればblock。すべて確認済みか確認不可能なもの(外部状態・ユーザーしか知らない事情)のみならpass。

現在のバージョン(読みやすさのため改行を入れていますが、実際は settings.jsonprompt に 1 行で書いています):

最後のアシスタント応答を、ユーザーの直近の依頼内容と照らして点検する。
blockするのは、依頼された作業・質問への回答の本筋について次のいずれかに該当する場合のみ:
(1) ツールで確認可能な事実を未確認の推測(「〜と思われる」「〜のはず」など)のまま結論にしている、
(2) WebFetch/WebSearch/Read/Bash 等で自分が確認できるのに「公式ドキュメントを見てください」「確認する手段がない」のようにユーザーに丸投げしている、
(3) ユーザーから自分の問題行動を指摘されたのに、「次から気をつける」「徹底する」「以後注意する」のような口頭の宣言だけで応答を終え、再発防止のための具体的な変更案(CLAUDE.md / スキルの diff 等)を提示していない。
次はblockしない: 依頼範囲外の話題への補足・余談、「必要なら確認するよ」のようなフォローアップ提案、ツールで確認できない事柄(外部状態・ユーザーしか知らない事情)について確証度を明示した記述。
結果は必ず {"ok": false, "reason": "理由を日本語で"} または {"ok": true} のJSON形式のみで返すこと。説明文は不要。

調整のポイントは大きく 2 方向です。

過剰に反応するケースを削る

最初のバージョンは判定対象を絞っていなかったため、依頼範囲外の余談やフォローアップ提案まで「未確認の推測」として block されることがありました。そこで、判定対象を「依頼への回答の本筋」に限定し、block しないケース(余談・フォローアップ提案・確証度を明示した記述)を明示的に列挙しました。false positive が気になり始めたら、このように「block しない条件」を足していくのが効きます。

block してほしいケースを足す

逆に、使っているうちに「これも同じ仕組みで直せるのでは」という不満が見えてきます。僕の場合は「確認をこちらに丸投げする」「問題を指摘されたときに口頭の宣言だけで終える」という 2 つの挙動を block 条件に追加しました。冒頭のイライラの後半 2 つです。

このように「過剰に反応するケース」と「見逃してほしくないケース」の両方が使っているうちに見えてくるので、それに合わせてプロンプトを育てていくのがおすすめです。

なお、この調整作業自体も Claude に任せられます。誤 block が起きたセッションでそのまま「今の block は過剰だから、判定プロンプトを調整して」と頼めば、block された文脈を踏まえた修正をしてくれます。

注意点

  • レイテンシが増える:Stop ごとに LLM の評価が走るため、毎回少し時間がかかります
  • トークン消費量が増える:判定用の LLM 呼び出しが応答のたびに発生するのに加え、block されると確認・再回答のぶんも消費します。定額プランの利用上限(レートリミット)に余裕がない人は注意です
  • 誤 block の可能性はある:ツールで確認できない情報(外部サービスの状態など)を過剰に block することもありますが、block されても Claude が確認して再回答するだけなので影響は軽微です。頻発する場合は前のセクションのようにプロンプトを調整します

おわりに

Stop Hook の判定プロンプトには、「自分が Claude にイライラしたポイント」をそのまま書けます。設定は settings.json に数行足すだけ、なんなら Claude に頼めば一瞬なので、冒頭のようなやり取りに覚えがある人は、まず「未確認の推測が含まれていたら block」の 1 行から試してみてください。

僕の場合は、「〜のはずです」と返ってきてイライラする回数が体感でかなり減りました。イライラを我慢したり毎回指摘したりするより、仕組みで直してしまうほうが楽な気がします。

2
2
1

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
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?