常见陷阱
悄悄击溃缩容到零和智能体安全的反模式——以及如何避免它们。
这些模式不会报错。它们通过 CI,在 diff 里看起来合情合理,却 悄悄取消掉一项你正在为之付费的保证——缩容到零的节省或 智能体安全。下面的每一条:发生了什么,为什么它有害,以及修复。
Keep-alive code defeats scale-to-zero
一个智能体"帮忙"添加了按定时器触碰数据库的代码。每一次 周期性查询都算作活动并重置挂起计时器,因此一个你设计成 缩容到零的端点永远不会休眠——而空闲也不再免费。
它以几种形态出现。一个前端或后端心跳:
// runs forever, holds the compute awake forever
setInterval(async () => {
await pool.query("SELECT 1"); // "health check"
}, 30_000);一个指向某个其处理器恰好查询数据库的端点的外部 运行时间监控——因此每一次探测都唤醒计算:
app.get("/health", async (_req, res) => {
await pool.query("SELECT 1"); // monitored every 60s → never idle
res.send("ok");
});或者那些用验证查询保持一个活动连接的连接池设置——
一个大于零的 min,或一个无服务器"预热器":
const pool = new Pool({
min: 2, // keeps ≥2 connections open + validated
// a validation/keep-alive query on each idle connection resets the timer
});修复: 审计智能体写的 diff 是否有周期性的数据库访问。把存活性
检查保持在 HTTP 层——探测应用,而非数据库——并让连接池
排空到零(min: 0)。控制台的计算视图会显示一个端点 为什么 没有
缩容到零,这能很快浮现一个游离的心跳。
Handing the agent main credentials short-circuits ASCC
把 main 分支的连接字符串交给智能体——或让它以你自己的
用户身份登录运行 keon——绕过 ASCC 强制的一切:沙箱
隔离、LEASH 预算、LINT 和 BLAST 检查、提升闸门、归因,
以及撤销锚点。智能体现在正在直接写入生产环境,而那个
使智能体变更安全的循环一步也没有运行。
智能体绝不应持有能写入 main 的凭据。提升按设计是
服务端的——cp 是生产环境的唯一写入者。
修复: 在 Settings → API keys 铸造一个 智能体范围的 API 密钥
(capability = agent,scope = 某个项目,短过期),只交给智能体沙箱
连接字符串,并从其环境中移除更强的凭据(没有完整的
DATABASE_URL,没有 ~/.pgpass,同一 shell 中没有已登录的 keon
会话)。参见 Hand an agent a scoped credential 和
Guardrails 的 SCOPE 部分。
Retry storms around wake-up
挂起之后的第一次连接必须唤醒计算,因此它比一个 温热的更慢。一个看到单次缓慢连接的智能体可能会"修复"它——用 激进的客户端超时和重试循环去猛击端点,或者通过 添加一个 keep-alive(陷阱 #1)使它根本不必唤醒。二者都为掩盖一个 一次性成本而把节省交换掉了。
修复: 把客户端 connect_timeout 设在 10 秒左右,并在第一次
连接时保持耐心。一旦温热路径就位,冷启动在同区域内是亚秒级的——一次
稍慢的连接是预期之内的,而非一个需要工程绕过的故障。
postgresql://…?connect_timeout=10Credentials committed to code
智能体把连接字符串和 API 密钥硬编码进源码和 .env 文件,
它们随后进入 git。泄露的密钥在进入一个公开(或后来公开)的
仓库后几小时内就被采集——这个窗口以小时计,而非天。
修复: 通过环境注入密钥,绝不提交它们。把 .env
文件排除在 git 之外。一旦泄露,立即从 Settings → API keys 轮换
——吊销暴露的密钥并铸造一个新的。
Direct DDL on main bypasses the promote loop
直接对 main 运行迁移会跳过那个使变更可逆且可审计的
捕获。你会失去发出的 up/down 迁移(EMIT)、
签名的账本条目(LEDGER),以及撤销锚点(UNDO)。模式
变了,但没有平台可以重放或回滚的记录。
修复: 沙箱 → 提升,即使是一个"琐碎"的模式变更。提升循环
正是产出迁移、账本条目和锚点的东西;一次直接的
ALTER 一个都不产出。
Writes inside a rolled-back transaction still land
捕获记录智能体运行的语句——它没有
事务边界意识。在一个 BEGIN … ROLLBACK 内执行的语句
仍会被捕获,并且如果那个沙箱被提升,它们会被重放并
应用。沙箱中的一次 ROLLBACK 不会把它们取消捕获。
BEGIN;
UPDATE users SET plan = 'pro'; -- captured
ROLLBACK; -- does NOT remove it from the capture修复: 不要依赖沙箱内的 ROLLBACK 来"撤销"探索性工作。
如果你想让工作消失,丢弃沙箱——那才是捕获所尊重的处置
路径。
Parking work in a sandbox past its budget or TTL
沙箱是短暂的。当一个沙箱耗尽其 LEASH 预算时,它会被
自动丢弃(status=discarded),其中任何未提升的工作都会丢失。
把一个沙箱当作一个你会"稍后回来"的长期分支,正是一天的
变更消失的方式。
修复: 提升它,或在一个全新的沙箱中重新捕获该工作。默认把沙箱 当作可丢弃的——耐久的制品是提升,而非沙箱。
Misreading the guarantees
关于这些检查实际承诺什么的三个具体陷阱:
-
爆炸半径试运行是建议性的。 它测量一次提升 会 做 什么——真实的锁、时长、行数——但它绝不给提升设闸门。一次 干净的试运行是信息,而非一盏绿灯;而一次试运行在提升前是否 被要求,是 POLICY 的决定,而非试运行的。
-
Approve 无法覆盖策略 block。 一次人工的 Approve 绝不能放行 一个策略
block或一条越出通道的语句。绕过一个 block 的 唯一途径是一次经过审计的策略更改——按设计,没有手动 覆盖开关。 -
一个脱敏 fork 脱敏的是值,而非形态。 在一个脱敏 fork 上,值 是 脱敏的,因此智能体针对那些脱敏值写的依赖数据的 DML 在重放时 不会匹配父分支上的真实行。模式通道的工作是 与值无关的,会忠实重放;针对脱敏数据开发的依赖值的 DML 则不会。