Guardrails
予算、ポリシー階層、スコープ限定の権限、リスクリント、被害範囲チェック — エージェントが害を及ぼす前にそれを止めるもの。
AI エージェントサンドボックス の周りには 5 つのガードレールが 配置されています。2 つはエージェントが物理的に超えられないハード制限(LEASH、SCOPE)、 1 つはデータを判定に変え(POLICY)、2 つはそれが重み付けするためのデータを生成します (LINT、BLAST)。いずれも信頼の経路に LLM を置きません。
LEASH — サンドボックスごとの予算
ブロックするもの: ステートメント、コンピュート、実時間を無制限に消費して暴走する エージェント。
サンドボックスを作成または実行するときに予算を設定します。
keon sandbox create \
--budget-statements 500 \
--budget-compute-seconds 120 \
--budget-wall-seconds 900
# or the same three flags on: keon sandbox run …違反すると、サンドボックスは自己終了します: 状態は discarded になり、
budget_breached がどの制限を超えたか — statements、compute_seconds、または
wall_clock — を示します。CLI はエラーコード sandbox_budget_breached で終了します。
いずれかの予算の 80% で警告が発火します。
ワイヤー上では、予算は budgets オブジェクトで、予算ごとに 1 エントリです。
"budgets": {
"statements": { "limit": 500, "used": 218, "warned": false },
"compute_seconds": { "limit": 120, "used": 41, "warned": false },
"wall_clock": { "used": 63 }
}limit の欠如は無制限を意味します — 誤読するセンチネル値はありません。
デフォルトと上限の設定(オーナー/管理者):
# Per-project defaults double as the create-time cap.
curl -X PATCH .../v1/projects/{id} \
-d '{"sandbox_budget_defaults": {"statements": 500, "compute_seconds": 120}}'デフォルトが上限です: 作成時にプロジェクトデフォルトを超える値を要求すると
422 sandbox_budget_exceeds_cap を返します。プロジェクトごとのデフォルトは設定可能です —
ここに引用する固定の数値はありません。
2 つの不変条件:
- 作成時に凍結。 予算はサンドボックス作成時に固定されます。後からプロジェクトデフォルトを 変更しても、稼働中のサンドボックスを再拘束することは決してありません。
- エージェントキーは厳しくするだけ。 エージェントケイパビリティのキーは作成時により厳しい 予算を要求できますが、プロジェクトデフォルトを設定することは決してできません。
POLICY — promote ポリシー階層
ブロックするもの: 変更がプロジェクトの許容よりも危険な promote — エージェント(または 人間)が素通しさせようとするものであっても。
POLICY は、リスククラスをアクションにマッピングするプロジェクトごとのオーバーレイで、
ベースの promote_mode の上に重ねられます。
設定(CLI):
keon projects promote-policy <projectId> # show effective policy
keon projects promote-policy <projectId> \
--set destructive=block \
--set unbounded=require_human \
--on-conflicts require_human # merge-write
keon projects promote-policy <projectId> --clear # drop the overlay--set <class=action> は繰り返し可能で、既存のオーバーレイにマージされます。
設定(API): GET / PUT / DELETE /v1/projects/{projectId}/promote-policy:
{ "version": 1,
"rules": { "destructive": "block", "unbounded": "require_human" },
"on_conflicts": "require_human" }| フィールド | 有効な値 |
|---|---|
| リスククラス | additive, mutating, destructive, unbounded, rewrite |
| アクション | auto_promote, require_human, block |
on_conflicts | ignore, require_human, block |
ignore は on_conflicts に対してのみ有効で、クラスアクションとしては決して使えません。
ベース姿勢は promote_mode から来て、オーバーレイがクラスごとに上書きします。
promote_mode | すべてのクラスのデフォルト |
|---|---|
self | auto_promote |
human | require_human |
各実効ルールは、それがどこから来たか — source: overlay、promote_mode、または default —
を報告します。
ブロックされたときにエージェントが見るもの: ブロックされた promote は、決定の詳細を添えて
409 promote_blocked_by_policy を返します。
重要なルール: 人間の Approve はポリシーの block を上書きできません。 ポリシーは
approve 時に再評価されるため、block はクリックに対して保持されます。ポリシー検証は
フェイルクローズド/厳格です — 解析できないポリシーは拒否し、決してオープンに劣化しません。
SCOPE — スコープ限定の権限(レーン)
ブロックするもの: 宣言したスキーマとテーブル — エージェントのレーン — の外側での あらゆる読み取りまたは書き込み。
設定(作成時のみ):
keon sandbox create \
--schemas app,analytics \
--tables app.users,app.orders
# omit both flags → unscoped (full-branch authority)--schemas を伴わない --tables は、クライアント側で invalid_lane として拒否されます。
フォーク内では、エージェントはレーンの外に物理的に書き込めません — レーン外の書き込みは
フォーク内で SQLSTATE 42501(権限不足)または 3F000(無効なスキーマ名)で失敗します。
レーン外の読み取りも拒否されます。そして promote はレーン外の変更を
409 sandbox_out_of_lane で拒否します。
ワイヤー上では、サンドボックスは scope オブジェクトを持ちます(欠如 = スコープなし)。
"scope": { "v": 1, "schemas": ["app", "analytics"], "tables": ["app.users", "app.orders"] }keon sandbox diff は、それぞれ理由付きの violations[] を列挙する lane オブジェクトを
得ます — out_of_lane_schema、out_of_lane_relation、または unresolvable_targets。
2 つの制限:
- 作成後は不変。 レーンを広げるには破棄 + 再作成が必要です — その場で広げる手段は ありません。
- 直接ターゲットのみ。 レーンは、トリガーや
SECURITY DEFINER関数を通じて到達する 推移的な副作用は統制しません。
LINT — 静的リスク分類
生成するもの: 再生集合内のすべてのステートメントに対する静的なリスククラス — 実行なし、 LLM なし、行数なし。純粋にテキスト的です。
新しいコマンドはありません。LINT は、すでにあなたが読んでいる 3 か所に表面化します。
keon sandbox diff内でrisk_reportとして、keon sandbox logでアクションごとに、- サンドボックスワイヤー上で
max_risk_classとして。
クラス、重大度の低い順から高い順:
additive < mutating < rewrite < unbounded < destructive空の再生集合は none として分類されます。
レポートの形:
{ "statements": [
{ "seq": 4, "risk_class": "unbounded",
"reasons": ["where_trivially_true: DELETE guarded by WHERE 1=1 matches every row"] }
],
"rollup": { "max_class": "unbounded", "counts": { "additive": 2, "unbounded": 1 } } }各理由は <snake_case_code>: <sentence> です。たとえば、WHERE true または
WHERE 1=1 は、理由 where_trivially_true でステートメントを unbounded に分類します。
覚えておくべき 2 点:
- LINT はデータであり、決定ではありません。 それは分類します。POLICY が強制します。 LINT は単独では何もブロックしません。
- 上に倒れます。 不確実な場合、LINT は最悪の妥当なクラスを割り当てます。検出はテキスト的
であるため、実際の行数は入力になりません —
WHEREのないDELETEは、テーブルが 1 行を 持とうと 10 億行を持とうとdestructiveと読まれます。
BLAST — 被害範囲のドライラン
生成するもの: promote が実際に何をするかの測定された証拠 — 実際のロックレベル、実際の ロック保持時間、実際の行数、およびフォーク以降の任意の分岐。
keon sandbox dry-run <id>これは、サンドボックスの正確なステートメント集合を親の2 つの使い捨てフォークに対して
再生し、実際のコストを測定し、分岐を検出し、その後両方を破棄します。main には決して
触れず、エージェントはフォークの認証情報を決して受け取りません。BLAST は助言的です
— promote を決してブロックしません。コンフリクトはデータとして表面化されます。
完全なフィールド参照、コンフリクトのセマンティクス、およびエンドポイント一覧は Sandboxes & promote → 被害範囲のドライラン に あります。
5 つがどう組み合わさるか
- LINT と BLAST はデータを生成します — 一方は静的、他方は測定。
- POLICY はそのデータを決定に変えます。フェイルクローズドで、
blockと言うときは人間の クリックで上書きできません。 - LEASH と SCOPE は、エージェントがまったく超えられないハードな制限です — フォーク内で 強制され、作成時に凍結されます。
これら 5 つが日常的に誤読される様々な形については よくある落とし穴 を 参照してください。