
最近好多读者来问同一个问题公司内网的代码数据出不去又想用上现在最流行的AI编程助手到底怎么搭我给的答案基本都指向一个组合——Qwen Code vLLM Qwen3-Coder。这套方案我在几台不同配置的服务器上反复折腾过从个人开发机的单卡部署到团队共享的大显存机器踩过的坑算是比较全了。今天这篇就把整个流程摊开讲从模型选型到服务启动从客户端配置到延迟优化尽量做到一步一个命令照着敲就行。这篇内容适合两类人一是公司数据敏感、只能在隔离网络里做开发的人二是想在本地搞一套低延迟、不受限的AI编程环境、但不想被各家云服务绑定的人。1. 项目概述为什么要在纯内网做AI编程助手1.1 核心需求拆解这个项目要解决的事情非常明确在一个不允许访问公网的开发环境里给团队或自己搭建一个AI编程助手。AI编程助手最常见的形态就是编辑器里的代码补全、代码生成、对话式解释和重构。市面上的在线AI编程服务确实好用但对很多企业来说代码本身就是核心资产把代码片段、业务逻辑甚至注释发送到外部服务在合规上就是过不去的坎。于是就有了“私服”这个词把模型、推理框架、客户端全部部署在内网形成一个闭环。拆开标题里的几个关键词就清楚了Qwen Code 是面向编码场景的客户端工具负责跟开发者交互Qwen3-Coder 是整个系统的大脑也就是真正理解代码、生成代码的模型vLLM 则是架在两者中间的推理引擎负责把模型高效地跑起来对外提供OpenAI兼容的接口。三者的关系可以类比成一个餐馆模型是厨师的厨艺vLLM是后厨的流水线和灶台Qwen Code则是前台点餐和上菜的窗口。1.2 方案选型背后的思考很多人问为什么不直接用 Ollama 或 llama.cpp这两个工具单机跑小模型确实香安装简单、资源占用低但到了多用户并发、需要稳定 API 接口的“私服”场景就有点吃力了。Ollama 本身的并发调度能力相对有限llama.cpp 更适合单客户端直连。vLLM 的优势在于它把连续批处理、PagedAttention、前缀缓存这些底层优化都做进了引擎里外部只需要按 OpenAI 的格式发请求就能在有限的显存上把吞吐拉起来。再看 SGLang它和 vLLM 的定位有重叠某些长上下文场景下甚至更激进但社区生态和文档完善度目前还是 vLLM 更稳。我的原则是生产环境求稳能用成熟方案就不当小白鼠。后面第6章我会专门讲这两个框架怎么选。确定技术栈之后整个部署路径就是准备硬件和模型 → 用 vLLM 拉起服务 → 配置 Qwen Code 连接本地端点 → 调优和排障。下面按这条线走。2. 环境准备与模型选型2.1 硬件配置与最低要求先说硬件底线。以 Qwen3-Coder 家族里最轻量的 7B 模型为例16GB 显存的显卡就能流畅跑起来量化后连 8GB 都能勉强带动。但如果团队有几个人同时用我建议至少 24GB 起步比如 3090、4090、L20 这类。30B-A3B 这个型号比较有意思总参数量 30B但走 MoE 架构每次推理只激活 3B 参数生成速度并不慢只是要把全部参数装进显存实测 24GB 显卡非常紧张48GB 以上才舒服。下面是常见硬件配置和推荐模型的对照表方便你快速判断硬件条件推荐模型说明单卡 16GB如 T4、P100、3060Qwen3-Coder-7BAWQ量化个人开发机够用补全体验可以接受单卡 24GB如 3090/4090/L20Qwen3-Coder-7B、30B-A3B紧7B无压力30B-A3B需要控制并发单卡 48GB 以上如 A6000/L40S/A800Qwen3-Coder-30B-A3B团队共享的甜点配置多卡 96GB 以上30B-A3B 或更大尺寸配合张量并行吞吐显著提升这里有一个常见的认知误区30B-A3B 虽然叫 30B但由于是 MoE实际推理速度跟 7B 密集模型有些接近前提是显存够装下全部参数和 KV 缓存。显存不足的时候vLLM 会把部分参数换到 CPU 内存速度直接崩盘体验还不如老老实实跑 7B。所以选模型不能只看“能跑起来”要看“在目标并发下能不能稳定跑”。2.2 Qwen3-Coder 模型选型细节Qwen3-Coder 系列目前最常用的是两个7B 和 30B-A3B。前者适合单机自用、低显存场景代码补全和常见代码生成任务表现已经不错后者适合团队共用代码理解能力、复杂任务拆解能力明显更强。拿 30B-A3B 来说它的 MoE 结构决定了它跟传统 30B 密集模型不一样虽然总参数量大但每个 token 只会激活一部分专家网络所以在 token 生成速度上比同尺寸密集模型快不少。代价是显存占用依然要看全量参数部署时不能按“激活 3B 参数”来预估显存。量化是另一个关键决策。vLLM 原生支持 AWQ、GPTQ、FP8 等量化方式。如果你用的是 4090、L20 这类相对新的显卡优先试 FP8如果是老架构卡AWQ 兼容性更好。量化的本质是拿一点点精度换显存和速度在代码生成任务里4bit 或 8bit 量化造成的输出质量下降通常不明显但能显著降低部署门槛。2.3 模型下载与目录规划纯内网环境下获取模型文件一般走这样的流程在有网的机器上下载好模型再通过隔离网闸、移动硬盘或内部文件服务器拷入。模型文件的目录结构保持 Hugging Face 仓库原样最省事vLLM 直接读取这个目录就能识别。建议在数据盘上建立一个统一的目录规范例如mkdir -p /data/models cd /data/models # 保留原始仓库结构 ln -s /data/models/Qwen3-Coder-30B-A3B-Instruct ./qwen3-coder-30b把模型放在独立数据盘而不是系统盘有两个好处一是模型文件动辄几十GB不会把系统盘撑爆二是后续升级模型时只要替换软链接指向即可服务端的配置也不用频繁改。我习惯在模型目录里放一个 README记录模型来源、量化方式、部署日期和验证结果团队协作时这个信息特别有用。3. vLLM 部署的核心实操3.1 vLLM 安装与版本选择vLLM 的安装方式主要有两种pip 直装和 Docker 容器化。如果你只是想快速验证pip 安装最方便pip install vllm但纯内网环境里 pip 直装往往会遇到依赖包下载问题。我的建议是在内网部署阶段就直接走 Docker把镜像从有网环境导出再导入或者用公司内部的私有镜像仓库这样最接近生产环境。Docker 部署的关键点是给足共享内存否则模型加载阶段很容易报 NCCL 相关错误。一个可直接参考的 docker-compose 配置如下services: vllm-qwen: image: vllm/vllm-openai:latest command: --model /models/Qwen3-Coder-30B-A3B-Instruct --served-model-name qwen3-coder --host 0.0.0.0 --port 8000 --gpu-memory-utilization 0.92 --max-model-len 32768 --enable-prefix-caching volumes: - /data/models:/models ports: - 8000:8000 shm_size: 2g environment: - HF_HUB_OFFLINE1 - TRANSFORMERS_OFFLINE1HF_HUB_OFFLINE1和TRANSFORMERS_OFFLINE1这两个环境变量一定要加它们告诉 Hugging Face 相关库不要尝试访问网络否则纯内网环境里启动时可能会卡在超时阶段白白等好几分钟。3.2 启动命令与 OpenAI 兼容 APIvLLM 启动后对外暴露的是 OpenAI 兼容的 API这意味着任何支持 OpenAI 协议的工具都能直接接入。启动命令的核心参数是模型路径和端口vllm serve /data/models/Qwen3-Coder-30B-A3B-Instruct \ --served-model-name qwen3-coder \ --host 0.0.0.0 \ --port 8000 \ --gpu-memory-utilization 0.92 \ --max-model-len 32768 \ --enable-prefix-caching \ --max-num-seqs 64启动后可以用 curl 做一次最简单的连通性测试curl -X POST http://127.0.0.1:8000/v1/chat/completions \ -H Content-Type: application/json \ -d { model: qwen3-coder, messages: [{role: user, content: 用Python写一个快速排序}], max_tokens: 512 }如果返回了正常的 JSON 响应说明推理链路已经通了。--served-model-name这个参数很实用它相当于给模型起了个别名后续客户端配置里只要记住这个别名模型文件路径怎么变都不影响对外接口。3.3 关键参数再深挖一步--max-model-len指定模型能处理的最大上下文长度这个值直接决定显存里 KV 缓存的预算。如果你设得太大30B-A3B 在 24GB 卡上可能连模型权重都装不下设得太小长文件的代码分析能力又受限。我的经验是个人使用 7B 模型可以拉到 3276830B-A3B 先在 32768 上试探如果显存紧张就降到 16384优先保证服务不崩。--max-num-seqs控制的是同一时间内最多处理多少个请求序列它直接和连续批处理机制挂钩。连续批处理的意思是vLLM 不再等一个请求全部生成完再处理下一个而是像流水线一样只要当前请求出一个 token就把算力让给别的请求。这个参数调得太小并发多了就排队调得太大显存里的 KV 缓存会被占满也可能引发 OOM。团队场景下建议设为 32~64单机自用 8~16 就够了。还有一个容易忽略但收益明显的参数是--enable-prefix-caching。编程场景里同一个项目的文件往往有大量重复的上下文前缀比如文件头部的引用、公共代码片段。开启了前缀缓存后vLLM 会复用之前算过的部分结果多轮对话和重复请求的响应速度能明显提升。3.4 量化方案怎么选vLLM 加载量化模型的方式不复杂关键是权重文件和启动参数要对上# 以 AWQ 量化模型为例 vllm serve /data/models/Qwen3-Coder-7B-Instruct-AWQ \ --served-model-name qwen3-coder-7b \ --quantization awq \ --gpu-memory-utilization 0.9AWQ 和 GPTQ 属于需要事先量化的方案模型文件必须是量化好的版本FP8 则可以在部分新显卡上用原始权重直接跑也可以加载 FP8 的预量化权重。实测下来FP8 的精度损失最小、部署最省事但老架构显卡不支持AWQ 的兼容面更广7B 模型量化后显存占用能控制在 8GB 左右这在小显存机器上非常关键。4. Qwen Code 客户端接入与日常使用4.1 Qwen Code 在整条链路中的角色Qwen Code 是连接开发者和本地推理服务的客户端。它把代码补全、代码解释、代码评审、重构建议这些能力整合到编辑器或命令行里本身不跑模型而是把请求发给前面用 vLLM 搭好的服务。这个设计带来的好处是客户端和推理服务完全解耦换模型不用动客户端换客户端也不影响服务端。安装方式建议直接看官方文档不同版本对操作系统的要求略有差异。安装完成后第一件事是找到配置文件把默认的云端服务地址替换成本地 vLLM 端点。以常见的 JSON 配置为例{ api_base: http://127.0.0.1:8000/v1, api_key: EMPTY, model: qwen3-coder }注意 base URL 要带/v1vLLM 的 OpenAI 兼容路由都挂在/v1下面。api_key填一个非空字符串即可本地方案不校验密钥但客户端可能要求这个字段存在。4.2 团队内网环境的连接配置如果服务器和开发机不在同一台机器上127.0.0.1要换成 vLLM 服务所在服务器的内网 IP并确保 8000 端口在防火墙里放行。这时候最容易踩的坑是vLLM 启动时监听了0.0.0.0但宿主机防火墙没开端口客户端一直连接超时。配置完以后用客户端自带的一个简单对话功能验证连通性比如输入“解释一下这段代码做了什么”。如果响应正常说明端到端链路已经打透。建议团队统一使用一份配置模板只改 IP 和模型名降低大家上手的门槛。4.3 编辑器和命令行场景怎么用在日常开发里我自己的使用频率最高的是以下三个场景代码补全写函数、起变量名、补完整段注释Qwen Code 能根据光标位置和上文生成候选接受或忽略都很灵活。代码评审选中一段代码让模型按“可读性、性能、安全性、错误处理”四个维度给出意见比自己逐行看效率高很多。命令行批量任务把整仓库的测试用例生成、README 补全这类任务交给模型在终端里批量执行。有一点要提醒内网模型的能力上限就是本地部署的那个模型不要拿 7B 模型去跟云端几百B的大模型比复杂需求理解。能接受这个预期这套私有方案用起来会非常顺手不能接受那就得考虑更大规模的服务器了。5. 性能优化与监控5.1 延迟和卡顿的根因分析有几个读者都反馈过类似问题30B 模型用 FP8 量化部署后总觉得有延迟偶尔还会卡顿。这个问题通常不是单一因素造成的而是要按下面的顺序逐项排查。第一是并发模型里的排队。vLLM 的连续批处理虽然能拉高吞吐但并发请求太多时单个请求的等待时间仍然会变长。这时候先看--max-num-seqs是不是设得太高把算力切得太碎或者反过来如果大量请求都在排队说明这个参数太小吞吐上不去。第二是 KV 缓存的压力。打开 prefix caching 之后代码前缀的重复计算被大量省掉首 token 延迟会明显改善。如果你发现 TTFT首 token 时间偏高且--enable-prefix-caching没有打开建议先把它加上。第三是模型权重的加载方式。FP8 在部分显卡上需要额外的格式转换如果走的算子路径没有优化好速度反而不如 AWQ。遇到这种情况可以换一个量化格式对比一轮 token 生成速度再决定。第四是显存碎片和执行器调度。排查时可以直接看 vLLM 的启动日志和/metrics里的指标而不是凭感觉猜测。5.2 显存不足问题的排查手册显存不足是非常经典的问题现象大概分两类启动时报错直接退出或者运行一段时间后返回 500 错误。下面是我整理的一个快速排查表现象最可能原因解决方向启动时报 CUDA out of memorygpu-memory-utilization 设得过高降到 0.85~0.90 再试启动时报模型权重过大模型超过单卡显存换小模型或走多卡张量并行运行中偶发 OOMmax-num-seqs 过大减半再观察上下文长时报 OOMmax-model-len 预算不足调小 max-model-len或打开 prefix caching同环境下多卡不生效张量并行参数缺失添加 --tensor-parallel-size 2还需要注意一点gpu-memory-utilization不是越高越好。显存里除了模型权重和 KV 缓存还有激活值、临时算子缓冲区留出一点余量才能避免毛刺。5.3 用 Prometheus 指标做监控vLLM 默认会暴露一个/metrics端口只要服务启动时加上--enable-metrics参数就能用 Prometheus 抓取到推理服务的核心指标。我最常看的是这几个vllm:num_requests_running当前正在推理的请求数过高说明并发饱和。vllm:request_success_total累计成功请求数观察服务是否正常。vllm:generation_tokens_total累计生成的 token 数用来估算整体吞吐。缓存命中率相关指标反映 prefix caching 的收益命中率高说明重复请求多。部署方式不复杂Prometheus 抓取http://服务器IP:8000/metricsGrafana 里配上对应的面板就能在离线环境里看到全链路状态。监控这件事别等服务真的出问题了再做长期运行的服务必须有一块“仪表盘”。6. 常见问题与排错记录6.1 昇腾910B 上 vLLM 无法启动 embedding/rerank 模型这是最近被问到非常频繁的问题昇腾 910B 服务器上通过 vLLM 跑 LLM 没问题但启动 embedding 或 reranker 模型时报不支持。这个现象的根本原因在于 vLLM 的昇腾后端目前主要针对 LLM 文本生成链路做了适配embedding 和 rerank 这两类任务的模型类型在 CANN 环境下没有被完整支持。遇到这个情况一般有两个选择第一昇腾节点上单独用 MindIE 或者其他昇腾推理工具启动 embedding/rerank 服务再提供一个独立的 HTTP 接口第二把 embedding/rerank 这类相对轻量的服务放到网络里的一台 CPU 或 GPU 机器上跑只把主生成模型留在昇腾节点。实测下来第二种路线虽然多了一台机器但部署难度反而更低。6.2 SGLang 和 vLLM 到底选哪个SGLang 在长上下文和复杂的 agent 推理场景里确实有它的独到之处RadixAttention 的自动前缀复用比 vLLM 的 prefix caching 在某些场景下更灵活。但谈到生产部署的稳定性和文档丰富度vLLM 目前依然占优。我的建议是你的目标是快速搭一个内网代码助手就老老实实用 vLLM如果你要做一个需要高并发长对话的模型网关再考虑 SGLang 也不迟。没必要为了让技术栈看着更前沿而去承担额外的学习成本。6.3 纯内网环境下的依赖和镜像问题离线环境的第一个麻烦是依赖包。我的做法是在同样架构的有网机器上先用 pip download 把所有依赖包拉到一个目录然后整体拷入内网也可以用公司内部的 pip 私有源。第二个麻烦是 Docker 镜像最省事的办法是在有网环境把镜像 push 到私有仓库内网机器直接从私有仓库 pull。第三个麻烦是模型文件上面已经说过准备好原始目录结构后整体导入即可。这三个问题提前规划好离线部署就基本不会卡住。6.4 一些实操中的细节提醒最后分享几个调试中的小经验。日志是最好的排查依据vLLM 启动时如果想看更细的调度信息可以加上VLLM_LOGGING_LEVELDEBUG环境变量但注意生产环境别一直开着 DEBUG日志量很大。共享内存 2g 是最低要求如果并发人数多可以加到 8g避免加载阶段报 NCCL 错误。服务启动后不要急着接客户端先用 curl 跑几个典型请求确认模型确实稳定生成再接入团队体验会好很多。这个项目做下来我最大的体会是AI 编程助手的价值不在于模型参数有多大而在于它能不能在你的工作流里稳定存在。vLLM 负责把模型潜力榨干Qwen Code 负责把能力送到指尖而你要做的就是把这条链路维护好。后续如果团队需求上来了可以考虑扩大并发、接更多模型或者在前端加一层统一网关都是顺手的事。