让企业微信、公众号查订单:n8n + LangBot + GPT-6 客服工作流,查不到也不乱答
“我的订单到哪了?”看起来是一个聊天问题,实际需要先查数据。直接把问题扔给模型,它写得再像客服,也不知道某个包裹有没有发货。
我把查单拆成了两部分:订单号识别和数据库查询走确定规则,GPT-6 Astra 只解释查到的结果。n8n 把步骤连起来,LangBot 再把这条工作流接到企业微信或公众号等入口。这个拆法也让“没有订单号”和“订单不存在”有了明确出口。

这篇要搭建的完整工作流
1. 从零启动 n8n 和 LangBot
准备 Docker Compose 和一个能调用 gpt-6-astra 的兼容 API 凭据。实测版本是 n8n 2.39.6 / LangBot 4.10.11,界面切为 English。这里的模型 ID 来自本次使用的网关;换供应商时请确认自己的模型名与接口路径。
新建目录,把下面内容保存为 compose.yaml。
name: langbot-n8n-astra
services:
n8n:
image: docker.n8n.io/n8nio/n8n:2.39.6
environment:
TZ: Asia/Shanghai
GENERIC_TIMEZONE: Asia/Shanghai
N8N_HOST: localhost
N8N_PORT: 5678
N8N_PROTOCOL: http
N8N_EDITOR_BASE_URL: http://localhost:5690
WEBHOOK_URL: http://localhost:5690/
N8N_DIAGNOSTICS_ENABLED: 'false'
N8N_PERSONALIZATION_ENABLED: 'false'
ports:
- '127.0.0.1:5690:5678'
volumes:
- n8n_data:/home/node/.n8n
- ./workflows:/imports:ro
restart: unless-stopped
demo-api:
image: python:3.12-alpine
command: [python, /app/demo-api.py]
volumes:
- ./demo-api.py:/app/demo-api.py:ro
- ./data/demo:/data
restart: unless-stopped
runtime:
image: rockchin/langbot:v4.10.11
command: [uv, run, --no-sync, -m, langbot_plugin.cli.__init__, rt]
volumes:
- ./data/langbot/plugins:/app/data/plugins
restart: unless-stopped
langbot:
image: rockchin/langbot:v4.10.11
depends_on: [runtime, n8n]
environment:
TZ: Asia/Shanghai
BOX__ENABLED: 'false'
PLUGIN__RUNTIME_WS_URL: ws://runtime:5400/control/ws
API__WEBUI_URL: http://localhost:5370
API__WEBHOOK_PREFIX: http://localhost:5370
volumes:
- ./data/langbot:/app/data
ports:
- '127.0.0.1:5370:5300'
restart: unless-stopped
volumes:
n8n_data:
同目录保存 demo-api.py。下面这个小服务只包含虚构订单和会议任务草稿,完整代码可直接运行。
"""Local synthetic order/task service. No real customer data or external writes."""
from http.server import BaseHTTPRequestHandler, HTTPServer
import json
import sqlite3
from pathlib import Path
Path('/data').mkdir(exist_ok=True)
db = sqlite3.connect('/data/tasks.sqlite')
db.execute('CREATE TABLE IF NOT EXISTS meetings (id TEXT PRIMARY KEY, payload TEXT NOT NULL)')
ORDERS = {
'DEMO-1001': {'order_id':'DEMO-1001','status':'shipped','carrier':'Demo Express','tracking':'DEMO-TRACK-901','estimated_delivery':'2026-09-20','items':['USB-C Hub']},
'DEMO-1002': {'order_id':'DEMO-1002','status':'processing','carrier':None,'tracking':None,'estimated_delivery':None,'items':['Desk Lamp']},
}
class Handler(BaseHTTPRequestHandler):
def reply(self, data, code=200):
body=json.dumps(data,ensure_ascii=False).encode();self.send_response(code);self.send_header('Content-Type','application/json');self.end_headers();self.wfile.write(body)
def do_GET(self):
if self.path.startswith('/orders/'):
key=self.path.rsplit('/',1)[-1];self.reply({'found':key in ORDERS,'order':ORDERS.get(key),'source':'Synthetic tutorial order database'})
elif self.path=='/tasks':
self.reply({'meetings':[json.loads(x[0]) for x in db.execute('SELECT payload FROM meetings ORDER BY rowid DESC LIMIT 20')]})
else:self.reply({'service':'LangBot tutorial demo API','fixtures':True})
def do_POST(self):
if self.path!='/tasks':return self.reply({'error':'Not found'},404)
try:
obj=json.loads(self.rfile.read(int(self.headers.get('Content-Length','0'))));key=obj['meeting_id']
if not isinstance(obj.get('tasks'),list) or not key:raise ValueError('Invalid task payload')
db.execute('INSERT INTO meetings VALUES (?,?) ON CONFLICT(id) DO UPDATE SET payload=excluded.payload',(key,json.dumps(obj,ensure_ascii=False)));db.commit()
self.reply({'saved':True,'meeting_id':key,'task_count':len(obj['tasks']),'record':obj})
except (ValueError,KeyError):self.reply({'error':'Invalid task payload'},400)
HTTPServer(('0.0.0.0',8080),Handler).serve_forever()
mkdir -p workflows
docker compose up -d
docker compose ps
打开 http://localhost:5690 创建 n8n 本地管理员,再打开 http://localhost:5370 初始化 LangBot;可跳过快速创建机器人,先建流水线。端口只绑定本机,适合跟做。数据存储在 Docker 卷与 data 目录中,重启容器不会清空。

n8n 首次启动的本地账户界面
LangBot 4.10.11 的 n8n 入口在流水线里。若你使用更新版本,入口可能迁移到 Runner 插件,别把本文的菜单位置当作所有版本都一样。
2. 搭建工作流,把每一步写清楚
演示数据库只有两个订单:DEMO-1001 已发货,DEMO-1002 处理中。它们都是虚构数据,没有接真实商家后台。
Extract Order ID 从消息里识别 DEMO- 加四位数字。没有编号时用 MISSING 进入同一个查询接口。接口返回 found 和 order,IF 节点只判断 found 是否为 true。
true 分支将订单记录和原问题交给模型;false 分支返回固定提示。这样用户问 DEMO-9999 时,模型没有机会“热心”补出一条物流信息。处理中订单的预计送达日期为空,也应原样解释成尚无日期。
这只是演示查询。上线真实订单系统时,必须先验证聊天用户是否有权查询该订单,不能把“知道订单号”当成授权。

完整工作流:消息进入后,数据沿节点流动
在 n8n 新建工作流,按图添加节点,并保持下面的节点名称一致;表达式会用名称读取上游结果。配置如下。
| 节点 | 类型 | 关键设置 |
|---|---|---|
| Chat Webhook | webhook | POST; Path: astra-order; Respond: Using Respond to Webhook Node |
| Extract Order ID | code | Run Once for All Items; JavaScript |
| Look Up Demo Order | httpRequest | GET; http://demo-api:8080/orders/{{$json.order_id}} (Expression) |
| Order Found? | if | {{ $json.found }} → is true |
| GPT-6 Astra - Explain Status | httpRequest | POST; https://newapi.rockchin.top/v1/chat/completions; Header Auth |
| Missing Order Reply | code | Run Once for All Items; JavaScript |
| Reply to LangBot | code | Run Once for All Items; JavaScript |
| Respond to Chat | respondToWebhook | Text; Content-Type: application/json; charset=utf-8 |

用提取出的编号请求订单接口

IF 节点按 found 字段分流
模型凭据和请求体
在 n8n Credentials 新建 Header Auth:Name 填 Authorization,Value 填 Bearer YOUR_API_KEY。回到模型 HTTP Request 节点,选择 Generic Credential Type → Header Auth,绑定这个凭据。不要把真实 Key 写进 Code 节点或导出的工作流。LangBot 不会自动把自己的模型凭据传给 n8n。
打开 Send Body → JSON → Using JSON,切换 Expression,填入下面表达式。示例 URL 是本次网关的 OpenAI 兼容地址;使用自己的服务时连同模型 ID 一起调整。
{{
JSON.stringify({
model: "gpt-6-astra",
messages: [
{
role: "system",
content:
"Explain the supplied synthetic order status in the language of the customer question. Use only the order data. Do not invent arrival guarantees, carrier links, contact details or refunds. Mention that this is demo data. Keep under 120 words. An estimated date is not a guarantee.",
},
{
role: "user",
content: JSON.stringify({
question: $("Extract Order ID").first().json.question,
data: $json,
}),
},
],
stream: false,
max_tokens: 1500,
})
}}
Code 节点内容
Extract Order ID
const text = String($json.body.chatInput || "");
const id = (text.match(/DEMO-\d{4}/i) || [])[0];
return [
{ json: { question: text, order_id: id ? id.toUpperCase() : "MISSING" } },
];
Missing Order Reply
const id = $("Extract Order ID").first().json.order_id;
return [
{
json: {
response:
id === "MISSING"
? "Please include a demo order ID, such as DEMO-1001. 请提供演示订单号,例如 DEMO-1001。"
: "No matching demo order was found. Please check the ID. 没有找到该演示订单,请核对编号。",
},
},
];
Reply to LangBot
return [{ json: { response: $json.choices[0].message.content } }];
连线与最终返回
- Chat Webhook → Extract Order ID
- Extract Order ID → Look Up Demo Order
- Look Up Demo Order → Order Found?
- Order Found? → GPT-6 Astra - Explain Status (
true) - Order Found? → Missing Order Reply (
false) - GPT-6 Astra - Explain Status → Reply to LangBot
- Missing Order Reply → Respond to Chat
- Reply to LangBot → Respond to Chat
最后的 Respond to Chat 是 Respond to Webhook 节点。Respond With 选 Text,在 Options 添加响应头 Content-Type: application/json; charset=utf-8,Response Body 切到 Expression:
{{
JSON.stringify($json).replace(
/[\u007f-\uffff]/g,
(c) => "\\u" + c.charCodeAt(0).toString(16).padStart(4, "0"),
)
}}
这仍然是 JSON 响应,只是把非 ASCII 字符转义后传输。我在当前 LangBot 版本的长中文回复中遇到过分块解码乱码,这个返回方式已通过复测;接收端解析 JSON 后显示的仍是正常中文。最终对象必须包含字符串 response。模型 HTTP Request 的 Options → Timeout 设置为 90000 毫秒。
配置完成后点击 Publish。本文 n8n 2.39.6 用这个按钮启用生产 Webhook;不要只点 Execute workflow 后把 /webhook-test/ 地址填给 LangBot。

明确返回 JSON,并兼容中英文内容
3. 让 LangBot 调用它
在 LangBot 新建一条流水线,打开 Configuration → AI,Runner 选 n8n Workflow API。填写:
- Webhook URL:
http://n8n:5678/webhook/astra-order - Output Key:
response - Timeout:
150 - Webhook Response Handling: Forward as chat reply
- Authentication Type: None(仅限本文隔离的本地演示网络)
这里最容易填错的是主机名。浏览器访问的是 localhost:5690;LangBot 容器访问同一 Compose 网络里的 n8n,应该用服务名 n8n:5678。在容器里填 localhost,会指向 LangBot 容器自己。
本机演示没有开放公网 Webhook。跨主机部署时应配置 HTTPS,并把 n8n Webhook 的 Header Auth / Basic Auth 与 LangBot 对应认证设置对齐。不要直接公开本文未鉴权的演示接口。

LangBot 中的 n8n Runner 设置;按本案例替换 Webhook 路径
保存后打开 Debug Chat,发送:
帮我查一下 DEMO-1001,发货了吗,预计什么时候到?

从 LangBot Debug Chat 走通的真实请求与回复
已发货订单返回了演示承运商、DEMO-TRACK-901 和预计日期,回复没有把预计日期说成保证送达。查 DEMO-9999 得到“没有找到该演示订单”,不带任何编造的物流。
再测一条不带编号的消息,应提示补充订单号;这两个失败分支都不需要调用 GPT。原始订单接口保持为事实来源,模型只是把字段解释成人能读懂的话。

n8n 执行记录,可以逐节点核对输入与输出
4. 接到自己的消息平台
本文已经实际验证 LangBot Debug Chat → n8n → 模型/数据接口 → LangBot 回复。没有使用真实飞书、钉钉、企微或 QQ 账号收发消息,下面是平台接入步骤,不把配置说明当作实投结果。
| 平台 | 在平台侧准备什么 | LangBot 里怎么接 |
|---|---|---|
| 飞书 | 企业自建应用、机器人能力、收发消息权限、事件订阅 | 选 Lark,填 App ID / App Secret;国内飞书可用长连接。Webhook 模式按页面给出的回调地址配置。配置 |
| 钉钉 | 企业应用机器人,启用 Stream 推送 | 选 DingTalk,填 Client ID / Client Secret;卡片回复另配模板与权限。配置 |
| 企业微信应用 | 企业 ID、应用 Secret、通讯录同步 Secret;消息接收 Token / EncodingAESKey | 选 WeCom,先保存机器人,再把生成的回调 URL 填回企微后台。配置 |
| 企微智能机器人 / 微信客服 | 对应机器人或客服接口凭据 | 分别选 WeComBot / WeComCustomerService,按对应适配器配置,不能混用应用机器人凭据。机器人、客服 |
| 微信公众号 | AppID / AppSecret、Token / EncodingAESKey、服务器 IP 白名单 | 选 Official Account,填凭据,把 LangBot 回调 URL 填入服务器配置,使用安全模式。配置 |
| QQ 官方机器人 | QQ 开放平台应用、AppID / Token / AppSecret、测试范围及 IP 白名单 | 选 QQ Official API,按所选模式配置事件回调,并把机器人加入允许的群或频道。配置 |
| QQ / OneBot v11 | 已运行的 OneBot 实现,例如 NapCat | 选 OneBot v11,对齐反向 WebSocket 地址、端口和访问令牌;本地演示也可用协议模拟器。配置 |
| 个人微信 | 对应微信适配器的服务或登录状态 | 按当前版本选 OpenClaw WeChat / WeChatPad 等适配器;各协议能力和登录方式不同。当前平台列表 |
| Slack / Discord / TG / LINE | 各平台机器人凭据;需要回调的平台准备公网 HTTPS | 分别选择同名适配器,再绑定本文流水线。Slack、Discord、TG、LINE |
此外还有 KOOK、Matrix、Mattermost、Satori、HTTP Bot 和网页机器人入口,完整清单以所安装版本的 Create Bot 为准。

LangBot 英文界面的平台选择入口
创建机器人后,把它的流水线绑定为刚才保存的这一条,再开启机器人。群聊还需要检查 Trigger 中的 @ 或前缀规则;机器人在线但不回复,不一定是 n8n 的问题。回调型平台需要公网 HTTPS,长连接或轮询型平台则按对应适配器要求配置网络。
5. 出问题时,从这三处查
- Webhook 404:检查是否已 Publish,以及填的是生产
/webhook/地址。 - n8n 成功但聊天没正文:查看最终 JSON 是否有字符串
response,并确认 Output Key 一致。 - 模型节点 401、超时或空结果:核对 n8n 自己绑定的模型凭据、完整接口 URL、模型 ID 和该次执行数据。LangBot 和 n8n 的模型配置不是一份。
我更愿意先把输入、数据来源和返回字段固定下来,再考虑多加几个模型或平台。LangBot 的价值在这里很直观:业务步骤留在 n8n,换一个消息入口时,主要处理的是适配器和流水线绑定,而不是重写整套业务。
参考:LangBot n8n 配置、LangBot 源码、n8n 源码与版本。
本文配置与截图来自本地实际运行;示例输入为虚构数据。文章使用 AI 辅助整理,并依据执行记录核对。

“我的订单到哪了?”看起来是一个聊天问题,实际需要先查数据。直接把问题扔给模型,它写得再像客服,也不知道某个包裹有没有发货。
浙公网安备 33010602011771号