Announcement

👇Official Account👇

Welcome to join the group & private message

Article first/tail QR code

Skip to content

Janus:一个 Go 二进制搞定本地 LLM 推理——Vulkan + GGUF + OpenAI 兼容 API 全解析 ​

为什么它火了 ​

本地跑 LLM 这件事,过去三年的"入门税"高得离谱:装 Python、建虚拟环境、配 CUDA 驱动、和 ROCm 搏斗、最后才发现显卡不兼容。Ollama 解决了一部分,但它是一个常驻 daemon,CI 环境和 Go 项目里塞一个守护进程并不优雅。

2026 年 10 月初,一个叫 Janus 的开源项目登上了 Hacker News(Show HN 79 分),核心卖点一句话:一个约 50MB 的 MIT 协议 Go 二进制,包办 GGUF 模型的本地推理,暴露 OpenAI 兼容 API,AMD/Intel/NVIDIA GPU 通吃,CPU 兜底。没有 Python,没有 Docker,没有 Ollama daemon。

对 Go 开发者来说,这才是关键:你可以在 go build 出的 CLI 工具、后台服务、CI 流水线里,用两个环境变量就接入本地模型推理,不引入任何 Python 运行时。

架构拆解:Go 壳 + llama.cpp Vulkan 内核 ​

Janus 的设计哲学是"薄壳":Go 不重写推理,只做路由、生命周期管理和 API 层,推理内核直接复用 llama.cpp 的 Vulkan 后端。

文字版架构图:

┌─────────────────────────────────────────────────┐
│                  Janus (Go binary)               │
│                                                  │
│  ┌──────────┐   ┌───────────┐   ┌────────────┐  │
│  │ HTTP API │──▶│  Router   │──▶│ Engine     │  │
│  │ /v1/*    │   │ hot-swap  │   │ Manager    │  │
│  │ /models/*│   │ fallback  │   └─────┬──────┘  │
│  └──────────┘   └───────────┘         │ cgo/FFI │
│                                       ▼         │
│                            ┌──────────────────┐ │
│                            │ llama.cpp (Vulkan│ │
│                            │ backend DLL/.so) │ │
│                            └────────┬─────────┘ │
└─────────────────────────────────────┼───────────┘
                                      ▼
              ┌──────────────────────────────────┐
              │  Vulkan ICD(驱动无关的 GPU 抽象) │
              │  NVIDIA / AMD / Intel / CPU      │
              └──────────────────────────────────┘

几个值得注意的设计决策:

  1. Vulkan 而非 CUDA。Vulkan 是跨厂商的 GPU 计算抽象,一份 llama.dll/libllama.so 就能在 AMD、Intel、NVIDIA 上跑,绕开了"每家 GPU 一套驱动路径"的碎片化。代价是 Vulkan 计算管线的开销略高于 CUDA 原生路径(HN 讨论中 Intel 显卡用户反馈驱动版本差时甚至不如纯 CPU)。
  2. 预编译 DLL 分发。Windows 下 build.ps1 直接下载预编译的 llama.cpp Vulkan DLL,Go 侧通过 FFI 调用,用户完全不碰 C++ 工具链。
  3. API 刻意最小化:只实现 /v1/chat/completions、/v1/models 两个 OpenAI 端点,加上自有的 /models/load(热切换)、/engine/status(VRAM/后端状态)、/health。契约严格对齐 OpenAI 规范,意味着 Cursor、Cline、任何 OpenAI SDK 改个 base URL 就能用。

五分钟上手 ​

Linux 构建 ​

bash
git clone https://github.com/Vibra-Ingenn/Janus.git
cd Janus
go mod tidy
go build -o dist/janus ./cmd/janus
cp .env.example .env
# 编辑 .env:设置 JANUS_MODEL_PATH,无 Vulkan 显卡则 INFERENCE_BACKEND=cpu
./dist/janus

Linux 需要确保 libllama.so 在二进制同目录或 LD_LIBRARY_PATH 中。

下载模型 ​

项目自带一个 Hugging Face 下载器 modelget:

bash
go build -o dist/modelget ./cmd/modelget
./dist/modelget \
  -repo meta-llama/Llama-3.2-3B-Instruct \
  -file Llama-3.2-3B-Instruct-Q8_0.gguf \
  -out ./models/

调用验证 ​

默认监听 127.0.0.1:8990(注意不是 8080,README 特意强调了这点):

bash
curl http://127.0.0.1:8990/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "local",
    "messages": [{"role": "user", "content": "用 Go 写一个快速排序"}]
  }'

任何 OpenAI 兼容客户端(以 Go 官方 SDK 为例):

go
package main

import (
	"context"
	"fmt"
	"os"

	openai "github.com/openai/openai-go"
	"github.com/openai/openai-go/option"
)

func main() {
	client := openai.NewClient(
		option.WithBaseURL("http://127.0.0.1:8990/v1"),
		option.WithAPIKey("not-needed"), // 本地无需鉴权,占位即可
	)

	resp, err := client.Chat.Completions.New(context.Background(), openai.ChatCompletionNewParams{
		Model: "local",
		Messages: []openai.ChatCompletionMessageParamUnion{
			openai.UserMessage("解释一下 Go 的 GMP 调度模型"),
		},
	})
	if err != nil {
		fmt.Fprintln(os.Stderr, "请求失败:", err)
		os.Exit(1)
	}
	fmt.Println(resp.Choices[0].Message.Content)
}

VRAM 调优:三个环境变量 ​

Janus 的资源控制收敛在几个环境变量上,这是它比 Ollama 更"运维友好"的地方:

变量默认值说明
INFERENCE_BACKENDvulkanvulkan / cpu / openrouter
JANUS_MODEL_PATH必填GGUF 模型路径
JANUS_GPU_LAYERS-1-1 全部层上 GPU,0 强制 CPU
JANUS_VRAM_CEILING_MB9216VRAM 预算上限(MiB)
JANUS_LISTEN_ADDR127.0.0.1:8990监听地址
JANUS_AUTHfalsetrue 时管理路由要求 Basic Auth
JANUS_SAFE_MODEfalsetrue 时禁用内置 shell 工具

JANUS_VRAM_CEILING_MB 配合 JANUS_GPU_LAYERS 可以做"部分 offload":显存不够放整个模型时,把一部分层留在 CPU 上。用 GET /engine/status 可以实时看到当前 VRAM 占用和活跃后端:

bash
curl -s http://127.0.0.1:8990/engine/status | jq

两个差异化功能:热切换与云端降级 ​

模型热切换 ​

Ollama 换模型要么重启进程,要么依赖其内部的多模型调度。Janus 直接把热切换做成 API:

bash
# 不重启服务,直接换模型
curl -X POST http://127.0.0.1:8990/models/load \
  -H "Content-Type: application/json" \
  -d '{"path": "./models/qwen2.5-coder-7b-instruct-q4_k_m.gguf"}'

# 查看本地可用模型
curl http://127.0.0.1:8990/models/list

对于"今天测 5 个 GGUF"的模型评测场景,这个设计把迭代周期从分钟级压到秒级。

云端降级(Cloud Fallback) ​

设置 INFERENCE_BACKEND=openrouter 后,本地 GPU 饱和或需要更大模型时,请求自动路由到 OpenRouter。这是一个很务实的逃生舱:开发期本地验证、偶发重活上云,两套预算互不拖累。

当然,代价是你要清楚降级发生后数据出了本机——生产环境务必评估合规边界。

Thinking 模型的 reasoning_content 分离 ​

DeepSeek-R1 类推理模型会在 <think> 标签里输出思考过程。Janus 自动把思考内容拆到响应的 reasoning_content 字段,content 只保留最终答案——客户端无需自己写解析器。

Janus vs Ollama vs llama.cpp server ​

维度JanusOllamallama.cpp server
分发单二进制 + 预编译 DLL二进制 + 常驻 daemon需自行编译 C++
GPU 后端Vulkan(跨厂商)CUDA/ROCm/MetalCUDA/ROCm/Metal/Vulkan
Apple Silicon仅 CPUMetal(最快)Metal(快)
模型热切换API 支持不支持(需重启)不支持
模型仓库无(裸 .gguf)内置 registry + pull无
云端降级OpenRouter无无
适用场景CI/Go 服务集成、混合 GPU 团队日常本地使用极客自编译调优

结论很明确:

  • Apple Silicon 用户 → Ollama 的 Metal 加速无可替代,Janus 没有价值
  • 高并发生产推理 → 用 vLLM/SGLang,别用这三者
  • CI/CD 流水线、Go 项目集成、AMD/Intel 显卡的 Linux 开发机 → Janus 的单二进制 + 双环境变量是最省事的路径

冷静看待:它还很年轻 ​

必须泼两盆冷水。截至发稿,这个仓库只有 13 颗星、两个 commit、没有打 tag 的 release,也没有任何与 llama.cpp/Vulkan 后端的对比 benchmark。HN 讨论中 Intel GPU 用户反馈:某些驱动版本下 Vulkan 路径的开销可能吃掉并行收益。

所以我的建议是:把它当作 CI 和开发环境的效率工具来用,别放进生产。等它出了 v0.1 正式 release、社区跑过更多硬件组合之后,再评估升级。

写在最后 ​

Janus 的价值不在技术突破——llama.cpp 的 Vulkan 后端早就存在——而在集成形态的取舍:Go 生态苦"Python 依赖地狱"久矣,一个单二进制、OpenAI 兼容、可编程控制(热切换、降级、VRAM 预算)的推理端点,恰好填上了"Go 服务想随手调个本地模型"这个缝隙。这也是 2026 年的一个明显趋势:AI 基础设施正在从"重型平台"向"嵌入式轻组件"分化,Go 正在这个位置上持续收割。

参考资料 ​

上次更新于: