前端 Agent 编排中的工具调用拦截器:实现人机协同的确认机制
·
前端 Agent 编排中的工具调用拦截器:实现人机协同的确认机制

在把大模型 Agent 接入真实业务系统时,最让安全团队和业务方担心的就是“Agent 失控”——比如 Agent 自作主张地调用了转账接口、删除了线上数据表,或者向客户群发了未经审核的营销短信。
如果完全不给 Agent 工具调用权限,Agent 就退化成了只能聊天的普通问答机器人;如果把所有权限无条件开放给 Agent 全自动执行,又面临巨大的不可控风险。
解决这一矛盾的核心工程架构是:前端工具调用拦截器(Tool Invocation Interceptor)与人机在环协同(Human-in-the-Loop, HITL)。通过在前端与 Agent 运行时之间构建拦截插槽,对于高危或涉及金钱/敏感数据的动作,挂起(Suspend)模型执行流程,弹出交互卡片由人类用户二次确认或修改参数后,再继续后续的执行链路。
工具风险分级与拦截状态机
我们将 Agent 可调用的工具按照破坏性划分为三个等级:
- L1 只读无害工具(Auto-Pass):查询天气、搜索文档、读取只读配置等,由 Agent 全自动静默调用;
- L2 读写轻量工具(User Notification):创建草稿、添加收藏、发送测试日志等,自动执行但需在前端界面明确提示;
- L3 高危敏感工具(User Confirmation Required):发起支付扣款、删除生产资源、修改权限配置等,必须强制拦截并挂起执行。
[Agent 规划出 Tool Call: "transferMoney"]
│
▼
[前端 Interceptor 识别为 L3 风险工具] ──> (挂起 Agent 执行管道)
│
▼
[前端 UI 渲染受控授权确认卡片: "是否向账户 X 转账 ¥5000?"]
│
├─ 用户点击【拒绝】──> 向 Agent 回传: { status: "rejected", reason: "User cancelled" }
└─ 用户点击【确认】──> 真实发起请求,并将成功结果喂回 Agent 继续推理
核心实现:可挂起的前端工具调用拦截器
借助 TypeScript 和 Promise 的挂起机制,我们实现一个完全受控的拦截器引擎:
export type ToolRiskLevel = 'L1' | 'L2' | 'L3';
export interface ToolActionPayload {
callId: string;
toolName: string;
riskLevel: ToolRiskLevel;
parameters: Record<string, any>;
}
export interface InterceptorDecision {
action: 'proceed' | 'reject' | 'modify';
modifiedParams?: Record<string, any>;
rejectReason?: string;
}
export class AgentToolInterceptor {
private pendingResolvers = new Map<string, (decision: InterceptorDecision) => void>();
private onPromptUserCallback?: (payload: ToolActionPayload) => void;
public registerUserPromptHandler(callback: (payload: ToolActionPayload) => void) {
this.onPromptUserCallback = callback;
}
// 当 Agent 引擎尝试调用工具时触发此拦截钩子
public async intercept(toolName: string, riskLevel: ToolRiskLevel, params: Record<string, any>): Promise<any> {
const callId = crypto.randomUUID();
// 如果是 L1 级低风险工具,直接放行
if (riskLevel === 'L1') {
return { status: 'proceed', finalParams: params };
}
// L3 级高风险工具:挂起当前执行流,向前端 UI 发出确认事件
return new Promise<any>((resolve) => {
this.pendingResolvers.set(callId, (decision) => {
if (decision.action === 'reject') {
resolve({
status: 'cancelled',
error: decision.rejectReason || 'Operation was rejected by user.',
});
} else if (decision.action === 'modify') {
resolve({
status: 'proceed',
finalParams: decision.modifiedParams || params,
});
} else {
resolve({
status: 'proceed',
finalParams: params,
});
}
});
// 通知前端 UI 弹出确认弹窗
if (this.onPromptUserCallback) {
this.onPromptUserCallback({
callId,
toolName,
riskLevel,
parameters: params,
});
}
});
}
// 前端用户点击按钮后触发的反馈方法
public resolveUserAction(callId: string, decision: InterceptorDecision) {
const resolver = this.pendingResolvers.get(callId);
if (resolver) {
resolver(decision);
this.pendingResolvers.delete(callId);
}
}
}
在 React UI 中的受控确认卡片渲染
结合 React Hook,将拦截器事件转化为清晰的用户确认浮层:
import React, { useState, useEffect } from 'react';
import type { AgentToolInterceptor, ToolActionPayload } from './interceptor';
export const AgentHumanConfirmationSlot: React.FC<{ interceptor: AgentToolInterceptor }> = ({
interceptor,
}) => {
const [currentRequest, setCurrentRequest] = useState<ToolActionPayload | null>(null);
useEffect(() => {
interceptor.registerUserPromptHandler((payload) => {
setCurrentRequest(payload);
});
}, [interceptor]);
if (!currentRequest) return null;
const handleConfirm = () => {
interceptor.resolveUserAction(currentRequest.callId, { action: 'proceed' });
setCurrentRequest(null);
};
const handleReject = () => {
interceptor.resolveUserAction(currentRequest.callId, {
action: 'reject',
rejectReason: '用户在界面主动终止了操作',
});
setCurrentRequest(null);
};
return (
<div className="my-3 p-4 bg-amber-50 border border-amber-300 rounded-xl shadow-sm">
<div className="flex items-center gap-2 text-amber-900 font-semibold text-sm">
<span>⚠ 高危操作确认请求</span>
<span className="px-2 py-0.5 text-xs bg-amber-200 text-amber-800 rounded">
{currentRequest.toolName}
</span>
</div>
<p className="text-xs text-amber-700 mt-1">
Agent 申请执行敏感操作,请核实以下调用参数:
</p>
<pre className="mt-2 p-2 bg-white rounded border border-amber-200 text-xs font-mono overflow-x-auto text-gray-800">
{JSON.stringify(currentRequest.parameters, null, 2)}
</pre>
<div className="mt-3 flex gap-2 justify-end">
<button
onClick={handleReject}
className="px-3 py-1.5 text-xs font-medium bg-white text-gray-700 border border-gray-300 rounded hover:bg-gray-50"
>
拒绝授权
</button>
<button
onClick={handleConfirm}
className="px-3 py-1.5 text-xs font-medium bg-amber-600 text-white rounded hover:bg-amber-700"
>
确认执行
</button>
</div>
</div>
);
};
工程落地收益
- 构筑安全信任底线:业务方与风控团队无需再担心大模型由于幻觉乱调接口,所有高危动作均有清晰的人工审批留痕。
- 支持参数在线修正:当 Agent 生成的 SQL 查询条件或转账金额有轻微瑕疵时,用户可以在前端直接修改参数后放行,避免推倒重来,大幅提升协同效率。
- 完美融入大模型多轮会话:通过拦截器返回的标准状态,模型能够准确理解“用户拒绝了此项操作”并根据反馈调整下一步规划(例如:“检测到您取消了该操作,请问是否需要换一种方式处理?”)。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐

所有评论(0)