Guardrails
预算、策略层级、受限权限、风险检查和爆炸半径检查——在智能体伤到你之前阻止它的东西。
五道护栏环绕着一个 AI 智能体沙箱。其中两道是 智能体物理上无法逾越的硬限制(LEASH、SCOPE),一道把 数据变成裁定(POLICY),还有两道产出供其权衡的数据 (LINT、BLAST)。没有任何一道把 LLM 放进信任路径。
LEASH — per-sandbox budgets
阻止: 一个失控的智能体——无界地烧掉语句、计算或挂钟时间。
在你创建或运行沙箱时设定一个预算:
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 对象,每个预算一个条目:
"budgets": {
"statements": { "limit": 500, "used": 218, "warned": false },
"compute_seconds": { "limit": 120, "used": 41, "warned": false },
"wall_clock": { "used": 63 }
}一个 缺失的 limit 意味着无限制 —— 没有会被误读的
哨兵值。
配置默认值和上限(owner/admin):
# 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。每项目的默认值是
可配置的——这里没有固定的数字可引用。
两条不变量:
- 在创建时冻结。 预算在沙箱创建时即固定;此后更改 项目默认值绝不会给一个活动沙箱重新套上束缚。
- 智能体密钥只能收紧。 一个智能体能力密钥可以在创建时请求 更紧的 预算;它绝不能设定项目默认值。
POLICY — promote-policy tiers
阻止: 一次其变更比项目所允许的更危险的提升——即使 是一个智能体(或一个人)试图放行的那一次。
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。
智能体被阻止时看到什么: 一次被阻止的提升返回
409 promote_blocked_by_policy,并附带决策细节。
关键规则: 一次人工的 Approve 绝不能覆盖策略 block。 策略
会在批准时重新评估,因此 block 顶住那次点击。策略
验证是 失败即关闭 / 严格 的——一个无法解析的策略拒绝,绝不
退化为放行。
SCOPE — scoped authority (lanes)
阻止: 任何在你所声明的模式和表之外的读或写——即智能体的 通道。
配置(仅创建时):
keon sandbox create \
--schemas app,analytics \
--tables app.users,app.orders
# omit both flags → unscoped (full-branch authority)--tables 不带 --schemas 会在客户端被拒绝为 invalid_lane。
在 fork 内部,智能体 物理上无法 在通道之外写入——一次
越出通道的写入会在 fork 内以 SQLSTATE 42501(权限不足)
或 3F000(无效模式名)失败。越出通道的 读取也被拒绝。并且
提升会以 409 sandbox_out_of_lane 拒绝任何越出通道的变更。
在传输层面,沙箱携带一个 scope 对象(缺失 = 未限定范围):
"scope": { "v": 1, "schemas": ["app", "analytics"], "tables": ["app.users", "app.orders"] }keon sandbox diff 会增加一个 lane 对象,列出任何 violations[],每个带一个
原因——out_of_lane_schema、out_of_lane_relation 或
unresolvable_targets。
两条限制:
- 创建后不可变。 拓宽一个通道意味着丢弃 + 重建—— 没有就地拓宽。
- 仅直接目标。 一个通道 不 治理经由触发器或
SECURITY DEFINER函数达成的传递性副作用。
LINT — static risk classification
产出: 重放集中每一条语句的静态风险类别——没有 执行、没有 LLM、没有行数。纯粹是文本层面的。
没有新命令。LINT 在你已经在读的三处地方浮现:
- 在
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 会把该语句分类为 unbounded,原因为
where_trivially_true。
两件要记住的事:
- LINT 是数据,不是决策。 它 分类;POLICY 执行。LINT 绝不自行阻止任何东西。
- 它向上取。 在不确定时,LINT 赋予最坏的合理类别。
因为检测是文本层面的,实际 行数并非输入——一条
无
WHERE的DELETE无论表里有一行还是十亿行都读作destructive。
BLAST — blast-radius dry-run
产出: 关于一次提升 实际会做什么 的测量证据——真实的 锁级别、真实的持锁时长、真实的行数,以及自 fork 以来的任何分歧。
keon sandbox dry-run <id>它将沙箱的确切语句集对父分支的 两个一次性 fork 进行
重放,测量真实成本,检测分歧,然后销毁两者。它
绝不触及 main,且智能体 绝不收到 fork 凭据。
BLAST 是 建议性的——它绝不阻断提升;冲突被暴露为
数据。
完整的字段参考、冲突语义和端点列表见 Sandboxes & promote → Blast-radius dry-run。
How they fit together
- LINT 和 BLAST 产出 数据——一个静态,一个测量。
- POLICY 把那份数据变成一个 决策,失败即关闭,当它说
block时无法被一次人工点击覆盖。 - LEASH 和 SCOPE 是智能体完全无法逾越的硬 限制—— 在 fork 中执行,在创建时冻结。
参见 Common pitfalls 了解这五道被 惯常误读的方式。