手搓生产级 AI Agent 系统(14):Agent as Code——Prompt、Tool、Schedule、Memory 全部进 Git
文章摘要
前面十三篇已经把生产级 Agent 的关键运行能力补得差不多了:任务规划、Tool Calling、状态管理、Checkpoint、Human-in-the-Loop、多 Agent 协作、安全沙箱、质量门禁、模型路由、预算和降级。
但只要 Agent 的 Prompt 还散落在控制台、Tool 权限靠人工记忆、Schedule 配在某台机器、Memory 规则写在数据库后台、谁改了什么无法追溯,这套系统就仍然很难规模化。
今天这一篇不再加一个“更聪明”的运行节点,而是解决一个更像软件工程的问题:Agent 本身应该如何被版本化、Review、发布、回滚和审计。
我会把 Agent 拆成一组可声明的配置资产:Prompt、Model Profile、Tool Policy、Trigger、Schedule、Memory Policy、Budget、Approval Policy、Eval Suite 和 Runtime Profile;每个 Agent 都有 Owner、Manifest、版本和变更记录;所有修改通过 Pull Request;Merge 后才生成不可变 Agent Version;发布走 Dev、Shadow、Canary、Production;运行中的 Run 永远绑定创建时的版本,不被后台“偷偷改配置”。
这套做法我把它叫做:
Agent as Code
它不是让每个业务人员都写 Java,而是让任何会影响 Agent 行为的东西,都变成可读、可 Diff、可 Review、可回滚的软件资产。
先看一个很常见的 Agent 目录
很多系统最开始只有:
agent_name
prompt
model
真正上线以后,很快会多出:
Tools
Schedule
Credentials
Memory
Budget
Approval
Fallback
Eval
Runtime
所以我更倾向一个 Agent 一个目录:
agents/
└── invoice-reconciliation/
├── agent.yaml
├── prompt/
│ ├── system.md
│ ├── examples.yaml
│ └── output-schema.json
├── tools/
│ └── policy.yaml
├── memory/
│ └── policy.yaml
├── evals/
│ └── regression.yaml
├── deployment/
│ ├── dev.yaml
│ ├── shadow.yaml
│ └── production.yaml
└── README.md
这已经能解决一半治理问题。
因为任何人打开仓库,都能回答:
这个Agent是谁?
它做什么?
能调用什么?
多久跑一次?
预算多少?
什么时候需要人工?
怎么测试?
生产到底跑哪个版本?
agent.yaml 不应该只是几个字段
一个可用的 Manifest 可以长这样:
apiVersion: zyentor.ai/v1
kind: Agent
metadata:
name: invoice-reconciliation
owner: finance-automation
risk: medium
description: >
对账供应商付款记录,生成差异清单,
人工确认后再写入财务系统。
spec:
prompt:
system: prompt/system.md
examples: prompt/examples.yaml
outputSchema: prompt/output-schema.json
model:
profile: finance-standard-v3
tools:
policy: tools/policy.yaml
memory:
policy: memory/policy.yaml
trigger:
type: cron
expression: "0 7 * * 1-5"
timezone: Asia/Shanghai
budget:
maxCostPerRun: 1.20
maxModelCalls: 12
maxToolCalls: 30
approval:
requiredFor:
- apply_payment
- modify_invoice
eval:
suite: evals/regression.yaml
runtime:
profile: standard-agent-runtime-v5
这里有个很重要的原则:
Manifest 描述意图,运行平台决定如何实现。
业务人员不应该在这里写:
docker run --privileged ...
也不应该直接填数据库密码。
Secret 永远不要进 Git
Agent as Code 很容易被误解成:
什么都放仓库
不对。
仓库里放的是 Secret Reference。
credentials:
netsuite:
secretRef: finance/netsuite-prod
slack:
secretRef: finance/slack-bot
真正凭证来自:
- Vault;
- Cloud Secret Manager;
- Kubernetes Secret;
- Credential Broker。
Run 启动时根据真实用户、Agent、环境和 Purpose 决定能不能领取。
Prompt 也要有“编译”步骤
不要把 Markdown 原样读出来就发模型。
发布时先编译成一个不可变 Prompt Artifact。
输入:
system.md
examples.yaml
output-schema.json
policy fragments
生成:
{
"promptId": "invoice-reconciliation",
"version": "sha256:...",
"system": "...",
"examples": [...],
"outputSchema": {...},
"contentHash": "..."
}
这样线上每个 Run 都能记录:
prompt_hash
出了问题以后,不需要猜“当时是不是已经有人改过控制台 Prompt”。
Tool Policy 单独版本化
Prompt 里写:
不要调用删除工具
不够。
真正 Tool 白名单应该由程序读取:
allow:
- invoice.query
- payment.query
- reconciliation.create_report
approvalRequired:
- payment.apply
forbid:
- invoice.delete
- vendor.modify_bank_account
模型即使输出:
我要调用 invoice.delete
Gateway 也直接拒绝。
Agent Version 记录:
tool_policy_hash
为什么 Tool Policy 不能跟 Prompt 放一个文件
因为 Review 人不同。
Prompt 改写可能由业务和算法 Review。
Tool 权限变化应该由:
业务Owner
+研发
+安全
共同看。
如果一次 PR 同时把:
+ payment.apply
加入 Allowlist,Git 平台可以自动加安全 Reviewer。
Schedule 也必须进版本
很多 Agent 最容易失控的不是 Prompt,而是定时任务。
例如:
每周一次
有人临时改成:
每小时一次
模型成本直接涨 168 倍。
所以 Schedule 改动应该像代码一样 Review。
- expression: "0 7 * * 1"
+ expression: "0 * * * *"
CI 可以自动计算:
Old estimated runs/month: 4
New estimated runs/month: 720
Estimated monthly cost:
$24 → $4,320
这比 Review 人肉发现靠谱。
Budget 也是配置,不应该散在代码里
budget:
maxCostPerRun: 1.20
maxModelCalls: 12
maxToolCalls: 30
maxRuntime: 10m
高风险 Agent 还可以设置:
monthlyBudget:
hardLimit: 5000
alertAt: 3500
Merge 一个 PR 之前,CI 直接做预算影响分析。
Memory Policy 更值得单独放
Memory 是 Agent 最容易出现隐性行为变化的地方。
一个配置示例:
shortTerm:
enabled: true
scope: run
longTerm:
enabled: true
scope: tenant-user
writableTypes:
- user_preference
- approved_business_fact
forbiddenTypes:
- password
- otp
- medical_record
- temporary_agent_guess
ttl:
user_preference: 365d
approved_business_fact: 90d
如果有人改成:
- scope: tenant-user
+ scope: tenant
这可能直接改变数据隔离。
必须进入安全 Review。
Approval Policy 也不能藏在 Prompt
approval:
rules:
- action: payment.apply
required: true
role: finance_manager
- action: email.send_external
requiredWhen:
recipientDomainNotIn:
- company.com
这些是确定性策略。
LLM 不能说:
“这次金额不大,我认为无需审批。”
然后跳过。
Agent Version 应该是不可变对象
Merge PR 后,CI 生成:
public record AgentVersion(
String agentName,
String versionId,
String gitCommit,
String manifestHash,
String promptHash,
String toolPolicyHash,
String memoryPolicyHash,
String evalSuiteHash,
String runtimeProfile,
Instant createdAt,
String createdBy) {
}
这个 Version 一旦生成:
永远不修改
任何变更生成新 Version。
为什么不能直接“更新 Agent”
因为运行中可能有:
3小时前启动的Run
正在等待人工审批
此时后台把 Prompt V18 改成 V19。
恢复以后,到底用哪个?
正确答案:
Run创建时绑定V18
恢复仍然用V18
除非显式迁移。
Run 必须保存 Version Binding
public record AgentRun(
String runId,
String agentName,
String agentVersionId,
String graphVersion,
String runtimeProfile,
RunStatus status,
Instant createdAt) {
}
以后任何 Trace 都能关联:
run → version → git commit
PR 是 Agent 最好的控制面之一
为什么我喜欢 PR?
因为软件行业已经为它解决了大量问题:
Diff
Reviewer
Comment
Approval
CI
History
Rollback
Owner
Agent 不需要重新发明一套“AI 专用审批系统”。
至少配置层完全可以复用。
但业务人员不熟 Git 怎么办
这确实是现实问题。
可以给业务人员一个管理后台:
编辑Prompt
选择Tool
修改Schedule
调整Budget
点击保存以后,后台不是直接改生产。
而是:
生成Git Branch
→写文件
→开Pull Request
用户仍然使用图形界面。
底层事实仍然在 Git。
这叫:
UI over Git
而不是:
UI instead of Git
CI 里应该跑什么
至少五类。
1. Schema
agent.yaml合法
字段完整
Tool存在
Model Profile存在
2. Static Policy
危险Tool有没有审批
Schedule是否异常
Budget是否突破上限
Memory Scope是否扩大
3. Prompt Lint
检查:
- 未定义变量;
- 输出 Schema 不匹配;
- 过长 Prompt;
- 冲突指令;
- 明显敏感信息。
4. Eval
跑:
Frozen Regression
Safety Cases
Tool Cases
5. Cost Impact
输出:
Prompt tokens: +12%
Expected model cost: +8%
Schedule frequency: unchanged
Tool cost: +2%
一个 PR 报告应该长什么样
Agent: invoice-reconciliation
Version: v31 → v32
Changes:
- Prompt wording updated
- Added payment.query tool
- No new write permission
- Model unchanged
Evaluation:
- 184 / 190 passed
- Baseline: 181 / 190
- Critical failures: 0
Cost:
- Avg input tokens: +4.2%
- Estimated monthly cost: +$37
Risk:
- LOW
Required reviewers:
- Finance AI Owner
- Platform Engineering
Review 人不需要自己运行一堆脚本。
高风险 Diff 自动升级审批
CI 解析 Git Diff。
如果发现:
新增写Tool
扩大Memory Scope
降低审批要求
开放公网网络
提升Budget > 50%
自动:
Risk=HIGH
增加安全 Reviewer。
这比让作者自己勾“本次变更风险等级”靠谱。
Merge 不等于直接 100% Production
发布阶段:
DEV
→SHADOW
→CANARY
→PRODUCTION
Agent Version Registry 保存:
哪个Version在哪个Stage
Shadow 阶段
候选 Version 收到真实请求副本,但:
写Tool BLOCKED
通知 BLOCKED
长期Memory BLOCKED
比较:
- Tool选择;
- 结果;
- 成本;
- 延迟;
- 安全。
Canary 阶段
给少量真实 Run。
关键是:
按Run粘性路由
不要一个 Run 中途 V31 切到 V32。
Production 不代表永远不回滚
发布以后仍然监控:
Task Success
Error Rate
Tool Failure
Human Reject Rate
Cost
Latency
Safety
达到硬阈值:
停止新Run进入V32
新Run回V31
已在 V32 运行中的任务,继续 V32 或人工处理。
Rollback 为什么可以很简单
因为 Version 不可变。
不是:
把一堆配置手工改回去
而是:
production pointer:
v32 → v31
然后新 Run 全部绑定 V31。
Agent Owner 必须是强制字段
metadata:
owner: finance-automation
没有 Owner 的 Agent 不允许进 Production。
原因非常现实。
凌晨失败时必须知道:
谁解释业务逻辑?
谁决定暂停?
谁看人工反馈?
谁对成本负责?
“AI 平台团队”不能替所有业务 Agent 做 Owner。
我还会要求一个 README.md
内容不需要长。
Agent做什么
不做什么
触发条件
依赖系统
风险
人工节点
常见失败
Owner
Dashboard
Runbook
半年后新同事接手,会非常有用。
Agent Catalog
所有 Agent 进入一个目录:
Name
Owner
Status
Version
Risk
Monthly Runs
Monthly Cost
Task Success
Last Deployment
一下就能发现:
没人负责的
半年没运行的
成本异常的
成功率很低的
Agent 也需要生命周期管理。
不要让 Zombie Agent 一直跑
一个定时 Agent 可能业务早已停止,但 Cron 还在执行。
所以增加:
lifecycle:
reviewEvery: 90d
expiresAt: 2027-01-01
到期:
Owner重新确认
否则自动暂停
Agent 的版本号到底用 SemVer 还是 Commit Hash
我通常两者都留。
对人:
v2.4.0
对机器:
commit SHA
manifest SHA256
因为人需要理解版本,机器需要确保内容完全一致。
什么时候升 Major Version
如果发生这些变化:
- Goal 改变;
- Tool 权限扩大;
- 输出契约不兼容;
- Memory Scope 改变;
- Approval Policy 有重大调整。
可以升 Major。
普通 Prompt 优化升 Patch。
Agent Config Migration
Manifest Schema 自己也会升级。
例如:
apiVersion: zyentor.ai/v1
未来变:
apiVersion: zyentor.ai/v2
提供:
v1 → v2 migration tool
不要让旧 Agent 一夜全部失效。
Credential Rotation 不能要求 Agent PR
凭证本身轮换:
Secret Manager内完成
Agent 只引用逻辑名:
finance/netsuite-prod
Secret 版本变化不需要改 Prompt。
这样安全团队可以独立轮换。
Runtime Profile 也要版本化
runtime:
profile: standard-agent-runtime-v5
Profile 里面控制:
Network
Sandbox
Timeout
MaxMemory
AllowedConnectors
Telemetry
如果 runtime-v6 出现兼容问题,可以把 Agent 回退到 v5。
Agent as Code 不等于所有东西都必须是 YAML
这是我想特别强调的。
可以使用:
- YAML;
- JSON;
- Markdown;
- TypeScript DSL;
- Java DSL;
- Terraform 风格 HCL。
格式不重要。
核心是:
可声明
可版本
可Diff
可测试
可发布
一个 Agent 发布平台的核心表
create table agent_version (
version_id varchar(128) primary key,
agent_name varchar(128) not null,
git_commit varchar(64) not null,
manifest_hash varchar(128) not null,
prompt_hash varchar(128) not null,
tool_policy_hash varchar(128) not null,
eval_suite_hash varchar(128) not null,
runtime_profile varchar(128) not null,
created_at timestamptz not null,
unique(agent_name, git_commit)
);
发布指针:
create table agent_release (
agent_name varchar(128) not null,
environment varchar(32) not null,
version_id varchar(128) not null,
traffic_percent integer not null,
updated_at timestamptz not null,
primary key(agent_name, environment, version_id)
);
Run 创建时解析一次 Release
AgentVersion version =
releaseResolver.resolve(
agentName,
tenantId,
request);
AgentRun run =
AgentRun.create(
UUID.randomUUID().toString(),
agentName,
version.versionId());
后面永远按:
version_id
加载。
不要每一步重新读“最新生产配置”。
这还能解决一个很烦的问题:事故复现
用户投诉:
8月12日 14:32
Agent错误地拒绝了申请
没有 Agent as Code 时:
现在Prompt已经改了
模型也换了
Tool也加了
根本复现不了
有 Version Binding:
Run ID
→ Agent Version v27
→ Git Commit
→ Prompt Hash
→ Tool Policy
→ Model Profile
→ Knowledge Snapshot
可以真正重放。
Feedback 也应该关联版本
用户点踩:
feedback.run_id
feedback.agent_version
feedback.failure_code
否则新版本已经修好了,旧版本的投诉仍然混在统计里。
自动 Tuner 能不能直接改 Prompt
可以让它:
分析反馈
生成Diff
开PR
但我不建议直接 Merge。
至少生产 Agent 应保持:
AI proposes
Human/Policy approves
CI validates
Platform deploys
特别是涉及 Tool、权限、Memory 和业务规则时。
AI Review AI 也可以,但最后要有边界
一个 PR 可以由:
Security Agent
Cost Agent
Eval Agent
Style Agent
自动 Review。
但它们负责:
提供证据
不是绕过组织审批。
需要记录的审计事件
AgentVersionCreated
AgentVersionEvaluated
ReleasePromoted
ReleaseRolledBack
ToolPolicyChanged
MemoryPolicyChanged
BudgetChanged
ApprovalPolicyChanged
AgentPaused
AgentRetired
每个事件带:
who
why
commit
version
risk
result
管理后台最终应该是什么样
不是一个 Prompt 编辑器。
而是 Agent Catalog:
invoice-reconciliation
Owner: Finance Automation
Production: v32
Canary: v33 @ 10%
Task Success: 96.4%
30d Cost: $1,842
Human Reject: 2.1%
Last Change: 2h ago
Risk: MEDIUM
点进去:
Versions
Runs
Evals
Cost
Artifacts
Approvals
Feedback
Deployments
这才接近企业 Agent 控制面。
一套最小发布流程
开发者或业务 Builder:
修改Agent目录
↓
开PR
↓
Schema/Policy检查
↓
Eval
↓
Cost Impact
↓
Reviewer
↓
Merge
↓
生成不可变Version
↓
Shadow
↓
Canary
↓
Production
事故:
Gate触发
↓
停止新Run进入候选版
↓
指针回旧Version
↓
保留问题Run做Replay
最后给一份检查表
如果准备让 Agent 真正规模化,至少确认:
□ 每个Agent有Owner
□ 每个Agent职责单一明确
□ Prompt在Git中
□ Tool Policy在Git中
□ Schedule在Git中
□ Memory Policy在Git中
□ Budget在Git中
□ Approval Policy在Git中
□ Secret只保存Reference
□ 每个变更经过PR
□ CI会跑Schema和Policy检查
□ CI会跑Eval
□ CI会计算成本影响
□ Merge生成不可变Agent Version
□ Run绑定创建时Version
□ Shadow不产生真实副作用
□ Canary按Run粘性
□ Production可一键回旧Version
□ Feedback关联Agent Version
□ Agent有生命周期和到期Review
□ 事故能从Run追到Git Commit
总结
Agent 真正开始进入企业以后,会出现一个很明显的变化:
一开始大家最关心:
Prompt怎么写?
模型选哪个?
规模上来以后,问题会变成:
谁改的?
谁批准的?
哪个版本?
怎么测试?
花多少钱?
为什么上线?
出了问题怎么退?
这就是为什么我认为 Agent as Code 会成为生产 Agent 的基础能力之一。
它没有让 Agent 更聪明。
但它让 Agent 变得:
可理解
可控制
可审计
可回滚
而企业真正敢把越来越多业务交给 Agent,靠的最终不会只是模型智力。
更重要的是:这个 Agent 能不能像一套正常的软件系统一样被管理。
下一篇我会继续往这个方向推进:
手搓生产级 AI Agent 系统(15):Agent Control Plane——目录、运行、成本、审批与反馈统一治理。
更多企业级 AI 应用、Agent、RAG 与模型工程化内容,我会继续整理在 智元界:
https://www.zyentor.com/