快来看,n8n更新了!在NVIDIA DGX Spark上运行你的n8n AI工作流,无需云API

qimuai 发布于 阅读:46 一手编译

快来看,n8n更新了!在NVIDIA DGX Spark上运行你的n8n AI工作流,无需云API

内容来源:https://blog.n8n.io/n8n-nvidia-dgx-spark/

内容总结:

许多中小企业对AI的需求其实相当集中:起草邮件、汇总通话、读取发票、检索内部文档,偶尔生成图片。但其中不少企业无法把客户数据发送到云端API来完成这些工作,原因通常是数据隐私与主权要求。诊所、律所或会计师事务所持有的记录,可能因合同、监管规定或职业义务而必须留在自己的掌控之下。数据一旦进入云端API,就存放在别人的硬件上,可能位于另一个司法辖区,且适用条款由服务商单方面变更。在自己拥有的硬件上运行模型,数据始终留在自己的场所内,也能向审计人员清楚说明数据的去向。

通过n8n工作流,用户可以在不向云端API发送任何字节的情况下完成通话转写、发票读取、基于自有文档的问答以及回复起草。本文介绍如何用一台NVIDIA DGX Spark为这些工作流提供所需的全部模型,并统一在一个OpenAI兼容端点之后,n8n的OpenAI节点可直接对接。

DGX Spark拥有128GB统一内存,由CPU和GPU共享。只要选对模型,就足以同时承载上述所有任务。本文的重点正在于此。此前的自托管AI入门套件只能让一个人在笔记本电脑上运行本地模型,而本文介绍的是下一步:在办公室放一台机器,服务整个团队,每项任务配一个模型,并附上运行代码。

本文涵盖以下内容:选择在DGX Spark内存带宽上运行较快的模型;用vLLM以NVFP4精度 serving 一个35B混合专家模型;把完整的AI栈装入一台机器的共享内存;通过一个LiteLLM端点路由所有任务,并按团队分配密钥;将n8n接入该栈并构建一个完全不调用云端API的工作流;测量有人生成图片时聊天速度受到的影响。

按带宽选模型,而非按参数规模

DGX Spark的GB10 Grace Blackwell超级芯片内存读取速度为273 GB/s。模型生成文本时,每生成一个token都要从内存读取其激活权重,因此读取的字节越少,运行越快。混合专家(MoE)模型每个token只激活少量专家。Qwen3.6-35B-A3B总参数为35B,但激活参数约3B。以NVFP4(NVIDIA的4位浮点格式)存储时,这些激活权重只占稠密模型的一小部分。在笔者的DGX Spark上,Qwen3.6-35B-A3B单用户生成速度为每秒120个token,八用户合计每秒359个token,其中包含模型作答前写出的推理token。

由此得出第一条设计原则:用一个强大的MoE模型处理所有文本类任务。摘要、分类、抽取、起草、编码、读取截图都交给同一个模型。每多一个模型,就多一个需要固定、打补丁和监控的容器,因此只有在LLM无法胜任的场景才给专用模型留位置。

模型栈

以下模型常驻于一台DGX Spark:聊天、编码、视觉、抽取使用nvidia/Qwen3.6-35B-A3B-NVFP4(Apache 2.0);嵌入使用Qwen/Qwen3-Embedding-4B(Apache 2.0);重排序使用Qwen/Qwen3-Reranker-0.6B(Apache 2.0);语音转文字使用openai/whisper-large-v3-turbo(MIT);文档OCR使用zai-org/GLM-OCR(MIT);图像生成使用Tongyi-MAI/Z-Image-Turbo(Apache 2.0);文字转语音使用hexgrad/Kokoro-82M(Apache 2.0)。栈中所有模型均允许商业使用。这条规则付出的代价比任何技术规则都大,后文有专门章节说明。

第一步:缓存全部模型以供离线使用

该栈必须能在拔掉网线的情况下运行,因此要一次性下载所有模型并记录确切的commit。以下循环会缓存每个模型,并写入包含仓库与commit的锁文件。启动模型时,通过--revision传入models.lock中的commit,vLLM便会提供经过测试的权重,而非main当前指向的版本,从而避免上游变更悄悄替换权重或许可证。

检查vLLM版本,而非标签

容器也要同样固定,并检查其内部版本。笔者第一次尝试使用vLLM的cu130-nightly标签,该标签已停止更新,拉取的镜像已是五个月前版本,内含vLLM 0.19,完全无法加载Qwen3.6。报错信息并未提及版本,而是缺少某个权重缩放参数导致的KeyError,看起来像是检查点损坏。在信任某个镜像前,先打印其中的vLLM版本。可用的是官方vllm/vllm-omni:v0.30.0镜像,内含vLLM 0.30.0并带arm64构建。vLLM-Omni是完整vLLM加上图像和语音的额外服务模式,因此也能服务普通LLM。其默认入口不是vllm serve,需包装一次。Kokoro-FastAPI镜像需要社区修复才能在arm64上构建,其Dockerfile来自NVIDIA开发者论坛上swann.schilling的帖子。

第二步:启动主干模型

该命令在vLLM中启动主模型。在笔者的DGX Spark上,docker run之后311秒模型即准备好提供服务。端口发布在172.17.0.1(Docker桥接地址)上,因此只有本机容器可访问。VLLM_API_KEY使vLLM拒绝任何不带密钥的请求。

在DGX Spark上最关键的几个参数:--gpu-memory-utilization 0.32将该模型内存上限设为约三分之一,其余模型需要剩下的部分;--kv-cache-dtype fp8将KV缓存所需内存减半,同样预算可容纳更多上下文;--moe-backend marlin为量化专家层选择内核,草稿模型的专家未量化,因此--speculative-config为其单独设置moe_backend;--speculative-config开启多token预测(MTP),模型一次向前起草三个token并一并校验;--reasoning-parser和--tool-call-parser必须与模型家族匹配,工具调用失败时先检查这些参数再归咎于模型。

启动日志中应检查vLLM选择的内核。最后那条警告对理解DGX Spark上的“NVFP4”很重要:在此vLLM构建下,权重以4位存储,但计算以16位进行。由于生成token受限于从内存读取权重,而4位权重只有四分之一大小,因此仍能获得大部分收益。针对GB10的更快的FP4内核可能在后继版本中提升这些数字。

第三步:将其余模型装入共享内存

在DGX Spark上,GPU与操作系统共享一个128GB内存池。笔者的机器上Linux报告总内存为121GB。这改变了两件在独立GPU上理所当然的事。第一,--gpu-memory-utilization是启动时占整个内存池的比例,而非占空闲内存的比例。没有上限的实例会试图占用几乎全部内存,导致下一个模型无法启动。第二,vLLM根据启动时可用内存来确定缓存大小。同时启动两个模型会争抢同一块内存。应逐个启动,等每个模型通过健康检查后再启动下一个。如果模型报CUDA error: out of memory而free显示大量可用,通常是Linux页缓存在占用,下载220GB权重后尤为常见。清理缓存后重新启动即可。

以下是各服务冷启动(先清空页缓存)所需时间及占用内存:qwen3.6为237秒、43GB;rerank为106秒、8GB;whisper为136秒、8GB;ocr为137秒、11GB;image为96秒、20GB;tts为21秒、3GB。嵌入模型在vLLM 0.30.0上还需额外参数--convert embed,并在其余模型之后启动。七个模型全部加载后,DGX Spark占用108GB,剩余13GB,这就是一台DGX Spark的容量上限。应提前启动该栈:表中六个服务约需12分钟。vLLM启动时还可能因内存分析报错而失败,原因是在其测量可用内存时有其他进程释放内存;在共享内存环境下,若另一模型正在加载或关闭就会出现。待机器空闲后重新启动即可。

第四步:前置统一端点

LiteLLM为每个客户端提供单一的OpenAI兼容端点。每项任务都是一个命名路由,因此可以在不改动任何客户端的情况下替换路由背后的模型。LiteLLM开源版还处理虚拟密钥、团队和预算。LiteLLM配置中的每个路由指向一个后端。务必保留drop_params: true。否则LiteLLM会对任何设置response_format的图像请求返回400错误,因为它不会为自托管图像模型映射该字段。开启后LiteLLM会丢弃该字段,vLLM-Omni仍会以base64返回图像。

LiteLLM也运行在Docker中。为使其能访问后端,需在其Compose服务中将host.docker.internal映射到宿主机。后端仅监听Docker桥接网络并要求共享密钥,因此LiteLLM是办公网络上唯一暴露的服务。应将LiteLLM指向桥接地址而非机器的LAN地址;若使用LAN地址,后端会拒绝连接,因为它们并未监听该地址。

LiteLLM应按镜像摘要固定,并使用1.84.0或更高版本。2026年3月有两个PyPI版本被植入后门,1.84.0修复了两个认证绕过漏洞。笔者运行的是1.99.1。至此任何OpenAI客户端都可对接DGX Spark。示例Python代码通过同一客户端调用三种不同模型。num_inference_steps不是OpenAI参数,因此放在extra_body中,LiteLLM会将其透传给vLLM-Omni,下一节说明其重要性。

第五步:接入n8n

n8n的OpenAI凭证接受自定义Base URL,因此n8n可以像对接OpenAI一样对接DGX Spark。步骤如下:首先用LiteLLM主密钥为该工作流所属团队创建一个虚拟密钥,限制其只能访问所需路由;然后在n8n中创建OpenAI凭证,在API Key中填入虚拟密钥,将Base URL设为http://:4000/v1。由于每个团队都有自己的虚拟密钥,LiteLLM可按团队跟踪开销与用量,尽管并不按token计费。

若要在AI Agent或Basic LLM Chain中使用DGX Spark,添加一个使用该凭证的OpenAI Chat Model节点,将其模型按ID设为LiteLLM路由名(如qwen3.6),然后在节点选项中关闭Use Responses API。从1.3版本起,该节点默认调用OpenAI较新的Responses API,而DGX Spark的模型通过chat completions提供服务。

工作流:读取发票

该工作流提供一个网页表单。上传发票图片后,表单显示供应商、发票号、日期和总额,全程无任何云端调用。它包含六个节点:上传发票(n8n表单触发器)提供含一个文件字段的表单;图像转base64(从文件提取)将上传内容转为base64字符串;DGX Spark上的OCR(HTTP请求)将图像发送到ocr路由并以文本形式取回页面;用Qwen3.6抽取字段(HTTP请求)要求qwen3.6以JSON返回供应商、发票号、日期、总额和币种;发票字段(编辑字段)将回复整理为包含这五个字段的单个条目;显示字段(表单结束)在表单结果页展示这些字段。若要自动归档发票,可将表单替换为Email Trigger(IMAP)节点,将结果页替换为Postgres或Google Sheets节点。

两个HTTP请求节点都使用上一节的OpenAI凭证,因此发送相同的密钥。它们使用HTTP请求而非OpenAI节点,是因为每次调用都需要一个OpenAI节点不设置的字段:OCR调用中的图像,以及抽取调用中的思考开关。抽取请求体中通过chat_template_kwargs关闭该次调用的模型思考,速度提升正来自这里。在一张干净的单页样本发票上测试:OCR一页耗时1.8秒;抽取在开启思考时耗时12.1秒、1,616个token,关闭思考时耗时0.59秒、76个token。开启思考时,模型在给出78个token的答案前写下了1,538个推理token;关闭思考时,不到一秒就正确抽取了全部五个字段。对于固定格式抽取,应关闭思考。OCR正确读取了每一行项目和总额,但漏掉了增值税号中的一位零。归档前应校验各类ID。表单接受PNG和JPEG文件,PDF发票需先转为图像,因为GLM-OCR只接受图像。扫描或拍摄的发票比这张干净页面更难,因此在依赖OCR前应用自己的文档测试。

同一栈支持的其他工作流

通话摘要:Whisper转写录音,qwen3.6生成摘要,Kokoro将摘要读回,用于语音留言或内部通报。基于自有文档的问答:embed填充向量库,rerank对匹配结果排序,由qwen3.6上的AI Agent据此作答。

一台DGX Spark的实测数据

所有数字来自2026年10月13日的一次基准测试,使用vLLM 0.30.0并固定模型commit。不同镜像、模型或提示词会得到不同结果。每个聊天提示词都以随机标签开头,以免前缀缓存美化结果。

聊天速度方面:单用户时首token中位延迟0.11秒,每用户每秒120个token,合计每秒87个token;四用户时分别为0.21秒、68、242;八用户时分别为0.29秒、47、359。每用户速度是各请求首token之后生成速度的中位数;合计为全部生成token除以测试耗时,因此也计入首token时间和较慢的请求。单用户测试中,五个请求里有一个以每秒43个token运行,其余四个为117至129,拉低了合计值。使用8,537个token的提示词时,首token耗时1.14秒。随着用户增加,每用户速度下降,但总吞吐持续上升,这正是批处理的作用:vLLM在每次遍历权重时为多个用户服务。

其他任务方面(三次中位数):文字转语音一段30秒音频耗时0.87秒;语音转文字转写该片段耗时0.58秒;图像生成一张1024x1024、8步耗时12.2秒;OCR一页耗时1.80秒;抽取(OCR文本转发票JSON,关闭思考)耗时0.59秒;嵌入32段文本耗时0.18秒;重排序32个候选耗时0.03秒。语音双向都远快于实时:30秒音频往返各不到一秒。Whisper转写准确,但末尾多加了一句音频中不存在的“Thank you.”。Whisper已知会在静音处凭空生成文本,转写前应剪掉尾部静音。图像生成的步数需自行设置。Z-Image-Turbo设计为约8步生成一张图,但vLLM-Omni默认约48步、每步1.5秒,每张图耗时72秒;设为8步后,同样的1024x1024图像耗时12.2秒。

图像生成时的聊天表现

机器上所有模型共享一块GPU。装得进内存不等于能良好并行运行。笔者未找到DGX Spark上这方面的公开基准,因此自行测量:在按服务器默认步数连续生成1024px图像的同时重跑聊天测试。单用户时每用户每秒token从122降至38,下降69%;四用户时从69降至24,下降65%。图像生成期间聊天速度约为正常的三分之一,图像本身也变慢,从73秒增至85至122秒。一台DGX Spark能容纳整个栈,但无法在不让所有人聊天变慢的情况下运行图像生成。对小团队而言,可将图像任务排队或安排在工作时间之外。一旦图像生成成为日常工作,应将媒体任务迁到第二台DGX Spark,第一台专用于文本。

许可证问题

模型质量并非难点,许可证才是。多款最好的开源文字转语音和图像模型不允许商业使用。与Z-Image-Turbo竞争的FLUX模型为非商业许可或需单独付费授权。写作时最强的开源图像模型Qwen-Image-2.1以研究许可发布。有些仓库甚至在宽松原版旁附带一个非商业双胞胎,缓存错误的仓库ID就会改变许可证。本栈遵循的规则是:只允许Apache 2.0、MIT、CC-BY-4.0、OpenMDW和NVIDIA Open Model License;将每个模型固定到Hugging Face commit,使上游许可证变更无法悄悄落地;在文档中注明CC-BY-4.0模型。这不是法律建议。在销售或为客户部署前,应请人审核每一个固定的许可证。

注意事项

新检查点可能需要比所固定版本更新的vLLM,而报错不会说明这一点,应先检查镜像内的版本。faster-whisper没有arm64的CUDA构建,基于它的工具会在不提示的情况下跑在CPU上。本栈使用vLLM而非入门套件中的Ollama。Ollama适合一个人在笔记本电脑上使用;对团队而言,vLLM会将多用户请求批处理进每次权重遍历,多用户吞吐正来源于此。

动手尝试

导入发票工作流模板。若尚无DGX Spark,可先从自托管AI入门套件小规模起步。若在自己的DGX Spark上运行,欢迎在n8n社区论坛分享实测数据。

中文翻译:

大多数小企业想要的 AI 任务其实就那么几样:起草邮件、总结通话、读取发票、检索自己的文档,偶尔生成张把图片。但其中不少企业没法把客户数据发到云端 API 去做这些事。

原因往往是数据隐私和数据主权。一家诊所、律所或会计师事务所手里的记录,可能因合同、监管规定或职业义务而必须留在自己的控制范围内。数据一旦发到云端 API,就落在别人的硬件上,可能还在另一个司法管辖区,适用的条款供应商随时可以改。把模型跑在自己的硬件上,数据就留在自己的楼里,还能向审计人员明确指出数据去了哪里。

你的 n8n 工作流可以转写通话、读取发票、基于自己的文档回答问题、起草回复,而且一个字节都不用发给云端 API。本文介绍如何用一台 NVIDIA DGX Spark 为这些工作流所需的全部模型提供服务,统一挂在一个 OpenAI 兼容端点后面——n8n 的 OpenAI 节点本来就会说这套协议。

一台 DGX Spark 有 128GB 统一内存,由 CPU 和 GPU 共享。只要选对模型,足够把上述所有任务同时常驻在内存里。本文大部分内容讲的就是这件事。

自托管 AI 入门套件能让一个人在笔记本电脑上跑起本地模型。下一步是:在办公室里放一台机器,服务整个团队,每个任务配一个模型,再加上运行它们的代码。

本文你将学到:

按带宽选模型,而不是按参数量

DGX Spark 的 GB10 Grace Blackwell 超级芯片内存读取速度为 273 GB/s。模型生成文本时,每生成一个 token 都要从内存读取其激活权重,所以读的字节越少,跑得越快。

混合专家(MoE)模型每个 token 只激活少数几个专家。Qwen3.6-35B-A3B 总共有 35B 参数,但激活的只有约 3B。以 NVFP4(NVIDIA 的 4 位浮点格式)存储时,这些激活权重只占稠密模型的一小部分。

在我的 DGX Spark 上,Qwen3.6-35B-A3B 单用户生成速度为每秒 120 token,八个用户合计每秒 359 token。这些数字包含模型作答前写出的推理 token。

由此得出第一条设计原则:用一个大而强的 MoE 模型处理所有文本类任务。摘要、分类、抽取、起草、写代码、读截图,全部交给同一个模型。每多一个模型,就多一个要固定、打补丁、监控的容器,所以专用模型只有在 LLM 干不了这个活时才占一个位置。

这套栈

以下模型常驻在一台 DGX Spark 上:

任务 模型 许可证
聊天、编码、视觉、抽取 nvidia/Qwen3.6-35B-A3B-NVFP4 Apache 2.0
嵌入 Qwen/Qwen3-Embedding-4B Apache 2.0
重排序 Qwen/Qwen3-Reranker-0.6B Apache 2.0
语音转文字 openai/whisper-large-v3-turbo MIT
文档 OCR zai-org/GLM-OCR MIT
图像生成 Tongyi-MAI/Z-Image-Turbo Apache 2.0
文字转语音 hexgrad/Kokoro-82M Apache 2.0

栈里每个模型都允许商用。这条规则的代价比任何技术规则都大,后面有专门一节讲。

第 1 步:缓存一切,供离线使用

这套栈必须能在拔掉网线的情况下运行,所以每个模型只下载一次,并记录你拿到的确切 commit。下面这个循环会缓存每个模型,并写出一个包含仓库和 commit 的锁定文件:

pipx install "huggingface_hub[cli]"
hf auth login
for repo in nvidia/Qwen3.6-35B-A3B-NVFP4 Qwen/Qwen3-Embedding-4B Qwen/Qwen3-Reranker-0.6B \
openai/whisper-large-v3-turbo zai-org/GLM-OCR Tongyi-MAI/Z-Image-Turbo; do
hf download "$repo"
echo "$repo $(cat ~/.cache/huggingface/hub/models--${repo//\//--}/refs/main)" >> models.lock
done

启动模型时,把 models.lock 里的 commit 通过 --revision 传进去。这样 vLLM 服务的就是你测试过的权重,而不是 main 今天指向的东西,上游的改动就无法在你不知情的情况下换掉权重或许可证。

检查 vLLM 版本,别只看标签

容器也要同样固定,并且检查里面装的是什么。我第一次尝试用的是 vLLM 的 cu130-nightly 标签。那个标签早已停止更新,拉下来的镜像还是五个月前的,里面是 vLLM 0.19,根本加载不了 Qwen3.6。报错信息里完全没提版本,而是一个 KeyError,说缺少某个 weight-scale 参数,看起来像是 checkpoint 坏了。

信任一个镜像之前,先打印里面的 vLLM 版本:

docker run --rm --entrypoint python3 <image> -c "import vllm; print(vllm.__version__)"

能用的镜像是官方的 vllm/vllm-omni:v0.30.0,自带 vLLM 0.30.0,并有 arm64 构建。vLLM-Omni 是完整的 vLLM,额外支持图像和语音的服务模式,所以也能服务普通 LLM。它的默认 entrypoint 不是 vllm serve,所以要包一层:

printf 'FROM vllm/vllm-omni:v0.30.0\nENTRYPOINT ["vllm", "serve"]\n' | docker build -t vllm-spark:omni-0.30.0 -

Kokoro-FastAPI 镜像需要一个社区修复才能在 arm64 上构建。Dockerfile 来自 swann.schilling 在 NVIDIA 开发者论坛上的一篇帖子。

第 2 步:服务主干模型

这条命令在 vLLM 中启动主模型:

docker run -d --name qwen3.6 --gpus all --ipc=host \
-p 172.17.0.1:8001:8000 \
-e HF_HUB_OFFLINE=1 -e VLLM_API_KEY=<your-backend-key> \
-v ~/.cache/huggingface:/root/.cache/huggingface \
vllm-spark:omni-0.30.0 \
nvidia/Qwen3.6-35B-A3B-NVFP4 --revision <commit-from-models.lock> \
--served-model-name qwen3.6-35b \
--gpu-memory-utilization 0.32 --max-model-len 65536 \
--max-num-seqs 8 --max-num-batched-tokens 8192 \
--trust-remote-code --kv-cache-dtype fp8 --moe-backend marlin \
--enable-chunked-prefill --async-scheduling --enable-prefix-caching \
--speculative-config '{"method":"mtp","num_speculative_tokens":3,"moe_backend":"triton"}' \
--reasoning-parser qwen3 --enable-auto-tool-choice --tool-call-parser qwen3_xml \
--host 0.0.0.0 --port 8000

在我的 DGX Spark 上,docker run 之后 311 秒,这个模型就绪可服务。

端口发布在 172.17.0.1,即 Docker 的桥接地址上,所以只有本机上的容器能访问。VLLM_API_KEY 让 vLLM 拒绝任何不带密钥的请求。

在 DGX Spark 上最要紧的几个参数:

查看启动日志,看 vLLM 选了哪些 kernel。下面几行来自我的运行:

Using MarlinNvFp4LinearKernel for NVFP4 GEMM
Using 'MARLIN' NvFp4 MoE backend out of potential backends: ['FLASHINFER_TRTLLM', 'FLASHINFER_CUTEDSL', ...]
Your GPU does not have native support for FP4 computation but FP4 quantization is being used.
Weight-only FP4 compression will be used leveraging the Marlin kernel.

最后那条警告关系到怎么理解 DGX Spark 上的“NVFP4”。在这个 vLLM 构建里,权重以 4 位存储,但计算是 16 位。你仍能拿到大部分好处,因为生成 token 的瓶颈在于从内存读权重,而 4 位权重只有四分之一大小。后续构建中针对 GB10 的更快 FP4 kernel 可能会把这些数字再提上去。

第 3 步:把其余模型塞进共享内存

在 DGX Spark 上,GPU 和操作系统共用一个 128GB 内存池。我这台机器上 Linux 报告可用总量为 121GB。这改变了两件你在独立显卡上可能习以为常的事。

第一,--gpu-memory-utilization 是整池内存的比例,在启动时取值,而不是空闲内存的比例。不设上限的实例会试图占掉几乎全部内存,下一个模型就起不来。

第二,vLLM 根据启动时的空闲内存来确定缓存大小。同时启动两个模型,它们会抢同一块内存。要一个一个启动,等前一个通过健康检查再启动下一个:

until curl -sf http://172.17.0.1:8001/health; do sleep 5; done

如果模型报 CUDA error: out of memory,而 free 显示还有很多可用内存,那是 Linux 页缓存在占着。刚下载完 220GB 权重后这很常见。清掉缓存,再启动模型:

sudo sh -c 'sync; echo 3 > /proc/sys/vm/drop_caches'

下面是先清掉页缓存后,每个服务冷启动所需的时间,以及占用的内存:

服务 就绪秒数 占用 GB
qwen3.6 237 43
rerank 106 8
whisper 136 8
ocr 137 11
image 96 20
tts 21 3

嵌入模型在 vLLM 0.30.0 上还需要多一个参数 --convert embed,并且它启动较晚,排在其余模型之后。七个模型全部加载后,这台 DGX Spark 用了 108GB,剩余可用 13GB。这就是一台 DGX Spark 的承载上限。要提前很久启动这套栈:表里六个服务花了大约 12 分钟。

vLLM 启动时还可能报 Error in memory profiling,原因是 vLLM 测量可用内存时另一个进程释放了内存。在共享内存的情况下,只要有别的模型在加载或关闭就会发生。等机器安静下来再启动一次。

第 4 步:在前面放一个统一端点

LiteLLM 给每个客户端一个 OpenAI 兼容端点。每个任务是一条命名路由,这样你可以在不动任何客户端的情况下换掉路由背后的模型。LiteLLM 的开源版还处理虚拟密钥、团队和预算。

LiteLLM 的 config.yaml 里每条路由指向一个后端。下面摘录其中三条:

model_list:
- model_name: qwen3.6 # main model: chat, coding, vision, extraction
litellm_params:
model: hosted_vllm/qwen3.6-35b
api_base: http://host.docker.internal:8001/v1
api_key: os.environ/BACKEND_API_KEY
- model_name: whisper
litellm_params:
model: openai/whisper-large-v3-turbo
api_base: http://host.docker.internal:8101/v1
api_key: os.environ/BACKEND_API_KEY
model_info:
mode: audio_transcription
- model_name: image # Z-Image-Turbo on vLLM-Omni
litellm_params:
model: openai/z-image-turbo
api_base: http://host.docker.internal:8103/v1
api_key: os.environ/BACKEND_API_KEY
model_info:
mode: image_generation
litellm_settings:
drop_params: true # needed for the image route, see below
general_settings:
master_key: os.environ/LITELLM_MASTER_KEY
database_url: os.environ/DATABASE_URL

务必保留 drop_params: true。没有它,LiteLLM 会以 400 错误拒绝任何设置了 response_format 的图像请求,因为它不会为自托管图像模型映射这个字段。有了它,LiteLLM 会丢掉该字段,vLLM-Omni 照样以 base64 返回图片。

LiteLLM 也跑在 Docker 里。要让它能访问后端,在其 Compose 服务里把 host.docker.internal 映射到宿主机:

extra_hosts:
- "host.docker.internal:host-gateway"

后端只监听 Docker 桥接网络,并要求共享密钥,所以 LiteLLM 是办公网络上唯一的服务。要让 LiteLLM 指向桥接地址,而不是这台机器的局域网地址。如果用局域网地址,后端会拒绝连接,因为它们没在那里监听。

LiteLLM 按镜像 digest 固定,并使用 1.84.0 或更高版本。2026 年 3 月有两个 PyPI 发布版本被植入后门,1.84.0 修复了两个认证绕过漏洞。我用的是 1.99.1。

现在任何 OpenAI 客户端都能对这台 DGX Spark 使用。下面这个 Python 示例通过同一个客户端调用三个不同的模型:

from openai import OpenAI
client = OpenAI(base_url="http://<spark-ip>:4000/v1", api_key="<your-litellm-key>")

reply = client.chat.completions.create(
model="qwen3.6",
messages=[{"role": "user", "content": "Draft a two-line reply confirming Tuesday's delivery."}],
)

with open("call.wav", "rb") as audio:
transcript = client.audio.transcriptions.create(model="whisper", file=audio)

image = client.images.generate(
model="image",
prompt="a delivery van outside a bakery at dawn",
size="1024x1024",
extra_body={"num_inference_steps": 8},
)

num_inference_steps 不是 OpenAI 的参数,所以放在 extra_body 里。LiteLLM 会把它透传给 vLLM-Omni。下一节解释为什么它重要。

第 5 步:连接 n8n

n8n 的 OpenAI 凭据接受自定义 base URL,所以 n8n 可以像对 OpenAI 说话一样对 DGX Spark 说话。要把 n8n 指向 DGX Spark,按以下步骤操作:

curl -s http://<spark-ip>:4000/key/generate \
-H "Authorization: Bearer <your-litellm-master-key>" -H 'Content-Type: application/json' \
-d '{"key_alias": "n8n-accounts", "models": ["qwen3.6", "ocr"]}'

因为每个团队有自己的虚拟密钥,LiteLLM 能按团队跟踪花费和用量,尽管并不按 token 计费。

要在 AI Agent 或 Basic LLM Chain 中使用 DGX Spark,添加一个 OpenAI Chat Model 节点并使用该凭据,把其模型 By ID 设为一个 LiteLLM 路由名,比如 qwen3.6。然后关闭节点选项里的 Use Responses API。从 1.3 版起,该节点默认调用 OpenAI 较新的 Responses API,而 DGX Spark 的模型是通过 chat completions 提供的。

工作流:读取发票

这个工作流给你一个网页表单。上传发票图片,表单会显示供应商、发票号、日期和总额,全程不调用云端。获取模板。

它有六个节点:

要自动归档发票,把表单换成 Email Trigger (IMAP) 节点,把结果页换成 Postgres 或 Google Sheets 节点。

两个 HTTP Request 节点都使用上一节的 OpenAI 凭据,所以发送的是同一个密钥。它们用 HTTP Request 而不是 OpenAI 节点,是因为每次调用都需要一个 OpenAI 节点不提供的字段:OCR 调用需要传图片,抽取调用需要一个思考开关。下面是抽取请求的 body:

{
model: 'qwen3.6',
max_tokens: 512,
temperature: 0,
chat_template_kwargs: { enable_thinking: false },
messages: [{ role: 'user', content: 'Extract supplier name, invoice number, invoice date, total amount and currency from this OCR text. Reply with one JSON object and nothing else. Use null for anything missing.\n\n' + $json.choices[0].message.content }]
}

chat_template_kwargs 为这次调用关掉了模型的思考,速度就来自这里。我在一张干净的单页样例发票上做了测试:

步骤 开思考 关思考
OCR,一页 1.8s 1.8s
抽取 12.1s,1,616 token 0.59s,76 token

开思考时,模型在给出 78 token 的答案之前写了 1,538 个推理 token。关思考时,不到一秒就把五个字段全取对了。对固定格式的抽取,关掉思考。

OCR 把每一行明细和总计都读对了,但从一个增值税号里漏掉了一个零。归档之前要校验这些编号。表单接受 PNG 和 JPEG 文件。PDF 发票要先转成图片,因为 GLM-OCR 只吃图片。

扫描件或拍照的发票比这张干净页面难得多,所以在依赖 OCR 之前,先用自己的文档测一测。

同一套栈还能服务的其他工作流

一台 DGX Spark 的实测数据

所有数字来自 2026 年 10 月 13 日的一次基准测试,环境是 vLLM 0.30.0,模型 commit 已固定。换用其他镜像、模型或提示词,你的数字会不同。每个聊天提示词开头都带一个随机标签,免得前缀缓存美化结果。

聊天速度

用户数 首 token 时间(中位数) 每用户每秒 token 每秒总 token
1 0.11s 120 87
4 0.21s 68 242
8 0.29s 47 359

每用户速度是各请求首 token 之后生成速度的中位数。总计是所有生成的 token 除以测试耗时,所以也计入了首 token 时间和较慢的请求。单用户那一轮,五个请求里有一个跑在每秒 43 token,其余四个在 117 到 129 之间,把总计拉低到 87。

用 8,537 token 的提示词时,首 token 耗时 1.14 秒。

用户越多,每用户速度越低,但总吞吐持续上升。这就是批处理在起作用:vLLM 每遍历一遍权重就服务多个用户。

其他各项任务

任务 内容 秒数(3 次中位数)
文字转语音 一段话,30 秒音频 0.87
语音转文字 转写该片段 0.58
图像生成 一张 1024x1024 图,8 步 12.2
OCR 一页 1.80
抽取 OCR 文本转发票 JSON,关思考 0.59
嵌入 32 段文本 0.18
重排序 32 个候选 0.03

语音两个方向都远快于实时:30 秒音频每个方向都不到一秒。Whisper 的转写很准,但在结尾加了一句音频里没有的“Thank you.”。Whisper 有在静音处编造文本的毛病,转写前先剪掉结尾的静音。

图像生成的步数要自己设。Z-Image-Turbo 设计上约 8 步出图,但 vLLM-Omni 的默认跑了约 48 步,每步 1.5 秒,一张图要 72 秒。设为 8 步后,同样一张 1024x1024 图只要 12.2 秒。

生成图片时的聊天表现

机器上所有模型共用一个 GPU。装得下不等于能一起跑得好。我没找到公开的 DGX Spark 这方面的基准,所以自己测了。我按服务器默认步数连续生成 1024px 图片,同时重跑聊天测试。

用户数 单独运行时每用户每秒 token 生成图片时 变化
1 122 38 -69%
4 69 24 -65%

生成图片期间,聊天速度约为正常的三分之一,图片本身也变慢,从 73 秒变成 85 到 122 秒。

一台 DGX Spark 装得下整套栈,但没法在不拖慢所有人聊天的情况下跑图像生成。对小团队来说,把图像任务排队,或者放在非工作时间跑。一旦图像生成成为日常工作的一部分,就把媒体类任务迁到第二台 DGX Spark,第一台专用于文本。

许可证问题

难的不是模型质量,而是许可证。

几个最好的开源文字转语音和图像模型不允许商用。与 Z-Image-Turbo 竞争的 FLUX 系列模型要么非商用,要么需要单独付费的许可证。写作时最强的开源图像模型 Qwen-Image-2.1 以研究许可证发布。有些仓库甚至在宽松许可证的原版旁边放一个非商用的孪生版本,缓存错仓库 ID 就换了你的许可证。

这套栈遵循的规则:

这不是法律意见。在出售或为客户部署之前,请找人审阅每一个固定下来的许可证。

注意事项

动手试试

如果你在自己的 DGX Spark 上跑了这套东西,欢迎在 n8n 社区论坛分享你的数字。

英文来源:

Most small businesses want the same handful of AI jobs. They want to draft emails, summarize calls, read invoices, search their own documents, and make the odd image. Plenty of them can't send customer data to a cloud API to do it.
Often the reason is data privacy and sovereignty. A clinic, a law firm, or an accountant may hold records that a contract, a regulator, or a professional duty says must stay under their control. Once that data goes to a cloud API, it sits on someone else's hardware, possibly in another jurisdiction, under terms the provider can change. Running the models on hardware you own keeps the data in your building, and lets you show an auditor exactly where it went.
Your n8n workflows can transcribe calls, read invoices, answer questions from your own documents, and draft replies without sending a byte to a cloud API. This post shows you how to serve every model those workflows need from one NVIDIA DGX Spark, behind one OpenAI-compatible endpoint that n8n's OpenAI nodes already speak.
A DGX Spark has 128GB of unified memory shared by the CPU and GPU. That's enough to keep every one of those jobs loaded at once, if you pick the right models. Most of this post is about that.
The self-hosted AI starter kit gets one person running local models on a laptop. This is the next step: a box in the office that serves a whole team, with a model for each job and the code to run it.
In this post, you'll:

n8n

文章目录


    扫描二维码,在手机上阅读