手搓生产级 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/