Agent 能跑不等于岗位还合格:给 AI 员工做一次可回滚的上岗发布

客服 Agent 昨天还会从商品库读取当前价格,今天却在回复里引用了一段旧 FAQ。调用链没有异常,JSON 结构也完全正确。

排查后才发现,团队做了三个看似独立的小改动:换了默认模型,重新索引知识库,把查询工具从 v2 升到 v3。每个改动单独看都能运行,组合起来却已经不是上周批准上线的那个岗位版本。

Agent 进入长期生产后,真正麻烦的不是 crash,而是 silent drift:它还在工作,却不再按岗位约定工作。

先做 diff,再问"要不要修"

最近 Google Cloud 在 VM Extension Manager 的官方更新中提到声明式策略、持续漂移检测、分阶段发布和自动回滚。官方讨论的是 VM 扩展治理,并不是 AI Agent。这里借用的是控制面的思路:先描述目标状态,再持续比较观测状态。

不要把目标状态压成一段 system prompt。一个岗位版本至少要锁住这些内容:

ts 复制代码
type RoleRelease = {
  id: string;
  modelPolicy: {
    primary: string;
    fallbacks: string[];
    paramsHash: string;
  };
  instructionBundle: string;
  skills: Array<{ name: string; version: string; checksum: string }>;
  tools: Array<{
    name: string;
    schemaHash: string;
    permission: "read" | "draft" | "execute";
    approval: "never" | "before_execute";
  }>;
  knowledgeSnapshot: string;
  outputSchema: string;
  evalSuite: string;
  rollbackTo: string;
};

这份 release 不负责动态发现所有能力。它只记录当前岗位已经批准使用的组合,是下一次 diff、评测和回滚的锚点。

配置相同,也可能已经漂移

如果 reconciler 只对比 checksum,它最多发现"装错了什么",发现不了"同样的配置为什么做差了"。因此观测数据至少分两组:

ts 复制代码
type RoleObservation = {
  inventory: {
    model: string;
    skillChecksums: string[];
    toolSchemas: string[];
    knowledgeSnapshot: string;
    approvalPolicy: string;
  };
  behavior: {
    contractPassRate: number;
    citationCoverage: number;
    policyViolationCount: number;
    humanRewriteRate: number;
    costPerTask: number;
    p95Ms: number;
  };
};

模型别名背后的实现变化、知识检索分布变化、上游接口返回值变化,都可能让 inventory 一致但 behavior 退化。

这里有个重要边界:humanRewriteRate 上升不一定是模型退化,也可能是本周任务更难。行为指标适合触发复核,不能独立充当裁决器。

Reconciler 应该输出决策,不要直接"自愈"

轻量 reconciler 可以保持为纯函数:

ts 复制代码
type Action =
  | { type: "NOOP" }
  | { type: "RESTORE_PINNED_CONFIG"; paths: string[] }
  | { type: "RUN_EVAL"; suite: string }
  | { type: "REQUEST_APPROVAL"; reasons: string[] };

function decide(
  desired: RoleRelease,
  observed: RoleObservation,
  diff: Diff,
): Action {
  if (diff.empty) return { type: "NOOP" };

  if (diff.expandsPermissions || diff.removesApproval) {
    return { type: "REQUEST_APPROVAL", reasons: diff.riskReasons };
  }

  if (diff.onlyPinnedArtifactsChanged && diff.isReversible) {
    return { type: "RESTORE_PINNED_CONFIG", paths: diff.paths };
  }

  return { type: "RUN_EVAL", suite: desired.evalSuite };
}

自动恢复只适用于可确定比较、可逆、不会扩大权限的字段。模型行为、知识语义、外部写权限都不应被一个 autoHeal=true 粗暴覆盖。

给岗位升级加一个 canary cohort

一次发布可以先通过离线 fixture,再按稳定哈希分配少量真实任务:

ts 复制代码
function chooseRelease(taskId: string, plan: RolloutPlan): string {
  const bucket = stableHash(`${plan.salt}:${taskId}`) % 100;
  return bucket < plan.canaryPercent
    ? plan.candidateRelease
    : plan.stableRelease;
}

为什么用稳定哈希,而不是每次随机?同一个任务重试时应该留在同一 cohort,否则一次失败重试可能换版本,排查证据会被污染。

canary 的门禁不要只看总成功率。至少分别监控:

  • 输出契约通过率;
  • 来源引用覆盖率;
  • 权限违规次数;
  • 人工改写或驳回率;
  • 单任务成本与 P95 时延;
  • fallback 和人工接管比例。

高风险指标一旦越界,停止扩大 cohort。外部发布、付款、删除文件等动作,即使质量指标漂亮,也应保留独立审批门槛。

本地 override 不是"脏数据"

全局岗位模板经常允许门店、项目或个人做定制。全局版本升级时,本地 override 不能被静默覆盖。

官方的全局 VM 扩展策略文档也专门定义了冲突行为,默认不覆盖独立修改,并允许显式选择覆盖。映射到岗位版本,可以保存三方 diff:

ts 复制代码
type MergeInput = {
  base: RoleRelease;
  desired: RoleRelease;
  local: Partial<RoleRelease>;
};

type MergeResult = {
  merged?: RoleRelease;
  conflicts: Array<{
    path: string;
    severity: "semantic" | "permission";
  }>;
};

门店地址这类数据可能安全合并,价格来源、审批策略和写权限则应进入人工裁决。把所有 override 当作脏数据清掉,短期整齐,长期一定伤害业务。

回滚的是 release,不是某一个模型名

一个 Agent release 是经过测试的组合:模型、指令、Skill、工具 schema、知识快照和权限策略。只把模型切回旧版,剩余组件仍是新版,就构造了一个从未验证过的状态。

所以回滚应该是原子的:把流量路由回上一份不可变 release。候选 release 可以继续保留,用于复现失败,不要在事故中直接覆盖它。

重试也要幂等:

ts 复制代码
const idempotencyKey = [
  tenantId,
  roleId,
  candidateRelease,
  "rollout",
].join(":");

同一个 key 返回原发布任务,避免网络抖动创建两组 canary。

别把控制面做成新的配置税

小团队不需要先建设"Agent Kubernetes"。可以从最小闭环开始:

  1. 选一个会对外写入的高风险岗位;
  2. 保存一份不可变 release manifest;
  3. 准备十几个覆盖失败边界的 fixture;
  4. 只设一个 canary 百分比和一个稳定回滚版本;
  5. 把 diff、评测、批准和回滚写入同一审计记录。

低风险草稿、一次性脑暴没有必要通过完整发布链。治理的目标是降低不确定性,不是增加一套每天要维护的表单。

岗位产品真正要保留什么

这也是我们做 Tipkay 时的一个取舍。Tipkay 面向小微企业、一人公司和小团队,提供按需使用的垂类 AI 员工。不同岗位助手可以复制、定制和协作,携带各自的 Skill、MCP、工具与流程。

这样的系统不能只解决"第一次配置成功",还要避免岗位经验在模型、工具和知识变化时悄悄丢失。本文的基线与发布控制面是一种工程方法,不代表语义差异都应自动修复;高风险变化仍需要评测与明确批准。

Agent 上线后的关键指标,不只是今天成功调用了多少次。更值得追问的是:当前运行的到底是哪份岗位 release,它和批准版本差在哪里,失败后能否回到一个经过验证的组合。

相关推荐
神鹰手哥_起飞版35 分钟前
深度研判任务里,开思考、换更强的模型、套方法论,哪个更有用?
人工智能
ObserverX2139 分钟前
我是如何用 13KB 纯 Python,发现本地 RAG 的“隐式递归记忆”的
人工智能
Jason_gao99941 分钟前
Github Copilot 新手极速上手指南
人工智能·github·copilot
用户3600555790041 分钟前
Kestrel_VLLM - 35 · 把 m 循环搬进内核:一次「先量发射、再合并发射」的完整回合
人工智能
Linye41 分钟前
为什么让 AI 修个小 Bug 却把底层全重写了?聊聊 Coding Agent 的最小修改边界
人工智能·架构
深度开源1 小时前
我扫了自己电脑里的 402 个 AI 技能,光目录就吃掉 4.5 万 token
人工智能
skywalk81631 小时前
hive 常驻集群:像 OSPF 一样在线,像 git 一样可信
运维·人工智能·hive
王中阳Go1 小时前
你那条三跳链路的单跳超时,是按压测的 p95 定的,还是拍出来的?
人工智能·后端·架构
方方洛1 小时前
ray教程-27-附录F-RayClient与runtime_env与多语言
人工智能·分布式·机器学习
神鹰手哥_起飞版1 小时前
用 Agent 越多,硬盘越满?我在隐藏目录里发现了 334 GB 的 .mimosa
人工智能