コンテンツにスキップ

Reyn が止まる理由を理解する

Reyn が実行を中断するときは、必ず次の 3 つのいずれかが原因です:

  1. ループ検知 — 同じことを繰り返している
  2. タイムアウト — 時間がかかりすぎている
  3. 予算超過 — トークン / USD の上限に達した

各カテゴリは独自の設定名前空間を持ち、エラーメッセージには「このキーを上げれば 継続できる」というヒントが組み込まれています。本ページは、止まる理由と設定キーの 対応関係をまとめます。

TL;DR: 統一された名前空間は、ループ / タイムアウトが safety.*、 財務的な上限は cost.* です。


① ループ検知 — safety.loop.*

ループ検知は暴走的な繰り返しを捕まえる仕組みです。フェーズが永遠に再入する、 ルーターが何度もルーティングし直す、委譲チェーンが青天井に伸びる、など。 開発中に到達するのは正常です。本当に必要な反復数なら上限を引き上げ、 そうでないなら原因を調査してください。

上限 検出対象 既定値 設定キー
フェーズ訪問回数 1 スキル実行内で同じフェーズに入りすぎた 25 safety.loop.max_phase_visits
フェーズ内 act ターン数 1 フェーズ訪問内の LLM ↔ op の往復 10 safety.loop.max_act_turns_per_phase (skill / phase frontmatter が優先)
1 ターンあたりの router 呼び出し 1 ユーザーターン内のルーター起動回数 3 safety.loop.max_router_calls_per_turn (0 = 無制限)
エージェント委譲の深さ user → A → B → C のチェーンが深すぎる 3 safety.loop.max_agent_hops
チェーン内スキル起動回数 同一スキルが同一チェーンで起動しすぎた 無制限 safety.loop.skill_calls_per_chain.hard_limit
チェーン内スキルトークン数 同一スキルが同一チェーンでトークンを使いすぎた 無制限 safety.loop.skill_tokens_per_chain.hard_limit

エラーの例

Phase 'revise' reached max_phase_visits=25.
→ Raise safety.loop.max_phase_visits to allow more iterations.

修正の例

# reyn.local.yaml
safety:
  loop:
    max_phase_visits: 50      # フェーズあたり 50 回まで許可
    max_router_calls_per_turn: 5

② タイムアウト — safety.timeout.*

タイムアウトは時間がかかりすぎているものを捕まえます。LLM 呼び出しが遅い、 委譲先が応答しない、フェーズが 1 時間動き続けている、など。本当に時間が 必要なら上限を引き上げ、そうでないなら原因を調査してください。

上限 検出対象 既定値 設定キー
LLM 1 呼び出し 1 回の litellm.acompletion がタイムアウトを超えた 60 秒 safety.timeout.llm_call_seconds
LLM リトライ 一時エラー時のリトライ上限 3 safety.timeout.llm_max_retries
フェーズ wall-clock 1 フェーズ訪問が時間予算を超えた 無制限 (0) safety.timeout.phase_seconds
Chain 待機 マルチエージェントの pending chain が委譲応答を待ちすぎた 60 秒 safety.timeout.chain_seconds (0 = タイムアウトなし)

エラーの例

chain timeout: 1 delegate(s) (writer) did not respond within 60s.
→ Raise safety.timeout.chain_seconds to wait longer (0 = no timeout).

修正の例

# reyn.local.yaml
safety:
  timeout:
    llm_call_seconds: 120     # 遅いモデル用
    chain_seconds: 300        # 長時間動く委譲先用

③ 予算超過 — cost.*

予算上限は財務的なキャップ(トークン数、USD 額、日次 / 月次クォータ) です。 意図的に cost: 配下に残し、safety: には統合しません。運用者の感覚として、 ループ / タイムアウトは「上げるべき」ことが多いのに対し、予算は「調査するか 明示的に承認する」ことが多いためです。

上限 検出対象 設定キー
エージェントごとのトークン 1 エージェントがトークン上限に達した cost.per_agent_tokens.hard_limit
エージェントごとの USD 1 エージェントが USD 上限に達した cost.per_agent_cost_usd.hard_limit
日次クォータ 当日の合計が daily_tokens / daily_cost_usd を超えた cost.daily_tokens.hard_limit, cost.daily_cost_usd.hard_limit
月次クォータ 当月の合計が monthly_tokens / monthly_cost_usd を超えた cost.monthly_tokens.hard_limit, cost.monthly_cost_usd.hard_limit
レートリミット モデルごとの requests-per-minute 上限 cost.rate_limit_per_minute.<model>

((chain, skill) ごとの起動回数 / トークン上限はループ検知側にあり、 safety.loop.skill_calls_per_chain / safety.loop.skill_tokens_per_chain を参照してください — 上記 §① 参照。)

上限到達時のユーザー承認フロー (FP-0003)

(chain, skill) ごとの起動回数キャップは、即時拒否ではなく対話的な承認に 切り替えできます:

# reyn.local.yaml
safety:
  loop:
    skill_calls_per_chain:
      hard_limit: 5
      ask_on_exceed: true       # ask_user 経由で問い合わせる
      extension_calls: 3        # 承認時に +3 回付与

上限到達時に Reyn が 「Skill X が上限 5 回に達しました。+3 回追加で 継続しますか?」 と問います。承認は何度でも可能で、毎回 extension_calls 分だけキャップが拡張されます。


limit 到達時の挙動 (safety.on_limit)

既定の挙動: limit 到達時、 ask_user で user に延長を確認します (= mode: interactive / ask_timeout_seconds: 0 — 無制限待機)。 拒否 / timeout / intervention surface 不在の場合は abort、 Reyn は RunResult を返し、 statusloop_limit_exceeded / phase_budget_exceeded / budget_exceeded のいずれかに設定し、 partial_data には最後に完了したフェーズの artifact (= 「今ここまでの 成果物」) を入れます。ヘッドレス path (= bus=None / 非 TTY stdin) は intervention 不要に自動的に abort 経路へ短絡するので、 interactive 既定はどの環境でも安全です。

挙動は safety.on_limit.mode で変更できます:

# reyn.local.yaml
safety:
  on_limit:
    mode: interactive      # 既定 — ask_user で user に確認、 承認時に limit 延長して継続
    # mode: unattended     # 到達時に abort (= CI / cron / スクリプト実行向けのオプトイン)
    # mode: auto_extend    # N 回まで自動延長、 それ以降は abort
    auto_extend_times: 1   # auto_extend 時のみ参照
    ask_timeout_seconds: 0  # interactive 時のみ参照; 0 = 無制限待機
モード 用途
interactive (既定) reyn chat / TUI / a2a session — user が到達可能で延長判断を返せる
unattended CI / cron / スクリプト実行で human を待てない用途のオプトイン、 fail fast
auto_extend 信頼済みの長時間タスクで「N 回までは自動延長して良い」と分かっているとき

各 limit がどの site で wiring されているか (FP-0005 Phase 2 — landing 完了):

Limit Site モード挙動
safety.loop.max_phase_visits OSRuntime._enter_phase interactive / auto_extend
safety.timeout.phase_seconds OSRuntime._check_phase_budget interactive / auto_extend
safety.loop.max_act_turns_per_phase OSRuntime act-loop interactive / auto_extend
safety.loop.max_router_calls_per_turn ChatSession._check_and_increment_router_cap interactive / auto_extend
safety.loop.max_agent_hops ChatSession._send_to_agent interactive / auto_extend
safety.timeout.chain_seconds chain timeout watchdog interactive / auto_extend (再 arm)
safety.loop.skill_calls_per_chain spawn budget gate interactive (= FP-0003 ask_on_exceed)

safety.timeout.llm_call_seconds は設計上対象外です — litellm が safety.timeout.llm_max_retries 内で自動再試行するため、 追加の ask_user 層はレイテンシを増やすだけになります。