CLAUDE LABEN
FABLE 5 — 米国の輸出規制解除を受け、7月1日よりClaude Fable 5が全世界のユーザーへ提供再開されましたSCIENCE — 研究者向けワークベンチClaude Scienceがベータ公開され、AI for Science支援プログラムの応募は7月15日までですCODE — Claude Codeにdynamic workflows(研究プレビュー)が加わり、週次利用上限は7月13日まで50%引き上げられていますMODEL — Claude Sonnet 5が全プランの既定モデルとなり、導入価格は100万トークンあたり入力$2・出力$10(8月31日まで)ですGATEWAY — Amazon BedrockとGoogle Cloud向けにセルフホスト型のClaude apps gatewayが提供されました(SSO・ポリシー・コスト管理)SECURITY — Fable 5の再提供に合わせ、新しいサイバーセキュリティ分類器が追加されましたFABLE 5 — 米国の輸出規制解除を受け、7月1日よりClaude Fable 5が全世界のユーザーへ提供再開されましたSCIENCE — 研究者向けワークベンチClaude Scienceがベータ公開され、AI for Science支援プログラムの応募は7月15日までですCODE — Claude Codeにdynamic workflows(研究プレビュー)が加わり、週次利用上限は7月13日まで50%引き上げられていますMODEL — Claude Sonnet 5が全プランの既定モデルとなり、導入価格は100万トークンあたり入力$2・出力$10(8月31日まで)ですGATEWAY — Amazon BedrockとGoogle Cloud向けにセルフホスト型のClaude apps gatewayが提供されました(SSO・ポリシー・コスト管理)SECURITY — Fable 5の再提供に合わせ、新しいサイバーセキュリティ分類器が追加されました
記事一覧/Cowork
Cowork/2026-07-05中級

Claudeが安全上の理由で応答を断ったとき、無人パイプラインは何を返すべきか

エラーでも正常終了でもない第三の結末——安全上の理由でモデルが応答を断った場合の扱いを、無人で回すパイプラインにどう組み込むか。判別コードとレビューキューの設計を、個人開発の実務からまとめます。

cowork11automation48unattendedreliability12

夜間に走らせている定期処理のログを翌朝に見返したら、ある1回だけ、いつものエラーとも成果物とも違う結末が記録されていました。例外は飛んでいません。HTTP は 200 です。それでも本文には、依頼した内容そのものではなく「その要求にはお応えできません」という丁寧な断りが返っていました。パイプラインはそれを「応答が返ってきた=成功」と判断し、断り文をそのまま成果物として書き出していたのです。

エラーでもなく、望んだ完了でもない。この「安全上の理由による応答拒否」は、無人運用のパイプラインが最も取りこぼしやすい第三の結末です。人が横にいれば一目で気づくものが、成否を二分岐でしか見ていない自動処理では、静かにすり抜けていきます。

2026年7月に Claude Fable 5 が全世界で提供を再開し、その際に最上位モデルの悪用を抑えるための新しいサイバーセキュリティ分類器が追加されました。高性能なモデルを個人が日常的に使う時代になったということは、正当な作業がまれに安全側の判定に触れる場面も、これまでより現実的になったということです。だからこそ、拒否という結末を「例外」ではなく「起こりうる正常な分岐の一つ」として設計に織り込んでおく価値があります。

結末は二分岐では足りない

多くのパイプラインは、1回の呼び出しの結末を「成功」か「失敗」の二択で扱います。ところが実際には、少なくとも四つの状態を区別する必要があります。

結末典型的なサイン取るべき対応
正常完了HTTP 200 / stop_reason が end_turn / 期待した構造の本文成果物として採用
インフラ起因の失敗HTTP 429・529・5xx / タイムアウト指数バックオフで再試行
安全上の拒否HTTP 200 だが本文が依頼と無関係の断り / stop_reason が refusal再試行せず、人間のレビューキューへ退避
劣化・空応答HTTP 200 だが本文が空・途中で切れている完了条件アサーションで赤くする

この四分割のうち、インフラ失敗と劣化応答については、Claude API の stop_reason を読み解く — 応答の途切れを「終了」と誤認しないための設計「成功」と記録されたのに成果がゼロだった — Cowork スケジュールタスクの無音失敗を終了前アサーションで止める で扱いました。ここで一段深く踏み込みたいのは、三つ目の「安全上の拒否」です。

拒否とインフラエラーを混同しない

拒否がやっかいなのは、HTTP レイヤーでは成功に見えることです。ステータスコードだけを見ている限り、拒否は「正常な200」に紛れます。判別には、ステータスと stop_reason、そして本文の三つを合わせて見る必要があります。

from dataclasses import dataclass
from enum import Enum
 
 
class Outcome(Enum):
    OK = "ok"                    # 採用してよい正常完了
    INFRA_ERROR = "infra_error"  # 再試行してよい一時的失敗
    DECLINED = "declined"        # 安全上の理由で断られた
    DEGRADED = "degraded"        # 空・途中切れ
 
 
@dataclass
class RunResult:
    http_status: int | None
    stop_reason: str | None
    text: str
 
 
def classify(result: RunResult) -> Outcome:
    # 1. まずインフラ起因かどうか(ネットワーク層で失敗)
    if result.http_status is None or result.http_status >= 429:
        return Outcome.INFRA_ERROR
 
    # 2. モデルが明示的に拒否を返した場合
    if result.stop_reason == "refusal":
        return Outcome.DECLINED
 
    # 3. 200 だが本文が空・極端に短い=劣化
    if not result.text or len(result.text.strip()) < 40:
        return Outcome.DEGRADED
 
    # 4. それ以外は正常完了
    return Outcome.OK

ここで大切なのは、refusal を最優先で拾い、インフラエラーと決して同じ袋に入れないことです。両者を混ぜると、本来は人の判断が要る拒否を、機械が延々と再試行してしまいます。再試行は一時的な不調に効く手当てであって、拒否には効きません。同じ依頼を同じ条件で投げ直しても、返ってくるのは同じ断りです。

stop_reasonrefusal として明示されない断り方(本文中で丁寧に辞退するケース)も現実には起こります。無人運用で確実に拾いたい場合は、stop_reason に加えて、成果物が満たすべき構造——たとえば「見出しを2つ以上含む」「指定した JSON キーが揃っている」——を完了条件として検証し、それを満たさない200を DEGRADED として一度止めるのが安全側の設計です。断りかどうかの意味解釈まで機械で断定しようとせず、「期待した形をしていない」という観測可能な事実で止める、という割り切りです。

やってはいけない: 断られた依頼を自動で言い換えて押し通す

ここが本稿で最も伝えたい一点です。拒否を受け取ったとき、プロンプトを機械的に言い換えて再送し、通るまで繰り返す——という手当てを組み込みたくなる場面があります。これは避けてください。

理由は二つあります。ひとつは、それが正当な理由のある拒否だった場合、自動化で回避しようとすること自体が、安全設計の意図に反するからです。断られるべきものは、断られたままにしておくのが筋です。もうひとつは、仮に正当な作業への誤判定だったとしても、言い換えの当否を判断できるのは、その作業の文脈を知っている人間だけだからです。無人のループに「通す」役目を負わせると、正当性の確認という一番大事な工程が抜け落ちます。

正当な作業がまれに判定に触れることはあります。その場合の正しい対処は、機械に言い換えさせることではなく、人が作業の目的と背景を具体的に添えて、あらためて依頼し直すことです。判断を人に戻す。無人パイプラインの役目は、押し通すことではなく、止めて渡すことです。

拒否はレビューキューへ退避させる

では止めた後にどう渡すか。拒否を受け取ったら、その回の入力と出力を丸ごと、後から人が見返せる場所へ退避させます。成果物として採用せず、成功としても数えず、しかし失われもしない——この「保留」の置き場所がレビューキューです。

import json
import hashlib
from datetime import datetime, timezone
from pathlib import Path
 
REVIEW_DIR = Path("review_queue")
 
 
def enqueue_for_review(task_id: str, prompt: str, result: RunResult) -> Path:
    REVIEW_DIR.mkdir(exist_ok=True)
    # 同一入力の拒否を1件にまとめる冪等キー
    key = hashlib.sha256(f"{task_id}\n{prompt}".encode()).hexdigest()[:16]
    path = REVIEW_DIR / f"{key}.json"
 
    record = {
        "task_id": task_id,
        "queued_at": datetime.now(timezone.utc).isoformat(),
        "outcome": "declined",
        "prompt": prompt,
        "response_text": result.text,
        "stop_reason": result.stop_reason,
    }
    path.write_text(json.dumps(record, ensure_ascii=False, indent=2))
    return path
 
 
def handle(task_id: str, prompt: str, result: RunResult) -> str:
    outcome = classify(result)
    if outcome is Outcome.DECLINED:
        enqueue_for_review(task_id, prompt, result)
        return "held"          # 成功でも失敗でもなく「保留」
    if outcome is Outcome.INFRA_ERROR:
        raise TransientError(result.http_status)  # 再試行に回す
    if outcome is Outcome.DEGRADED:
        raise DefinitionOfDoneError(task_id)      # 完了条件で赤く
    return "ok"
 
 
class TransientError(Exception): ...
class DefinitionOfDoneError(Exception): ...

冪等キーで同じ入力の拒否を1件にまとめておくと、毎晩同じ依頼で断られてもキューが同じ内容で膨れません。週に一度キューをまとめて見返せば、断られた依頼にどんな傾向があるかも見えてきます。特定のトピックばかりが触れているなら、その領域は無人ではなく手元で扱う、という運用判断につなげられます。

この設計のもう一つの利点は、「保留」という第三の返り値を持つことで、パイプライン全体のログが正直になることです。拒否を成功に数えなければ成功率は実態を映し、拒否を失敗に数えなければ再試行やアラートを無駄に焚かずに済みます。長時間の無人運用に上限や退避先を用意しておく発想は、pause_turn の継続ループに上限を入れる — 無人で長時間サーバーツールを安全に回す とも地続きです。

まとめと次の一歩

安全上の拒否は、無人パイプラインにとって障害ではなく、想定しておくべき正常な分岐のひとつです。二分岐の成否判定に「保留」という三つ目を足し、拒否はインフラエラーと分けて判別し、自動で言い換えて押し通さず、レビューキューへ渡して人の判断に戻す——この四点を仕込んでおくだけで、静かにすり抜けていた結末が、翌朝きちんと目に入るようになります。

まずは手元の一番大切な定期処理を一つだけ選び、classify 相当の判別を1回の呼び出しに挟んでみてください。断りを一度でも「保留」として拾えたなら、その仕組みは他のタスクへ広げる価値があります。私自身、Dolice として複数サイトを個人開発で無人運用するなかで、この保留の置き場所を用意してから、夜間の処理を安心して任せられるようになりました。お読みいただきありがとうございました。

シェア

お読みいただきありがとうございます

Claude Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

Cowork2026-06-27
「成功」と記録されたのに成果がゼロだった — Cowork スケジュールタスクの無音失敗を終了前アサーションで止める
Cowork のスケジュールタスクが exit 0 で終わったのに成果物が一つも生まれていない。終了コードだけを信じると見逃すこの無音失敗を、完了条件をアサーションに落として証拠ログとともに赤くする設計をまとめます。
Cowork2026-05-04
Cowork スケジュールタスクが黙って止まる理由と、自動で立ち直る仕組みの作り方
Coworkのスケジュールタスクが途中で止まる原因を体系的に整理し、許可ダイアログ問題・ファイルツール制限・ディスク不足など実運用で頻発する障害パターンとその復旧方法を解説します。
Cowork2026-04-29
Cowork mode の Artifacts で「毎朝開く1ページ」を設計する — MCP連携で生きたダッシュボードを作る方法
Cowork mode のArtifactsは、再現可能な情報源を持つ「生きたページ」を生み出します。MCP連携で毎朝の確認業務を1クリックに集約する設計手順と落とし穴を、実例付きで解説します。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →