经 AI Skill Hub 精选评估,MLX-VLM 获评「强烈推荐」。已获得 4.8k 颗 GitHub Star,这款AI工具在功能完整性、社区活跃度和易用性方面表现出色,AI 评分 8.5 分,适合有一定技术背景的用户使用。
MLX-VLM 是一款基于 Python 开发的开源工具,专注于 AI、视觉语言模型、Python 等核心功能。作为 GitHub 开源项目,它拥有活跃的社区支持和持续的版本迭代,代码完全透明可审计,支持本地部署以保护数据隐私。无论是个人使用还是集成到企业工作流,都能提供稳定可靠的解决方案。
MLX-VLM 是一款基于 Python 开发的开源工具,专注于 AI、视觉语言模型、Python 等核心功能。作为 GitHub 开源项目,它拥有活跃的社区支持和持续的版本迭代,代码完全透明可审计,支持本地部署以保护数据隐私。无论是个人使用还是集成到企业工作流,都能提供稳定可靠的解决方案。
# 方式一:pip 安装(推荐)
pip install mlx-vlm
# 方式二:虚拟环境安装(推荐生产环境)
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install mlx-vlm
# 方式三:从源码安装(获取最新功能)
git clone https://github.com/Blaizzy/mlx-vlm
cd mlx-vlm
pip install -e .
# 验证安装
python -c "import mlx_vlm; print('安装成功')"
# 命令行使用
mlx-vlm --help
# 基本用法
mlx-vlm input_file -o output_file
# Python 代码中调用
import mlx_vlm
# 示例
result = mlx_vlm.process("input")
print(result)
# mlx-vlm 配置文件示例(config.yml) app: name: "mlx-vlm" debug: false log_level: "INFO" # 运行时指定配置文件 mlx-vlm --config config.yml # 或通过环境变量配置 export MLX_VLM_API_KEY="your-key" export MLX_VLM_OUTPUT_DIR="./output"
In multi-turn conversations about an image, the vision encoder runs on every turn even though the image hasn't changed. VisionFeatureCache stores projected vision features in an LRU cache keyed by image path, so the expensive vision encoder is only called once per unique image.
mlx_vlm.server --trust-remote-code
mlx_vlm.server --api-key <secret-token>
The easiest way to get started is to install the mlx-vlm package using pip:
pip install -U mlx-vlm
The Gradio chat UI needs an extra dependency that is not part of the base install:
pip install -U 'mlx-vlm[ui]'
Quote the package name so that shells which expand square brackets, such as zsh, do not treat [ui] as a glob pattern.
from mlx_vlm import load, generate
from mlx_vlm.prompt_utils import apply_chat_template
from mlx_vlm.utils import load_config
model_path = "mlx-community/Qwen2-VL-2B-Instruct-4bit"
model, processor = load(model_path)
config = model.config
images = ["path/to/image1.jpg", "path/to/image2.jpg"]
prompt = "Compare these two images."
formatted_prompt = apply_chat_template(
processor, config, prompt, num_images=len(images)
)
output = generate(model, processor, formatted_prompt, images, verbose=False)
print(output)
mlx_vlm.generate --model mlx-community/Qwen2-VL-2B-Instruct-4bit --max-tokens 100 --prompt "Compare these images" --image path/to/image1.jpg path/to/image2.jpg
#### Command Line
mlx_vlm.generate --model mlx-community/Qwen2-VL-2B-Instruct-4bit --max-tokens 100 --prompt "Describe this video" --video path/to/video.mp4 --fps 1.0
These examples demonstrate how to use multiple images with MLX-VLM for more complex visual reasoning tasks.
```sh
mlx_vlm.server --model Qwen/Qwen3.5-4B \ --enable-thinking \ --thinking-budget 512 \ --thinking-start-token "<think>" \ --thinking-end-token "</think>"
print(apc.coordinator(model).plan.describe())
document = Path("long_document.txt").read_text()
try: # First request computes the full prefix and stores reusable K/V blocks. prompt1 = apply_chat_template( processor, model.config, prompt=f"{document}\n\nSummarize the key decisions.", num_images=0, ) for _ in stream_generate( model, processor, prompt1, max_tokens=128, temperature=0.0, apc_manager=apc ): pass
# Second request shares the same document prefix and only prefills the suffix. prompt2 = apply_chat_template( processor, model.config, prompt=f"{document}\n\nList the open engineering risks.", num_images=0, ) for chunk in stream_generate( model, processor, prompt2, max_tokens=128, temperature=0.0, apc_manager=apc ): print(chunk.text, end="", flush=True)
print(apc.stats_snapshot()) finally: apc.close()
To compare cold, warm-memory, warm-disk, and disk-eviction behavior with a
model, use the same direct API path:
python import os import tempfile import time from pathlib import Path
from mlx_vlm import load, stream_generate from mlx_vlm.apc import APCManager, DiskBlockStore from mlx_vlm.prompt_utils import apply_chat_template
model_id = "Qwen/Qwen3-VL-4B-Instruct" contexts = [8000, 20000, 50000, 100000] disk_cap_gb = 0 # 0 means uncapped shard_max_blocks = 256 context_sweep_max_tokens = 1 # one token is enough to measure prefill reuse
test_prompt_tokens = 8000 fill_prompts = 80 eviction_disk_cap_gb = 3.0
os.environ["APC_DISK_SHARD_MAX_BLOCKS"] = str(shard_max_blocks)
model, processor = load(model_id) tokenizer = processor.tokenizer if hasattr(processor, "tokenizer") else processor
def disk_cap_bytes(gb: float): return None if gb <= 0 else int(gb * (1 << 30))
def make_context(target_tokens: int, seed: int = 0) -> str: line = ( f"Document {seed}: APC benchmark content with deterministic facts, " "dates, identifiers, and repeated technical notes.\n" ) line_tokens = max(1, len(tokenizer.encode(line, add_special_tokens=False))) text = line * max(1, target_tokens // line_tokens) while len(tokenizer.encode(text, add_special_tokens=False)) < target_tokens: text += line return text
def make_prompt(context: str, question: str) -> str: return apply_chat_template( processor, model.config, prompt=f"{context}\n\n{question}", num_images=0, )
def run_once(apc: APCManager, context: str, question: str, max_tokens: int = 32): prompt = make_prompt(context, question) apc.reset_stats()
last = None output = [] start = time.perf_counter() for chunk in stream_generate( model, processor, prompt, max_tokens=max_tokens, temperature=0.0, apc_manager=apc, ): output.append(chunk.text) last = chunk
if last is None: raise RuntimeError("generation returned no chunks")
return { "wall_s": time.perf_counter() - start, "prompt_tokens": last.prompt_tokens, "prompt_tps": last.prompt_tps, "generation_tps": last.generation_tps, "apc": apc.stats_snapshot(), "text": "".join(output).strip(), }
def print_result(label: str, result: dict) -> None: stats = result["apc"] print( f"{label:<12} " f"prompt_tokens={result['prompt_tokens']:>7} " f"prompt_tps={result['prompt_tps']:>8.1f} " f"gen_tps={result['generation_tps']:>7.1f} " f"matched={stats.get('matched_tokens', 0):>7} " f"disk_hits={stats.get('disk_hits', 0):>5} " f"disk_evictions={stats.get('disk_evictions', 0):>5}" )
def open_apc(cache_root: Path, namespace: str, disk_gb: float) -> APCManager: disk = DiskBlockStore( cache_root, namespace=namespace, max_bytes=disk_cap_bytes(disk_gb), ) return APCManager(num_blocks=4096, block_size=16, disk=disk)
def run_context_sweep() -> None: print("cold / warm-memory / warm-disk") with tempfile.TemporaryDirectory() as tmp: cache_root = Path(tmp) for target_tokens in contexts: context = make_context(target_tokens) namespace = f"{model_id}-context-{target_tokens}" apc = open_apc(cache_root, namespace, disk_cap_gb) try: print(f"\ncontext ~= {target_tokens} text tokens") print_result( "cold", run_once( apc, context, "Summarize the key decisions.", max_tokens=context_sweep_max_tokens, ), ) print_result( "warm-memory", run_once( apc, context, "List the open engineering risks.", max_tokens=context_sweep_max_tokens, ), ) finally: # Closing waits for queued disk writes before reopening the disk tier. apc.close()
apc = open_apc(cache_root, namespace, disk_cap_gb) try: print_result( "warm-disk", run_once( apc, context, "Extract the implementation timeline.", max_tokens=context_sweep_max_tokens, ), ) finally: apc.close()
def run_disk_eviction_workload() -> None: print("\ndisk eviction workload") with tempfile.TemporaryDirectory() as tmp: cache_root = Path(tmp) namespace = f"{model_id}-eviction" test_context = make_context(test_prompt_tokens, seed=0)
apc = open_apc(cache_root, namespace, eviction_disk_cap_gb) try: print_result( "seed", run_once(apc, test_context, "Summarize the retained test prefix."), ) finally: apc.close()
apc = open_apc(cache_root, namespace, eviction_disk_cap_gb) try: for i in range(fill_prompts): fill_context = make_context(test_prompt_tokens, seed=i + 1) run_once( apc, fill_context, f"Summarize filler document {i + 1}.", max_tokens=1, ) if (i + 1) % 10 == 0: stats = apc.stats_snapshot() print( f"filled={i + 1:>3} " f"disk_gb={stats.get('disk_bytes', 0) / (1 << 30):.2f} " f"disk_evictions={stats.get('disk_evictions', 0)}" ) finally: apc.close()
apc = open_apc(cache_root, namespace, eviction_disk_cap_gb) try: print_result( "post-fill", run_once( apc, test_context, "Check whether the retained test prefix still restores.", ), ) finally: apc.close()
run_context_sweep() run_disk_eviction_workload()
#### Server
Enable in-memory APC for the server with environment variables:
sh APC_ENABLED=1 \ APC_NUM_BLOCKS=4096 \ mlx_vlm.server --model Qwen/Qwen3-VL-4B-Instruct --port 8080
APC works with KV-cache quantization (`--kv-bits`):
sh APC_ENABLED=1 \ APC_NUM_BLOCKS=4096 \ mlx_vlm.server --model Qwen/Qwen3-VL-4B-Instruct --kv-bits 8 --port 8080
Enable the persistent disk tier:
sh APC_ENABLED=1 \ APC_NUM_BLOCKS=4096 \ APC_DISK_PATH=~/.cache/mlx-vlm/caching \ APC_DISK_MAX_GB=3 \ APC_DISK_SHARD_MAX_BLOCKS=256 \ mlx_vlm.server --model Qwen/Qwen3-VL-4B-Instruct --port 8080
Repeated requests with the same long prefix will hit APC automatically:
sh curl -X POST "http://localhost:8080/v1/chat/completions" \ -H "Content-Type: application/json" \ -H "X-APC-Tenant: demo" \ -d '{ "model": "Qwen/Qwen3-VL-4B-Instruct", "messages": [{ "role": "user", "content": "Paste a long shared document here.\n\nNow answer question A." }], "max_tokens": 128 }'
Use the same `X-APC-Tenant` value for requests that may share cached prefixes. Use different tenant values to isolate cache entries between users or workspaces.
Inspect and reset APC state:
sh curl http://localhost:8080/v1/cache/stats curl -X POST http://localhost:8080/v1/cache/reset
Common APC environment variables:
| Variable | Default | Description |
|----------|---------|-------------|
| `APC_ENABLED` | `0` | Set to `1` to enable APC |
| `APC_NUM_BLOCKS` | `2048` | Number of in-memory APC blocks |
| `APC_BLOCK_SIZE` | `16` | Tokens per APC block |
| `APC_CHECKPOINT_ENTRIES` | `2` | In-memory checkpoint entries for hybrid/stateful cache layouts |
| `APC_CHECKPOINT_GUARD_TOKENS` | `1` | Tokens retained after a reusable hybrid checkpoint boundary; the default preserves the normal final-token prefill boundary |
| `APC_DISK_PATH` | unset | Directory for persistent disk shards |
| `APC_DISK_MAX_GB` | `0` | Disk cap in GB; `0` means uncapped |
| `APC_DISK_SHARD_MAX_BLOCKS` | `256` | Max blocks per disk segment shard |
| `APC_MAX_POOL_TENSORS` | `450000` | Stops adding memory blocks before the Metal resource limit; disk writes continue |
| `APC_LAYER_MAJOR_MEMORY_MIN_TOKENS` | `50000` | Store long warm-memory prefixes as compact layer-major snapshots instead of per-block tensors |
| `APC_HASH` | `fast` | Set to `sha256` for a stable cryptographic hash |
| `APC_TRACE` | unset | Set to `1` for greppable store/reject/self-check log lines |
Custom cache layouts can opt in without APC model-name checks by implementing `prefix_cache_snapshot()` and `prefix_cache_restore(snapshot)`. In-tree dense, sliding-window, recurrent, composite, VLM, and Omni cache layouts are detected automatically. APC works with `--kv-bits` (including TurboQuant): the live KV cache stays quantized; pageable APC K/V blocks are stored as dequantized float K/V, so block-pool size does not shrink with quant.
When APC is enabled on the server, a non-fatal layout self-check runs at model load.
#### KV Cache Quantization
Reduce KV cache memory during continuous batching with `--kv-bits`. Both uniform quantization and TurboQuant are supported. Compatible with Automatic Prefix Caching (`APC_ENABLED=1`).
sh
Generate output from a model using the CLI:
```sh
Start the server: ```sh mlx_vlm.server --port 8080
Pass quantize_activations=True to the load function:
```python from mlx_vlm import load, generate
The following models support video chat:
With more coming soon.
mlx-vlm 是一个专为 Apple Silicon 优化的视觉语言模型库,旨在通过 MLX 框架实现高效的视觉推理任务。该项目支持多种先进的视觉模型,能够处理图像与视频输入,为开发者提供高性能、低延迟的视觉理解能力。
项目引入了 Vision Feature Caching 机制,专门优化了多轮对话场景。在处理涉及同一张图片的连续对话时,系统会通过 LRU cache 缓存已投影的视觉特征,避免在每一轮对话中重复调用高能耗的 vision encoder,从而显著提升推理效率并降低计算开销。
在使用某些特定模型时,需要确保在启动时启用 `--trust-remote-code` 参数,以允许执行远程代码。这对于加载部分非标准架构的模型至关重要,请根据实际需求在命令行中进行配置。
安装 mlx-vlm 非常简单,推荐使用 pip 进行快速部署。您可以通过运行命令 `pip install -U mlx-vlm` 来安装或更新至最新版本,确保您的开发环境已配置好 Python 包管理工具。
项目提供了灵活的使用方式,支持 Python Script 和 Command Line 两种模式。开发者可以通过 Python 调用 `load` 和 `generate` 函数进行复杂的视觉推理任务(如对比多张图片);同时,也可以直接使用命令行工具 `mlx_vlm.video_generate` 来实现对视频内容的描述与分析。
mlx-vlm 提供了完善的接口支持。通过 Command Line Interface (CLI),用户可以直接在终端生成模型输出;此外,项目还内置了基于 FastAPI 的 Server 模式,支持通过指定端口启动服务;对于开发者,还提供了 Python API,可以通过在 `load` 函数中传递 `quantize_activations=True` 等参数来优化性能。
目前 mlx-vlm 已支持多种主流视觉模型的视频对话功能,包括 Qwen2-VL、Qwen2.5-VL、Idefics3 以及 LLaVA 等。随着项目的持续迭代,未来将支持更多类型的视觉模型,为开发者提供更丰富的模型生态选择。
高质量的开源AI工具,实现视觉语言模型的推理和微调
AI Skill Hub 为第三方内容聚合平台,本页面信息基于公开数据整理,不对工具功能和质量作任何法律背书。
建议在沙箱或测试环境中充分验证后,再部署至生产环境,并做好必要的安全评估。
✅ MIT 协议 — 最宽松的开源协议之一,可自由商用、修改、分发,仅需保留版权声明。
AI Skill Hub 点评:MLX-VLM 的核心功能完整,质量优秀。对于AI 技术爱好者来说,这是一个值得纳入个人工具库的选择。建议先在非生产环境试用,再逐步推广。
| 原始名称 | mlx-vlm |
| 原始描述 | 开源AI工具:MLX-VLM is a package for inference and fine-tuning of Vision Language Models (VL。⭐4.8k · Python |
| Topics | AI视觉语言模型Python |
| GitHub | https://github.com/Blaizzy/mlx-vlm |
| License | MIT |
| 语言 | Python |
收录时间:2026-05-30 · 更新时间:2026-05-30 · License:MIT · AI Skill Hub 不对第三方内容的准确性作法律背书。