Qwen3-32B开源模型实战:Clawdbot Web网关配置与跨域/CORS问题解决

Qwen3-32B开源模型实战:Clawdbot Web网关配置与跨域/CORS问题解决

1. 为什么需要Web网关与跨域处理

你是不是也遇到过这样的情况:本地跑通了Qwen3-32B模型,Ollama服务正常响应,Clawdbot前端页面也能打开,但一点击发送按钮,控制台就报错——CORS policy: No 'Access-Control-Allow-Origin' header is present
这不是模型没跑起来,也不是代码写错了,而是浏览器在“多管闲事”:它默认禁止网页向不同源(协议、域名、端口任一不同)的后端发起请求。而我们典型的开发结构是——

  • 前端页面运行在 http://localhost:3000(Clawdbot Web界面)
  • Ollama API 默认监听 http://localhost:11434/api/chat
  • 中间又加了一层代理转发到 18789 端口

三者端口全不一致,浏览器直接拦截请求,连请求都发不出去。
所以,网关不是可选项,而是必选项;CORS不是小问题,而是阻断整个交互链路的关键门槛。本文不讲抽象理论,只聚焦一件事:怎么用最轻量、最稳定、最易维护的方式,把Qwen3-32B真正“接进”你的Web聊天界面。

2. 整体架构与角色分工

2.1 各组件职责一目了然

组件运行位置职责默认端口是否暴露给前端
Qwen3-32B 模型本地服务器执行推理,生成文本——(由Ollama托管)❌ 不直连
Ollama 服务localhost提供标准 /api/chat 接口11434❌ 浏览器无法直调
Clawdbot Web 前端浏览器渲染聊天界面,发送请求3000(开发)或 80(生产)用户直接访问
Web 网关代理localhost接收前端请求,转发至Ollama,注入CORS头18789前端唯一通信目标
关键理解:Clawdbot前端只和网关说话,网关再替它去和Ollama“交涉”。网关的核心任务有三个——把 POST /v1/chat/completions 这类前端请求,改写成 Ollama 能认的 /api/chat 格式;在响应里加上 Access-Control-Allow-Origin: * 等必要头,让浏览器放行;处理流式响应(SSE),把Ollama返回的 data: {...} 分块正确透传给前端。

2.2 为什么选 18789 端口?不是随便定的

你可能注意到,文档里反复出现 18789。这不是一个玄学数字,而是经过实测验证的“安全端口”:

  • 它避开了常见服务端口(如 8080 常被其他开发服务占用,3000/5000 是前端默认端口);
  • 它高于 1024,无需 root 权限即可绑定(Linux/macOS 下普通用户可直接启动);
  • 它在 10000–20000 区间内,既不冲突又便于记忆(18789 → “要发吧久”,谐音提醒这是“对外发请求”的端口)。
    实际部署时,你完全可以改成 80889001,只要前后端配置保持一致即可。

3. 三步完成网关配置(无依赖、纯Node.js)

我们不引入Nginx、不装Docker、不配K8s——用一个不到50行的 gateway.js 文件搞定全部逻辑。它轻、快、透明,出问题一眼就能定位。

3.1 创建网关脚本:gateway.js

// gateway.js const http = require('http'); const url = require('url'); const { parse } = require('querystring'); // Ollama服务地址(确保能从本机curl通) const OLLAMA_BASE_URL = 'http://localhost:11434'; const server = http.createServer((req, res) => { const parsedUrl = url.parse(req.url, true); const path = parsedUrl.pathname; // 只处理 /v1/chat/completions 请求(Clawdbot前端默认路径) if (req.method === 'POST' && path === '/v1/chat/completions') { // 设置CORS头(允许任意源,生产环境请替换为具体域名) res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization'); res.setHeader('Access-Control-Allow-Credentials', 'true'); // 处理预检请求(OPTIONS) if (req.method === 'OPTIONS') { res.writeHead(200); res.end(); return; } // 构造Ollama请求选项 const options = { method: 'POST', hostname: 'localhost', port: 11434, path: '/api/chat', headers: { 'Content-Type': 'application/json', } }; // 创建Ollama请求 const ollamaReq = http.request(options, (ollamaRes) => { // 将Ollama响应头透传(保留流式特性) res.writeHead(ollamaRes.statusCode, ollamaRes.headers); ollamaRes.pipe(res); }); ollamaReq.on('error', (err) => { console.error('Ollama request failed:', err); res.writeHead(500, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Failed to connect to Ollama' })); }); // 将前端请求体原样转发给Ollama let; req.on('data', chunk => body += chunk); req.on('end', () => { try { const frontendData = JSON.parse(body); // 关键转换:将OpenAI格式转为Ollama格式 const ollamaPayload = { model: 'qwen3:32b', // 必须与Ollama中模型名完全一致 messages: frontendData.messages.map(msg => ({ role: msg.role, content: msg.content })), stream: frontendData.stream ?? true, options: { temperature: frontendData.temperature ?? 0.7, num_ctx: 32768 // Qwen3-32B推荐上下文长度 } }; ollamaReq.write(JSON.stringify(ollamaPayload)); ollamaReq.end(); } catch (e) { console.error('Parse error:', e); res.writeHead(400, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Invalid JSON in request body' })); } }); } else { // 其他路径返回404 res.writeHead(404, { 'Content-Type': 'text/plain' }); res.end('Not Found'); } }); const PORT = 18789; server.listen(PORT, () => { console.log(` Clawdbot Web网关已启动`); console.log(`➡ 前端请请求: http://localhost:${PORT}/v1/chat/completions`); console.log(`⬅ 网关已连接Ollama: http://localhost:11434`); }); 

3.2 启动网关并验证连通性

打开终端,执行:

node gateway.js 

你会看到类似输出:

 Clawdbot Web网关已启动 ➡ 前端请请求: http://localhost:18789/v1/chat/completions ⬅ 网关已连接Ollama: http://localhost:11434 

接着,用 curl 模拟一次前端请求,验证网关是否真正打通:

curl -X POST http://localhost:18789/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "qwen3:32b", "messages": [{"role": "user", "content": "你好,你是谁?"}], "stream": false }' 

如果返回包含 "message": {"role": "assistant", "content": "我是Qwen3..."} 的JSON,说明网关、Ollama、模型三者已全线贯通。

3.3 配置Clawdbot前端指向新网关

打开Clawdbot项目的前端配置文件(通常是 src/config.js.env),修改API基础地址:

# .env VUE_APP_API_BASE_URL=http://localhost:18789 # 或 React 项目中的 config.ts export const API_BASE_URL = 'http://localhost:18789'; 

然后重启前端服务(npm run devyarn start)。此时前端所有 /v1/chat/completions 请求,都会先打到 18789 网关,再由网关转发给Ollama——跨域问题彻底消失,流式响应完整保留

4. 常见CORS问题与精准修复方案

即使按上述步骤操作,仍可能遇到五花八门的CORS报错。以下是真实项目中高频出现的4类问题及对应解法,不绕弯、不猜疑、直接定位根因。

4.1 报错:Response to preflight request doesn't pass access control check

现象:浏览器控制台显示 OPTIONS 请求返回403或500,后续 POST 根本不发出。
原因:网关未正确处理预检请求(OPTIONS),或Ollama服务本身拒绝了OPTIONS方法。
修复:确认 gateway.jsif (req.method === 'OPTIONS') 分支存在且执行 res.end()。不要试图让Ollama处理OPTIONS——它不支持,必须由网关拦截并快速响应。

4.2 报错:The value of the 'Access-Control-Allow-Origin' header contains the invalid value '*'

现象:Chrome报错,但Firefox能用;或带上 credentials: true 时失败。
原因:当需要携带Cookie或认证头时,Access-Control-Allow-Origin 不能为 *,必须指定确切域名。
修复:将网关中 res.setHeader('Access-Control-Allow-Origin', '*') 改为:

// 开发环境 res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000'); // 生产环境(假设部署在 https://chat.yourcompany.com) res.setHeader('Access-Control-Allow-Origin', 'https://chat.yourcompany.com'); 

同时确保前端请求中 credentials: 'include' 与后端设置严格匹配。

4.3 报错:No 'Access-Control-Allow-Headers' header is present

现象:前端设置了 Authorization: Bearer xxx 或自定义Header,但被拦截。
原因:网关未声明允许该Header。
修复:在网关CORS头中补充:

res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With'); 

4.4 报错:Failed to fetch 但无CORS字样

现象:控制台只显示网络错误,点开Network标签页发现请求状态为 (failed)net::ERR_CONNECTION_REFUSED
原因:网关根本没运行,或端口被占用,或防火墙拦截。
排查顺序

  1. lsof -i :18789(macOS/Linux)或 netstat -ano | findstr :18789(Windows)确认端口是否被占用;
  2. curl -v http://localhost:18789/health(若你加了健康检查)或 curl -v http://localhost:18789 看是否返回 Not Found(证明网关在运行);
  3. 临时关闭防火墙测试。

5. 进阶优化:让网关更健壮、更易维护

基础版网关能跑通,但生产环境还需三点加固。

5.1 添加请求日志与错误追踪

gateway.js 的请求处理开头加入:

console.log(`[${new Date().toISOString()}] ${req.method} ${req.url} ← ${req.socket.remoteAddress}`); 

在Ollama请求的 on('error') 回调中,不仅打印错误,还记录时间戳和请求体摘要(脱敏后):

ollamaReq.on('error', (err) => { const logEntry = { timestamp: new Date().toISOString(), error: err.message, remoteAddr: req.socket.remoteAddress, requestBodyPreview: body.substring(0, 100) + '...' }; console.error('GATEWAY_ERROR:', JSON.stringify(logEntry)); // 此处可对接Sentry、写入文件等 }); 

5.2 支持多模型动态路由

如果你不止部署了 qwen3:32b,还有 qwen2.5:7bphi3:mini,可扩展网关支持模型名透传:

// 从请求路径提取模型名,例如 /v1/chat/completions/qwen3:32b const modelMatch = parsedUrl.pathname.match(/\/v1\/chat\/completions\/(.+)/); const targetModel = modelMatch ? modelMatch[1] : 'qwen3:32b'; // 在ollamaPayload中使用 model: targetModel, 

前端请求改为 POST /v1/chat/completions/qwen3:32b 即可切换模型,无需改网关代码。

5.3 集成健康检查端点

添加一个 /health 路径,供前端或运维监控网关存活状态:

if (req.method === 'GET' && path === '/health') { // 尝试快速探测Ollama是否可达 const healthReq = http.request({ hostname: 'localhost', port: 11434, path: '/api/tags', method: 'GET' }, (healthRes) => { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ status: 'ok', timestamp: new Date().toISOString() })); }); healthReq.on('error', () => { res.writeHead(503, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ status: 'unavailable', reason: 'Ollama unreachable' })); }); healthReq.end(); return; } 

6. 总结:一条清晰、可复现、零踩坑的落地路径

回看整个过程,你其实只做了三件确定性极高的事:

  • 明确边界:前端只认网关,网关只认Ollama,各司其职不越界;
  • 最小实现:50行Node.js脚本,无框架、无构建、无依赖,复制即用;
  • 精准归因:CORS不是玄学,是HTTP头的显式声明,是预检请求的正确响应,是流式数据的无损透传。

你不需要成为网络协议专家,也不必深究浏览器同源策略的RFC文档。只要记住这个铁律:前端能访问的地址,必须是网关地址;网关返回的响应,必须带正确的Access-Control头;Ollama的请求体,必须是它能解析的格式。其余,都是细节优化。

现在,打开你的Clawdbot页面,输入第一句话,看着Qwen3-32B以32B参数量带来的扎实回答缓缓浮现——那不是魔法,是你亲手搭起的、稳稳当当的数据桥梁。


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 ZEEKLOG星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

Read more

无人机相关法律法规全体系梳理

无人机相关法律法规全体系梳理 随着无人机产业的高速发展,我国已构建起以“国家行政法规为核心、部门规章为支撑、地方细则为补充”的无人机法律体系,覆盖无人机生产、登记、飞行、监管全链条。本梳理结合2024-2025年最新法规修订内容,聚焦不同主体(个人/企业)的合规要点,明确权利与义务边界。 一、国家层面核心行政法规(基础遵循) 此类法规具有最高法律效力,是无人机管理的根本依据,核心包括《无人驾驶航空器飞行管理暂行条例》及关联法律修订内容。 1. 《无人驾驶航空器飞行管理暂行条例》(2024年1月1日实施) 我国首部专门规范无人机的行政法规,共6章63条,确立“分类管理、安全优先”的核心原则,覆盖无人机全生命周期管理。核心条款如下: (1)无人机分类与适航管理 按性能指标将无人机分为五类,差异化设定适航要求,是后续所有管理的基础: 类别 核心指标(空机重量/最大起飞重量) 适航许可要求 生产标注要求 微型 <0.25千克

Sublime配置verilog开发环境-具备语法高亮、代码补全、自定义代码段及语法检查等功能,提升FPGA开发效率!

Sublime配置verilog开发环境-具备语法高亮、代码补全、自定义代码段及语法检查等功能,提升FPGA开发效率!

对于在学习FPGA开发之前使用过其他集成开发工具如VS、pycharm、keil或编辑工具如Sublime、VScode、Notepad的朋友,在使用Vivado时可能会像博主一样感觉自带编辑器用起来不太舒服,比如不支持语法高亮显示,不支持代码自动补全等功能。因次,使用第三方编辑器来编写Verilog代码是很有必要的。 本文将详细介绍如何在文本编辑器Sublime中配置verilog开发环境,最终实现语法高亮、代码补全、自定义代码段及语法检查等功能,使得可以在Sublime中高效编写verilog代码,大幅提升FPGA开发效率!附带自己在配置中的踩坑经验,希望朋友们按着下面的流程走可以一步配置到位!下面两图为使用Vivado编写代码及使用Sublime编写代码的对比图。 1.Sublime的介绍与安装配置         Sublime Text,是一款由 Sublime HQ 开发的跨平台轻量级代码编辑器,以 “启动快、插件丰富、自定义性强” 为核心特点,广泛用于代码编写、文本编辑和开发效率提升,支持 Windows、macOS、Linux 三大操作系统。

Microi吾码:开源低代码,微服务开发的利器

Microi吾码:开源低代码,微服务开发的利器

前言 在微服务架构的应用中,服务的灵活性和可扩展性至关重要。Microi吾码作为一个高效的微服务框架,凭借其轻量级、可插拔的特性,已经成为开发者构建分布式应用的首选工具。除了基础的微服务开发功能外,Microi吾码还提供了丰富的扩展功能,其中表单引擎是一个重要亮点。本篇博客将详细介绍Microi吾码的特点,以及如何使用其表单引擎和其他实用功能。 一. Microi吾码简介 Microi吾码是一个基于Spring Boot构建的微服务框架,致力于为开发者提供简单、灵活的解决方案,帮助他们高效构建分布式应用。它整合了常用的微服务功能,如服务注册与发现、负载均衡、熔断器、API网关、配置中心等,使得开发者无需从零开始构建基础设施,从而专注于业务逻辑。 1.1 核心特点 Microi吾码的核心特点: * 轻量级:基于Spring Boot,极大地简化了项目配置和开发流程。 * 高度可扩展:提供丰富的插件支持,可以根据需要定制功能。 * 开箱即用:内置常见的微服务功能,减少了开发者的重复工作。 * 开发友好:支持热部署和自动化构建,提升开发效率。 1.2 功能介绍

区块链|WEB3:时间长河共识算法(Time River Consensus Algorithm)

区块链|WEB3:时间长河共识算法(Time River Consensus Algorithm)

区块链|WEB3:时间长河共识算法(Time River Consensus Algorithm)(原命名为时间证明公式算法(TCC)) 本共识算法以「时间长河」为核心设计理念,通过时间节点服务器按固定最小时间间隔打包区块,构建不可篡改的历史数据链,兼顾区块链的金融属性与信用属性,所有优化机制形成完整闭环,无核心逻辑漏洞,具体总结如下: 一、核心机制(闭环无漏洞) 1. 节点准入与初始化:候选时间节点需先完成全链质押,首个时间节点由所有质押节点投票选举产生,彻底杜绝系统指定带来的初始中心化问题,实现去中心化初始化。 2. 时间节点推导与防作弊:下一任时间节点通过共同随机数算法从上一区块推导(输入参数:上一区块哈希、时间戳、固定数据顺序),推导规则公开可验证;时间节点需对数据顺序签名,任一节点发现作弊(篡改签名、操控随机数等),该节点立即失去时间节点资格并扣除全部质押。质押的核心目的是防止节点为持续获取区块打包奖励作弊,作弊损失远大于收益,确保共同随机数推导百分百不可作弊。 3. 节点容错机制:每个时间节点均配置一组合规质押节点构成的左侧顺邻节点队列(队列长度可随全网节点规