前端开发者做 Agent:模型说执行就执行?先加 3 道闸门再碰真实业务

作者:前端转 AI 深度实践者

【省流助手/核心观点】:Agent 真正进入业务系统后,最大风险不是它不会调用工具,而是它太容易调用工具。查询订单、搜索政策这类只读工具可以相对宽松,但取消订单、发起退款、修改权限、发送邮件这类有副作用的工具,必须经过 3 道闸门:权限检查、风险分级、用户确认。模型可以提出动作建议,但程序必须掌握最终执行权。


做到第 28 篇,我们的 Agent 已经有了不少工程能力。

它能调用工具。

它有工具 Schema。

它能多步执行。

它会失败处理。

它还有 trace、结构化日志和运行报告。

这时候,一个很自然的问题会出现:

能不能让它开始真正办事?

比如:

text 复制代码
帮我取消订单 A1001。
text 复制代码
帮我给用户发一封延迟补偿邮件。
text 复制代码
帮我把这张工单升级成 P0。

从能力上说,当然可以。

但从工程安全上说,必须非常谨慎。

Agent 一旦能调用有副作用的工具,就不再只是"回答系统"。

它开始变成"操作系统"。

而操作系统必须有权限、确认和审计。

1. 痛点:模型可以建议动作,但不能拥有最终执行权

先看一个危险写法:

ts 复制代码
async function runModelDecision(modelOutput: {
  toolCall: {
    toolName: string;
    args: Record<string, unknown>;
  };
}) {
  return runTool(modelOutput.toolCall);
}

这意味着:

text 复制代码
模型说调用什么,程序就调用什么。

如果工具都是查询类,还勉强可以接受。

但如果工具里有:

  • cancelOrder
  • refundPayment
  • deleteFile
  • sendEmail
  • changeUserRole

这就很危险。

因为模型可能误解用户意图。

用户说:

text 复制代码
这个订单是不是能取消?

模型可能误判成:

json 复制代码
{
  "toolName": "cancelOrder",
  "args": {
    "orderId": "A1001"
  }
}

用户只是问能不能取消,不是要立刻取消。

所以先建立一个原则:

模型可以提出动作建议,但最终能不能执行,必须由程序决定。

2. 错误做法:只靠 Prompt 提醒模型"谨慎执行"

很多人会在 Prompt 里写:

text 复制代码
如果用户没有权限,不要调用取消订单工具。
遇到高风险操作时要谨慎。

这有帮助,但不够。

Prompt 是软约束。

权限检查、风险分级、二次确认必须是程序层硬约束。

否则一旦模型误判、提示词被绕过、上下文被污染,系统就可能执行真实副作用。

Agent 安全边界不能靠模型自觉。

3. 正确做法:先把工具分成风险等级

不是所有工具风险都一样。

可以先分三类:

ts 复制代码
type RiskLevel = "low" | "medium" | "high";

type ToolDefinition = {
  name: string;
  riskLevel: RiskLevel;
  requiredPermission: string;
  requiresConfirmation: boolean;
  handler: (args: Record<string, unknown>) => Promise<unknown>;
};

低风险工具通常是只读:

  • 查询订单状态。
  • 查询政策文档。
  • 查询库存。
  • 计算退款金额。

中风险工具通常会创建记录,但影响可控:

  • 创建客服工单。
  • 添加备注。
  • 保存草稿。

高风险工具通常会改变真实业务状态:

  • 取消订单。
  • 发起退款。
  • 修改用户权限。
  • 删除资料。
  • 发送外部邮件。

风险等级的意义是:不同工具走不同执行规则。

text 复制代码
low    -> 可以自动执行,但要记日志
medium -> 需要权限检查
high   -> 需要权限检查 + 用户确认 + 审计日志

这不是把系统搞复杂。

这是让 Agent 进入真实业务时不乱来。

4. 权限检查必须在程序层

先定义用户上下文:

ts 复制代码
type AgentUser = {
  id: string;
  permissions: string[];
};

function hasPermission(user: AgentUser, permission: string) {
  return user.permissions.includes(permission);
}

再定义工具:

ts 复制代码
const cancelOrderTool: ToolDefinition = {
  name: "cancelOrder",
  riskLevel: "high",
  requiredPermission: "order:cancel",
  requiresConfirmation: true,
  handler: async (args) => {
    return {
      cancelled: true,
      orderId: args.orderId
    };
  }
};

const toolRegistry: Record<string, ToolDefinition> = {
  cancelOrder: cancelOrderTool
};

执行前必须检查权限:

ts 复制代码
function checkPermission(user: AgentUser, tool: ToolDefinition) {
  if (hasPermission(user, tool.requiredPermission)) {
    return null;
  }

  return {
    ok: false as const,
    errorType: "permission_denied",
    message: `缺少权限:${tool.requiredPermission}`
  };
}

这才是可靠边界。

AI 工程有个朴素但重要的原则:

不能靠模型自觉来保护系统安全。

5. 高风险工具必须先生成确认请求

取消订单这类动作,不能直接执行。

应该先生成一个确认请求:

ts 复制代码
type ConfirmationRequest = {
  confirmationId: string;
  toolName: string;
  argsSummary: string;
  riskLevel: RiskLevel;
  message: string;
  status: "pending" | "approved" | "rejected";
};

function createConfirmation(tool: ToolDefinition, args: Record<string, unknown>) {
  const orderId = String(args.orderId ?? "");

  return {
    confirmationId: crypto.randomUUID(),
    toolName: tool.name,
    argsSummary: JSON.stringify({ orderId }),
    riskLevel: tool.riskLevel,
    message: `该操作将取消订单 ${orderId},是否确认继续?`,
    status: "pending"
  } satisfies ConfirmationRequest;
}

此时 Agent 的回答应该是:

text 复制代码
取消订单 A1001 是高风险操作。请确认是否继续。

而不是:

text 复制代码
已为你取消订单。

前端同学可以把它理解成危险操作的确认弹窗:

text 复制代码
你确定要删除这个项目吗?
[取消] [确认删除]

Agent 只是把按钮点击换成了自然语言流程,但安全原则没有变。

6. 前端确认组件:让暂停有地方停

高风险操作必须能暂停,也必须能恢复。

前端可以展示确认卡片:

tsx 复制代码
function ToolConfirmationCard({
  confirmation,
  onApprove,
  onReject
}: {
  confirmation: ConfirmationRequest;
  onApprove: (id: string) => void;
  onReject: (id: string) => void;
}) {
  return (
    <section>
      <h3>需要确认</h3>
      <p>{confirmation.message}</p>
      <pre>{confirmation.argsSummary}</pre>
      <button
        type="button"
        onClick={() => onReject(confirmation.confirmationId)}
      >
        取消
      </button>
      <button
        type="button"
        onClick={() => onApprove(confirmation.confirmationId)}
      >
        确认执行
      </button>
    </section>
  );
}

真实系统里,这个确认请求可能对应:

  • 前端确认弹窗。
  • 审批流。
  • 管理后台任务。
  • 人工审核队列。
  • 消息通知。

重点是:高风险操作不能直接消失在模型输出和工具执行之间。

它必须被暂停、展示、确认、记录。

7. 安全版工具执行器:像网关一样把关

安全版 runTool 应该像一个执行网关。

它不只是把工具跑起来,还要检查:

  • 工具是否存在。
  • 用户是否有权限。
  • 工具风险等级。
  • 是否需要确认。
  • 是否已经确认。
ts 复制代码
type ToolCall = {
  toolName: string;
  args: Record<string, unknown>;
  confirmationId?: string;
};

type SafeToolResult =
  | {
      ok: true;
      toolName: string;
      data: unknown;
    }
  | {
      ok: false;
      toolName?: string;
      errorType:
        | "unknown_tool"
        | "permission_denied"
        | "confirmation_required"
        | "tool_error";
      message: string;
      confirmation?: ConfirmationRequest;
    };

async function runToolSafely(
  user: AgentUser,
  toolCall: ToolCall,
  approvedConfirmations: Set<string>
): Promise<SafeToolResult> {
  const tool = toolRegistry[toolCall.toolName];

  if (!tool) {
    return {
      ok: false,
      toolName: toolCall.toolName,
      errorType: "unknown_tool",
      message: `未知工具:${toolCall.toolName}`
    };
  }

  const permissionError = checkPermission(user, tool);
  if (permissionError) {
    return {
      ...permissionError,
      toolName: tool.name
    };
  }

  const needsConfirmation =
    tool.requiresConfirmation &&
    (!toolCall.confirmationId ||
      !approvedConfirmations.has(toolCall.confirmationId));

  if (needsConfirmation) {
    const confirmation = createConfirmation(tool, toolCall.args);

    return {
      ok: false,
      toolName: tool.name,
      errorType: "confirmation_required",
      message: confirmation.message,
      confirmation
    };
  }

  try {
    const data = await tool.handler(toolCall.args);

    return {
      ok: true,
      toolName: tool.name,
      data
    };
  } catch (error) {
    return {
      ok: false,
      toolName: tool.name,
      errorType: "tool_error",
      message: error instanceof Error ? error.message : "工具执行失败"
    };
  }
}

这就是 Agent 的安全执行层。

它让模型从"执行者"退回到"建议者"。

程序才是最后的把关者。

8. 审计日志:高风险操作必须留痕

高风险工具即使确认后执行,也要记录审计日志。

ts 复制代码
type AuditLog = {
  traceId: string;
  userId: string;
  toolName: string;
  argsSummary: string;
  confirmationId?: string;
  resultStatus: "success" | "failed";
  createdAt: string;
};

function createAuditLog(input: Omit<AuditLog, "createdAt">): AuditLog {
  return {
    ...input,
    createdAt: new Date().toISOString()
  };
}

审计日志回答的是:

  • 谁执行了?
  • 执行了什么?
  • 参数摘要是什么?
  • 是否经过确认?
  • 结果是什么?
  • 什么时候执行?

这对真实业务很重要。

因为高风险操作不能只看最终状态,还要能追溯过程。

9. 生产环境避坑指南

1. 查询类工具和写入类工具分开注册

不要把只读工具和写入工具混在一个默认可执行池里。

查询类可以宽一些,写入类必须严格走权限和确认。

2. confirmed 不能只信前端传参

不要因为前端传了 confirmed: true 就执行。

后端必须校验 confirmationId 是否存在、是否属于当前用户、是否仍然有效、是否已批准。

3. 确认页要展示操作摘要

不要只写"是否确认执行"。

要展示工具名、关键参数、风险说明和可能影响。

4. 高风险操作要防重复提交

用户可能重复点击确认,模型也可能重复发起同一个工具调用。

需要通过 confirmationId、幂等 key 或业务单号防止重复执行。

5. 审计日志不要存完整敏感参数

审计日志要可追溯,但不代表要裸存所有字段。

建议记录参数摘要、脱敏字段或哈希。

10. 常见误区

误区 1:Prompt 里写"谨慎执行"就够了

不够。Prompt 是软约束,权限和确认必须在程序层实现。

误区 2:只有删除才算高风险

不是。发邮件、退款、改权限、批量修改、取消订单都可能是高风险。

误区 3:用户说了"帮我取消",就可以直接取消

仍然不建议。真实业务里应该展示操作摘要,让用户明确确认。

误区 4:确认后就不用记录了

不对。确认只是执行前边界,审计日志是执行后追溯。

11. 给前端开发者的落地清单

如果你要让 Agent 调用真实业务工具,可以先按这份清单检查:

  1. 所有工具都有风险等级。
  2. 所有工具都有所需权限。
  3. 所有写入类工具默认不可直接执行。
  4. 高风险工具必须二次确认。
  5. 确认请求包含工具名、参数摘要、风险说明。
  6. 用户确认后才带 confirmationId 恢复执行。
  7. 权限检查在程序层完成。
  8. Prompt 不能替代权限系统。
  9. 高风险操作写审计日志。
  10. 拒绝、取消、确认、执行都要有 trace。

这份清单听起来很像后台系统安全规范。

没错,Agent 进入业务系统后,它就是后台系统的一部分。

结语

Agent 不能"模型说执行就执行"。

模型擅长理解意图,擅长生成建议,但它不应该拥有最终执行权。

真正可靠的 Agent,需要把工具分级,把权限做硬,把高风险操作停下来确认,把执行过程写入审计。

这不是给智能泼冷水。

这是让智能能进入真实世界。

因为真实世界里,能办事的系统必须先能负责。

相关推荐
前端那点事1 小时前
企业级Vue前端鉴权方案全解析|从Token到OAuth2.0,覆盖多端适配+权限管控
前端·vue.js
亲亲小宝宝鸭1 小时前
从Vben-Admin里面学习hooks
前端·vue.js
Mintopia1 小时前
MSW Mock Feature-First 方案
前端·架构
joshchen2151 小时前
强化学习基础(赵世钰)第一章
人工智能·深度学习·算法·机器学习·强化学习
拜特说1 小时前
RAG 进化史:从基础检索到智能体驱动
人工智能
weixin_398187751 小时前
YOLOv11改进:全维度动态卷积ODConv与C3k2模块创新
人工智能·yolo
李昊哲小课1 小时前
Hermes Agent Dashboard 二次开发指南
人工智能·智能体·hermesagent
MATLAB代码顾问1 小时前
RAG技术详解:从检索增强生成到知识库问答实战
人工智能
sin6031 小时前
Talk is cheap 之后:AI Agent 时代,程序员真正要交付什么?
前端