Agent Governance Toolkit 原生预算强制执行:从 SpendGuard 组合 Demo 到 ACS Manifests 与 budgets.rego
【免费下载链接】agent-governance-toolkitAI Agent Governance Toolkit — Policy enforcement, zero-trust identity, execution sandboxing, and reliability engineering for autonomous AI agents. Covers 10/10 OWASP Agentic Top 10.项目地址: https://gitcode.com/GitHub_Trending/ag/agent-governance-toolkit
本文以仓库中 examples/spendguard-composite/README.md 为切入点,说明旧的本地策略组合 Demo 已被移除、预算管控能力已内化为 Agent Governance Toolkit(AGT)的原生能力:宿主只需在 ACS manifest 中声明预算阈值,运行时通过 Rego 规则库agt.budgets对工具调用次数、Token 用量、耗时与成本四类计数器进行强制校验并输出标准 verdict。读完本文,你将掌握预算计数器的快照契约、fail-safe / fail-closed 语义、默认策略中的判决优先级,以及如何编写和验证自己的预算策略。
一、背景:为什么旧的 SpendGuard 组合 Demo 被移除
examples/spendguard-composite/README.md 的正文只有两句话,但它交代了一个重要的架构决策:
The old local policy-composition demo was removed. Native budget enforcement uses ACS manifests and
policy-engine/policy/lib/budgets.rego.
即:不再通过本地拼装策略代码来模拟预算控制,而是由 AGT 运行时原生提供预算强制能力,策略作者只负责在ACS manifest(Agent Control Specification 清单)中声明阈值,真正的判定逻辑统一收敛到 policy-engine/policy/lib/budgets.rego 这一份可复用的 Rego 规则库中。
这个目录目前的实际内容也印证了这一点——examples/spendguard-composite 下仅保留README.md与 requirements.txt,没有任何策略组合代码:
examples/spendguard-composite/ ├── README.md └── requirements.txt其中 requirements.txt 还保留了运行环境的分层说明:Mock 模式只需 AGT 本体(agent-governance-toolkit>=3.4);真实模式则需要 SpendGuard SDK(spendguard-sdk[agt]>=0.4,该包以 alpha 版本发布在 PyPI,需要pip install --pre 'spendguard-sdk[agt]>=0.4'安装,[agt]extra 会带入 protobuf 绑定与 gRPC 客户端)。也就是说,预算数据的来源(宿主跟踪的计数器)可以先用 Mock 模拟,再切换到真实计费 SDK。
二、原生预算强制执行的架构:ACS Manifest 声明 + Rego 库判定
移除本地组合 Demo 后,预算强制执行的职责边界变得非常清晰:
| 角色 | 职责 | 位置 |
|---|---|---|
| ACS Manifest | 声明干预点(intervention point)、绑定策略、给出预算阈值配置 | 宿主项目内的 manifest 文件 |
| 快照(snapshot) | 宿主在每个干预点构建的输入,包含只读的envelope.budgets计数器 | 由 AGT 宿主 SDK 构建 |
agt.budgets规则库 | 读取计数器、对比阈值、输出 deny verdict | policy-engine/policy/lib/budgets.rego |
| 默认策略绑定 | 把 manifest 中的阈值接入统一判决链 | policy-engine/policy/lib/agt_default.rego |
从源码结构看,规则库的入口注释明确描述了它的契约来源:budgets.rego 读取input.snapshot.envelope.budgets块(对应 AGT-SNAPSHOT-1.0.md 第 1 节),并按 SPECIFICATION.md 第 13.1 节输出 AGT 标准 verdict;当计数器缺失时规则 fail safe(按 0 处理),当计数器存在但格式错误时 fail closed(直接 deny,绝不将其强转为 0)。
三、快照契约:envelope.budgets的四个标准计数器
预算判定不是各宿主自行定义的数据结构,而是遵循统一规范 policy-engine/spec/agt/AGT-SNAPSHOT-1.0.md。该文档定义每个干预点快照的公共信封(common envelope),其中budgets字段是必填的:
{ "envelope": { "agent": { "id": "string", "version": "string", "name": "string" }, "session": { "id": "string", "started_at": "ISO-8601 UTC" }, "intervention_point": "agent_startup|input|...|agent_shutdown", "timestamp": "ISO-8601 UTC", "budgets": { "tool_call_count": 0, "token_count": 0, "elapsed_seconds": 0.0, "cost_usd": 0.0 }, "trace": { "trace_id": "string", "span_id": "string" }, "tenant": { "id": "string", "name": "string" } } }四个计数器构成预算管控的最小集合(对应 budgets.rego 中的budget_counter_names):
| 计数器 | 类型 | 语义 |
|---|---|---|
tool_call_count | 整数 | 宿主累计的工具调用次数 |
token_count | 整数 | 累计 Token 消耗量 |
elapsed_seconds | 浮点 | 累计运行时长(秒) |
cost_usd | 浮点 | 累计成本(美元) |
规范特别强调(AGT-SNAPSHOT-1.0.md):envelope.budgets是宿主跟踪的计数器值,且是"本次评估开始时"的快照。这些值在引擎内部是只读的,宿主在post_*钩子之后才递增它们——这保证了策略评估的确定性(同一份快照在任何时刻求值结果一致),也意味着预算检查发生在动作执行之前,先拦截、后执行。
四、budgets.rego 源码剖析:fail-safe 与 fail-closed 语义
policy-engine/policy/lib/budgets.rego 是预算判定的核心实现(包名agt.budgets)。它值得逐层拆解:
4.1 计数器的读取与防御性默认
budget_counter(name) := value if { value := input.snapshot.envelope.budgets[name] is_number(value) } else := 0 if { not budget_counter_present(name) }- 计数器存在且为数字 → 读取实际值;
- 计数器缺失→ 按
0处理(fail safe,不误伤正常请求); - 计数器存在但非数字(如字符串
"999999"或null)→ 该规则不匹配,被判定为畸形计数器。
畸形检测单独成规则,防止"缺失=0"的默认逻辑被畸形值污染:
malformed_budget_counter(name) if { budget_counter_names[name] value := input.snapshot.envelope.budgets[name] not is_number(value) }4.2 四类阈值判定
每个计数器对应一个独立的超限判断,且都要求阈值为数字(is_number(limit)),避免用非数字阈值产生意外比较结果:
max_tool_calls_exceeded(limit) if { is_number(limit); tool_call_count >= limit } max_tokens_exceeded(limit) if { is_number(limit); token_count >= limit } timeout_exceeded(limit) if { is_number(limit); elapsed_seconds >= limit } max_cost_exceeded(limit) if { is_number(limit); cost_usd >= limit }注意边界语义:>=意味着计数器恰好等于阈值时即判定超限(见下文的测试用例验证)。
4.3 统一入口deny_if_budget_exceeded与判决优先级
所有预算判定收敛到一个入口函数,按固定优先级输出 deny verdict:
deny_if_budget_exceeded(thresholds) := verdict if { some name in budget_counter_names malformed_budget_counter(name) verdict := { "decision": "deny", "reason": "budget_counter_invalid", ... } } else := verdict if { max_tool_calls_exceeded(thresholds.tool_call_count) ... } } else := verdict if { max_tokens_exceeded(thresholds.token_count) ... } } else := verdict if { timeout_exceeded(thresholds.elapsed_seconds) ... } } else := verdict if { max_cost_exceeded(thresholds.cost_usd) ... }优先级为:budget_counter_invalid(计数器畸形)→budget_tool_calls_exceeded→budget_tokens_exceeded→budget_timeout_exceeded→budget_cost_exceeded。任何一类超限都会给出标准化的reason与含实际值/阈值的message,便于审计与告警定位。所有阈值都不超限时该函数不产生 verdict,交由上层策略继续判定。
五、接入默认策略:AGT 统一判决链中的位置
预算判定并不孤立存在,而是作为 policy-engine/policy/lib/agt_default.rego 中默认策略库的一环被调用。该库把 manifest 中的阈值配置(data.agt.defaults.config)映射到各个 stock helper,预算部分如下:
budgets_verdict := value if { thresholds := cfg.budgets is_object(thresholds) value := budgets.deny_if_budget_exceeded(thresholds) }也就是说,策略作者在 manifest 中只需提供形如budgets: {tool_call_count: 50, token_count: 10000, elapsed_seconds: 600, cost_usd: 5}的 YAML 配置,无需编写任何 Rego。最终判决按 AGT 的严重度排序(deny > escalate > transform > warn > allow)组合各维度规则(agt_default.rego):
- IFC(信息流控制)deny
- 置信度 deny
- 预算 deny(budgets_verdict)
- 内容哈希 deny
- 出口(egress)deny
- 模式匹配 deny
- 审批升级(escalate)
- 改写(transform)
- 漂移告警(warn)
- allow
从优先级看,预算拦截排在信息流与置信度检查之后、内容与出口检查之前——即先确认"数据流是否合规、结果是否可信",再对"消耗是否超限"做闸门,避免预算检查被畸形数据绕过(畸形计数器优先于一切超限判定)。
如果策略作者不写 Rego 而直接绑定 manifest 的rego策略,默认绑定即是data.agt.defaults.verdict,这一绑定由 M6 GovernancePolicy 迁移工具对来自旧声明式策略的宿主自动生成(见 agt_default.rego 注释)。
六、实战:编写并验证自己的预算策略
6.1 手写 Rego 直接读取计数器
除默认策略外,你也可以在自定义策略中直接读取快照计数器。仓库生产示例 examples/policies/production/rego/strict.rego 演示了标准写法:
denials contains "Tool call budget exceeded (10)" if { budgets := object.get(object.get(object.get(input, "snapshot", {}), "envelope", {}), "budgets", {}) object.get(budgets, "tool_call_count", 0) >= 10 }object.get三级防御式取值保证快照结构不完整时不会直接求值报错。同类示例还有 enterprise.rego(工具调用上限 50)、financial.rego(30)、healthcare.rego(25)、minimal.rego(100),可据此为不同业务线设置差异化的消耗闸门。
6.2 Cedar 镜像及其限制
预算规则还提供 Cedar 语言的镜像实现 policy-engine/policy/cedar-lib/budgets.cedar,用forbid策略在context.envelope.budgets.tool_call_count >= resource.max_tool_calls等条件下拒绝动作,@id注解会直接成为 AGT 的 deny reason。但需要注意其明确边界:Cedar 的长整型只覆盖整数预算,elapsed_seconds与cost_usd这两个浮点计数器在 Cedar 中未建模;需要浮点预算闸门时必须使用 Rego 库的agt.budgets.max_cost_exceeded与agt.budgets.timeout_exceeded。
6.3 测试验证:budgets_test.rego
规则库自带完整的 OPA 测试 policy-engine/policy/lib/budgets_test.rego,是理解语义的最佳教材:
- 缺失预算默认 0:
input中envelope为空时四个计数器均为0(不触发超限); - 畸形计数器不归零:
token_count为字符串"999999"时budgets.token_count不成立,且malformed_budget_counter("token_count")成立——存在但畸形的值不会被静默当作 0; - 边界语义:
tool_call_count == 10且阈值 10 时max_tool_calls_exceeded(10)匹配(等于即超限);而计数 3 时不匹配; - 各类 reason:分别验证
budget_counter_invalid、budget_tool_calls_exceeded、budget_tokens_exceeded、budget_timeout_exceeded、budget_cost_exceeded的输出; - 缺失快照:
input == {}时deny_if_budget_exceeded不产生 verdict(fail safe)。
例如对 cost 超限的验证:
test_max_cost_exceeded_matches if { budgets.max_cost_exceeded(1.5) with input as snapshot_with({"cost_usd": 2.0}) }七、与成本治理体系的关系:从预算闸门到软/硬双阈值
预算强制执行的判定内核与仓库中的成本治理架构设计(docs/adr/0012-cost-governance-observability-policies.md)互相呼应。该 ADR 提出分层成本模型:
- 三层成本来源:工具注解的
cost_hint(估算)→ 策略 YAML 的cost_map(覆盖估算)→ 运行时计量(实际账单,修正估算); - 双阈值模型:
soft_cap(软上限,超限只告警不拦截)+hard_cap(硬上限,超限阻断后续动作),并支持按agent与global两种 scope、以滚动时间窗(如 1h/24h)计数; - 事后强制(post-action)为主:因为事前预测 LLM Token 数与工具成本不可靠,先记录实际成本再检查预算状态。
这与budgets.rego的"宿主在post_*钩子后递增计数器"的只读快照模型天然契合:硬上限对应的就是deny_if_budget_exceeded输出的 deny verdict,软上限则可在宿主侧基于同批计数器数据发出 OTel 告警事件。也就是说,cost_usd计数器的宿主实现既可以来自 Mock 模拟,也可以来自 SpendGuard SDK 的实际计量(requirements.txt 中的spendguard-sdk[agt]模式)。
八、落地清单
要在自己的 AGT 宿主中启用原生预算强制执行,按以下步骤操作:
- 确认宿主版本:Mock 模式安装
agent-governance-toolkit>=3.4;需要真实计费数据时pip install --pre 'spendguard-sdk[agt]>=0.4'; - 在 ACS manifest 中声明预算:在
rego策略的配置数据中给出budgets: {tool_call_count, token_count, elapsed_seconds, cost_usd}四类阈值(可只声明需要的维度,缺失维度 fail safe); - 确保宿主按 AGT-SNAPSHOT-1.0.md 构建快照:每个干预点携带
envelope.budgets,值取"评估开始时"的只读计数器,并在post_*钩子之后递增; - 绑定默认策略或自研规则:默认绑定
data.agt.defaults.verdict即包含预算闸门;自研 Rego 时复用import data.agt.budgets并调用deny_if_budget_exceeded(thresholds); - 回归验证:参照 budgets_test.rego 覆盖"缺失默认 0、畸形 fail closed、等于阈值即超限、各类 reason"等关键语义,确认畸形计数器不会被静默放行。
至此,预算管控从"Demo 级的本地策略拼装"演进为"ACS manifest 声明 + 标准 Rego 规则库判定"的原生能力,且四类计数器(工具调用、Token、耗时、成本)的判定语义在 budgets.rego 与配套测试、规范、Cedar 镜像中保持一致,可直接迁移到任意 AGT 宿主。
【免费下载链接】agent-governance-toolkitAI Agent Governance Toolkit — Policy enforcement, zero-trust identity, execution sandboxing, and reliability engineering for autonomous AI agents. Covers 10/10 OWASP Agentic Top 10.项目地址: https://gitcode.com/GitHub_Trending/ag/agent-governance-toolkit
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考