项目简介
AirLLM 是一个面向大语言模型推理的 Python 工具,它的核心思路不是把整个模型一次性塞进显存,而是把模型拆成更细的层级,在推理时按需加载。这样一来,我们就能在很低的显存环境里尝试运行 70B、235B、671B,甚至 Kimi K3 这类更夸张的 MoE 大模型。
Kimi K3 最近很受关注,原因也很直接:它属于超大规模 MoE 模型,参数规模很大,但每个 token 实际只会路由到一部分专家。AirLLM 正好利用了这一点,通过专家级别的流式加载,避免把完整模型一次性放进显存。
这篇文章我们一起来搭一个最小项目:先跑通 Kimi K3 推理,再把它封装成一个简单 HTTP API。最后也会聊一下什么时候适合本地低显存推理,什么时候直接使用 Defapi 这类兼容接口会更省事。
难度: 中级 | 时长: 30-60 分钟 | 收获: 理解 AirLLM 的低显存推理流程,并跑通一个 Kimi K3 最小推理服务
目标读者画像
- 想在单卡、低显存机器上体验超大模型的开发者
- 想评估 Kimi K3、DeepSeek-V3、Qwen3-235B 这类模型推理成本的工程师
- 正在做 AI 应用原型,希望先本地验证模型效果的人
- 需要把本地模型封装成 API,再接入 Agent、RAG 或业务系统的开发者
核心依赖与环境
Kimi K3 对环境比较挑,我们先把依赖边界说清楚:
| 依赖 | 建议版本 | 说明 |
|---|---|---|
| Python | 3.10+ | 建议使用虚拟环境隔离依赖 |
| NVIDIA Driver | 支持 CUDA 12 | 需要能正常运行 CUDA 12 版本 PyTorch |
| PyTorch | CUDA 12 构建 | Kimi K3 依赖链更适合 CUDA 12 |
| transformers | 4.56.x | Kimi K3 的远程模型代码对版本有要求 |
| airllm | 最新版 | 使用 AutoModel 统一加载模型 |
| compressed-tensors | 最新版 | Kimi K3 相关权重格式会用到 |
| flash-attn | 兼容 CUDA 12 | Kimi K3 模型代码通常会要求 flash attention |
WARNING
AirLLM 可以降低显存占用,但不会让模型文件变小。首次运行会下载并拆分模型,磁盘空间和网络质量依然很关键。Kimi K3 这类模型体积巨大,建议先准备充足的模型缓存空间。
完整项目结构树
我们先准备一个最小项目,结构保持简单:
airllm-kimi-k3-demo/
├── .env.example
├── requirements.txt
├── run_kimi_k3.py
├── server.py
└── README.md
1. 创建 Python 虚拟环境
先创建目录和虚拟环境:
mkdir airllm-kimi-k3-demo
cd airllm-kimi-k3-demo
python -m venv .venv
source .venv/bin/activate
Windows PowerShell 可以这样激活:
mkdir airllm-kimi-k3-demo
cd airllm-kimi-k3-demo
python -m venv .venv
.venv\Scripts\Activate.ps1
确认 Python 版本:
python --version
建议输出至少是 Python 3.10.x。如果你机器上有多个 Python 版本,可以用 py -3.10 -m venv .venv 明确指定。
2. 安装 AirLLM 和 Kimi K3 依赖
新建 requirements.txt:
airllm
accelerate
sentencepiece
safetensors
compressed-tensors
python-dotenv
fastapi
uvicorn[standard]
先安装 PyTorch。以 CUDA 12.1 为例:
pip install --upgrade pip
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
再安装核心依赖:
pip install -r requirements.txt
pip install "transformers>=4.56,<4.57"
最后安装 flash-attn:
pip install flash-attn --no-build-isolation
TIP
如果 flash-attn 编译很慢,先确认当前环境里能正确 import torch,并且 torch.version.cuda 显示的是 CUDA 12 系列。Kimi K3 这类模型的依赖更偏工程环境,不建议直接在混乱的全局 Python 环境里安装。
3. 配置模型权限和缓存目录
有些 Hugging Face 模型需要登录或授权。我们准备一个 .env.example:
HF_TOKEN=hf_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
HF_HOME=/data/huggingface
AIRLLM_MODEL_ID=moonshotai/Kimi-K3
实际使用时复制一份:
cp .env.example .env
Windows PowerShell:
Copy-Item .env.example .env
然后填入自己的 Hugging Face Token。模型 ID 需要以官方实际发布的仓库名为准,如果你使用的是镜像仓库或内部同步仓库,也可以直接改成对应的 repo id。
WARNING
不要把 .env 提交到 Git 仓库。模型 Token 属于敏感信息,泄露后可能导致私有模型权限被滥用。
4. 编写最小 Kimi K3 推理脚本
新建 run_kimi_k3.py:
import os
from dotenv import load_dotenv
from airllm import AutoModel
def main() -> None:
"""加载 Kimi K3 并执行一次最小文本生成。"""
load_dotenv()
model_id = os.getenv("AIRLLM_MODEL_ID", "moonshotai/Kimi-K3")
hf_token = os.getenv("HF_TOKEN")
# 控制输入长度,先用短 prompt 跑通链路,避免首次验证时等待太久。
max_length = 256
# AutoModel 会自动识别模型类型;hf_token 用于访问需要授权的模型仓库。
model = AutoModel.from_pretrained(
model_id,
hf_token=hf_token,
profiling_mode=True,
)
prompt = "用三句话解释 AirLLM 为什么能降低大模型推理显存。"
# tokenizer 仍然使用 Hugging Face 风格,迁移成本比较低。
input_tokens = model.tokenizer(
[prompt],
return_tensors="pt",
return_attention_mask=False,
truncation=True,
max_length=max_length,
padding=False,
)
# AirLLM 推理时会按层加载权重;max_new_tokens 先设小一点,便于快速验证。
generation_output = model.generate(
input_tokens["input_ids"].cuda(),
max_new_tokens=128,
use_cache=True,
return_dict_in_generate=True,
)
output = model.tokenizer.decode(
generation_output.sequences[0],
skip_special_tokens=True,
)
print(output)
if __name__ == "__main__":
main()
运行:
python run_kimi_k3.py
首次运行通常会比较慢,因为 AirLLM 需要下载模型、分析权重文件,并把模型拆成后续可按层加载的格式。你会发现第二次运行会明显顺畅一些,因为模型缓存和拆分结果已经准备好了。
5. 观察显存和磁盘占用
打开另一个终端观察显存:
nvidia-smi -l 1
你关注两个指标:
Memory-Usage
GPU-Util
如果一切正常,显存不会像传统 transformers.from_pretrained() 那样瞬间被完整模型撑爆。AirLLM 的做法更像是“边走边取”,需要哪一层、哪一部分专家,就把对应权重加载进来,用完再释放。
再观察磁盘:
du -sh "$HF_HOME"
Windows PowerShell 可以用:
Get-ChildItem $env:HF_HOME -Recurse | Measure-Object Length -Sum
TIP
低显存推理不是免费午餐。AirLLM 把压力从显存转移到磁盘 IO、CPU 内存和模型缓存上,所以 SSD 速度会直接影响体验。
6. 开启压缩参数
AirLLM 支持通过 compression 参数降低权重加载体积。我们可以在初始化模型时加入 4bit:
model = AutoModel.from_pretrained(
model_id,
hf_token=hf_token,
compression="4bit", # 使用 4bit 权重压缩,降低加载体积并改善部分场景速度
profiling_mode=True,
)
如果你想更保守,可以先试 8bit:
model = AutoModel.from_pretrained(
model_id,
hf_token=hf_token,
compression="8bit", # 8bit 更保守,速度和精度之间更平衡
profiling_mode=True,
)
通常我们会这样选:
| 场景 | 建议 |
|---|---|
| 只是体验 Kimi K3 能不能跑 | 不开压缩,先确认链路 |
| 磁盘 IO 明显拖慢 | 尝试 8bit |
| 更关注速度和低成本验证 | 尝试 4bit |
| 要做严肃评测 | 固定参数,多轮对比输出质量 |
WARNING
压缩会改变权重加载方式,虽然很多推理任务影响很小,但如果你在做模型评测、长文本推理或敏感业务验证,一定要保留未压缩版本作为基准。
7. 封装成一个简单 HTTP API
本地脚本跑通后,我们通常会希望把它接到应用里。下面用 FastAPI 做一个最小服务。
新建 server.py:
import os
from typing import List, Literal
from dotenv import load_dotenv
from fastapi import FastAPI
from pydantic import BaseModel
from airllm import AutoModel
class ChatMessage(BaseModel):
"""OpenAI 风格的消息结构,方便后续替换为兼容 API。"""
role: Literal["system", "user", "assistant"]
content: str
class ChatRequest(BaseModel):
"""最小聊天请求,只保留教程需要的字段。"""
messages: List[ChatMessage]
max_tokens: int = 256
class ChatResponse(BaseModel):
"""返回文本结果,先保持简单,便于调试。"""
text: str
load_dotenv()
app = FastAPI(title="AirLLM Kimi K3 Demo")
model_id = os.getenv("AIRLLM_MODEL_ID", "moonshotai/Kimi-K3")
hf_token = os.getenv("HF_TOKEN")
# 服务启动时加载模型;真实生产环境可以加健康检查和懒加载。
model = AutoModel.from_pretrained(
model_id,
hf_token=hf_token,
profiling_mode=False,
)
def build_prompt(messages: List[ChatMessage]) -> str:
"""把多轮消息拼成一个简单 prompt,后续可替换为模型官方 chat template。"""
lines: list[str] = []
for message in messages:
lines.append(f"{message.role}: {message.content}")
lines.append("assistant:")
return "\n".join(lines)
@app.post("/chat", response_model=ChatResponse)
def chat(request: ChatRequest) -> ChatResponse:
"""执行一次 Kimi K3 文本生成。"""
prompt = build_prompt(request.messages)
input_tokens = model.tokenizer(
[prompt],
return_tensors="pt",
return_attention_mask=False,
truncation=True,
max_length=1024,
padding=False,
)
generation_output = model.generate(
input_tokens["input_ids"].cuda(),
max_new_tokens=request.max_tokens,
use_cache=True,
return_dict_in_generate=True,
)
text = model.tokenizer.decode(
generation_output.sequences[0],
skip_special_tokens=True,
)
return ChatResponse(text=text)
启动服务:
uvicorn server:app --host 0.0.0.0 --port 8000
调用测试:
curl -X POST http://localhost:8000/chat \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "user",
"content": "用中文解释 Kimi K3 这类 MoE 模型为什么适合按专家流式加载。"
}
],
"max_tokens": 200
}'
现在我们已经有了一个最小 API。它还不算生产级,但足够用于本地实验、Agent 原型、Prompt 调试和模型效果对比。
8. 本地推理和 Defapi 接口怎么取舍
AirLLM 的价值很明确:它让我们能用低显存机器摸到超大模型,尤其适合学习、验证、评测和离线实验。但生产环境还要考虑吞吐、延迟、并发、模型更新、容灾和运维成本。
如果你只是想快速把模型能力接到应用里,或者需要 OpenAI/Anthropic/Gemini 风格的统一 API,Defapi 会更轻。它的优势是价格通常只有官方的一半,并且常见模型基本兼容这些协议:
v1/chat/completionsv1/messagesv1beta/models/
比如我们可以把上面的本地 /chat 调用抽象成一个统一客户端。开发阶段走本地 AirLLM,线上环境切到 Defapi:
import os
import requests
def ask_model(prompt: str) -> str:
"""根据环境变量选择本地推理或 Defapi 兼容接口。"""
provider = os.getenv("MODEL_PROVIDER", "local")
if provider == "defapi":
response = requests.post(
"https://api.defapi.org/api/v1/chat/completions",
headers={
"Authorization": f"Bearer {os.environ['DEFAPI_API_KEY']}",
"Content-Type": "application/json",
},
json={
"model": os.getenv("DEFAPI_MODEL", "anthropic/claude-sonnet-4.5"),
"messages": [{"role": "user", "content": prompt}],
"temperature": 0.7,
},
timeout=60,
)
response.raise_for_status()
return response.json()["choices"][0]["message"]["content"]
response = requests.post(
"http://localhost:8000/chat",
json={
"messages": [{"role": "user", "content": prompt}],
"max_tokens": 256,
},
timeout=300,
)
response.raise_for_status()
return response.json()["text"]
这种写法的好处是边界很清楚:本地模型负责探索和验证,Defapi 负责稳定、统一、低成本地接入线上模型。对 OpenClaw、RAG 服务、自动化 Agent 来说,这种双模式很实用。
常见问题排查
1. flash-attn 安装失败怎么办?
先确认 PyTorch 能正常识别 CUDA:
python -c "import torch; print(torch.__version__); print(torch.version.cuda); print(torch.cuda.is_available())"
如果 torch.cuda.is_available() 是 False,先别急着装 flash-attn,应该先修 PyTorch、驱动和 CUDA 版本。Kimi K3 这类模型建议使用 CUDA 12 构建,避免 CUDA 13 暂时没有合适预编译包导致编译失败。
2. transformers 版本报错怎么办?
把版本固定在 4.56.x:
pip uninstall -y transformers
pip install "transformers>=4.56,<4.57"
然后重新运行:
python run_kimi_k3.py
如果你看到模型远程代码加载失败,通常就是 transformers 版本和模型代码不匹配。
3. Hugging Face 下载失败怎么办?
先确认 Token 是否可用:
huggingface-cli whoami
如果没有登录:
huggingface-cli login
也可以只用 .env 里的 HF_TOKEN。如果模型需要申请访问权限,要先在模型页面同意协议。
4. 磁盘空间不足怎么办?
AirLLM 首次运行会下载原始权重,还会生成拆分后的模型文件。你可以把缓存目录放到更大的磁盘:
export HF_HOME=/mnt/models/huggingface
或者在 .env 中配置:
HF_HOME=/mnt/models/huggingface
如果磁盘依然吃紧,可以尝试 AirLLM 的 delete_original=True,只保留转换后的文件:
model = AutoModel.from_pretrained(
model_id,
hf_token=hf_token,
delete_original=True, # 转换完成后删除原始权重,节省磁盘空间
)
5. 首次运行为什么特别慢?
这是正常现象。首次运行包括下载权重、读取索引、拆分模型、写入缓存等步骤。第二次运行会复用缓存,速度会好很多。
可以打开 profiling:
model = AutoModel.from_pretrained(
model_id,
hf_token=hf_token,
profiling_mode=True, # 输出加载和推理耗时,方便定位瓶颈
)
如果耗时主要在磁盘读取,换更快的 NVMe SSD 往往比换更大显存更有效。
6. 显存没有降到预期怎么办?
先确认代码没有混用传统加载方式:
from airllm import AutoModel
model = AutoModel.from_pretrained(model_id)
不要混用传统加载方式:
from transformers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained(model_id)
后者会走传统加载路径,很容易直接把显存打满。
7. API 服务并发一高就卡住怎么办?
本地低显存推理更适合串行实验,不适合直接承接高并发。最简单的做法是先限制并发:
uvicorn server:app --host 0.0.0.0 --port 8000 --workers 1
如果你需要更稳定的生产调用,可以把线上请求切到 Defapi,本地 AirLLM 保留给评测和调试。
扩展阅读 / 进阶方向
- 对比 Kimi K3、DeepSeek-V3、Qwen3-235B 在 AirLLM 下的加载时间、显存和输出质量。
- 把 FastAPI 服务改造成 OpenAI 兼容的
/v1/chat/completions接口。 - 接入 Defapi,用
v1/chat/completions或v1/messages统一管理线上模型调用。 - 为 AirLLM 服务加队列,避免多个请求一起争抢磁盘 IO 和 GPU。
- 在 OpenClaw 或其他 Agent 框架中配置本地模型和云端模型,根据任务复杂度动态选择。