hermes-credential-pool-failover
同一プロバイダーのフェイルオーバーに対応した永続的なマルチ認証情報プールで、429/402エラー時のクールダウンと戦略選択機能を備えています。複数の認証情報を管理し、APIレート制限やリクエスト失敗時に自動的に別の認証情報に切り替えることができます。クールダウン期間を設定することで、エラー発生後の不要なリトライを防ぎ、適切な回復時間を確保します。複数のフェイルオーバー戦略から選択でき、システムの要件に応じた最適な動作を実現できます。
description の原文を見る
Persistent multi-credential pool for same-provider failover with 429/402 cooldowns and strategy selection.
SKILL.md 本文
同一プロバイダーの認証情報プールとフェイルオーバー
コンテキスト
単一の OpenRouter / OpenAI / Kimi / Anthropic キーは、長いエージェントセッション中に最終的に 429(レート制限)または 402(クォータ)に達します。別のプロバイダーに切り替えるとモデルが失われます。同じプロバイダーの N 個のキーを保持し、1 つが枯渇したときにローテーションすることは、安価な保険です。Hermes の CredentialPool(agent/credential_pool.py)はまさにそれを行い、再起動後も状態を永続化し、4 つの選択ストラテジーをサポートします。
使用時期
- プロバイダーで実行中に定期的に 429 / 402 に達している。
- 同じプロバイダーの複数のキーにアクセスできる(チームアカウント、トライアルティア)。
- 枯渇が再起動後もスティッキーであることを望む — クラッシュが 1 時間のクールダウンクロックをリセットすべきではない。
手順
1. 認証情報ごとのデータ形式
@dataclass
class PooledCredential:
provider: str
id: str
label: str
auth_type: str # "oauth" | "api_key"
priority: int
source: str # "manual" | "cli_import" | ...
access_token: str
refresh_token: Optional[str] = None
last_status: Optional[str] = None # "ok" | "exhausted"
last_status_at: Optional[float] = None
last_error_code: Optional[int] = None
last_error_reason: Optional[str] = None
last_error_message: Optional[str] = None
last_error_reset_at: Optional[float] = None
expires_at: Optional[str] = None
last_refresh: Optional[str] = None
request_count: int = 0
extra: Dict[str, Any] = None # round-tripped JSON fields
(agent/credential_pool.py:91-115)
2. 4 つの選択ストラテジー
STRATEGY_FILL_FIRST = "fill_first" # stay on #1 until exhausted
STRATEGY_ROUND_ROBIN = "round_robin"
STRATEGY_RANDOM = "random"
STRATEGY_LEAST_USED = "least_used"
fill_first はデフォルトで、認証情報にクォータがある場合の正しい選択です(最良のものを使い切るまで使用)。round_robin は負荷を分散します。least_used は再開後にリバランスします。
3. 429 と 402 は両方とも 1 時間クールダウン
EXHAUSTED_TTL_429_SECONDS = 60 * 60
EXHAUSTED_TTL_DEFAULT_SECONDS = 60 * 60
プロバイダーはレスポンスヘッダーで reset_at を返すことがあります — 存在する場合はそれを優先し、それ以外はデフォルトを使用します。
4. カスタムエンドポイントはプロバイダープレフィックスを共有
Anthropic 互換のエンドポイントで Anthropic 自体ではないものはすべて provider="custom" を使用しますが、プール内では custom:<normalized_name> としてキー化されます:
CUSTOM_POOL_PREFIX = "custom:"
これにより、ユーザーは「Kimi via Moonshot」と「Kimi via custom-proxy」をプロバイダー名の衝突なしに個別のプールエントリとして追加できます。
5. ステータス + エラーは空の場合も常に往復
_ALWAYS_EMIT = {
"last_status", "last_status_at", "last_error_code",
"last_error_reason", "last_error_message", "last_error_reset_at",
}
これらを常に JSON に出力します(null の場合でも)。そうすることで管理 UI は「last error: —」をレンダリングでき、キーが欠落しているためにクラッシュしません。
6. 前方互換性のための extra への属性フォールバック
_EXTRA_KEYS = frozenset({
"token_type", "scope", "client_id", "portal_base_url",
"obtained_at", "expires_in", "agent_key_id", ...
})
def __getattr__(self, name: str):
if name in _EXTRA_KEYS:
return self.extra.get(name)
raise AttributeError(...)
「JSON を往復させるが論理に使用されない」フィールドは dataclass ではなく extra に入ります。スキーママイグレーションなしで新しいフィールドを追加できます。
7. トークンリフレッシュスキュー
CODEX_ACCESS_TOKEN_REFRESH_SKEW_SECONDS = 300 # refresh 5min before expiry
DEFAULT_AGENT_KEY_MIN_TTL_SECONDS = 900
expires_at まで待たないでください — 先制的にリフレッシュして、実行中のツール呼び出しが 401 で失敗しないようにします。
8. 認証ストアファイルをプロセス間でロック
with _auth_store_lock:
state = _load_auth_store()
...
_save_auth_store(state)
ゲートウェイ + CLI + クーロンはすべて同時に書き込む可能性があります。ロックなしでは、1 つが勝利して他が回転ブックキーピングを失います。
注意点
request_countを更新時にリセットしないでください。これは有用なテレメトリで、ローテーションするたびに失うと「least used」ストラテジーが嘘をつきます。- プロバイダー提供の
reset_at> あなたのデフォルト。 プロバイダーからの 1 秒のリセットであっても、1 時間のフォールバックに勝つべきです。 - ディスクに永続化し、メモリではなく。 そうしないと、枯渇直後のクラッシュが悪いキーへの再試行嵐を引き起こします。
- ログからリフレッシュトークンを除外してください。 認証情報レコード全体は、ログ出力前に
redact_sensitive_text()を通すべきです。
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- kjuhwa
- リポジトリ
- kjuhwa/skills-hub
- ライセンス
- MIT
- 最終更新
- 2026/4/26
Source: https://github.com/kjuhwa/skills-hub / ライセンス: MIT