用 n8n 与 Laravel 构建人工审批客服智能体
方案由 n8n 编排智能体工作流,Laravel 保存权威业务状态,退款等操作须经人工批准后执行。文章讨论上下文检索、安全工具暴露与异步暂停,避免推理循环占用同步 PHP 请求。
AI 深度解读
客服 AI 最危险的一步,是把「建议退款」直接变成「执行退款」。原文给出的方案,是让 n8n 负责推理和编排,Laravel 保存业务状态、执行权限校验,模型先生成草稿,人工审核后再继续执行。
这套设计值得看的地方,是把「等待人工」变成了正式的工作流状态。不过,架构方向合理,不代表示例代码已经达到生产要求。审批回调、重复执行和权限边界,仍有几处需要补齐。
模型可以提议,执行权应该留在哪里
原文把系统分成两个部分。
n8n 负责接收 IMAP 邮件或 webhook,调用 LLM API,管理对话记忆,并串联外部客服系统与内部服务。Laravel 负责认证、授权、数据库事务和业务规则,是业务状态的权威来源。
这个分工针对的是一个具体问题:如果把模型反复推理、调用工具、观察结果的循环,全塞进 Laravel 的同步请求,HTTP 请求可能超时,PHP-FPM worker 也可能被长时间占用。人工审核的等待时间更无法塞进一次普通请求。
因此,请求应该尽快结束,后续过程交给可以暂停、恢复的工作流。
模型生成的动作参数,只能算提案,不能算执行许可。
原文的关键设计,是把工具调用落成 AgentDraft 草稿。它记录目标用户、工具名称、参数和 pending_review 状态。审核通过后,系统才创建工单、发送邮件或更新账单。
这里还要补一道边界:原文后半段说由 n8n 的内部执行节点完成数据库修改,但没有交代这个节点是否再次调用 Laravel。按前面的职责划分,最终动作仍应经过 Laravel 的业务规则和事务,避免执行节点绕过权威系统。
先找对上下文,再把工具收窄
模型需要两类上下文:客户的订阅等级、账单、历史工单等业务事实,以及帮助解释问题的技术文档。前者从 Laravel API 或数据库获取;后者适合通过检索增强生成(RAG)补充。
原文指出,客户的问法与文档的术语经常对不上。例如,客户问「昨天部署为什么坏了」,文档却写着「CI/CD GitHub Actions runner timeout exceptions」。直接拿客户原话做向量检索,可能找不到真正相关的内容。
作者建议先做查询改写,或者用多查询检索,从不同表达中召回文档。这个思路合理,但文章没有提供召回率、准确率或幻觉减少幅度,不能把它当成已经验证的效果承诺。
拿到上下文之后,模型还需要明确的工具接口。原文选择 Model Context Protocol(MCP),通过结构化 JSON-RPC 接口暴露工具及参数 schema。
下面这段代码解决的是「让模型提交工单草稿」,而不是直接创建正式工单:
<?php
namespace App\Mcp\Tools;
use App\Models\Ticket;
use Illuminate\Support\Facades\Gate;
class DraftSupportTicket
{
public function schema(): array
{
return [
'name' => 'draft_support_ticket',
'description' => 'Drafts a new support ticket based on the customer email. Requires human approval before final commit.',
'inputSchema' => [
'type' => 'object',
'properties' => [
'user_id' => ['type' => 'integer'],
'subject' => ['type' => 'string'],
'proposed_response' => ['type' => 'string'],
'priority' => ['type' => 'string', 'enum' => ['low', 'medium', 'high', 'critical']]
],
'required' => ['user_id', 'subject', 'proposed_response', 'priority']
]
];
}
public function execute(array $input): array
{
Gate::authorize('create', Ticket::class);
// Instead of creating the ticket, we create a Draft
$draft = AgentDraft::create([
'user_id' => $input['user_id'],
'tool_name' => 'create_ticket',
'payload' => $input,
'status' => 'pending_review'
]);
return [
'success' => true,
'draft_id' => $draft->id,
'message' => 'Draft created. Awaiting human approval via dashboard.'
];
}
}
要看的是 AgentDraft::create 这一行:调用成功表示草稿已保存。代码中的 Gate::authorize('create', Ticket::class) 也只展示了创建权限检查,没有展示目标 user_id 的归属校验;AgentDraft 的导入和实际输入校验同样未给出。
MCP 提供统一的工具描述方式,安全仍然依赖服务端实现。严格校验的 REST API 也可以完成这类任务,不能因为换成 MCP,就省掉授权和参数检查。
人工审核要成为可以恢复的状态
草稿创建后,n8n 进入 Wait 节点,等待 webhook 唤醒。客服人员在 Vue.js 或 Blade 构建的后台里,查看原始邮件、建议回复和优先级,再选择批准、编辑或拒绝。
原文约定了两种恢复指令:
- 批准后回传
draft_id、action: execute和final_payload,继续执行最终动作。 - 拒绝后回传
action: abort,终止自动处理。
这里有两处示例与说明没有接上。文章说 Laravel 返回草稿 ID 和 webhook URL,但前面的工具代码只返回 draft_id,没有说明恢复地址如何生成、保存和绑定。后台被描述为支持修改优先级,审批控制器却只处理 edited_response。
审批控制器还直接接收请求中的 n8n_webhook_url,随后通过 Http::post 访问它。仅用 required|url 检查地址格式,无法证明它属于当前工作流。更稳妥的实现应由服务端保存可信的恢复地址,并校验它与草稿的对应关系。
此外,先把草稿改成 approved,再发送回调,会留下「审批成功、恢复失败」的中间状态。文章没有给出重试和幂等处理,也没有展示拒绝接口的授权校验。这些都需要在上线前补齐。
作者还列出三类异常:Wait 节点超过 24 小时仍未收到审核,就恢复流程、将草稿标记为 expired,并转入纯人工队列;邮件中的提示注入交给人工复核与 Laravel Gate 策略共同拦截;历史上下文只提供最近 3 次交互和摘要,避免塞入 50 行原始记录。
这些是设计建议,文章没有给出容量或效果测试。尤其是「等待必然持续消耗内存」的表述,缺少部署和状态持久化方式的说明,不能直接推广到所有 n8n 环境。
我的判断
我赞同把草稿与正式动作分开。它让审核对象有了明确载体,也让系统能够回答:模型提议了什么、人工接受了什么、最后执行了什么。
但原文将人工审核后的风险描述为「接近零」,依据不足。审核员可能漏看,权限策略可能配置错误,回调也可能重复到达。人工批准只是执行条件之一,不能替代执行时的权限校验和幂等控制。
同样,Gate 是应用层授权机制,文章所说的「在数据库层拒绝」并没有在示例中体现。模型即使调用了 issue_refund,是否能拦住跨账户操作,仍取决于服务端如何识别调用身份、目标账户和可执行范围。
这篇更适合作为架构草图。它没有提供处理时延、客服节省时间、误操作率,也没有与 Laravel 队列加业务状态表的方案比较。已有 Laravel 异步任务体系的团队,可以先实现草稿和审核流程;外部系统多、编排经常变化的团队,再评估 n8n 的价值。
能拿来做什么
- 如果正在给客服系统接 AI,先把会修改业务数据的工具改成生成草稿,并保存原始输入、拟执行参数、审核人和最终结果,让执行过程可追溯。
- 如果负责审核后台,把客户原文、相关业务事实、AI 建议和人工修改放在一起展示,明确提供批准、编辑、拒绝三条路径。
- 如果准备用 n8n Wait 等待审批,先补齐恢复地址绑定、回调鉴权、重复审批处理和失败重试,再配置原文建议的 24 小时过期转人工流程。
- 如果模型经常答非所问,先检查检索结果是否相关,再尝试查询改写和多查询检索;历史上下文可从最近 3 次交互加摘要起步,用实际客服问题检验取舍。