手搓生产级 AI Agent 系统(15):Agent Control Plane——目录、运行、成本、审批与反馈统一治理
文章摘要
前十四篇已经把生产级 Agent 的大部分“局部能力”补齐了:模型路由、Tool Calling、Planner、Reviewer、Checkpoint、Human-in-the-Loop、多 Agent、代码与浏览器沙箱、评测发布、Agent as Code。
接下来会遇到一个很现实的问题:能力越来越多,但平台越来越难管。
你会发现同一个企业里同时存在:
20 个 Agent
8 套模型路由
60 个 Tool
15 份 Prompt
7 套审批策略
多种 Schedule
多套 Memory
不同预算
不同 Owner
不同运行环境
每个 Agent 单独看都能跑。
真正出问题时却没人能快速回答:
这个 Agent 现在是谁负责?
线上到底跑的是哪个版本?
今天花了多少钱?
哪些 Run 在等待审批?
这个 Tool 为什么突然调用暴增?
这个 Agent 上周的任务成功率是多少?
哪个租户正在触发异常成本?
现在能不能一键暂停某个高风险能力?
这就是 Agent Control Plane 要解决的问题。
它不是另一个 Agent,也不是一个“AI 管理后台”。更准确地说,它是整个 Agent 平台的控制面:把目录、版本、运行、策略、预算、审批、Artifact、观测、反馈和发布统一到同一个治理模型里。
本篇给出一套 Spring Boot 可落地的 Control Plane 设计,并把今天几篇现实案例串起来:OpenAI 对高能力模型增加运行时监控、Asana 用多个 Coding Agent 并行但由工程师集中 Review、Model Spec 把 Authority 和 Side Effect 显式建模、Mandiant 用多 Agent Harness 做高强度代码审计——这些案例背后其实都需要同一类平台能力:谁能运行、运行了什么、花了多少、出了什么问题、谁能暂停。
一、为什么 Agent 数量超过 5 个以后,Excel 很快就不够用了
刚开始只有:
客服 Agent
知识库 Agent
大家知道谁在用。
后来增加:
合同 Agent
销售 Agent
报表 Agent
研发 Agent
代码 Agent
浏览器 Agent
安全 Agent
很快会出现:
同名 Tool 重复
Prompt 版本不清
模型成本没人负责
Owner 离职
测试环境配置被当生产
审批规则各写一套
日志散在不同系统
这不是模型问题。
这是平台治理问题。
二、Control Plane 和 Runtime Plane 要分开
我会明确拆成两部分。
Runtime Plane
负责真正执行:
Model Call
Tool Call
RAG
Checkpoint
Browser
Sandbox
Workflow
Control Plane
负责:
Agent Catalog
Version
Policy
Budget
Approval
Release
Observability
Feedback
Kill Switch
关系:
Control Plane
│ policy / manifest / quota
▼
Runtime Plane
│ events / metrics / artifacts
▲
└──────── feedback
运行面不能自己修改控制策略。
三、第一个核心对象:Agent Catalog
不要再用:
Spring Bean 名称
当 Agent 身份。
定义:
public record AgentDefinition(
String agentId,
String displayName,
String description,
String ownerTeam,
AgentRiskLevel riskLevel,
Set capabilities,
Set toolIds,
String promptId,
String modelRouteId,
String memoryProfileId,
String approvalPolicyId,
String budgetPolicyId,
String runtimeProfileId,
AgentLifecycle lifecycle) {
}
每个 Agent 都有稳定 ID。
例如:
agent.customer-support.refund
agent.engineering.migration
agent.security.source-review
四、Agent Definition 和 Agent Version 不能是一个对象
Definition 描述:
“它是谁”
Version 描述:
“这次上线具体是什么”
public record AgentVersion(
String agentId,
String version,
String promptVersion,
String modelRouteVersion,
String toolCatalogVersion,
String graphVersion,
String policyVersion,
String runtimeImageDigest,
String evaluatorVersion,
String gitCommit,
String manifestHash,
Instant createdAt) {
}
五、为什么一定要 Manifest Hash
假设事故发生在:
2026-08-19 14:21
你要能还原:
Prompt v17
Model Route v8
Tool Catalog v23
Policy v11
Graph v6
Image sha256:...
如果只是查当前配置:
已经变了
所以 Run 必须绑定不可变 Manifest。
六、Run 是 Control Plane 的第二核心对象
public record AgentRun(
String runId,
String tenantId,
String subjectId,
String agentId,
String agentVersion,
String manifestHash,
RunStatus status,
RiskLevel risk,
String currentStep,
String checkpointRef,
CostUsage usage,
Instant startedAt,
Instant deadline,
Instant completedAt) {
}
七、Run Status 不要只用 RUNNING / DONE
生产建议至少:
public enum RunStatus {
CREATED,
QUEUED,
RUNNING,
WAITING_TOOL,
WAITING_HUMAN,
PAUSED,
DEGRADED,
SUCCEEDED,
FAILED,
CANCEL_REQUESTED,
CANCELLED,
UNKNOWN
}
WAITING_HUMAN 和 UNKNOWN 尤其重要。
八、Control Plane 首页,我只想先看到 8 个数字
不是炫酷聊天记录。
我会显示:
Active Runs
Waiting Human
Failed Runs
Critical Alerts
Today Cost
P95 Latency
Task Success Rate
Side-effect Operations
这八个数字比“今天调用了多少 Token”更接近运营状态。
九、第三个核心对象:Capability
不要让 Agent 直接绑定一堆 Tool 名字。
先定义业务能力:
customer.read_order
customer.issue_refund
repo.read
repo.create_patch
browser.read
browser.submit
email.send
再映射 Tool。
public record Capability(
String capabilityId,
RiskLevel risk,
SideEffectLevel sideEffect,
boolean approvalRequired,
Set allowedToolIds) {
}
十、Capability 比 Tool 更稳定
今天:
customer.read_order
→ REST API Tool
明天:
customer.read_order
→ MCP Tool
Agent Policy 不需要改。
这就是抽象层的价值。
十一、第四个核心对象:Policy
Policy 不应该散在:
Prompt
代码 if
数据库配置
Nginx
人工 SOP
统一 Policy Model:
public record AgentPolicy(
String policyId,
String version,
Set capabilityRules,
DataPolicy dataPolicy,
NetworkPolicy networkPolicy,
ApprovalPolicy approvalPolicy,
BudgetPolicy budgetPolicy,
RuntimePolicy runtimePolicy) {
}
十二、Policy 的优先级
结合昨天新版 Model Spec 的 Authority 思路,企业可以定义:
Platform Security
↓
Enterprise Policy
↓
Tenant Policy
↓
Agent Policy
↓
User Request
↓
External Data
越往下越不能覆盖上层。
十三、第五个对象:Budget
预算不能只在月末看账单。
Control Plane 需要实时预算:
public record BudgetPolicy(
String policyId,
BigDecimal dailyCostLimit,
BigDecimal perRunCostLimit,
long perRunTokenLimit,
int perRunModelCallLimit,
int perRunToolCallLimit,
int maximumParallelRuns) {
}
十四、预算分四层
Enterprise
Tenant
Agent
Run
例如:
企业:$20,000 / day
研发:$5,000 / day
Coding Agent:$1,000 / day
单 Run:$10
Run 开始前做 Reservation。
十五、为什么要 Reservation
剩余预算:
$10
同时来了 10 个任务。
如果大家都先看:
remaining = $10
然后一起启动:
最终花 $100
正确:
reserve
→ execute
→ settle
这和支付系统预授权很像。
十六、Cost 不是一个字段,而是一棵树
一次 Agent Run:
Planner $0.08
├─ Research Agent $0.42
│ ├─ Search Tool $0.02
│ └─ Model $0.40
├─ Finance Agent $0.31
├─ Reviewer $0.11
└─ Final $0.07
Control Plane 应该能 Drill Down。
十七、Cost Attribution
每笔费用至少带:
run_id
agent_id
step_id
model
provider
tool
tenant
cost_center
否则财务问:
“这个月 AI 为什么多花 30%?”
你只能回答:
“Token 变多了。”
没有意义。
十八、第六个对象:Approval
Human-in-the-Loop 不能只是一封邮件。
public record ApprovalRequest(
String approvalId,
String runId,
String actionId,
String capabilityId,
String requestedByAgent,
String businessObject,
String inputHash,
RiskLevel risk,
ApprovalStatus status,
String approverPolicyId,
Instant expiresAt) {
}
十九、审批中心应该像工单中心
用户打开:
Waiting Approval: 12
每条显示:
谁发起
要做什么
影响什么
证据
风险
成本
过期时间
不是只显示:
Approve / Reject
二十、审批必须绑定 Input Hash
用户批准:
退款 100 元
批准后 Agent 把金额改成:
1000 元
旧审批不能继续用。
所以 Approval 绑定:
action
+input_hash
+policy_version
任何关键输入变化都失效。
二十一、第七个对象:Artifact
Agent 不应该把所有产物塞消息里。
public record Artifact(
String artifactId,
String runId,
String type,
String contentHash,
String storageRef,
String producerStep,
String securityLabel,
Instant createdAt) {
}
例如:
代码 Patch
测试报告
PDF
截图
分析结果
检索 Evidence
二十二、Artifact 是 Run 的证据层
最终答案说:
“测试已经通过。”
Control Plane 能点进去看到:
artifact:test-report-828
128 passed
0 failed
commit=a31df2
这才叫可审计。
二十三、第八个对象:Event
我建议整个 Control Plane 用 Event Projection。
RunCreated
RunStarted
StepStarted
ToolCalled
ToolSucceeded
ArtifactCreated
ApprovalRequested
ApprovalGranted
RunPaused
RunCompleted
前端状态由 Event 投影得到。
二十四、为什么 Event 很适合 Agent
Agent 本身就是长流程。
如果只保存当前状态:
RUNNING
看不到发生过什么。
事件能回答:
怎么走到这里的?
二十五、事件表
create table agent_event (
event_id varchar(128) primary key,
run_id varchar(128) not null,
event_type varchar(64) not null,
sequence_no bigint not null,
payload jsonb not null,
occurred_at timestamptz not null,
unique(run_id, sequence_no)
);
二十六、Sequence 不要只靠时间排序
分布式系统里两个事件可能:
毫秒相同
时钟漂移
消息乱序
每个 Run 使用单调 Sequence。
二十七、第九个对象:Alert
不要把所有错误都叫 Exception。
public enum AgentAlertType {
COST_SPIKE,
TOOL_LOOP,
SECURITY_POLICY,
STALE_CONTEXT,
PROVIDER_OUTAGE,
DUPLICATE_SIDE_EFFECT,
APPROVAL_TIMEOUT,
DATA_ACCESS_ANOMALY,
QUALITY_REGRESSION
}
二十八、Alert 要能执行动作
public record AlertPolicy(
AgentAlertType type,
AlertSeverity severity,
KillScope autoKillScope,
boolean pageHuman,
Duration acknowledgementSla) {
}
例如:
Duplicate Side Effect
→ CRITICAL
→ Pause Run
→ Page On-call
二十九、第十个对象:Kill Switch
今天第一篇说到 OpenAI 的安全监控,这里就落地。
Pause Run
Disable Agent
Disable Capability
Disable Tool
Disable Tenant
Disable Model Route
Disable Provider
不是只有 Global Off。
三十、Kill Switch 必须在 Control Plane 外也生效
如果 Runtime Worker 缓存了旧策略:
Control Plane 已禁用 Tool
Worker 还继续跑
很危险。
高风险操作执行前再次检查:
policySnapshot =
policyClient
.requireFreshPolicy(runId);
或者使用短 TTL Capability Token。
三十一、Capability Token
public record CapabilityToken(
String tokenId,
String runId,
String capability,
String tenantId,
String subjectId,
Instant expiresAt,
String policyVersion) {
}
Runtime 每次高风险 Tool 调用都验证。
Kill 后不再签发。
三十二、第十一个对象:Feedback
质量平台不是只看 Eval Dataset。
线上反馈:
用户点赞/点踩
重复提问
人工修改
审批拒绝
任务撤销
Tool失败
业务结果
全部应关联 Run。
public record AgentFeedback(
String feedbackId,
String runId,
FeedbackType type,
Double score,
String reasonCode,
String artifactRef,
Instant createdAt) {
}
三十三、Feedback 要反哺 Agent Version
例如发现:
v18 的退款 Agent
人工拒绝率从 4% 升到 13%
Control Plane 应该能直接关联:
Prompt Change
Model Route Change
Policy Change
而不是人工猜。
三十四、第十二个对象:Release
Agent 也需要发布环境:
DRAFT
DEV
SHADOW
CANARY
PRODUCTION
RETIRED
不要保存以后直接全量。
三十五、Release Manifest
public record AgentRelease(
String releaseId,
String agentId,
String agentVersion,
ReleaseStage stage,
int trafficPercent,
Set tenantAllowlist,
GateDecision gateDecision,
Instant startedAt) {
}
三十六、Control Plane 和昨天的 Agent as Code 怎么连接
Git 是 Source of Truth:
Agent Manifest
Prompt
Tool Config
Policy
Eval
Control Plane 是运行状态:
当前发布版本
Run
Cost
Approval
Alert
Feedback
也就是说:
Git 管“应该是什么”
Control Plane 管“现在发生了什么”
三十七、部署同步
PR Merge:
Agent Manifest v15
CI:
validate
→ eval
→ sign
→ publish registry
Control Plane:
register version
→ shadow
→ canary
→ production
三十八、不要允许后台直接编辑生产 Prompt
如果 Control Plane 提供一个文本框:
Edit Prompt
Save
又回到昨天 Claude Workbench 的问题。
更好的按钮:
Edit
→ 创建 Git Branch / PR
所有变化仍走代码审查。
三十九、Control Plane API
GET /api/agents
GET /api/agents/{id}/versions
POST /api/agents/{id}/release
GET /api/runs
GET /api/runs/{runId}
POST /api/runs/{runId}/pause
POST /api/runs/{runId}/cancel
GET /api/approvals
POST /api/approvals/{id}/approve
POST /api/approvals/{id}/reject
GET /api/costs
GET /api/alerts
POST /api/kill-switch
四十、Spring Boot 模块划分
agent-control-plane
├── catalog
├── version
├── release
├── run
├── policy
├── capability
├── budget
├── approval
├── artifact
├── event
├── alert
├── kill
├── feedback
├── cost
└── observation
四十一、Run Query API
@RestController
@RequestMapping("/api/runs")
public class RunController {
private final RunQueryService service;
@GetMapping("/{runId}")
public RunDetail get(
@PathVariable String runId) {
return service.get(runId);
}
}
四十二、RunDetail 不要返回 10MB Trace
首页只返回 Projection:
public record RunDetail(
AgentRun run,
List steps,
List artifacts,
List approvals,
List alerts,
CostBreakdown cost) {
}
完整 Trace 按需读取。
四十三、成本看板
我会做几个固定视图:
Cost by Agent
Cost by Tenant
Cost by Model
Cost by Tool
Cost by Successful Task
Cost by Failed Task
最后两个最重要。
一个 Agent 很便宜,但失败率高,可能实际最贵。
四十四、真正该看的指标:Cost per Successful Task
总成本 $1,000
成功 1000 个任务
Cost per Success = $1
另一个模型:
总成本 $700
成功 500
Cost per Success = $1.40
便宜模型反而更贵。
四十五、Review Throughput 也进入 Control Plane
结合 Asana 案例,Coding Agent 还要看:
PR Generated
PR Reviewed
PR Merged
Review Minutes
Reject Rate
否则 Agent 生产速度超过人类 Review,队列一样会堵。
四十六、安全 Agent 还要看 Finding Funnel
结合今天 AVDH 案例:
Candidate Finding
→ Skeptic Passed
→ Validated
→ Human Confirmed
→ Fixed
每层都有转化率。
避免“发现 10 万漏洞”这种没有运营价值的指标。
四十七、RAG Agent 还要看 Evidence
结合 Box 多模态 RAG:
Text Evidence
Page Evidence
Table Evidence
Chart Evidence
Control Plane 能追踪:
最终 Claim 来自哪个 Artifact
四十八、Agent Health Score
可以做一个综合分,但不要只看一个数字。
例如:
Task Success 35%
Critical Failure 25%
Latency 10%
Cost 10%
Human Reject 10%
Tool Reliability 10%
Health Score 用于排序。
真正决策仍看具体指标。
四十九、异常检测
例如 7 天 Baseline:
Email Send:
平均 400 / day
今天:
4,800
直接告警。
不是等用户投诉。
五十、Agent Inventory 是安全资产清单
安全团队至少知道:
有哪些 Agent
谁 Owner
能访问哪些数据
能调用哪些 Tool
有哪些外部网络
风险等级
当前版本
这和传统 CMDB 很像。
未来可能会出现:
Agent CMDB
五十一、Owner 不能是空字段
Agent 没有 Owner:
不能进入 Production
Owner 离职:
进入 Orphan Alert
规定期限重新分配,否则自动下线高风险能力。
五十二、生命周期
public enum AgentLifecycle {
DRAFT,
PILOT,
ACTIVE,
DEPRECATED,
SUSPENDED,
RETIRED
}
Deprecated Agent 只允许现有 Run 完成,不接受新任务。
五十三、删除 Agent 不能删除审计
Retired 后保留:
Version
Release
Run Metadata
Cost
Approval
Security Event
按合规政策保留。
五十四、多租户 Control Plane
所有对象必须带:
tenant_id
但平台级 Agent 也可能跨租户。
这类能力需要:
explicit platform scope
不能因为没写 tenant 就默认全局。
五十五、数据权限
查询 Run:
public RunDetail get(
AccessContext access,
String runId) {
AgentRun run = repository.require(runId);
authorization.requireReadable(
access,
run);
return projection.load(runId);
}
管理后台本身也是高敏系统。
五十六、不要把 Prompt 明文全部显示给所有运营人员
Prompt 里可能包含:
- 内部 Policy;
- 业务规则;
- 防攻击逻辑;
- Secret 引用;
- 敏感 Schema。
Control Plane 根据角色展示。
五十七、Audit Log
任何管理动作:
发布
暂停
预算修改
审批
Kill
Policy修改
记录:
who
what
before
after
reason
ticket
time
五十八、Kill Switch 本身也要审批吗
通常:
暂停
应该比:
恢复
权限更宽。
事故中应允许 On-call 快速暂停。
恢复高风险 Capability 需要更高权限。
五十九、Control Plane 高可用
这是个有趣的问题。
Control Plane 挂了,Runtime 怎么办?
我的建议:
低风险读操作
可以使用短期缓存 Policy 继续。
高风险写操作
Control Plane 无法确认时:
Fail Closed
停止。
六十、Policy Cache 必须有 TTL
policy_cache:
low_risk_ttl: 5m
high_risk_ttl: 30s
超过 TTL 不能无限使用旧权限。
六十一、Control Plane 不应该在每个 Token 路径上
否则:
性能瓶颈
适合控制:
Run Start
Step Boundary
High-risk Tool
Approval
Budget Reservation
Release
模型每个 Token 不需要问一次 Control Plane。
六十二、事件流
Runtime
→ Kafka / Event Bus
→ Projection Worker
→ Control Plane DB
→ Web UI
Critical Event 走独立高优先级通道。
六十三、WebSocket / SSE
UI 订阅:
Run Event
Approval
Alert
Cost
用户能实时看到任务状态,而不是刷新页面。
六十四、首页应该有“今天最异常的 10 个 Agent”
排序:
Cost Spike
Failure Spike
Human Reject Spike
Tool Loop
Latency
Security Alert
这比 Agent 总数更有用。
六十五、第二个看板:“正在等人的任务”
Approval Age
Business Impact
Risk
Owner
很多 Agent SLA 最后不是模型慢,而是人没点审批。
六十六、第三个看板:“没有人负责的 Agent”
显示:
Orphan Owner
No Recent Eval
Deprecated Model
Expired Credential
No Active Maintainer
这是运营债务。
六十七、自动治理规则
例如:
governance:
no_owner:
after: 7d
action: block_release
no_eval:
after: 30d
action: require_revalidation
deprecated_model:
before_retirement: 14d
action: warn
critical_alert:
action: pause_agent
六十八、为什么 Control Plane 最后会变成企业 AI 的核心资产
模型可以换。
Prompt 可以换。
框架也会换。
今天 LangGraph,明天可能别的。
但企业真正长期积累的是:
Agent Catalog
Capability
Policy
Run History
Cost
Approval
Feedback
Evaluation
这些才是企业 AI 的运营数据。
六十九、不要和模型供应商控制台绑定
OpenAI、Anthropic、Google 都有自己的控制台。
它们很好用。
但企业内部 Control Plane 需要跨 Provider:
OpenAI
Anthropic
Gemini
国产模型
私有模型
统一看。
否则每次故障和成本分析都要切五个后台。
七十、最小 MVP 怎么做
不要一开始做 30 个页面。
第一版只做:
Agent Catalog
Run List
Run Detail
Cost
Approval Queue
Kill Switch
6 个页面已经能解决 70% 的生产痛点。
七十一、第二阶段
增加:
Release
Eval
Feedback
Alert
Artifact
七十二、第三阶段
再做:
自动治理
跨 Agent 分析
容量预测
成本优化建议
策略仿真
七十三、数据库建议
核心表:
agent_definition
agent_version
agent_release
agent_run
agent_step
agent_event
agent_artifact
agent_capability
agent_policy
agent_budget
agent_cost
agent_approval
agent_alert
agent_feedback
七十四、索引
create index idx_run_agent_time
on agent_run(agent_id, started_at desc);
create index idx_run_tenant_status
on agent_run(tenant_id, status);
create index idx_approval_status_expire
on agent_approval(status, expires_at);
create index idx_alert_severity_time
on agent_alert(severity, created_at desc);
七十五、Metrics
agent_run_total{agent,status}
agent_task_success_rate{agent}
agent_cost_total{agent,model}
agent_cost_per_success{agent}
agent_waiting_human{agent}
agent_approval_age_seconds{risk}
agent_tool_call_total{agent,tool}
agent_side_effect_total{agent,capability}
agent_alert_total{agent,type,severity}
agent_release_total{stage,result}
注意高基数 Run ID 不要放普通 Metric Label。
七十六、SLO
例如:
Critical Side-effect Duplicate = 0
Cross-tenant Access = 0
Production Agent Owner Coverage = 100%
Manifest Traceability = 100%
Approval Expired Execution = 0
Budget Hard Limit Bypass = 0
七十七、故障演练
至少每季度跑:
Provider Down
Budget Exhausted
Approval Service Down
Tool Compromised
Policy Service Down
Control Plane Down
Kill Switch
Stale Runtime Policy
七十八、发布门禁
Agent Version 进入 Production 前:
Owner
Risk
Eval
Budget
Policy
Tool Review
Rollback
缺一项不允许上线。
七十九、一个完整的 Agent 上线流程
Git PR
↓
Manifest Validate
↓
Offline Eval
↓
Register Agent Version
↓
Shadow
↓
Canary
↓
Production
↓
Runtime Monitoring
↓
Feedback
↓
Next PR
八十、这套系统最后解决的是“AI 运营”
很多公司现在已经不是:
有没有 AI?
而是:
已经有 20 个 AI 了,怎么管?
这就是下一个阶段。
Agent 数量增长以后,最稀缺的不是再多一个模型 API,而是:
可见性
责任
成本
权限
发布
反馈
总结
过去十四篇大多在解决“怎么把一个 Agent 做对”。
从这一篇开始,问题变成:
怎么把几十个 Agent 当成一个真正的软件生产系统来运营。
Agent Control Plane 的核心结构可以压缩成六句话:
Catalog 知道有哪些 Agent
Manifest 知道线上到底跑什么
Run 知道现在发生什么
Policy 知道什么可以做
Budget 知道还能花多少
Approval / Alert / Kill 知道什么时候必须让人接管
模型越强,这层控制面越重要。
因为未来企业真正面对的不会是“一个 AI 助手”,而是大量长期运行、会调用工具、会写数据、会产生费用、会等待审批、会升级版本的数字工作单元。
这些 Agent 最终一定需要自己的 CMDB、发布平台、成本中心、审计系统和运营看板。
与其等到第 50 个 Agent 上线以后再补,不如在第 5 个时就开始建 Control Plane。
下一篇我会继续写:
手搓生产级 AI Agent 系统(16):Agent Registry 与 Capability Marketplace——让 Tool、Skill、MCP 和 Agent 可以被发现、授权与复用。
更多企业级 AI 应用、Agent、RAG 与模型工程化内容,我会继续整理在 智元界:
https://www.zyentor.com/