通过Ollama+Qwen+Tailscale组合,在自有GPU机器上搭建私有AI编程助手,消除按用户付费的API成本和数据外传顾虑,适合10人左右工程团队。
几周前,我们大约十名工程师的团队想要获得"终端里的 AI 结对编程"体验,但不想为每个开发者支付按座位计费的 API 费用,也不想把代码库里的每一次查询都发给第三方。
我们的方案是:在一台我们已有 GPU 工作站上运行一个开源模型(Qwen),让每个人的笔记本通过私有网络与之通信。没有云端推理账单,没有代码离开我们自己的硬件,一台机器服务整个团队。
这篇文章是"为什么这么做、哪里出了问题、我们学到了什么"的经验总结。如果你想要复现,底部的分步设置指南。
我们有一台 GPU 工作站大部分时间都在闲置。云端托管的编程助手按用户或按 token 计费,对于我们这个规模的团队来说费用增长很快,而且有些代码库无论服务商的数据政策如何,我们都不想将其路由到别人的 API。在我们控制的硬件上自托管模型同时解决了这两个问题——唯一的持续成本就是电费。
架构设计上刻意保持简单:
Laptop (Qwen Code CLI) → Tailscale mesh → GPU box (Ollama, serving Qwen)
一台 GPU 机器运行 Ollama 提供 Qwen 模型服务。
Tailscale 创建私有网格网络,让每台笔记本都能安全地访问那台机器,无需将任何东西暴露在公共互联网上,也无需处理 VPN 配置。
Qwen Code 在每个开发者的笔记本上本地运行,通过 OpenAI 兼容的端点指向共享机器。它只看到开发者自己的本地代码库——GPU 机器从不碰任何人的文件,只负责"思考"。
每个人仍然在自己的代码库中工作,使用自己的 git 凭证,在自己的机器上。共享机器只是推理计算——一个模型加载,服务我们十个人,同时处理少量并发请求。
所有人同时狂敲模型会导致静默排队,而不是报错。Ollama 在那台机器上大约能同时服务四个请求,但一旦我们十个人在同一时间发出提示,其中一些只会排队等待,而不会大声报错。第一次发生这种情况时,有几个人以为出了什么问题,开始重启各种东西,而实际上解决办法只是"等几秒钟"。经验教训:提前文档化预期行为,这样人们就不会去追一个幻影 bug。我们现在明确告诉新队友——如果慢了,可能别人正在用,等着就行。
换模型会把其他人踢出去。Ollama 在 GPU 内存中一次只保留一个模型。有人为了快速实验加载第二个模型,会静默地将共享模型从内存中驱逐出去,团队中其他人的下一个请求就要付出 30-60 秒的重新加载惩罚(或者更糟,碰到一个与他们的工具预期不再匹配的模型)。这种事发生多次之后,我们制定了"一个模型,给所有人用,不警告团队就不要换"作为明确规则。
重启需要一个定义的恢复顺序。GPU 机器会因为更新、电源波动等原因被重启。服务(网络、推理服务器、防火墙)需要按合理顺序启动,需要一到两分钟来稳定,然后机器才能真正响应请求。与其每次都让人随意 SSH 进去到处戳,我们写了一个简短的、可重复的检查清单:等待启动,运行一行连接测试,如果失败,SSH 进去按固定顺序检查三个服务。这把"是不是只有我这样?"的 Slack 帖子变成了一个 90 秒、无需动脑的修复。
一条命令的健康检查比任何仪表盘都值。我们做的最有用的一件事是统一了一条 curl 命令,任何人从笔记本上运行就能回答"共享机器现在可以访问且健康吗?"这个问题。这是几乎所有问题的第一步排查,这意味着大多数问题在无需涉及机器管理者的情况下就能诊断出来。
以下是我们在内部给新队友的指南的清理版本。用你自己的值替换占位符。
从 tailscale.com/download 安装 Tailscale,通过系统托盘图标用团队的共享账户登录。
验证你可以访问推理机器:
curl http://<your-tailscale-ip>:11434/v1/models
你应该得到 JSON 列表显示可用的模型。如果挂起了,检查 Tailscale 是否显示"Connected",如果没有就重新连接。
如果你还没有 node -v 报告 v20 或更高版本,从 nodejs.org 安装 Node.js LTS。
npm install -g @qwen-code/qwen-code
通过设置三个环境变量指向你的共享机器:
[System.Environment]::SetEnvironmentVariable("OPENAI_API_KEY", "ollama", "User")
[System.Environment]::SetEnvironmentVariable("OPENAI_BASE_URL", "http://<your-tailscale-ip>:11434/v1", "User")
[System.Environment]::SetEnvironmentVariable("OPENAI_MODEL", "<your-model-name>", "User")
关闭并重新打开 PowerShell,然后用 echo $env:OPENAI_BASE_URL 确认。
cd C:\path\to\your-repo
qwen
问它类似"解释这个代码库"或"找出 auth.py 中的 bug"这样的问题。Git 工作流程完全正常——用你自己的凭证 commit 和 push。
Tailscale、环境变量和 CLI 安装在重启后都会保持。只需 cd 到一个代码库然后运行 qwen。
如果无法连接,上述的一条命令健康检查是你的第一步。如果返回 JSON,问题在本地——重启你的终端。如果挂起了,共享机器本身可能宕机了。
给它一到两分钟完全启动所有服务。
从任何笔记本运行相同的健康检查。
如果有响应,你就一切正常——第一个查询会慢一些(约 30-60 秒),因为模型要加载到 GPU 内存中,然后对所有人来说就又快了。
如果没有响应,SSH 进去按顺序检查服务:网格网络、推理服务器、防火墙——按顺序启动没有运行的那些。
一个模型,全队共识。加载第二个模型会驱逐第一个并让所有人停顿。
预期高负载下的排队。机器并行服务有限数量的请求——全队同时使用意味着一些请求会稍等,这是正常的,不是故障。
不要独自更改服务器设置。管理机器的人为全队调整防火墙和服务器配置等;变更应该先沟通。
如果你的团队在"为托管的按座位计费 AI 编程工具付费"和"在我们已有硬件上搭建点什么"之间权衡,我们已经用得很好,我会推荐试试——只是要提前预期去写下你自己的"出问题怎么办"检查清单,而不是等到第三条困惑的 Slack 帖子之后。