智能工作流的止损检查设计

将大模型(LLM)接入分类、标签或资源调度等自动化工作流,可能减少部分人工步骤,但收益需要由业务基线验证。

但也伴随着极大的风险。

退款、关闭工单等高风险动作应假定模型可能误判或行为发生变化。若没有金额、对象范围和人工确认门槛,错误可能被批量执行;这里采用假设场景,而非声称某次实际事故。

把非确定性的大模型放到自动化工作流的主驾位置上,如果不设确定性的安全熔断与止损巡检机制,自动化分分钟会变成“自动化灾难”。


1. 为什么 LLM 工作流极易失控?

传统规则引擎(If-Else、定时任务)的逻辑是完全可预测的。即使报错,也是明确的 Exception。

而 LLM 智能工作流存在三个致命的不确定性:

  1. Prompt 漂移与上游模型更新:即使你本地的 Prompt 一字未改,底层 API 模型的微调升级也可能导致相同的输入产生不同的结构化输出。
  2. 错误自激放大循环:在多 Agent 协作工作流中,Agent A 产生了一个轻微的幻觉,Agent B 把这个幻觉作为事实继续执行,最终将微小的偏差放大为严重的业务破坏。
  3. 静默失败(Silent Failure):模型输出的 JSON 格式完全正确,语法合规,但内容在业务语义上完全颠倒。传统的 Schema 校验器根本拦截不住。

应对这三个不确定性,核心思路就是:不能信任任何大模型的单一决策,必须建立独立的确定性止损巡检器(Guardrail Inspector)


2. 止损巡检与断路器控制架构

在这套架构里,LLM 工作流引擎只拥有“提出操作建议”的权限,最终的执行闸门被严格扣在确定性的巡检器手里。


3. 生产级 LLM 工作流止损巡检器代码实现

下面是用 TypeScript 实现的自动化止损巡检器。包含了滑动窗口误操作速率限制、高危操作强制拦截以及熔断器断路逻辑。

import { EventEmitter } from 'events';

export interface WorkflowAction {
  actionId: string;
  actionType: 'CLOSE_TICKET' | 'REFUND_MONEY' | 'DELETE_RESOURCE';
  targetId: string;
  amountUSD?: number;
  reason: string;
}

export interface InspectorPolicy {
  maxRefundSingleUSD: number;
  maxActionsPerMinute: number;
  maxConsecutiveHighRisk: number;
}

export class GuardrailInspector extends EventEmitter {
  private policy: InspectorPolicy;
  private recentActions: { timestamp: number; action: WorkflowAction }[] = [];
  private consecutiveHighRiskCount = 0;
  private isCircuitTripped = false;

  constructor(policy: InspectorPolicy) {
    super();
    this.policy = policy;
  }

  /**
   * 拦截并审查 LLM 工作流生成的 action 指令
   */
  public inspectAndExecute(action: WorkflowAction, executeFn: () => Promise<void>): Promise<boolean> {
    // 1. 全局断路器检查
    if (this.isCircuitTripped) {
      console.error(`[CRITICAL] Action ${action.actionId} BLOCKED: Circuit Breaker is TRIPPED!`);
      this.emit('alert', { level: 'P0', message: 'Attempted execution during circuit trip', action });
      return Promise.resolve(false);
    }

    // 2. 清理滑动窗口内超过 1 分钟的历史记录
    const now = Date.now();
    this.recentActions = this.recentActions.filter((a) => now - a.timestamp < 60000);

    // 3. 频次防刷闸门校验
    if (this.recentActions.length >= this.policy.maxActionsPerMinute) {
      this.tripCircuit(`Execution rate limit exceeded (${this.policy.maxActionsPerMinute}/min). Potential runaway loop.`);
      return Promise.resolve(false);
    }

    // 4. 金额与敏感操作硬限制
    if (action.actionType === 'REFUND_MONEY') {
      const amount = action.amountUSD || 0;
      if (amount > this.policy.maxRefundSingleUSD) {
        console.warn(`[SAFETY INTERCEPT] Single refund $${amount} exceeds limit $${this.policy.maxRefundSingleUSD}. Diverting to Manual Review.`);
        this.emit('manual_approval_required', { action, reason: 'Exceeded max refund threshold' });
        return Promise.resolve(false);
      }
      this.consecutiveHighRiskCount++;
    } else {
      this.consecutiveHighRiskCount = 0; // 重置高风险计数
    }

    // 5. 连续高风险操作熔断
    if (this.consecutiveHighRiskCount >= this.policy.maxConsecutiveHighRisk) {
      this.tripCircuit(`Detected ${this.consecutiveHighRiskCount} consecutive high-risk actions. Tripping breaker.`);
      return Promise.resolve(false);
    }

    // 所有安全检查通过,放行执行
    this.recentActions.push({ timestamp: now, action });
    return executeFn()
      .then(() => {
        console.log(`[SAFE EXECUTE] Action ${action.actionId} completed successfully.`);
        return true;
      })
      .catch((err) => {
        console.error(`[EXECUTION ERROR] Action ${action.actionId} failed:`, err);
        return false;
      });
  }

  private tripCircuit(reason: string): void {
    this.isCircuitTripped = true;
    console.error(`====================================================`);
    console.error(`[EMERGENCY TRIP] CIRCUIT BREAKER TRIPPED! Reason: ${reason}`);
    console.error(`====================================================`);
    this.emit('circuit_tripped', { reason, timestamp: Date.now() });
  }

  public resetCircuitManually(): void {
    this.isCircuitTripped = false;
    this.consecutiveHighRiskCount = 0;
    this.recentActions = [];
    console.log('[MANUAL OVERRIDE] Circuit breaker reset by administrator.');
  }
}

// 示例运行
if (require.main === module) {
  const inspector = new GuardrailInspector({
    maxRefundSingleUSD: 100,      // 单次退款不得超过 100 美元
    maxActionsPerMinute: 5,       // 每分钟最多自动执行 5 次操作
    maxConsecutiveHighRisk: 3,    // 连续 3 次高风险操作自动触发全局熔断
  });

  inspector.on('circuit_tripped', (event) => {
    // 实际生产环境中接入钉钉、飞书报警机器人或 PagerDuty
    console.error('[SMS / PagerDuty Alert Sent]:', event);
  });

  const sampleAction: WorkflowAction = {
    actionId: 'ACT_9001',
    actionType: 'REFUND_MONEY',
    targetId: 'ORD_8819',
    amountUSD: 50,
    reason: 'LLM Determined valid service latency complaint',
  };

  inspector.inspectAndExecute(sampleAction, async () => {
    // 真正的写库或调 API 逻辑
    await new Promise((resolve) => setTimeout(resolve, 50));
  });
}

4. 建立止损机制后的体会

将这套止损巡检机制嵌入 LLM 工作流后,团队在运营自动化功能时有了极大的底气:

  1. 确定性防线兜底:大模型可以尽情去推推理、抓语义,但凡是涉及扣费、退款、删除资源或发送外部邮件的高危指令,必须经过止损巡检器的强类型硬门禁。
  2. 熔断器挡住连续幻觉:一旦模型出现失控或者输入了异常数据导致连续报错,断路器会在 3 秒内切断自动执行流,并把任务压入人工审批队列(Human-in-the-loop)。
  3. 运营可视化:巡检日志记录了每次被拦截的决策与原因,为后续 Prompt 的针对性优化提供了最宝贵的数据样本。

大模型是极具潜力的“加速器”,但想要在真实的生产环境落地,工程师必须亲手为它装上灵敏的“刹车踏板”。没有刹车的高速列车,跑得越快,灾难就越近。

先写清暂停条件

这篇讨论的是开源智能工具与服务里的“智能工作流的止损检查设计”。判断不能只靠某一次顺利的结果,需要把仓库版本、本地进程、接口日志、依赖版本和复现步骤放回同一段执行过程里看。试运行前把可接受范围写成可观察信号,例如错误持续出现、人工处理量超过承受能力、关键依赖不可用。触发后谁有权限暂停、数据怎样保留、何时复盘,都比事后争论“要不要继续”更实际。

实际处理时,我会先选一个普通请求和一个边界请求,分别记下开始时间、关键输入与最终结果。若两者差异很大,就继续向下拆分,而不是马上把问题归因给某个工具。这里的目标不是把记录做得漂亮,而是让后来接手的人能够复走当时的路径。

交付前留下什么

对于这次“智能工作流的止损检查设计”,先把可变条件列成两三项即可,例如版本、输入规模或权限状态。每次试验只调整其中一项,并保存前后的差异。这样即使结论是否定的,也能知道否定的是哪一种假设。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐