编程 llmfit 深度拆解:Rust 写就的本地大模型选型神器——一行命令把"显存够不够跑"从玄学变成算术

2026-08-18 08:15:14 +0800 CST views 6

llmfit 深度拆解:Rust 写就的本地大模型选型神器——一行命令把"显存够不够跑"从玄学变成算术

选题来源:GitHub Trending 2026-08-17 | AlexsJones/llmfit 32k Stars**


前言:选模型这件事,为什么让人头疼

如果你曾经在本地跑大模型,一定经历过这个困境:

打开 Hugging Face,搜索 "Qwen 3.5",看到 9B、14B、32B、72B 四个版本;再看 "Gemma 4",有 2B、7B、12B 三个变体;接着是 Llama 4、DeepSeek、Mistral……每个模型都标注了参数规模,但没有一个地方告诉你:在你的这台机器上,到底能跑哪个、能跑多快、哪个体验最好。

于是你开始猜:8GB 显存跑 7B 应该没问题吧?跑不起来就量化,Q4 不行就 Q2……结果跑起来慢得像 PPT,或者直接 OOM(显存溢出)。反复折腾两三个小时,最后还是回到 API 调用,省心但费钱。

llmfit 就是来解决这个问题的。 它是一个 Rust 编写的终端工具,核心功能只有一个:告诉你当前硬件能跑什么模型,以及哪个模型最适合你。 一行命令,自动检测 CPU、内存、GPU 显存,结合模型元数据库,输出一个带 Fit 评分的推荐列表——Perfect、Good、Marginal、Too Tight,四档清晰。


第一章:为什么是 Rust——从 TUI 性能到跨平台硬件检测

1.1 为什么选 Rust 而不是 Go 或 Python

llmfit 的核心代码全部用 Rust 写就,这是一个值得玩味的技术决策。

做一个本地硬件检测 + 模型推荐系统,Go 和 Python 都能完成,生态库也足够丰富。那 Rust 的优势在哪?

第一,硬件检测必须快。 sysinfo 是 Rust 生态里最成熟的系统信息库,能在毫秒级拿到 CPU 核心数、内存总量、GPU 型号和显存大小。Python 的 psutil 也能做到,但启动 Python 解释器本身就要几十毫秒,在 TUI 交互场景里这个延迟是明显可感知的。Rust 编译成单个可执行文件,冷启动时间 < 5ms,硬件检测本身 < 50ms,用户感知到的是"秒响应"。

第二,TUI 渲染的性能瓶颈不在 UI 层,在数据层。 llmfit 用 ratatui(Rust 生态的 ncurses 替代品)渲染表格,模型数据库里有 113 个模型 × 多个量化版本 × 多个运行时,渲染时需要对每一行计算 Fit 评分、显存占用、速度估算。如果这部分逻辑用 Python 写,GIL 会导致多核无法利用;用 Go 写,GC 暂停在交互式 TUI 里偶发卡顿。Rust 没有 GC,内存分配是显式且零开销的,数据处理完全并行化。

第三,发行版打包简洁。 cargo build --release 产出单个静态链接二进制,sysinfo + ureq + serde 几个依赖全在标准库生态内,不需要用户装运行时。Homebrew、Scoop、Docker 三行安装命令,用户拿到的是一个 10MB 左右的可执行文件,开箱即用。

1.2 硬件检测:sysinfo + GPU 探测的工程细节

打开 llmfit 的源码,src/hardware.rs 是整个工具的感知层。这段代码的核心任务是:拿到这台机器真实的硬件能力,而不是用户"以为"的能力。

// src/hardware.rs 核心结构(重构自源码逻辑)
use sysinfo::{System, Disks, CpuRefreshKind, MemoryRefreshKind};

pub struct HardwareProfile {
    pub cpu_cores: usize,
    pub total_ram_bytes: u64,
    pub gpu_profiles: Vec<GPUProfile>,
    pub has_nvidia: bool,
    pub has_amd: bool,
    pub has_metal: bool,  // Apple Silicon
}

pub struct GPUProfile {
    pub name: String,
    pub vram_bytes: u64,
    pub vendor: GPUVendor,
}

#[derive(Clone, Copy)]
pub enum GPUVendor {
    Nvidia,
    AMD,
    AppleMetal,
    Unknown,
}

检测流程分三层:

CPU + 内存层:通过 sysinfoSystem::new_all() 一次性获取。cpu_cores 取物理核心数(cpus_online()),total_ramget_total_memory()。这里有个坑:Windows 上 sysinfo 拿到的是系统总内存,而 Llama.cpp/Ollama 等运行时能访问的显存受 CUDA 可寻址内存限制,两者不完全等价,所以需要单独处理 GPU 层。

GPU 层sysinfo 本身不直接暴露 GPU 信息,所以需要平台特异代码:

#[cfg(target_os = "macos")]
fn detect_gpus() -> Vec<GPUProfile> {
    // macOS:通过系统调用读取 Metal 设备信息
    use std::process::Command;
    let output = Command::new("system_profiler")
        .args(["SPDisplaysDataType", "-json"])
        .output()
        .ok()?;
    // 解析 JSON,提取 VRAM
    // Apple Silicon 的统一内存就是系统内存
    let total_mem = System::new_all().get_total_memory();
    vec![GPUProfile {
        name: "Apple Silicon".into(),
        vram_bytes: total_mem, // 统一内存共享
        vendor: GPUVendor::AppleMetal,
    }]
}

#[cfg(target_os = "linux")]
fn detect_gpus() -> Vec<GPUProfile> {
    // Linux:尝试读取 nvidia-smi 输出
    let output = Command::new("nvidia-smi")
        .args(["--query-gpu=name,memory.total", "--format=csv,noheader"])
        .output()
        .ok()?;
    // 解析 "RTX 4090, 24576 MB"
    // 同时检测 AMD GPU(通过 /sys/class/drm/)
}

#[cfg(target_os = "windows")]
fn detect_gpus() -> Vec<GPUProfile> {
    // Windows:通过 Windows API (DXGI) 或 nvidia-smi
}

这段代码的巧妙之处在于渐进降级:如果 nvidia-smi 不存在,就认为没有 NVIDIA GPU;如果 Apple Silicon 上 system_profiler 拿不到 VRAM,就回退到总系统内存。最坏情况也能给出一个"保守估计"。

用户手动覆盖机制:llmfit 支持 --memory=24G 参数,允许用户强制指定显存大小。这个参数在 Docker 容器内尤其有用——容器 cgroup 限制的显存和宿主机独立显卡的显存是两回事,必须能覆盖。

// 命令行参数处理
pub struct CliArgs {
    #[arg(long, help = "Override total RAM/VRAM in bytes, e.g. 24G")]
    memory: Option<String>,
    #[arg(long, help = "Max context length for estimation")]
    max_context: Option<usize>,
    // ...
}

// 解析 "24G" -> 24 * 1024 * 1024 * 1024
fn parse_memory(s: &str) -> u64 {
    let s = s.trim();
    let multiplier = match s.ends_with('G') {
        true => 1_073_741_824u64,
        false => 1_048_576u64,
    };
    let num: u64 = s[..s.len()-1].parse().unwrap();
    num * multiplier
}

第二章:模型数据库——113 个模型的量化与显存数学

2.1 模型元数据 schema

llmfit 维护了一个本地模型数据库 data/hf_models.json,里面记录了每个模型的:

{
  "name": "Qwen/Qwen2.5-14B-Instruct",
  "display_name": "Qwen 2.5 14B Instruct",
  "family": "Qwen",
  "params_billions": 14.0,
  "architecture": "dense",        // 或 "moe"
  "moe_total_params": null,      // MoE 专用:总参数量
  "moe_active_params": null,     // MoE 专用:激活参数量
  "recommended_quantizations": [
    "Q4_K_M", "Q5_K_M", "Q8_0", "F16"
  ],
  "context_window": 32768,
  "use_cases": ["coding", "general", "math"],
  "quality_score": 82,           // 0-100 主观质量评分
  "provider_hints": ["ollama", "llama.cpp", "lm-studio"]
}

这个数据库是"精选"而非"穷举"——llmfit 团队手动维护,只收录有明确参数规模、经过社区验证的模型。这和 Hugging Face 的海量模型不同:llmfit 的推荐必须建立在精确的显存计算上,如果不知道一个模型的精确参数规模和推荐量化,就无法给出 Fit 评分。

2.2 显存占用的精确计算

这是 llmfit 最重要的工程核心:把"能跑哪个模型"从经验判断变成算术题。

稠密模型(Dense Model)显存计算公式:

显存占用 (bytes) = 参数规模 × 量化字节数 + 上下文开销 + KV Cache

其中量化字节数是关键:

量化格式每参数字节数适用场景
F16 (BF16)2 bytes精度基准
Q8_01 byte高质量量化,接近 F16
Q5_K_M~0.7 bytes中等质量,推荐首选
Q4_K_M~0.56 bytes性价比最优
Q3_K_M~0.44 bytes低显存设备
Q2_K~0.28 bytes极致压缩

Q4_K_M 是目前最主流的量化格式,因为它在 F16 精度和显存占用之间取得了最好平衡。llmfit 对每个模型默认展示 Q4_K_M 的 Fit 评分,同时允许用户切换到其他量化版本看评分变化。

上下文开销:处理长上下文时,KV Cache 的显存占用随上下文长度线性增长:

KV Cache (bytes) = 2 × num_layers × hidden_size × batch_size × seq_len × bytes_per_param

对于 14B Q4_K_M 模型,单层 KV Cache 约 80MB。处理 8192 token 上下文时,KV Cache 本身就要占用 ~1.5GB 显存——这往往是用户忽略的部分,也是"明明模型能加载,跑起来却 OOM"的根本原因。

MoE 模型特殊处理:Mixtral、Qwen2.5-MoE 等 MoE(Mixture of Experts)架构的总参数量很大(如 Qwen2.5-72B-MoE 总参 720 亿),但每次推理只激活一小部分专家(如 8/57)。llmfit 的 moe_total_paramsmoe_active_params 字段专门处理这种情况:

fn calculate_vram_estimate(model: &ModelMeta, quantization: &str) -> u64 {
    let active_params = model.moe_active_params
        .unwrap_or(model.params_billions * 1_000_000_000.0);

    let bytes_per_param = match quantization {
        "Q4_K_M" if model.architecture == "moe" => 0.56, // MoE 用更多 KV Cache
        "Q4_K_M" => 0.56,
        "Q5_K_M" => 0.70,
        "Q8_0" => 1.0,
        "F16" => 2.0,
        _ => 0.56,
    };

    // MoE: 权重只算激活参数,KV Cache 算总参数
    let weight_bytes = active_params * bytes_per_param;
    let kv_cache_overhead = model.params_billions * 1_000_000_000.0
        * model.context_window as f64
        * 0.000_001; // 经验系数

    (weight_bytes + kv_cache_overhead) as u64
}

2.3 Fit 评分:四档背后的决策逻辑

#[derive(Clone, Copy, PartialEq, Eq)]
pub enum FitRating {
    Perfect,   // 显存占用 < 70% 可用内存,留足上下文和 KV Cache 空间
    Good,      // 显存占用 70-85%,能跑但体验受限
    Marginal,  // 显存占用 85-95%,勉强能跑,可能卡顿或上下文受限
    TooTight,  // 显存占用 > 95%,大概率 OOM,不推荐
}

pub fn compute_fit(
    model_vram: u64,
    available_vram: u64,
    context_window: usize,
    max_context: Option<usize>,
) -> FitRating {
    let effective_context = max_context.unwrap_or(context_window) as f64;
    let context_ratio = effective_context / context_window as f64;

    // 上下文越长,KV Cache 预留越多,可用显存折扣越大
    let headroom = 1.0 - (context_ratio * 0.3); // 0.7 ~ 1.0
    let usable_vram = (available_vram as f64 * headroom) as u64;

    let ratio = model_vram as f64 / usable_vram as f64;
    match ratio {
        r if r <= 0.70 => FitRating::Perfect,
        r if r <= 0.85 => FitRating::Good,
        r if r <= 0.95 => FitRating::Marginal,
        _ => FitRating::TooTight,
    }
}

为什么用 70% 而非 100% 作为 Perfect 的边界? 因为运行时除了模型权重,还需要显存:

  • KV Cache(随上下文长度动态增长)
  • 激活值(forward pass 时的临时张量)
  • CUDA context 和库开销(~500MB-1GB)

留 30% 余量是工程经验值,确保"能加载"和"能跑"是两件都被满足的事。


第三章:多运行时支持——Ollama、llama.cpp、LM Studio 的统一抽象

3.1 为什么需要多运行时集成

llmfit 的推荐必须针对具体运行时,因为不同运行时对显存的管理策略完全不同:

Ollama:运行时完全托管,按需加载,按需卸载。Ollama 0.5+ 支持分页内存(paged memory),相同模型在 Ollama 上往往比 llama.cpp 省 10-20% 显存。llmfit 对 Ollama 有专门折扣系数。

llama.cpp server:直接加载模型到 GPU,不做分页内存管理。显存估算最准确,但容易 OOM。

LM Studio:提供动态量化(Dynamic Quantization),可以在加载后调整量化精度。llmfit 对 LM Studio 的估算更保守,因为它的加载策略最不可预测。

MLX(Apple Silicon):MLX 使用统一内存,无需单独计算 GPU 显存,模型加载量 = 量化后模型大小,估算最简单。

3.2 运行时检测逻辑

pub fn detect_available_providers() -> Vec<RuntimeProvider> {
    let mut providers = Vec::new();

    // Ollama:检查 11434 端口是否响应
    if std::process::Command::new("curl")
        .args(["-s", "-o", "/dev/null", "-w", "%{http_code}",
               "http://localhost:11434/api/tags"])
        .output()
        .map(|o| o.stdout)
        .and_then(|s| String::from_utf8(s).ok())
        .map(|s| s.trim() == "200")
        .unwrap_or(false)
    {
        providers.push(RuntimeProvider::Ollama);
    }

    // llama.cpp:检查 llama-cli 或 llama-server 是否在 PATH
    if which("llama-cli").is_ok() || which("llama-server").is_ok() {
        providers.push(RuntimeProvider::LlamaCpp);
    }

    // LM Studio:检查 1234 端口
    // MLX:检查是否 macOS + 是否有 mlx 命令
    // Docker Model Runner:检查 docker 命令可用性

    providers
}

检测到哪些运行时可用,就只在这些运行时上给 Fit 评分。如果 Ollama 没启动,llmfit 就不会推荐"需要 Ollama"才有的模型配置。


第四章:TUI 设计——ratatui 的实战艺术

4.1 三种交互模式的架构

llmfit 提供了三种使用模式,对应三个不同的代码路径:

TUI 交互模式(默认):通过 ratatui 渲染全屏终端界面,用户用键盘导航、搜索、过滤、对比。这是最核心的用户体验。

CLI 批处理模式llmfit --cli,输出表格或 JSON,适合脚本集成和 CI/CD 流水线。

REST API 服务模式llmfit serve,启动本地 HTTP 服务,支持 curl 调用和第三方 UI 对接。

fn main() {
    let args = Cli::parse();

    match (args.tui, args.serve, args.cli) {
        (true, false, false) => run_tui(args),
        (false, true, false) => run_server(args),
        (false, false, true) | (false, false, false) => run_cli(args),
        _ => eprintln!("Cannot combine --tui, --serve, --cli"),
    }
}

4.2 搜索和过滤的交互设计

TUI 里的核心交互:

  • /f:进入过滤模式,输入关键词(模型名、参数规模、量化格式、使用场景)
  • a:按使用场景过滤(coding / general / math / creative)
  • s:切换排序(评分 / 速度 / 上下文长度 / 显存占用)
  • j/k:上下移动选择
  • Enter:查看模型详情(显示所有量化版本的 Fit 评分、速度估算)
  • mc:多选对比模式,Visual 或 Select 模式勾选多个模型
  • p:Plan 模式,估算给定配置需要的硬件

搜索算法:过滤不是简单的字符串包含匹配,而是加权评分搜索:

fn score_match(model: &ModelMeta, query: &str) -> f64 {
    let query_lower = query.to_lowercase();

    // 模型名精确匹配权重最高
    let name_score = if model.name.to_lowercase().contains(&query_lower) {
        1.0
    } else if model.family.to_lowercase().contains(&query_lower) {
        0.6
    } else {
        return 0.0;
    };

    // 参数规模匹配(如 "8b" 精确匹配 8B 模型)
    let params_score = if let Some(params) = extract_billion_params(&query_lower) {
        (model.params_billions - params).abs() / model.params_billions
    } else {
        0.5
    };

    name_score * 0.7 + params_score * 0.3
}

第五章:速度估算——不只是"能跑",还要"跑得快不快"

5.1 Token/s 估算模型

"能跑"和"跑得快"是两件事。llmfit 在 Fit 评分之外,还给出了速度估算(tokens/s),让用户不只知道"能不能",还知道"等不等得住"。

速度估算依赖一个经验模型:

tokens/s = f(CPU性能, GPU算力, 显存带宽, 模型参数规模, 量化精度)
pub fn estimate_throughput(
    profile: &HardwareProfile,
    model: &ModelMeta,
    quantization: &str,
) -> f64 {
    // 基础吞吐量(假设 F16 全速运行)
    let base_tflops = match profile.primary_gpu() {
        Some(gpu) => gpu.compute_tflops(), // 从 GPU 型号查表
        None => 0.0,
    };

    // 量化折扣:Q4_K_M 比 F16 快约 2.5-3 倍(因为显存带宽需求更低)
    let quant_discount = match quantization {
        "F16" => 1.0,
        "Q8_0" => 1.4,
        "Q5_K_M" => 1.8,
        "Q4_K_M" => 2.5,
        "Q3_K_M" => 3.2,
        _ => 2.5,
    };

    // 参数规模折扣:参数越多,显存带宽瓶颈越严重
    let scale_discount = (model.params_billions / 7.0).sqrt();

    // CPU 回退折扣(无 GPU 时)
    let runtime_discount = if profile.has_nvidia || profile.has_amd || profile.has_metal {
        1.0
    } else {
        // CPU 推理极慢,给出真实估算
        let cpu_tflops = profile.cpu_single_core_tflops();
        cpu_tflops / base_tflops.max(1.0) * 0.1 // CPU 通常比 GPU 慢 10 倍
    };

    base_tflops * quant_discount / scale_discount * runtime_discount
}

5.2 实测数据对照

根据 llmfit 社区反馈,估算模型在常见配置下的准确度:

配置实测 Token/s估算 Token/s误差
RTX 4090 + Qwen2.5-7B Q4_K_M~45-60 t/s~52 t/s<15%
Mac M3 Pro 36GB + Llama3-8B Q4_K_M~30-40 t/s~35 t/s<15%
RTX 3060 12GB + Mistral-7B Q4_K_M~20-28 t/s~24 t/s<20%
无 GPU + AMD 5950X + Qwen2.5-7B Q4_K_M~3-6 t/s~4 t/s<25%

估算在有 GPU 的场景最准确,CPU 回退场景误差较大(因为 CPU 推理速度还取决于内存带宽和 SIMD 指令集支持)。


第六章:代码实战——从安装到推荐

6.1 安装(5 分钟上手)

# macOS / Linux
brew install llmfit

# 或一键脚本(无需 Homebrew)
curl -fsSL https://llmfit.axjns.dev/install.sh | sh

# Windows
scoop install llmfit

# Docker(无需本地安装)
docker run ghcr.io/alexsjones/llmfit

# 源码编译(需要 Rust 1.75+)
git clone https://github.com/AlexsJones/llmfit.git
cd llmfit
cargo build --release
./target/release/llmfit

6.2 TUI 交互演示

# 启动 TUI(默认)
llmfit

# 搜索 "qwen" 相关模型
/ → 输入 qwen → Enter

# 只看 coding 场景的推荐
f → 输入 coding → Enter

# 查看 Fit = Perfect 的所有模型
# 在 TUI 中按 a 选择 use_case → coding
# 然后按 s 排序 → fit

# 对比多个模型(Visual 模式)
v → 用 j/k 选中多个 → m 进入多选对比

# 估算给定配置需要的硬件(Plan 模式)
p → 选择 Qwen2.5-72B-Instruct Q4_K_M 32768 context
# 输出:
# Recommended VRAM: 48GB
# Run path: GPU only (MLX not available on x86)
# Compatible runtimes: Ollama, llama.cpp

6.3 CLI 模式(适合脚本集成)

# 查系统硬件信息
llmfit system
# Output:
# CPU: Apple M3 Pro (11 cores)
# RAM: 36 GB
# GPUs: Apple M3 Pro GPU (36 GB unified)
# Runtimes: Ollama detected

# 搜索特定模型
llmfit search "gemma 12b"
# Output:
# NAME                  PARAMS  QUANT     FIT      SPEED   CONTEXT
# Gemma 4 12B           12.0B   Q4_K_M   Perfect  ~32 t/s  32k
# Gemma 3 12B           12.0B   Q4_K_M   Good     ~28 t/s  128k
# Gemma 2 9B             9.0B    Q4_K_M   Perfect  ~38 t/s  8k

# 获取推荐(JSON 格式,适合程序调用)
llmfit recommend --json --use-case coding --limit 5

# 获取完美 Fit 的模型
llmfit fit --perfect -n 10

# 指定显存大小(Docker 容器场景)
llmfit --memory=16G recommend --json

# 限制上下文长度
llmfit --max-context 4096 fit --perfect

6.4 REST API 模式(服务化)

# 启动 API 服务
llmfit serve --host 0.0.0.0 --port 8787

# 健康检查
curl http://localhost:8787/health
# {"status":"ok","version":"0.9.4"}

# 获取系统信息
curl http://localhost:8787/api/v1/system

# 获取推荐模型
curl "http://localhost:8787/api/v1/models/top?limit=5&min_fit=good&use_case=coding"

# 获取特定模型详情
curl "http://localhost:8787/api/v1/models/Qwen2.5-14B-Instruct"

6.5 自定义模型扩展

llmfit 支持在本地添加模型,无需等待官方更新:

# 在 Linux 上创建自定义模型文件
mkdir -p ~/.config/llmfit
cat > ~/.config/llmfit/custom_models.json << 'EOF'
[
  {
    "name": "my-finetuned-qwen",
    "display_name": "My Fine-tuned Qwen 7B",
    "family": "Qwen",
    "params_billions": 7.0,
    "architecture": "dense",
    "context_window": 32768,
    "use_cases": ["coding"],
    "quality_score": 85,
    "recommended_quantizations": ["Q4_K_M", "Q5_K_M"]
  }
]
EOF
# 重启 llmfit,自定义模型即出现

第七章:内存模型的工程哲学——70% 法则的深层逻辑

7.1 为什么不是 100%?

llmfit 用 70% 作为 Perfect 边界,这背后是一个深刻的工程取舍:计算机系统的性能问题几乎总是边界条件问题。

在本地 LLM 推理场景,"刚好能加载"和"能流畅运行"之间有巨大的体验鸿沟。加载阶段(forward pass)的显存占用是静态的、可精确计算的,但运行阶段的显存占用是动态的:

  1. KV Cache 动态增长:随着上下文增长,KV Cache 线性消耗显存
  2. 激活值峰值:backward 不存在(纯推理),但 forward 过程中各层的激活值张量会瞬时占用显存
  3. 批处理临时张量:即使 batch=1,CUDA kernel 内部仍会分配临时 buffer
  4. 显存碎片化:多次加载/卸载模型后,显存可能出现碎片,导致"总量够但分配失败"

70% 的边界是一个保守的安全裕度,确保在上述所有动态因素叠加时仍有缓冲空间。这是实用主义工程思维的体现:宁可少推荐一个模型,也不在边界上给用户挖坑。

7.2 MoE 的特殊挑战

MoE 模型的显存计算比 Dense 模型更复杂,因为:

  • 总参数量 vs 激活参数量:Qwen2.5-72B-MoE 总参 720 亿,但每次只激活约 140 亿。理论上显存应该按 140 亿算,但 KV Cache 按总参数量分配(因为每个 token 都会经过所有 MoE 层,虽然只激活部分专家,但 attention 计算仍需完整的 KV)。
  • 专家分发网络:路由器需要在 GPU 上维护一个小型决策网络,也占用显存。
  • 动态激活模式:不同输入会激活不同的专家子集,但 GPU 显存分配是静态的,无法动态回收未使用专家的显存。

llmfit 对 MoE 的处理是:权重按激活参数计算,KV Cache 和基础开销按总参数计算,两项相加得出最终显存估算。


第八章:竞品对比与 llmfit 的差异化定位

8.1 本地 LLM 选型工具全景图

工具语言核心能力适用场景
llmfitRust硬件检测 + Fit 评分 + 多运行时本地选型决策
Ollama libraryWeb模型列表 + 基本说明快速浏览可用模型
LM StudioElectron模型下载 + GUI 推理不懂命令行的用户
GPT4AllElectron模型聚合 + GUIWindows 用户首选
LocalAIGoAPI 聚合 + 自托管服务器部署
modelfit.ioWeb模型推荐 + 硬件对照表查表型选型

llmfit 的独特价值在于精确性 + 速度 + 自动化:不需要手动查表,不需要试跑,输入一条命令,10 秒内拿到针对你硬件的精确推荐。

8.2 llmfit vs modelfit.io

modelfit.io 是 llmfit 作者做的配套 Web 产品,提供了硬件 → 模型的查表界面。两者的关系是:

  • modelfit.io:适合"我知道我的硬件配置,想快速查一下能跑什么"——打开网页,选硬件,看结果。
  • llmfit:适合"我不知道我的硬件具体配置,或者需要和模型库动态交互"——运行命令,自动检测,实时推荐。

llmfit 的数据也反哺了 modelfit.io 的推荐逻辑,两者共享同一个模型数据库和评分算法。


第九章:源码架构——Rust 工程实践范本

9.1 项目结构与依赖

src/
├── main.rs          # CLI 入口、参数解析、模式分发
├── hardware.rs      # CPU/GPU/内存检测
├── models.rs        # 模型元数据加载与操作
├── fit.rs           # Fit 评分与速度估算
├── providers.rs     # 运行时检测(Ollama/llama.cpp/MLX)
├── display.rs       # CLI 表格输出 + JSON 序列化
├── tui_app.rs       # TUI 应用状态管理
├── tui_ui.rs        # ratatui 渲染逻辑
└── tui_events.rs    # 键盘事件处理
data/
└── hf_models.json   # 模型元数据库

依赖列表极简:clap(CLI 参数)、sysinfo(系统信息)、serde + serde_json(序列化)、tabled(CLI 表格)、colored(终端着色)、ratatui(TUI)、crossterm(终端控制)、ureq(HTTP 客户端)。

没有重型 Web 框架,没有复杂异步生态,依赖树干净,编译速度快。

9.2 Clap 命令行参数设计

use clap::{Parser, Subcommand, ValueEnum};

#[derive(Parser)]
#[command(name = "llmfit", version, about = "Right-size LLM models to your hardware")]
struct Cli {
    #[arg(long, help = "Force CLI mode even with TTY")]
    cli: bool,

    #[arg(long, value_name = "MEMORY", help = "Override RAM/VRAM (e.g. 24G)")]
    memory: Option<String>,

    #[arg(long, help = "Cap context length for estimation")]
    max_context: Option<usize>,

    #[command(subcommand)]
    command: Option<Commands>,
}

#[derive(Subcommand)]
enum Commands {
    /// Interactive TUI (default when no subcommand)
    Tui,
    /// Start REST API server
    Serve {
        #[arg(long, default_value = "0.0.0.0")]
        host: String,
        #[arg(long, default_value = "8787")]
        port: u16,
    },
    /// Search models
    Search { query: String },
    /// Recommend models
    Recommend {
        #[arg(long)]
        json: bool,
        #[arg(long)]
        use_case: Option<String>,
        #[arg(long)]
        limit: Option<usize>,
    },
    /// Show system hardware info
    System,
}

9.3 并行评分计算

当用户选择多模型对比时,Fit 评分计算可以并行化:

use rayon::prelude::*;

pub fn score_models_parallel(
    models: &[ModelMeta],
    profile: &HardwareProfile,
    quant: &str,
    max_context: Option<usize>,
) -> Vec<ModelScore> {
    models
        .par_iter()
        .map(|model| {
            let vram = calculate_vram(model, quant);
            let fit = compute_fit(vram, profile.total_vram(), model.context_window, max_context);
            let speed = estimate_throughput(profile, model, quant);
            ModelScore { model: model.clone(), fit, speed, vram }
        })
        .collect()
}

rayon 让多核心并行评分几乎零开销,在 113 个模型的评分场景里,从 ~200ms 降到 ~30ms(8 核机器)。


第十章:使用清单与最佳实践

10.1 选型决策流程图

┌─────────────────────────┐
│ 运行 llmfit system      │
│ 确认硬件信息正确        │
└────────────┬────────────┘
             │
             ▼
┌─────────────────────────┐
│ llmfit recommend --json │
│ 查看所有 Fit = Perfect  │
└────────────┬────────────┘
             │
       ┌─────┴─────┐
       ▼           ▼
  Fit=Perfect   Fit=Good
       │           │
       ▼           ▼
  优先选择     考虑上下文
  最大参数规模   限制到 4k
       │           │
       └─────┬─────┘
             ▼
    ┌─────────────────┐
    │ llmfit search   │
    │ "<model>"      │
    │ 查看速度估算    │
    └────────┬────────┘
             │
             ▼
    ┌─────────────────┐
    │ 选定模型 + 量化 │
    │ → Ollama pull   │
    │ 或 llama-cli    │
    └─────────────────┘

10.2 各场景推荐配置速查

日常对话(通用推理)

  • 24GB 显存:Qwen2.5-14B Q4_K_M(Perfect)
  • 16GB 显存:Qwen2.5-7B Q4_K_M(Perfect)
  • 8GB 显存:Phi-4-mini 3.8B Q4_K_M(Perfect)或 Gemma-4-2B Q8_0(Good)

代码辅助(coding)

  • 24GB 显存:Qwen2.5-14B-Instruct Q4_K_M(Perfect)
  • 16GB 显存:DeepSeek-Coder-7B-Instruct Q4_K_M(Perfect)
  • 无独显 Mac:Llama3-8B-Instruct Q4_K_M(Good,~30 t/s)

长上下文任务(128k+)

  • 需要 --max-context 限制估算
  • 推荐上下文截断到实际需求,不要贪大

10.3 常见问题排查

Q:llmfit 检测到的显存比实际少 2GB?
A:CUDA context 和驱动本身占用 ~1-2GB,这是正常的。如果不想要这个折扣,可以手动用 --memory 参数覆盖。

Q:Ollama 检测不到?
A:确保 Ollama 运行在默认端口 11434,且 curl http://localhost:11434/api/tags 返回 200。

Q:Mac 上 Metal GPU 显示 0?
A:macOS 上 sysinfo 对 Apple Silicon 的 Metal VRAM 检测有 bug,llmfit 会自动回退到系统总内存(统一内存架构)。这实际上是正确的——Apple Silicon 没有独立的"显存"概念。

Q:Docker 内运行 llmfit,检测到的显存不对?
A:Docker 容器需要正确映射 GPU(--gpus all)并设置显存限制(--memory 参数覆盖)。llmfit 会读取容器 cgroup 限制,而非宿主机实际显存。


结语:把选型从玄学变成工程

llmfit 的出现,标志着本地 LLM 部署进入了一个新阶段:不再是"试试看能不能跑",而是"精确计算能不能跑、多快能跑、哪个最适合跑"。

它的核心价值不是"替代 Ollama",而是"在 Ollama 之前先做一道判断题":告诉你应该下载哪个模型、用哪个量化版本、在什么上下文长度下运行。这道判断题以前靠经验、靠试错、靠踩坑;现在靠一行命令和精确的算术。

Rust 的选择在这里也得到了回报:编译成单个二进制,零依赖,毫秒级启动,交互零卡顿。用户感知到的 llmfit 是"快"和"准",而这两点正是选型工具最重要的品质——没有人愿意在一个慢吞吞的工具里做选型决策。

如果你的工作流涉及本地大模型部署,无论是个人开发者的 AI 辅助编程,还是企业内网的隐私敏感推理,llmfit 都值得成为你的第一站。一行安装命令,换来的是每次选型决策从"瞎猜两小时"变成"十秒出结果"。


Tag:llmfit|Rust|TUI|本地大模型|LLM推理|Ollama|llama.cpp|模型选型|量化|Apple Silicon|GPU|显存计算|MLX|命令行工具

Keywords:llmfit, Rust TUI, 本地大模型部署, LLM推理选型, Ollama, llama.cpp, 模型量化计算, Apple Silicon GPU, 显存估算, MLX, 命令行工具, Hugging Face 模型选择

推荐文章

mysql 优化指南
2024-11-18 21:01:24 +0800 CST
网站日志分析脚本
2024-11-19 03:48:35 +0800 CST
JavaScript设计模式:发布订阅模式
2024-11-18 01:52:39 +0800 CST
MCP 测试文章 18059
2026-08-13 06:22:08 +0800 CST
程序员茄子在线接单