生产级Agent(23):配置供应链与可信加载
文章摘要
前二十二篇已经把生产级 Agent 的运行、权限、审计、回放、SLO、多租户和 Policy-as-Code 串起来。
到这里,平台里真正决定 Agent 行为的东西已经远远不止模型:
Model
Prompt
Skill
Tool Schema
MCP Server
Policy
Routing
Memory Rule
Eval Gate
Sandbox Profile
这些对象任何一个被误改、被替换、加载了错误版本,Agent 行为都会变化。
但很多系统现在仍然这样部署:
Prompt从数据库读最新
Tool Schema启动时动态拉
Skill从共享目录加载
Policy看后台当前值
模型路由读一张配置表
结果就是:
同一个Agent版本
今天和明天
可能根本不是同一个运行环境
真正的生产 Agent 需要一套类似软件供应链的机制:
Source
→ Build
→ Validate
→ Sign
→ Publish
→ Promote
→ Trusted Load
→ Verify
→ Run
也就是说,把 Agent 配置本身当成可构建、可签名、可追踪、可回滚的供应链资产。
这一篇直接实现:
Agent Bundle
Content Hash
Dependency Lock
Provenance
Signature
Artifact Registry
Promotion
Trusted Loader
Revocation
Tenant Pinning
Rollback
让一次 Agent Run 能明确回答:
到底加载了哪一个模型、
哪一版Prompt、
哪一个Skill、
哪一组Tool和Policy,
以及这些东西是否来自可信发布链。
一、为什么“配置”已经等同于代码
传统应用里:
代码
决定主要行为。
Agent 系统里:
Prompt改一句
可能直接改变 Tool 选择。
Tool Description改一句
可能让模型开始频繁调用某个高风险能力。
Policy改一条
可能把:
REQUIRE_APPROVAL
变成:
ALLOW
Model Routing
也可能让敏感数据切到另一个 Provider。
这些都已经属于:
Executable Configuration
所以不能再把它们当“普通后台配置”。
二、先定义Agent Bundle
不要部署:
agent_id = research-agent
然后运行时再动态拼。
真正发布的应该是一个不可变 Bundle。
public record AgentBundleManifest(
String agentId,
String agentVersion,
String modelProfileRef,
String promptBundleRef,
String skillLockRef,
String toolCatalogRef,
String policyBundleRef,
String sandboxProfileRef,
String evalGateRef,
String contentHash,
String provenanceRef,
String signatureRef) {
}
Agent Run 只引用:
agent-bundle:v23
不直接引用一堆“当前最新”。
三、Bundle必须Immutable
错误:
agent:v23
发布后还允许后台修改 Prompt。
这样:
v23
已经失去版本意义。
正确:
任何组成变化
→生成v24
Bundle v23 永远不可变。
四、Prompt不能只保存template version
一个最终 Prompt 可能由:
System
Developer Instruction
Tenant Policy
Skill
Tool Description
Few-shot
Runtime Flags
编译而成。
所以要同时保存:
Source Version
Compiled Artifact Hash
五、Prompt Bundle
public record PromptBundle(
String bundleId,
String sourceVersion,
String compilerVersion,
String compiledContentRef,
String compiledHash,
int tokenCount,
List dependencyRefs) {
}
这样 Run 能证明:
模型真正看到的是哪一版
而不是只知道:
template=v12
六、Skill要做Lock File
Agent Skill 经常继续依赖:
Tool
MCP
Prompt Fragment
Schema
如果运行时:
自动加载latest
很容易漂移。
所以像 npm lock 一样:
skills:
contract-review:
version: 4.2.1
sha256: abc...
web-research:
version: 3.8.0
sha256: def...
发布时锁死。
七、Tool Catalog也必须版本化
Tool 不只是:
name
还包括:
Description
Input Schema
Output Schema
Risk
Timeout
Idempotency
Provider Binding
任何字段变化都可能影响 Agent。
八、一个Tool Descriptor
public record ToolDescriptor(
String capabilityId,
String version,
String description,
JsonNode inputSchema,
JsonNode outputSchema,
RiskLevel risk,
Duration timeout,
boolean idempotent,
String providerBindingRef,
String contentHash) {
}
Agent Bundle 锁:
tool-catalog-v81
九、Provider Binding不要和Capability混
全局 Capability:
crm.customer.read
Tenant A:
Salesforce
Tenant B:
Dynamics
所以 Bundle 可以锁:
Capability Contract
Tenant 运行时再选择:
Approved Provider Binding
但 Provider Binding 也必须有版本和签名。
十、Model Profile不能只写model_id
模型配置至少包括:
Provider
Model
Reasoning
Temperature
Region
Fallback
Data Retention
Context Limit
public record ModelProfile(
String profileId,
String primaryModel,
List fallbacks,
String region,
String retentionClass,
Map parameters,
String contentHash) {
}
如果 Provider 改了默认参数:
Profile Hash
应该发生变化。
十一、Policy Bundle继续沿用上一期的不可变版本
policy-v42
Bundle 直接锁:
policyBundleRef=policy-v42
不要运行时永远读:
current_active_policy
低风险动作可以使用 Run Snapshot。
高风险写操作执行前,再额外做:
Current Emergency Policy Re-evaluate
两层并存。
十二、Sandbox Profile也属于供应链
sandbox:
runtime: gvisor
image_digest: sha256:...
cpu: 2
memory: 2Gi
network_policy: egress-v8
artifact_policy: artifact-v11
如果:
image tag变了
但 Bundle 版本没变,就无法复现。
所以必须使用:
image digest
而不是:
python:3.12-latest
十三、Agent Bundle需要Dependency Graph
Agent Bundle
├─ Prompt Bundle
├─ Model Profile
├─ Skill Lock
│ ├─ Skill A
│ └─ Skill B
├─ Tool Catalog
├─ Policy Bundle
└─ Sandbox Profile
任一依赖变化:
哪些Agent受影响?
必须能算。
十四、Dependency表
create table agent_bundle_dependency (
bundle_id varchar(128) not null,
dependency_type varchar(64) not null,
dependency_id varchar(128) not null,
dependency_version varchar(64) not null,
dependency_hash varchar(64) not null
);
例如:
Tool v19
发现安全问题
可以直接查询:
哪些Bundle引用它
十五、内容Hash是第一层完整性
每个 Artifact:
Canonical Content
↓
SHA-256
Manifest 保存:
content_hash
下载后重新算。
如果不一致:
拒绝加载
十六、但Hash不能证明“是谁发布”
攻击者同时替换:
Artifact
+
Manifest Hash
Hash仍然对。
所以还需要:
Signature
十七、发布签名
public record ArtifactSignature(
String artifactId,
String artifactVersion,
String contentHash,
String signer,
String keyId,
String keyVersion,
String signature,
Instant signedAt) {
}
私钥放:
KMS / HSM
CI 只有:
sign permission
不能导出 Key。
十八、签名者不能是开发者个人Key
不要:
张三本地签
生产供应链应该是:
Trusted Build Identity
例如:
agent-release-prod
只有通过:
CI
Tests
Approvals
的构建能使用 Production Signing Key。
十九、Dev和Prod Key必须分开
dev-signing-key
staging-signing-key
prod-signing-key
Runtime:
Production
只信:
prod key
所以开发者自己构建一个 Bundle:
即使内容完全一样
也不能被生产加载。
二十、Trusted Loader是最关键的一层
如果 Bundle 签名很好,但 Runtime 启动时:
不验证
整个供应链等于没做。
加载流程:
Fetch Manifest
↓
Verify Manifest Signature
↓
Verify Dependency Hash
↓
Verify Artifact Signature
↓
Check Revocation
↓
Check Tenant Policy
↓
Load
任何一步失败:
Fail Closed
二十一、Trusted Loader接口
public interface TrustedAgentBundleLoader {
VerifiedAgentBundle load(
String bundleId,
TenantContext tenant);
}
不要让业务代码直接:
artifactStore.get(...)
所有 Agent 启动都走 Loader。
二十二、Verified Bundle
public record VerifiedAgentBundle(
AgentBundleManifest manifest,
VerifiedPromptBundle prompt,
VerifiedModelProfile model,
List skills,
VerifiedToolCatalog tools,
VerifiedPolicyBundle policies,
VerifiedSandboxProfile sandbox,
VerificationReceipt receipt) {
}
运行时拿到的是:
Verified
对象。
而不是普通字符串引用。
二十三、Verification Receipt
public record VerificationReceipt(
String bundleId,
String manifestHash,
String signingKeyVersion,
List verifiedDependencyHashes,
Instant verifiedAt,
String loaderVersion) {
}
每个 Run 保存它。
以后事故时:
能证明启动时做过验证
二十四、Artifact Registry不要允许覆盖
危险:
PUT /bundle/v23
把旧 v23 覆盖。
正确:
v23 immutable
任何新内容:
v24
Registry 开启:
Write Once
最好不允许 Delete,除非专门 Retention 流程。
二十五、Bundle Build要可复现
同样 Source:
相同Compiler
相同Dependencies
应该产出:
同一个Hash
如果每次 Build Hash 都不同:
说明有时间戳
随机字段
未锁依赖
供应链很难审计。
二十六、Build Manifest
{
"builder": "agent-bundle-builder/3.2",
"source_commit": "abc123",
"build_time": "...",
"dependencies_lock": "lock-v91",
"output_hash": "sha256:..."
}
再生成 Provenance。
二十七、Provenance要回答4个问题
从哪来的?
谁构建的?
构建时用了什么?
通过了哪些Gate?
例如:
{
"source_repo": "agent-config",
"source_commit": "abc123",
"builder_identity": "ci-prod",
"tests": [
"prompt-eval-v8",
"policy-simulation-v42",
"tool-contract-v19"
]
}
二十八、Agent配置也需要类似SBOM
软件有 SBOM。
Agent Bundle 可以做:
ABOM
Agent Bill of Materials
列出:
Model
Prompt
Skill
Tool
Policy
Sandbox
External MCP
以及版本和 Hash。
二十九、ABOM示例
agent: research-agent
version: 23
components:
model:
profile: reasoning-standard-v8
prompt:
bundle: research-prompt-v19
skills:
- web-research@3.8.0
- citation-check@2.1.4
tools:
catalog: tools-v81
policy:
bundle: policy-v42
sandbox:
profile: gvisor-v11
发布页面一眼就能看到:
Agent里面到底装了什么
三十、第三方MCP尤其需要签名和Pinning
最危险:
mcp-server@latest
第三方发布新版本:
Tool Description变了
行为变了
权限需求变了
Agent 无感升级。
生产必须:
version pin
+
hash pin
+
publisher trust
三十一、MCP升级流程
New Version
↓
Fetch
↓
Signature Verify
↓
Schema Diff
↓
Capability Diff
↓
Sandbox Test
↓
Eval
↓
Approve
↓
New Bundle
不要动态 auto-update。
三十二、Tool Schema Diff必须进入Review
例如:
input:
customer_id: string
+ include_private_notes: boolean
这不是普通兼容更新。
它扩大了数据访问面。
自动:
HIGH RISK
三十三、Skill Diff也要看Capability变化
Skill v3:
只读搜索
Skill v4:
增加email.send
不能只看版本号。
需要:
Capability Manifest Diff
三十四、Agent Plugin也应该当供应链包
一个 Agent Plugin 可能打包:
Skill
MCP
Prompt
Tool
越方便复用,供应链风险越集中。
所以导入前必须扫描:
Declared Capability
External Endpoint
Secret Requirement
Network Requirement
Publisher
Signature
三十五、Secret绝不能进入Bundle
Bundle 是可复制 Artifact。
所以只允许:
secret_ref
例如:
secrets:
crm:
ref: secret://tenant/crm-oauth
禁止:
crm_token: sk-xxxx
CI 可以做 Secret Scan。
三十六、Tenant配置也不要直接编进全局Bundle
全局:
Agent Template
Tenant-specific:
Provider Binding
Secret Ref
Policy Overlay
Brand Config
两者分离。
否则重新发布某个客户配置:
可能影响全部租户
三十七、Tenant Binding也需要版本
public record TenantAgentBinding(
String tenantId,
String agentId,
String bundleVersion,
String tenantConfigVersion,
String bindingHash) {
}
这样:
Tenant A用v23
Tenant B仍用v22
可以独立 Canary。
三十八、Promotion不要等同于复制文件
环境:
Dev
Staging
Prod
理想方式:
同一个Artifact Hash
逐级 Promotion。
不是:
Dev重新build
Staging再build
Prod再build
否则三个环境可能内容不同。
三十九、Promotion Pointer
prod/research-agent
→ bundle:v23@sha256:...
发布只是更新 Pointer。
Artifact 本身不变。
四十、Canary也是Binding变化
不要生成:
v23-canary-special
直接:
10% Tenant
→ v23
90%
→ v22
两个都是已签名 Bundle。
四十一、回滚也只切Pointer
v23出问题
回:
v22
不重新构建旧版。
这就是 Immutable Artifact 的价值。
四十二、但紧急撤销需要Revocation
如果发现:
v22也有严重漏洞
不能让人回滚到它。
所以 Registry 要有:
Revocation List
四十三、Revocation Record
public record BundleRevocation(
String bundleId,
String version,
String reason,
RiskLevel severity,
Instant revokedAt,
String revokedBy) {
}
Trusted Loader 每次加载检查。
Critical:
正在运行的Run
也可能要暂停。
四十四、Key本身也会被撤销
如果 Signing Key 泄漏:
签过的Bundle怎么办?
必须区分:
Key Compromised
和:
Key Rotated
正常 Rotation:
旧签名继续可信
Key Compromise:
从某个时间点后的签名全部重新评估
四十五、所以签名Receipt要保存签名时间
signed_at
很重要。
Security 可以判断:
泄漏发生前签的
是否继续可信
四十六、加载时还要检查Compatibility
Bundle v23:
requires runtime >= 4.2
旧 Runtime:
4.0
即使签名完全正确,也不能加载。
Manifest:
runtime:
min_version: 4.2
max_version: 4.x
四十七、Tool Protocol也要有Compatibility
MCP Spec、App Server Protocol、内部 Tool Schema 都可能变化。
所以依赖里保存:
protocol_version
启动时检查。
四十八、Bundle发布前至少要过5类Gate
Schema Gate
Security Gate
Eval Gate
Policy Gate
Compatibility Gate
任何一个 Fail:
不能签Prod
四十九、Schema Gate
检查:
字段完整
依赖存在
版本格式
Lock完整
五十、Security Gate
检查:
Secret
危险Capability新增
外部Endpoint
Network扩大
Unsigned Dependency
五十一、Eval Gate
运行固定 Agent Eval:
Task Success
Tool Sequence
Safety
Cost
Latency
达到阈值才继续。
五十二、Policy Gate
上一期的 Policy Simulation:
新Bundle里Policy变化
先跑历史 Decision Input。
五十三、Compatibility Gate
测试:
Runtime Version
Tool Provider
Model Availability
Region
Tenant Policy
避免 Artifact正确但生产根本跑不了。
五十四、签名发生在所有Gate之后
流程:
Build
↓
Test
↓
Simulation
↓
Approval
↓
Sign
不是:
Build
↓
Sign
↓
然后再测
Prod Signature 本身应该代表:
这个Artifact已经通过发布门禁
五十五、Release Signature和Author Signature不要混
开发者 Commit Signature:
证明谁写了Source
Prod Artifact Signature:
证明谁批准它进入生产
两者不同。
都可以保留。
五十六、Agent Runtime不允许“临时改Prompt”
线上排障最常见:
先在后台改一句Prompt看看
这会绕过供应链。
生产正确做法:
Hotfix Branch
→ Build
→ Fast Gate
→ Sign
→ Canary
流程可以很快,但不能没有。
五十七、Emergency Bundle可以有更快流程
Critical Incident
允许:
缩短普通Eval
但不能跳过:
Signature
Security Gate
Audit
而且 Emergency Bundle 要:
短TTL
事故后必须回归正式 Release。
五十八、运行中是否允许自动加载新Bundle
我不建议。
一个 Run 开始:
bundle=v23
跑到一半 Prod Pointer 变:
v24
不要中途切。
否则前半段和后半段行为不可复现。
五十九、Run Snapshot
每个 Run 创建时固定:
bundle_id
bundle_version
manifest_hash
verification_receipt
整个 Run 使用同一 Bundle。
只有安全 Emergency Deny 可以实时覆盖。
六十、长Run如果必须升级怎么办
例如:
运行24小时
新 Bundle 修复严重安全问题。
不要热替换内部组件。
更安全:
Pause
Checkpoint
End old Run
Start new Run with v24
Restore approved state
边界清晰。
六十一、Artifact Cache也必须按Hash
Runtime 为性能会缓存:
Prompt
Tool Schema
Skill
Cache Key 应该:
content_hash
而不是:
agent_id
否则 v24 发布后可能误拿 v23 Cache。
六十二、Trusted Loader失败要Fail Closed
错误:
签名服务暂时不可用
→先加载吧
生产不应该。
如果 Artifact 已经有:
可离线验证签名
Verifier 不需要在线 Signing Service。
Public Key / Trust Root 本地可用。
所以:
Verify失败
→不启动Agent
六十三、Trust Root也需要管理
Runtime信谁的Key?
prod-agent-release
prod-policy-release
approved-third-party
Trust Store 本身也是高价值配置。
不能让 Agent 修改。
六十四、Trust Store更新要双人审批
因为一旦加入恶意 Publisher Key:
后续签名都“合法”
所以:
Trust Root Change
属于 CRITICAL 级。
六十五、供应链安全还要看Source Repo
攻击路径:
Source Repo
↓
CI
↓
Artifact Registry
↓
Runtime
只保护最后签名还不够。
至少:
Branch Protection
Required Review
CI Identity
Build Isolation
Registry Immutability
都要做。
六十六、CI不能从不可信PR拿Prod Signing Permission
外部 PR:
触发CI
如果能使用:
Prod KMS Sign
这是严重供应链问题。
所以:
PR Validation
和:
Protected Branch Release
使用完全不同权限。
六十七、Build Worker也要短期身份
不要放:
长期KMS Key
CI 使用:
OIDC / Workload Identity
临时获得:
sign capability
并限制:
repo
branch
workflow
environment
六十八、一个发布身份Policy
signing:
key: prod-agent-bundle
allowed_if:
repository: agent-config
branch: main
environment: production
required_reviewers: 2
这样普通 Branch 不能签生产包。
六十九、Artifact Registry权限也分读写
Runtime:
Read
CI Release:
Write
Agent:
None
不要让 Agent 自己更新自己的 Bundle。
这条非常重要。
七十、Agent不能自我升级
Agent 可以:
发现新Skill
提出升级建议
但不能:
下载
签名
更新Prod Pointer
自己完成整条升级。
否则:
被Prompt Injection
以后可能升级进恶意组件。
七十一、自动更新只能到Proposal
Agent发现新版本
↓
生成Dependency Upgrade PR
↓
CI验证
↓
人/Policy批准
↓
发布
和 Dependabot 类似。
七十二、供应链需要一个统一Release Ledger
create table agent_release_ledger (
release_id varchar(128) primary key,
agent_id varchar(128) not null,
bundle_version varchar(64) not null,
manifest_hash varchar(64) not null,
signer_key_version varchar(64) not null,
source_commit varchar(64) not null,
promoted_by varchar(128) not null,
promoted_at timestamptz not null
);
以后可以追:
谁
什么时候
把什么
推到生产
七十三、运行记录引用Release Ledger
Run
→ Release ID
而不是只写:
agent_version=23
这样能直接串回:
Source Commit
Build
Signature
Gate
七十四、Metrics
agent_bundle_verify_total{
result
}
agent_bundle_signature_invalid_total
agent_bundle_revoked_load_total
agent_bundle_dependency_mismatch_total
agent_bundle_promotion_total{
env
}
agent_bundle_rollback_total
agent_unsigned_dependency_total
这些指标应该和普通 Agent Runtime 指标分开。
七十五、SLO
Unsigned Production Bundle = 0
Invalid Signature Load = 0
Revoked Bundle Start = 0
Unknown Dependency Hash = 0
Run Without Verification Receipt = 0
全部 Zero Tolerance。
七十六、供应链Incident怎么处理
发现:
skill-v8
被污染
流程:
Mark Dependency Revoked
↓
Find all Bundles
↓
Block new Runs
↓
Evaluate active Runs
↓
Freeze Evidence
↓
Build patched Skill
↓
New Bundle
↓
Canary
↓
Promote
Dependency Graph 这时非常关键。
七十七、Blast Radius查询
select bundle_id
from agent_bundle_dependency
where dependency_id = 'skill-x'
and dependency_version = '8.0';
几秒就知道影响面。
没有依赖图:
靠群里问谁用了这个Skill
会非常慢。
七十八、Tenant Pinning能降低Blast Radius
Tenant A:
v23
Tenant B:
v22
新版本问题只影响 Canary Tenant。
所以不建议所有 Tenant 永远:
auto latest
七十九、但版本太碎也会增加运维成本
如果 1000 个 Tenant:
300个版本
无法维护。
所以支持窗口:
Current
Previous
LTS
例如只允许:
v23
v22
v20-LTS
其他版本强制升级。
八十、Bundle生命周期
public enum BundleLifecycle {
DRAFT,
BUILT,
SIGNED,
STAGING,
CANARY,
PRODUCTION,
DEPRECATED,
REVOKED,
RETIRED
}
每次状态变化都进入 Release Ledger。
八十一、Deprecated和Revoked不同
Deprecated:
不建议新用
但还能跑
Revoked:
禁止启动
不要混成一个:
disabled
八十二、最终上线检查清单
□ Agent发布为Immutable Bundle
□ Prompt保存Compiled Hash
□ Skill有Lock File
□ Tool Catalog版本化
□ Model Profile版本化
□ Policy Bundle版本化
□ Sandbox使用Image Digest
□ 所有Dependency有Content Hash
□ Bundle有ABOM
□ Build可复现
□ Build生成Provenance
□ Prod Artifact由CI身份签名
□ Dev/Staging/Prod Signing Key分离
□ Runtime只信Prod Trust Root
□ Trusted Loader强制验签
□ Runtime检查Revocation
□ Registry禁止覆盖旧版本
□ Secret不进入Bundle
□ Tenant Config与Global Bundle分离
□ Promotion使用同一Artifact Hash
□ Canary通过Tenant Binding实现
□ Rollback只切Pointer
□ Third-party MCP Pin Version + Hash
□ Capability Diff进入Security Review
□ CI不允许不可信PR访问Prod Signing
□ Agent本身没有Artifact发布权限
□ 每个Run保存Verification Receipt
□ Unsigned Production Run = 0
总结
Agent 平台走到生产阶段以后,真正危险的已经不是只有:
代码供应链
而是:
行为供应链
因为 Model、Prompt、Skill、Tool、Policy 和 Sandbox 都在决定 Agent 会做什么。
如果这些组件仍然是:
运行时拉latest
后台随手改
启动时不验证
那么你即使给代码做了最严格的 CI/CD,也无法证明今天运行的 Agent 和昨天是同一个系统。
配置供应链要解决的核心只有三件事:
我加载的是什么?
它从哪里来的?
我为什么应该信它?
通过:
Immutable Bundle
+
Hash
+
Signature
+
Provenance
+
Trusted Loader
把这三件事固定下来,Agent 才真正拥有类似成熟软件系统的可信发布链。
下一篇继续推进:
生产级Agent(24):Agent Runtime升级与兼容矩阵——当模型、协议、Tool和State Schema同时演进,怎么做到不停机升级。
更多企业级 AI 应用、Agent、RAG 与模型工程化内容,我会继续整理在 智元界:
https://www.zyentor.com/