模型输出一条 SQL,只说明它提出了一个候选动作,并不表示已经获得生产数据库的执行权。即使是 SELECT,也可能扫描数 TB、读取敏感列、触发外部函数,或者因为错误 Join 返回一份看似合理的结果。
执行层的任务,是把自然语言意图转换成一个受控操作:在运行前校验,在运行时限制,在运行后核验,并为每次副作用留下可追踪记录。
生成与授权必须分开。模型可以建议“创建工单”或“运行查询”,真正的工具网关根据身份、策略、参数和风险决定是否允许。
执行请求需要结构化契约
不要让执行器直接接收一段自由文本。一个最小请求应包含:任务与步骤 ID、调用者与委派身份、工具名和版本、结构化参数、输入产物、语义版本、允许动作、预算、超时、幂等键、审批引用和输出契约。
工具返回也要标准化:状态、执行 ID、结果引用、行数与截断、成本、Schema、数据水位、警告、可重试标志和审计引用。模型只看到完成下一步所需的结果摘要;凭证、内部错误栈和不相关敏感数据留在可信运行时。
运行前先过三道门
语法与静态检查
解析 SQL 或代码为语法树,拒绝不支持的语句、动态执行、未限定目标和危险库。只靠正则匹配 DROP 不够:写操作可能隐藏在存储过程、多语句、外部函数或工具参数里。
语义与数据检查
确认表、字段、指标版本、粒度、Join 基数和时间范围符合计划。执行层不重新发明业务口径,但要验证请求引用的契约仍存在、版本兼容、输入数据已就绪。
策略与资源检查
根据最终用户或受限服务身份检查资源、行列范围、用途、地域、环境、读写类型和审批。再估算扫描量、运行时间、并发与外部调用成本。未知成本不是零成本;无法估算时使用更严格限额或要求审批。
沙箱不是一个开关
SQL、Python 和外部 API 需要不同隔离:
- SQL:只读事务、允许对象清单、查询超时、扫描上限、结果行数和敏感列控制。
- 代码:短期容器、固定依赖、CPU/内存/磁盘上限、无默认外网、只挂载任务所需文件。
- 文件工具:限定工作目录、文件类型、大小与恶意内容扫描,防止路径穿越。
- 外部 API:目标域名和动作清单、短期凭证、参数校验、速率限制和幂等策略。
隔离环境也要可复现。记录运行镜像、依赖、随机种子、输入版本与工具版本;否则“代码执行成功”无法转化为可复核结果。
查询成功之后还要验证
HTTP 200、进程退出码 0 或 SQL 完成,只代表工具没有报告执行失败。结果仍需检查:返回 Schema 是否符合合同、是否为空或被截断、数据水位是否满足任务、关键不变量是否成立、单位与分母是否正确。
对高影响查询,可以先 EXPLAIN 或运行限量探测,再执行完整版本。对工程变更,先生成差异、运行测试和影响分析;授权后执行,再回读目标状态。生成成功、提交成功和最终生效应是不同状态。
重试必须理解副作用
只读且确定性的查询在瞬时故障后通常可以限次重试。写操作则必须使用逻辑操作 ID,并核对下游是否真正支持幂等。超时并不等于失败;在未知状态下直接重发,可能创建两个工单、发送两封邮件或重复写入。
失败至少分为:调用者输入错误、策略拒绝、资源超限、可重试基础设施故障、工具确定性失败、结果验证失败和状态未知。每类错误都有不同去向,不能统一让模型“再试一次”。
审计记录什么,也要知道不记录什么
审计事件应连接任务、用户、委派身份、计划步骤、工具版本、参数摘要、策略决定、审批、执行 ID、结果引用和最终状态。高风险动作保存可验证的参数快照。
普通日志不应记录密钥、完整敏感结果或无需保留的提示词。审计系统本身需要访问控制、完整性保护和保留策略;“有日志”不等于日志足以追责。
最小上线测试
上线前至少验证:
- 越权表、列、行和工具调用全部被阻断;
- 注释、别名、子查询和多语句不能绕过静态规则;
- 扫描量、超时、输出行数和并发上限有效;
- 沙箱无法访问未授权文件、网络和环境变量;
- 相同幂等键不会造成重复副作用;
- 超时后的未知状态会先查询而不是盲目重试;
- 取消任务能在执行端真正停止,而非只停止聊天界面;
- 结果截断、空集、缺失数据和验证失败会被正确标记。
执行层成熟的标志,不是 Agent 能调用多少工具,而是每个动作都能回答:谁授权、在什么边界内运行、用了多少资源、产生了什么结果、失败后怎样恢复。只有这样,模型输出才可能安全地进入真实系统。