前端开发者做 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,需要把工具分级,把权限做硬,把高风险操作停下来确认,把执行过程写入审计。

这不是给智能泼冷水。

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

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

相关推荐
铁皮饭盒13 小时前
Bun 提供了许多 Node.js 原生没有的专属 API
前端·后端
destinying13 小时前
前端秒变AI全栈,我的核心资产是一套Node.js“中间件”
前端·后端·面试
环信14 小时前
即时通讯服务的数据安全与合规实践
前端
彬鸿科技14 小时前
bhSDR Studio/Matlab入门指南(十二):AI神经网络训练(Resnet-SE) 实验界面全解析
人工智能·神经网络·matlab·软件无线电·sdr
TMT星球14 小时前
齐向东:AI时代,三类安全需求集中爆发
人工智能·安全
暗夜猎手-大魔王14 小时前
转载--Hermes Agent 05 | 记忆系统(上):内置记忆的冻结快照模式与 agent-curated 策展
人工智能
zhangfeng113314 小时前
如果模型h200训练好的模型 要部署到华为 升腾 950导致的误差怎么处理
人工智能·机器学习
贺国亚14 小时前
Agent 工程实践 · 生产落地 Playbook
java·人工智能·aigc
轻闲一号机14 小时前
【语音】笔记
前端·笔记·算法
初心丨哈士奇14 小时前
一行 # 的差别:彻底搞懂前端路由的 hash 和 history 模式
前端·浏览器