文章摘要
前二十一篇已经把生产级 Agent 的运行、权限、审计、回放、SLO 和多租户隔离逐层搭起来。
到了这一阶段,平台已经有越来越多“规则”:
哪些模型能用 哪些Tool能调 哪些租户能访问哪些数据 什么动作需要审批 什么情况下要Fail Closed 哪个Artifact允许外发 哪个Sandbox可以联网 Error Budget烧到多少要冻结发布如果这些规则散落在:
System Prompt Java if/else 数据库配置 管理员后台 Kubernetes YAML 审批流程真正出问题时,团队很难回答:
当时为什么允许? 哪条规则生效? 如果改规则,会影响多少Run? 这次Policy变更会不会把某个租户直接打挂?所以 Agent Control Plane 下一步必须把“规则”从业务代码里抽出来,变成:
Policy-as-Code再进一步,在真正生效前运行:
Policy Simulation也就是拿历史真实 Run、当前配置和候选规则做离线重放,先看“如果昨天就用了新规则,会发生什么”。
这一篇直接把策略模型、版本、决策日志、影子评估、Impact Analysis、Historical Replay、Canary、Break Glass 和发布门禁串成一套。
一、为什么Agent平台特别容易Policy爆炸
一个普通 API 权限可能只是:
Role → PermissionAgent 决策通常同时看:
User Tenant Agent Purpose Capability Resource Data Class Risk Approval Model Region Budget Time例如:
legal-agent想调用:
document.export真正决策可能是:
用户属于Matter吗? Artifact是不是Privileged? 目标是不是外部邮箱? 当前租户允许外发吗? 是否有审批? 审批绑定的Action Hash一致吗?如果全部写在一个 Service 里:
if(...){if(...){if(...){}}}半年后没人敢改。
二、Policy首先必须独立版本化
定义:
publicrecordPolicyBundle(StringpolicyId,Stringversion,StringcontentHash,PolicyStatusstatus,InstantcreatedAt,StringcreatedBy){}状态:
publicenumPolicyStatus{DRAFT,VALIDATED,SHADOW,CANARY,ACTIVE,RETIRED}不要允许:
编辑ACTIVE Policy并原地保存每次修改都生成新版本。
三、Policy输入必须结构化
不要把整个 Prompt 丢给另一个 LLM:
“你觉得这次允许吗?”真正 Policy 输入应该是确定性对象。
publicrecordPolicyInput(StringtenantId,StringsubjectId,StringagentId,Stringpurpose,Stringcapability,StringresourceId,DataClassdataClass,RiskLevelrisk,StringmodelId,Stringregion,StringapprovalId,BigDecimalestimatedCost){}模型可以帮助提取 Purpose。
最终 Policy 不直接依赖自然语言。
四、Decision至少不是boolean
publicenumPolicyDecision{ALLOW,DENY,REQUIRE_APPROVAL,REQUIRE_STRONG_AUTH,REQUIRE_HUMAN_REVIEW}生产系统里:
允许 / 禁止通常太粗。
例如:
email.send可以允许,但需要:
Action-bound Approval这就不是 DENY。
五、Decision还必须给出Reason Code
publicrecordPolicyResult(PolicyDecisiondecision,List<String>reasonCodes,StringpolicyId,StringpolicyVersion,StringinputHash){}例如:
{"decision":"DENY","reasonCodes":["CROSS_TENANT_RESOURCE","EXTERNAL_EGRESS_BLOCKED"],"policyVersion":"v42"}以后排障不需要重新猜。
六、Policy定义最好声明输入和输出
例如 YAML:
policy:external-sendversion:v42match:capability:-email.send-messages.sendrules:-when:data_class:RESTRICTEDdestination:EXTERNALdecision:DENYreason:restricted_external_egress-when:risk:HIGHdecision:REQUIRE_APPROVAL-otherwise:decision:ALLOW真实实现可以使用:
OPA/Rego Cedar 自研DSL关键不是语言。
关键是:
Policy是数据和代码资产而不是散落逻辑。
七、Policy-as-Code必须进Git
目录:
policies/ ├── model/ ├── capability/ ├──>policies/data-egress/external-send-v42.yaml每次变更:
PR → Review → CI → Simulation → Canary → Active和代码发布一样。
八、Policy CI第一层:Schema Validation
最简单但必须有。
例如:
Decision拼错 Reason缺失 Risk Enum非法直接在 CI 拒绝。
policy_ci:schema_validation:requiredduplicate_rule_check:requiredunreachable_rule_check:required九、第二层:Static Conflict Detection
两个规则:
Rule A: HIGH → DENY Rule B: HIGH → ALLOW如果优先级没有明确:
Policy本身就不确定CI 要发现:
Overlapping Rules Conflicting Outcome十、第三层:Golden Policy Tests
每条高风险 Policy 都要有固定测试。
例如:
cases:-name:restricted_external_sendinput:data_class:RESTRICTEDdestination:EXTERNALexpect:DENY-name:high_risk_sendinput:data_class:INTERNALrisk:HIGHexpect:REQUIRE_APPROVALPolicy 修改后自动回归。
十一、但Golden Case远远不够
真正危险的是:
规则本身看起来对 但对真实流量影响巨大例如:
新的Region Policy可能误伤 30% 客户。
所以需要:
Policy Simulation十二、Simulation到底模拟什么
候选:
policy-v43不要立刻 Active。
拿过去 7 天真实 Decision Input:
100万条重新评估:
Current Policy v42 vs Candidate v43然后比较:
Decision Diff十三、Simulation不需要重跑LLM
Policy 输入已经结构化。
所以只重放:
Decision Input而不是整个 Agent Run。
成本非常低。
数据模型:
publicrecordPolicySimulationCase(StringdecisionId,PolicyInputinput,PolicyResultbaseline,PolicyResultcandidate){}十四、最核心指标:Decision Flip
ALLOW → DENY DENY → ALLOW ALLOW → REQUIRE_APPROVAL REQUIRE_APPROVAL → ALLOW这四种变化风险完全不同。
十五、我会给Flip分类
publicenumDecisionFlipType{MORE_RESTRICTIVE,LESS_RESTRICTIVE,APPROVAL_ADDED,APPROVAL_REMOVED,NO_CHANGE}尤其:
DENY → ALLOW必须重点 Review。
因为它在扩大权限面。
十六、Simulation报告至少输出
{"baseline":"v42","candidate":"v43","total_cases":1000000,"changed":18291,"more_restrictive":17200,"less_restrictive":1091,"approval_added":8200,"approval_removed":91}单看:
changed=1.8%不够。
必须知道变化方向。
十七、还要按Slice看
总体只变化 1%。
但:
Legal Tenant: 变化 38%就很危险。
Slice 至少:
Tenant Tier Risk Capability Data Class Agent Region十八、不要全组合Slice
否则样本爆炸。
只看业务关键:
HIGH_RISK RESTRICTED_DATA EXTERNAL_SEND FINANCE LEGAL这些 Slice 单独 Gate。
十九、Simulation要计算影响业务对象
不只是 Decision 数。
还要:
涉及多少用户? 多少Agent? 多少Tenant? 多少Scheduled Task? 多少关键Workflow?例如:
{"tenants_impacted":39,"agents_impacted":112,"critical_workflows_impacted":4}这才方便 Release Review。
二十、Policy Input必须长期可回放
每次生产 Decision 都保存:
input_hash policy_version decision reason完整 Input 可以加密保存。
例如:
publicrecordPolicyDecisionRecord(StringdecisionId,StringrunId,StringtenantId,StringinputRef,StringinputHash,StringpolicyVersion,PolicyDecisiondecision,List<String>reasons,InstantoccurredAt){}没有历史输入,就无法真正 Simulation。
二十一、Privacy要注意
Policy Input 可能包含:
Resource ID Subject Tenant Data Class不一定要保存原始业务内容。
策略评估应该尽量依赖:
Metadata而不是完整 Prompt。
这也会让 Simulation 更安全。
二十二、Shadow Policy
Simulation看的是历史。
下一步:
ShadowCandidate v43 在真实线上请求中同时计算:
但不执行Active v42: 真正决定 Shadow v43: 只记录结果二十三、Shadow可以发现历史数据没覆盖的新模式
比如上线后出现:
新Capability 新Tenant类型 新RegionSimulation 数据里根本没有。
Shadow 能看到。
二十四、Shadow结果不能影响用户
即使 Candidate:
DENY只记录。
不要在 Shadow 阶段偷偷改变:
Latency Approval Tool否则不是真 Shadow。
二十五、Shadow Metrics
policy_shadow_total policy_shadow_flip_total policy_shadow_less_restrictive_total policy_shadow_error_total再看:
Evaluation LatencyPolicy 本身也不能把 Agent Tool Call 拖慢几百毫秒。
二十六、Policy性能也有SLO
例如:
P95 Decision < 20ms P99 < 50ms如果 Candidate 正确但慢 10 倍:
不能直接上线二十七、Canary Policy
Shadow 没问题以后:
1% 5% 20% 100%真实执行。
Canary Key 可以按:
Tenant Workspace Agent稳定分配。
不要每个请求随机,这会让同一个用户体验来回变化。
二十八、高风险Tenant不要自动进入Canary
例如:
Legal Finance Critical Production可以最后再进。
先从:
内部Tenant 测试Tenant 低风险Agent开始。
二十九、Policy发布要有Risk Classification
publicenumPolicyChangeRisk{LOW,MEDIUM,HIGH,CRITICAL}比如:
修改Reason文字 → LOW 增加Approval → MEDIUM DENY变ALLOW → HIGH 跨租户访问放开 → CRITICAL风险越高,发布门禁越严格。
三十、自动判断Risk可以靠Diff
例如:
- decision: DENY + decision: ALLOW自动:
HIGH如果涉及:
cross_tenant restricted_data直接:
CRITICAL三十一、Policy Diff应该像数据库Migration一样严肃
Review 页面不要只展示整个 YAML。
应该直接告诉 Reviewer:
新增3条 删除1条 Decision扩大91个历史Case 影响4个Critical Workflow这比手工读文件强太多。
三十二、一个Release Gate
policy_release_gate:schema:passgolden_tests:pass_rate:1.0simulation:less_restrictive_critical:0changed_ratio_max:0.05shadow:error_rate_max:0.001latency:p95_ms:20approvals:high:securitycritical:-security-platform_owner三十三、Policy不能只管Security
同一套机制还可以管理:
Model Routing Cost Budget Release Fallback Autonomy例如:
when:error_budget_remaining_lt:0.20decision:BLOCK_RELEASE这也是 Policy。
三十四、Model Policy也非常适合Simulation
比如昨天那种 Copilot Global Model Policy。
企业准备:
默认Enabled →默认Disabled先跑 Simulation:
哪些Agent会失去Primary Model? 哪些Fallback也不可用? 哪些自动化会停止?再改。
三十五、Capability Policy也适合
准备禁掉:
shell.exec先看过去 30 天:
多少Agent使用? 哪些是Critical Path? 有没有替代Capability?不是直接关。
三十六、Data Egress Policy更需要Simulation
准备新增:
CONFIDENTIAL 禁止外部Email这可能影响大量销售 Workflow。
Simulation 告诉你:
过去7天本来会拦多少次业务 Owner 可以提前调整。
三十七、Policy Exception必须单独建模
现实里总会有:
这个租户临时例外不要去改主 Policy。
定义:
publicrecordPolicyException(StringexceptionId,StringpolicyId,StringtenantId,StringsubjectId,StringresourcePattern,Stringreason,StringticketId,InstantexpiresAt,StringapprovedBy){}异常一定要:
有期限 有理由 有审批三十八、Exception不能无限续
例如:
30天到期重新 Review。
如果连续续 6 次:
说明主Policy可能不适合应该进入治理分析。
三十九、Break Glass和Exception不是一回事
Exception:
事先批准Break Glass:
事故紧急访问Break Glass 需要更强:
MFA 短TTL 强审计 实时告警 自动撤销四十、Break Glass仍然走Policy
不要写:
if breakGlass: return ALLOW而是:
特殊Policy检查:
Incident ID Approver TTL Scope四十一、Policy Engine失败怎么办
这是一个非常重要的问题。
如果权限 Policy 服务挂了:
写操作怎么办?高风险:
Fail Closed低风险、且有短期签名缓存:
可以有限Fail Open但必须明确。
四十二、Fail Mode也是Policy
policy_failure:destructive:mode:fail_closedexternal_write:mode:fail_closedinternal_read:mode:signed_cachemax_age:30s不要等事故时现场讨论。
四十三、缓存Policy Decision要带Version
Key:
tenant agent purpose capability resource_class policy_versionPolicy v42 → v43:
缓存自动失效否则新规则上线后旧 Decision 还能活很久。
四十四、紧急撤权要跳过普通Cache
Security 触发:
Emergency Deny应该进入:
Global Revocation Layer优先于普通 Policy Cache。
例如:
disable capability=email.send立即生效。
四十五、Policy决策链要可解释
最终结果可能来自:
Tenant Policy + Enterprise Policy + Exception + Emergency DenyAudit 要保存:
Decision Trace四十六、Decision Trace示例
{"final":"DENY","steps":[{"policy":"enterprise-egress-v42","result":"ALLOW"},{"policy":"tenant-legal-v18","result":"REQUIRE_APPROVAL"},{"policy":"emergency-deny-v3","result":"DENY"}]}这比一个:
403可解释太多。
四十七、Policy优先级必须固定
例如:
Emergency Deny > Tenant Restriction > Enterprise Base > Exception但 Exception 是否能覆盖 Tenant Restriction,要明确。
我一般建议:
Exception只能放宽可放宽的Policy不能覆盖:
Zero Tolerance Cross Tenant Legal Hold四十八、Policy层级不要动态由模型决定
模型不能说:
“这个情况特殊,我认为Exception优先。”优先级是确定性系统规则。
四十九、Policy Simulation还可以做“反事实事故分析”
发生事故:
某个Agent错误外发你准备新 Policy:
v44可以拿事故 Run:
Replay Input问:
如果当时是v44, 会不会挡住?这就是非常直接的 Regression Evidence。
五十、每个事故最终应该沉淀Policy Regression Case
例如:
incident:INC-2026-081input:data_class:CONFIDENTIALdestination:EXTERNALcapability:email.sendexpected:decision:DENY以后任何 Policy Candidate 都跑。
五十一、Policy Dataset不要只来自事故
还要有:
Normal Cases Edge Cases Synthetic Cases Abuse Cases否则 Policy 会过拟合历史事故。
五十二、Property-based Policy Test
例如不变量:
任何CrossTenant Resource 永远不能ALLOW随机生成:
Tenant A Tenant B Resource Capability断言:
assertNotEquals(PolicyDecision.ALLOW,evaluate(crossTenantInput));这比只写几个固定样例强。
五十三、Policy Coverage也要统计
有多少生产 Decision:
命中了显式规则多少落到:
default如果:
default hit = 60%说明 Policy 设计过于粗糙。
五十四、我会看Default Decision Rate
default_decision_total / all_policy_decisions目标应该逐步下降。
特别是高风险 Capability:
最好0%每种情况都有显式规则。
五十五、Unknown Input不能默认Allow
新 Data Class:
TOP_SECRET旧 Policy 不认识。
最危险:
else → ALLOW更合理:
Unknown → DENY / REVIEWPolicy Schema 演进要采用:
Secure Default五十六、Policy版本和Run必须绑定
Run 中途 Policy 升级怎么办?
一般有两种:
Snapshot Policy
Run 创建时固定:
v42整个 Run 使用 v42。
适合:
低风险长任务Dynamic Policy
每个高风险 Action:
读取当前Active适合:
安全写操作两者不能混得不清楚。
五十七、高风险Action我建议Dynamic Re-evaluate
即使 Run 开始时:
允许email.send半小时后 Security 禁掉。
真正执行前应该重新评估:
current policy不要因为 Run Snapshot 还允许就继续写。
五十八、但Replay必须知道当时版本
Audit 保存:
Run Policy Snapshot Action Policy Version事故重放时才能还原。
五十九、Policy Rollback也要一键
Candidate v43 出现问题:
切回v42不能重新编辑一份“像v42”的文件。
Active Pointer:
policy_id → version回滚只切 Pointer。
六十、Rollback前也要注意新产生的数据
如果 v43 已经允许了一批新操作:
回滚Policy不会自动撤销历史副作用。
所以 Release Report 需要:
affected_runs side_effects方便补救。
六十一、Policy Observability
指标:
policy_decision_total{ policy, version, decision } policy_flip_total{ type } policy_eval_latency_ms policy_default_total policy_exception_total policy_break_glass_total高基数 Tenant 不一定放 Metric。
细节进 Trace / DB。
六十二、Policy SLO
P95 Decision < 20ms Error Rate < 0.01% Unknown Input Allow = 0 CrossTenant Allow = 0 Critical Default Decision = 0这些都可以成为平台 SLO。
六十三、Policy Dashboard第一页只看5件事
Active Version Decision Distribution Default Rate Exception Count Recent Less-Restrictive Changes不要把管理员淹没在上百条规则里。
六十四、Policy Owner必须明确
每条 Policy:
Security Finance Legal Platform Product谁负责?
publicrecordPolicyOwnership(StringpolicyId,StringownerTeam,StringapproverGroup,StringescalationChannel){}没人负责的 Policy 最终一定过期。
六十五、Policy要有Review周期
例如:
Security: 90天 Finance Budget: 30天 Temporary Exception: 14天超过时间:
REVIEW_REQUIRED不能一写永远有效。
六十六、完整发布链
Policy Change ↓ Schema ↓ Static Conflict ↓ Golden Test ↓ Historical Simulation ↓ Risk Classification ↓ Approval ↓ Shadow ↓ Canary ↓ Active ↓ Observe Burn ↓ Rollback if needed这套流程看起来很重。
但高风险 Agent 权限本来就不应该“后台改个开关立即全网生效”。
六十七、Spring Boot可以怎么组织
policy/ ├── model ├── loader ├── evaluator ├── simulation ├── shadow ├── release ├── exception ├── audit └── metrics不要让:
PolicyEvaluator同时负责 Git、发布、Simulation 和异常。
六十八、Simulation Service接口
publicinterfacePolicySimulationService{PolicySimulationReportsimulate(StringbaselineVersion,StringcandidateVersion,TimeWindowwindow);}输出:
publicrecordPolicySimulationReport(longtotal,longchanged,longlessRestrictive,longmoreRestrictive,List<ImpactSlice>slices,List<String>criticalCases){}六十九、Release Gate接口
publicinterfacePolicyReleaseGate{ReleaseDecisionevaluate(PolicySimulationReportsimulation,ShadowReportshadow,PolicyChangeRiskrisk);}Policy 本身也进入 Control Plane。
七十、本篇上线检查清单
□ Policy独立于Prompt和业务代码 □ 每次修改生成新Version □ Policy输入结构化 □ Decision不是只有Allow/Deny □ 每次Decision记录Reason Code □ Policy进入Git和PR □ CI做Schema和Conflict检查 □ 高风险Policy有Golden Cases □ Candidate上线前做历史Simulation □ Simulation区分权限扩大和收紧 □ 关键Tenant/Risk做Slice分析 □ Candidate支持Shadow □ Shadow不影响真实决策 □ Policy有P95性能SLO □ 高风险变更走Canary □ Exception有Owner/Reason/Expiry □ Break Glass独立治理 □ Policy失败模式提前定义 □ Emergency Deny能绕过普通Cache □ Decision Trace可解释 □ Incident沉淀Regression Case □ Unknown Input默认安全 □ 高风险Action执行前重新评估 □ Active Version可一键Rollback总结
Agent 平台发展到一定规模以后,真正难维护的往往不再是 Prompt。
而是:
“到底哪些事情允许发生。”如果这些规则散落在几十个 Service、管理员页面和配置文件里,系统越自动化,风险越难预测。
Policy-as-Code 解决的是:
规则可版本、可Review、可审计Policy Simulation 解决的是:
改规则之前, 先知道它会改变什么Shadow 和 Canary 再解决:
历史上没见过的新流量怎么办做到这一层以后,Agent 权限和风险治理才从:
“配置管理”真正升级成:
可测试的软件工程。下一篇继续推进:
生产级Agent(23):Agent配置供应链——模型、Prompt、Skill、Tool与Policy如何做签名发布和可信加载。