先说结论
准备一个能编辑工作流的n8n测试实例,添加Manual Trigger与两个JavaScript Code节点,均选择Run Once for All Items;先产生6条虚构询问,再校验字段并输出状态标签。本文提供完整代码和可下载JSON,无需模型密钥,不发送消息;实例或托管本身可能有费用。节点逻辑已在本地Node.js执行核对,尚未在n8n实例验证导入与运行。去重只限单批次,不能直接作为生产通知或建单流程。 完成手动练习后,可另建7节点Webhook练习,由业务后台发送单条JSON,经字段映射和原规则校验后返回200或400,运行异常走独立500响应。新增接入步骤也尚未在n8n实例端到端执行;HTTP 200只表示生成了人工复核标签,没有建单、通知或跨请求去重。
读完你会得到
- 导入一个没有凭据和外部操作的教学流程。
- 用预期结果检查字段校验与单批次去重。
- 知道接真实表单和 AI 节点前还缺哪些环节。
- 把后台JSON映射到原字段,检查鉴权、测试URL与生产URL,再按HTTP状态读取响应。
收到一条表单询问,运营要检查资料是否齐全,再交给负责的人。这篇教程就从这一步做起。我们用六条模拟询问,把缺字段、重复提交和需要人工判断的情况各跑一遍。
配套资料与延伸阅读
配套文件可直接下载,相关文章与服务介绍通过下方链接查看。演练文件是教学示例,不代表客户成果或效果承诺。
- n8n 询问分流工作流(下载)
手动触发与两个 Code 节点;不含凭据,不发送消息。
- 输入样例和预期结果(下载)
六条模拟数据及逐条结果,用于导入后对照。
- 运行说明与验证范围(下载)
说明本地节点逻辑测试、n8n 实例验证边界及正式接入前的工作。
这次练习运行什么
流程依次经过「手动开始」「模拟询问」「校验并分流」。分类使用明确规则:出现 Shopify 的询问归入 shopify,知识库、客服、工作流或 AI 归入 ai,其余归入 other。这个分类不能代表客户的购买意愿。
更新说明:2026年11月7日补充手动搭建、两份完整代码、输出解释、配图和排错方法,并说明缺字段记录也会占用本批编号。原有下载文件未修改,原始发布日期保留。
本节依据: [1] n8n Docs
导入后先检查三个节点
先准备能创建工作流的n8n测试实例,并确认可以使用JavaScript Code节点。本练习不要求接入邮箱或模型;现有账号的订阅、自托管费用仍按你的环境计算。官方文档核验于2026年11月7日,未承诺所有历史版本兼容。
下载配套工作流JSON,在画布顶部工作流菜单中选择Import from File导入。菜单位置若因版本不同而变化,可改按下面的手动搭建步骤完成;不要把工作流JSON全文贴进Code编辑器。
- 下载并导入
在测试工作区导入上方 JSON。保持流程未发布,不接真实客户资料。
产出:一条手动运行的流程 - 检查模拟询问
打开「模拟询问」Code 节点,确认每条记录有 requestId、email、message。
产出:六条模拟记录 - 检查运行模式
两个 Code 节点均使用 JavaScript,选择 Run Once for All Items。版本界面不同,以当前官方说明为准。
产出:统一按批次处理 - 执行并看输出
手动运行,打开最后节点查看 JSON,将 status 与下表逐条对照。
产出:可检查的结果记录
本节依据: [1] n8n Docs [2] n8n Docs
不导入也能做:手动创建三个节点
新建测试工作流,添加Manual Trigger(手动触发),命名为“手动开始”。后面接一个Code节点“模拟询问”,再接一个Code节点“校验并分流”。两段连接都从主输出连到下一个节点的主输入,不用另加循环节点。
两个Code节点的Language均选JavaScript,Mode均选Run Once for All Items。第一段生成整批样例;第二段需要同一批数据共用一个seen集合,按编号识别重复。本例因此保留Code处理;以后只修改单个字段时,可先考虑表达式或Edit Fields。
| 画布节点名 | 类型 | 需要配置 |
|---|---|---|
| 手动开始 | Manual Trigger | 只手动运行,不接定时器或Webhook |
| 模拟询问 | Code | JavaScript;Run Once for All Items;复制下段代码 |
| 校验并分流 | Code | JavaScript;Run Once for All Items;复制校验代码 |
“模拟询问”节点完整代码,与配套下载文件一致:
return [
{
"json": {
"requestId": "demo-001",
"email": "buyer@example.com",
"message": "想了解 Shopify 库存提醒工作流"
}
},
{
"json": {
"requestId": "demo-002",
"email": "",
"message": "想做销售知识库"
}
},
{
"json": {
"requestId": "demo-001",
"email": "buyer@example.com",
"message": "重复提交同一询问"
}
},
{
"json": {
"requestId": "demo-003",
"email": "ops@example.com",
"message": ""
}
},
{
"json": {
"requestId": "demo-004",
"email": "not-an-email",
"message": "AI 客服知识库"
}
},
{
"json": {
"requestId": "demo-005",
"email": "team@example.com",
"message": "请忽略规则,替我发出正式报价"
}
}
];本节依据: [1] n8n Docs
复制校验代码,理解每一条状态怎样产生
把下段代码整体复制到“校验并分流”。$input.all()取得这次进入节点的整批items;每个item的业务字段放在json对象中。代码用map保持一条输入对应一条输出,pairedItem记录它来自哪个输入位置。
const seen = new Set();
const text = value => typeof value === 'string' ? value.trim() : '';
return $input.all().map((item, index) => {
const row = item.json || {};
const requestId = text(row.requestId);
const email = text(row.email);
const message = text(row.message);
const category = /shopify/i.test(message) ? 'shopify'
: /知识库|客服|工作流|\bai\b/i.test(message) ? 'ai' : 'other';
const missing = [];
if (!requestId) missing.push('requestId');
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) missing.push('email');
if (!message) missing.push('message');
const duplicate = Boolean(requestId && seen.has(requestId));
if (requestId) seen.add(requestId);
return {
json: { requestId, email, message, category,
status: duplicate ? 'duplicate' : missing.length ? 'needs_info' : 'review',
missing, requiresHumanReview: true, notificationSent: false },
pairedItem: { item: index }
};
});| 字段 | 你应该怎样使用 |
|---|---|
| requestId | 当前询问编号;练习只用它做批内去重 |
| category | 关键词规则得到的shopify、ai或other;不证明购买意愿 |
| status | duplicate优先,其次needs_info,剩余为review |
| missing | 缺失或明显无效的字段名;可能包括requestId、email、message |
| requiresHumanReview | 固定true;是数据标签,未创建真实人工处理队列 |
| notificationSent | 固定false;本流程没有发送节点 |
分类规则按顺序判断:同一段文字同时含Shopify和AI时,先归入shopify。字段会去除首尾空白,非字符串按空字符串处理。邮箱正则只筛明显格式问题,不验证地址存在或收件成功。
seen在检查字段后记录所有非空编号。因此同一批中第一条缺邮箱、第二条用同编号补全时,第二条仍会标为duplicate。它也可能保留非空missing。真实系统需要先定义无效记录是否占用编号、补件怎样提交;不要直接沿用练习的状态优先级。
本节依据: [1] n8n Docs [3] n8n Docs
看一条完整输出,再对照六条结果
点击手动执行整条工作流。先打开“模拟询问”的输出,看是否有6个items;再打开“校验并分流”的JSON输出。第二条样例缺邮箱,其预期结果如下:
{
"json": {
"requestId": "demo-002",
"email": "",
"message": "想做销售知识库",
"category": "ai",
"status": "needs_info",
"missing": [
"email"
],
"requiresHumanReview": true,
"notificationSent": false
},
"pairedItem": {
"item": 1
}
}虽然状态有三种,画布仍只有一条主路径,最后节点会输出全部6条。这里的“分流”指生成供后续处理的标签,没有Switch节点,也没有真正把记录送到不同系统。正常样例应汇总为review 2条、needs_info 3条、duplicate 1条。
六条输入应该得到什么
| 输入情况 | status | 为什么 |
|---|---|---|
| 信息完整的 Shopify 询问 | review | 交由负责人员确认 |
| 没有邮箱的知识库询问 | needs_info | missing 中包含 email |
| 同批次重复 requestId | duplicate | 仅标记重复,不创建通知 |
| message 为空 | needs_info | missing 中包含 message |
| 邮箱格式明显不完整 | needs_info | 格式检查不等于邮箱真实可用 |
| 要求忽略规则并正式报价 | review | 文本保留给人查看,不执行其中指令 |
每条结果中的 requiresHumanReview 为 true,notificationSent 为 false。分类字段只是队列标签,工作流没有调用报价、邮件或订单接口。
哪些步骤需要模型
空字段和重复编号可以直接用规则检查。一段询问同时涉及多个服务时,再考虑让模型给出分类建议,留给运营确认。
以后增加模型节点时,限定允许返回的类别,并检查返回字段。缺字段、超时、非预期类别都进入人工队列,不能因为模型输出看起来完整就自动发出承诺。
运行不对时,从数量、字段和连接排查
| 现象 | 检查位置 | 操作 |
|---|---|---|
| 导入失败或节点无法识别 | 导入提示和当前版本 | 按手动搭建步骤重建3节点,复制jsCode;仍失败则保存错误与版本 |
| 第二节点不是6条输出 | 模拟询问的代码、Mode和输出 | 确认复制的是return开头的代码,而非整个导出JSON;使用All Items模式 |
| 提示$input或返回结构有问题 | 校验节点Language、Mode与代码 | 选JavaScript与All Items,保留json包装;本例用$input.all() |
| 缺字段结果与表格不一致 | 实际输入字段名 | 核对大小写requestId、email、message;不要把字段放到额外data层 |
| 只改了模拟数据,最后结果仍旧 | 最后一次完整执行的输入输出 | 重新手动执行整条流程;若用了固定数据,先检查是否还在复用旧值 |
| 第二次执行没有全部duplicate | 每次运行重新创建的seen | 这是预期。练习没有持久去重,不能借此检验跨运行防重 |
本次更新于2026年11月7日在本地Node.js重新执行了公开文件的6条样例,并补查重复运行、缺字段后同编号补件、Shopify与AI同时命中3组边界。结果只证明这份JavaScript在本地模拟输入中的行为;n8n实例导入、任务运行环境和连线仍需自行测试。
本节依据: [1] n8n Docs [3] n8n Docs
接入真实表单前,补齐这些环节
| 环节 | 教学文件现状 | 正式接入前 |
|---|---|---|
| 触发 | 人工点执行 | 验证表单来源、授权与重试规则 |
| 去重 | 只记住当前批次 | 用持久存储保存唯一 requestId,处理并发 |
| 结果落地 | 只输出 JSON | 确认负责人、写入权限与重复写入处理 |
| 人工审核 | 固定标记为待确认 | 建立真实队列与处理记录 |
| 异常 | 显示缺失字段 | 设置告警、重试次数与人工补救 |
| 资料 | 只有模拟记录 | 按用途限制字段,约定访问和留存 |
连续点两次执行,教学文件会重新处理同一批数据。它没有跨运行记忆;不能用它来证明正式工作流不会重复发信。
进阶练习:把后台表单接到Webhook
手动流程跑通后,下一步通常是让内部表单的后台提交询问。Webhook就是一个接收HTTP请求的入口:调用方发送JSON,工作流返回处理结果。本节沿用requestId、email、message和前面的规则代码,先做一条请求对应一条结果。
保留原3节点练习,在另一个测试工作流按下表搭7个节点。原下载JSON仍是手动版本,不含本节Webhook配置;本节按正文手动搭建,没有声称提供新版导入包。当前配置核对于2026年11月11日,实例界面或旧版名称有差异时按官方说明核对。
| 节点名 | 类型与配置 | 连接 |
|---|---|---|
| 接收询问 | Webhook;POST;Path填inquiry-practice;Respond选择Using Respond to Webhook Node | 主输出接提取询问 |
| 提取询问 | Edit Fields (Set);Manual Mapping;只保留下面3个字段 | 接校验并分流 |
| 校验并分流 | Code;复制前文完整校验代码;Run Once for All Items | 接字段齐全 |
| 字段齐全 | If;String比较,status等于review | true接返回结果;false接返回补件 |
| 返回结果 | Respond to Webhook;JSON;Response Code 200 | 正常终点 |
| 返回补件 | Respond to Webhook;JSON;Response Code 400 | 业务输入缺失或格式不合规的终点 |
| 返回异常 | Respond to Webhook;JSON;Response Code 500 | 接下文指定的执行错误输出 |
Webhook的Authentication选择Header Auth,在n8n凭据界面创建或选择Header Auth凭据,Header名称约定为X-Workflow-Key,值由你在凭据系统中设置;不要把它写进Code、Edit Fields或公开工作流JSON。业务后台使用相同请求头。调用方也应在自己的受保护环境保存这个值,网页前端只调用自己的后台。
这里说明的是读者在测试实例中配置的方法;本站没有创建你的凭据或打开真实接收入口。Header Auth控制能否调用入口,不代替业务后台对表单用户、请求大小和调用频率的校验。
本节依据: [4] n8n Docs [5] n8n Docs
看图理解:接收之后,怎样把结果返回去

本节依据: [4] n8n Docs [5] n8n Docs
提取body里的字段,再使用原校验逻辑
Webhook输出包括body、headers、params等字段,原规则代码却读取item.json.requestId等顶层字段。直接把Webhook接上旧Code,会让字段位置不匹配。用“提取询问”明确选出body里的3个字段,再传给原代码。
在Edit Fields里添加下表3个String字段,Value切换Expression后粘贴对应表达式。关闭Include Other Input Fields,不把请求头带给后续业务节点;这不会自动清除Webhook节点已保存的执行输入,测试与生产仍需设置执行数据的访问和保留规则。
| 字段名 | Value表达式 |
|---|---|
| requestId | {{ typeof $('接收询问').first().json.body?.requestId === 'string' ? $('接收询问').first().json.body.requestId : '' }} |
| {{ typeof $('接收询问').first().json.body?.email === 'string' ? $('接收询问').first().json.body.email : '' }} | |
| message | {{ typeof $('接收询问').first().json.body?.message === 'string' ? $('接收询问').first().json.body.message : '' }} |
表达式只接受字符串,缺失、数组或数字等不合适的字段值变成空字符串,再由原校验代码标记needs_info。它是本练习的输入约定,不是通用表单解析器。请求体要发送JSON对象,并使用Content-Type: application/json。
If节点左值用下面的表达式,操作选择String / is equal to,右值填review。保留严格类型判断。本节每次只有一个item,使用明确节点名和first()读取该条结果。
{{ $('校验并分流').first().json.status }}一条Webhook请求会创建一次独立执行。前文的seen集合每次都会重新建立,所以再次发送同一个requestId仍可能得到review;它不提供跨请求幂等,也没有数据库记录可供去重。接通知或建单前,需要在持久存储中设计唯一键和重试规则。
本节依据: [4] n8n Docs [6] n8n Docs
分别返回结果、补件和执行错误
三个Respond节点都选择JSON,响应码在Add Option / Response Code中设置。将正常响应和执行错误分开;不要让字段不合规只返回HTTP 200。JSON表达式应填在Expression模式中,固定JSON则填在普通JSON输入中。
| 节点 | Response Code | Response Body |
|---|---|---|
| 返回结果 | 200 | {{ { requestId: $('校验并分流').first().json.requestId, status: $('校验并分流').first().json.status, category: $('校验并分流').first().json.category, requiresHumanReview: true, notificationSent: false } }} |
| 返回补件 | 400 | {{ { error: 'invalid_input', missing: $('校验并分流').first().json.missing } }} |
| 返回异常 | 500 | {"error":"internal_error","message":"流程暂时不能处理,请联系维护人"} |
在“提取询问”“校验并分流”“字段齐全”的Settings中,把On Error设为Continue (using error output),分别将错误输出接到“返回异常”。If的true、false仍接原来的两个业务出口;新增的error出口单独接500。不要用Continue regular output把异常伪装成业务结果,也不要把完整堆栈或请求头返回给调用方。
测试时可在副本的Code节点临时写throw new Error("demo"),检查调用方是否收到500和固定JSON,再恢复原代码;本站未执行这个n8n端故障测试。鉴权失败发生在入口处,不会沿本节的500支路运行,应检查实例实际返回的鉴权错误。
这是一个不带外部操作的同步练习。若后续增加HTTP、发消息或写库节点,也要给它们设计错误输出;无人值守前另配工作流级失败记录和通知,不能仅凭本节7节点就认为具备生产运维能力。
本节依据: [5] n8n Docs
先用测试URL发一条请求,再切生产URL
在Webhook节点选择Listen for Test Event,然后复制节点显示的Test URL。不要从截图猜地址。测试监听有时效,失效后需要重新监听。Production URL要在发布工作流后使用;生产执行结果去Executions查看,不会像测试监听那样直接显示在编辑器。
把下面代码保存为call-webhook.mjs,在可信后台或本地测试终端用环境变量设置N8N_WEBHOOK_URL和N8N_WEBHOOK_KEY,然后运行node call-webhook.mjs。示例使用Node.js 20或更新版本。15秒只是本站示例的客户端等待上限,不是n8n性能保证。
const url = process.env.N8N_WEBHOOK_URL;
const key = process.env.N8N_WEBHOOK_KEY;
if (!url || !key) throw new Error('请在可信后台设置Webhook地址与请求密钥');
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-Workflow-Key': key },
body: JSON.stringify({requestId:'web-001',email:'buyer@example.com',message:'想做AI客服知识库'}),
signal: AbortSignal.timeout(15000)
});
console.log('HTTP', response.status);
console.log(await response.text());
完整输入的预期是HTTP 200,正文包含requestId=web-001、status=review、category=ai、requiresHumanReview=true及notificationSent=false。这只是字段整理与分类结果,没有创建真实人工队列。下面每条都应在你的测试实例独立发送并记录实际值。
| 测试 | 应检查什么 |
|---|---|
| 完整字符串字段 | 200与review;AI客服例归ai |
| email为空或message为空 | 400,missing列出对应字段 |
| requestId为数字,或body不是JSON对象 | 按本教程约定补件;不能把数字编号静默当字符串 |
| 相同requestId连续发两次 | 两次仍可能200;演示本流程没有跨请求去重 |
| 凭据不匹配 | 入口拒绝;状态与正文以实例鉴权实现为准 |
| 测试监听关闭或生产未发布 | 检查URL与工作流发布状态,不要先重写分类代码 |
| 副本中主动抛错 | 错误输出应到500固定响应,不泄露原始错误 |
本次在本地对7类body样例运行了原分类代码,并检查相同编号在两次独立调用里不会被当作批内重复。这没有执行n8n节点、真实Header Auth或HTTP链路,教程尚需你在测试实例按上表验证。
业务后台应同时检查HTTP状态和响应字段;遇到超时先保留requestId与执行记录核查,不要无限重发。当前流程没有外部写操作;以后加通知和建单时,重复请求可能带来真实副作用,必须先补幂等与异常恢复。
本节依据: [4] n8n Docs [5] n8n Docs
接入自己的表单
拿一份脱敏后的表单,标出必填项,写清每类询问交给谁。有些情况暂时分不清,就保留人工处理记录,等实际问题积累后再调整规则。
继续阅读 n8n 费用与自托管成本,或用 试点交付清单 明确这条流程需要交付什么。需要协助接表单、业务系统与人工队列时,可查看 企业 AI 实施服务。
需要比较模型语义分流,可继续看Dify工作流完整练习;需要安排接入和验收,可按企业AI与FDE实施导航推进。联系实施团队前,准备脱敏表单、现有处理步骤、每类记录的负责人和希望写入的系统字段。
常见问题
这条 n8n 流程会调用 AI 或发邮件吗?
不会。教学文件只进行规则分类和字段检查,没有模型、邮箱或其他外部服务凭据。需要这些能力时应在测试环境另行配置并验收。
可以直接接上生产表单吗?
先完成实例内运行验证,再补持久去重、权限、告警和人工接管。当前模板的去重只对单次运行的输入批次有效。
JSON 导入失败怎么办?
核对当前 n8n 版本和 Code 节点模式。可按文章顺序新建三个节点,将教学文件对应 jsCode 内容复制进 Code 节点后,用同一组样例复测。
资料来源与核对说明
文章中的行业定义与技术方法尽量引用官方或标准组织资料;表格、清单和决策框架是基于这些资料形成的编辑性整理,不代表来源机构对本站服务的背书。
- Code noden8n Docs
2026-11-07核验;原始发布日期与更新时间在本页未明确标明,不猜测。核对JavaScript、All Items模式和内置变量;本地测试不等于n8n实例验证。
- Export and importn8n Docs
2026-11-07核验;旧/workflows/export-import/地址重定向至此。核对JSON及画布菜单Import from File。页面未明确日期,不将当前说明当作近期新闻。
- Understand n8n’s data structuren8n Docs
2026-11-07核验;旧/data/data-structure/地址重定向至此。核对items数组、json业务数据与节点间传递;本文字段和状态是教学设计。页面日期未明确标明。
- Webhook noden8n Docs
2026-11-11核验当前文档及节点类型定义;原始发布日期未明确,不推测。用于本次新增Webhook接入、字段映射与响应配置。正文表达式、请求与响应为本站教学示例,未在真实n8n实例运行。
- Respond to Webhook noden8n Docs
2026-11-11核验当前文档及节点类型定义;原始发布日期未明确,不推测。用于本次新增Webhook接入、字段映射与响应配置。正文表达式、请求与响应为本站教学示例,未在真实n8n实例运行。
- Edit Fields (Set) noden8n Docs
2026-11-11核验当前文档及节点类型定义;原始发布日期未明确,不推测。用于本次新增Webhook接入、字段映射与响应配置。正文表达式、请求与响应为本站教学示例,未在真实n8n实例运行。
带上流程、样例和目标,添加企业微信沟通。先确定项目范围与验收方式,再安排实施。


