运行时的真实边界

加载模型只是本地 AI 可见的起点,并不等于运行时本身。一个可用的运行时要决定当前使用哪个模型、如何获取和校验它、该交给哪个后端执行、内存紧张时怎么办、流式生成如何停止,以及其他本地工具如何安全调用它。

LocalEngine 提供了一个具体的参考。它是面向 Apple 平台的原生 Swift 应用,在设备上运行开放权重的语言和视觉模型。它的价值不仅是能离线生成文本,更在于把让本地推理成为可靠子系统的运行环节一并封装起来。

因此,运行时的边界至少包括:

  • 模型发现、导入、下载与活动模型选择;
  • 推理提供商选择与硬件能力检查;
  • 对话状态、流式输出、取消与故障恢复;
  • 磁盘、内存和热量资源策略;
  • 面向配套应用的、受约束的集成接口。

Local-first 是一项架构约束

“本地”不应只是营销标签。在 LocalEngine 中,提示词、对话和图像输入都在设备上处理;模型安装完成后,应用可以离线工作。这会直接改变系统设计:

关注点云端默认本地运行时要求
模型可用性由服务商负责用户需要获取、存储和选择模型
容量可弹性扩展的远端集群固定的内存、磁盘、电池与散热
延迟网络与排队占比高Prefill、Decode 与 Metal 调度占主导
隐私边界数据跨越服务边界除非用户主动开启其他服务,输入保留在设备上
集成方式公网 HTTPS API本地、认证、按需启用的接口

这也是模型包装器很快就不够用的原因:本地运行时必须承担云端 SDK 可以交给服务商的策略。

一个产品,两个推理后端

LocalEngine 在 macOS 上提供两条互补路径:

  • 通过内置 llama.cpp 运行 GGUF。 这条路径兼容广泛的量化 GGUF 模型,并使用 Metal 加速。
  • 通过 mlx-swift 运行 MLX。 这条路径面向 Apple Silicon,使用原生、以 Metal 为核心的 MLX 推理栈。

产品应该将它们表达为能力,而不是让应用代码直接绑定其中一个实现。提供商抽象层可以回答“当前模型是否支持图像输入”“Metal 是否可用”“该请求能否流式返回”等问题,而应用始终只需发起一次聊天完成请求。

聊天 / 翻译 / 浏览器扩展

       提供商抽象层
       ↙          ↘
GGUF + llama.cpp   MLX + mlx-swift
       ↘          ↙
       Metal / Apple 硬件

重要的契约是请求与流式行为保持稳定,而不是假装两种后端拥有相同的格式、内存占用或性能特征。

模型生命周期属于推理的一部分

LocalEngine 将模型视为受管理的本地资产。用户可以导入 GGUF 文件或 MLX 模型目录、选择活动模型,也可以从目录下载精选模型。下载任务可在后台续传,并显示进度,且可取消。

这是运行时工作,因为一个文件存在并不代表模型可以使用。接受请求前,运行时应能确认:

  1. 所选路径和格式是否有效;
  2. 哪个后端可以加载它;
  3. 视觉能力是否需要额外的多模态投影器;
  4. 磁盘与工作内存是否足够;
  5. 下载或模型切换是否仍在进行。

活动模型应是显式状态,并与对话独立持久化。这样可以避免聊天界面、浏览器扩展和后台下载各自悄悄选择不同的模型。

窄而本地的 API,比“仿云端”更有用

配套产品往往需要本地推理,但并不希望各自内置推理引擎。LocalEngine 为此提供可选的 OpenAI 兼容 HTTP 与 WebSocket API:它仅绑定回环地址,默认关闭,启用后使用自动生成并存储在 macOS 钥匙串中的 Bearer Token。

这是一种很好的集成模式:

  • 熟悉的 chat/completions 请求与流式格式能降低客户端接入成本;
  • 仅绑定回环地址,使 API 不暴露在公网;
  • 按需开启,让用户看见并控制边界;
  • 可重新生成的 Token,避免浏览器扩展被永久隐式信任;
  • 无需认证的健康与能力端点可以用于发现,而不暴露推理能力。

这里的兼容性是一种产品接口,而非承诺模拟所有云服务特性。运行时必须准确说明支持的角色、图像输入、流式行为与错误响应。

引擎之上,才是运行时策略

可靠的端侧产品需要在 llama.cpp 或 MLX 之上再加一层策略。对于 LocalEngine 一类应用,这一层至少应协调以下事项。

上下文与请求策略

对话历史必须变成长度受控的 Prompt。运行时需要 Token 预算、截断规则,并清楚地区分用户内容、系统指令与本地状态。长上下文失败通常先是内存和延迟问题,之后才是模型质量问题。

资源策略

运行时不能将模型的标称大小等同于实际占用。它需要计算权重、随上下文增长的 KV Cache、多模态资源、临时缓冲区与并行下载。在笔记本上,它应报告有用的失败信息并保留现有会话,而不是让界面失去响应。

流式与取消策略

Token 流式输出是与 UI 的契约。启动、停止、切换模型和关闭对话都必须让引擎处于可预期状态。对交互式工具来说,取消延迟与吞吐同样重要。

可观测性

LocalEngine 会展示活动运行时、所选模型和 Metal 可用性等引擎状态,这是正确的起点。生产级运行时还应在不记录用户提示词的前提下,让模型加载失败、下载状态、请求耗时、首 Token 延迟和停止原因都可被检查。

能揭示真实瓶颈的指标

一个 “tokens/s” 数字掩盖了大部分关键问题。应在用户可感知的边界上衡量运行时:

  • 首 Token 时间: Prompt 准备、模型就绪和 Prefill 延迟。
  • 解码 tokens/s: 首 Token 后的持续生成速度。
  • 峰值常驻内存: 包含模型权重、KV Cache 与临时缓冲区。
  • 模型加载与切换时间: 改变活动状态的代价。
  • 下载续传与失败率: 模型获取是产品可靠性的一部分。
  • 取消延迟: 进行中的回复实际停止需要多久。
  • Metal 可用性与回退率: 用于区分配置问题和模型问题。
  • 本地 API 成功率与认证失败: 集成边界必须可观测。

这些指标能帮助我们判断问题究竟属于模型、后端、Prompt、设备,还是外围产品逻辑。

本地不保证什么

端侧推理改善了数据流的控制权,但不能让每个模型都适合每台机器或每项任务。用户仍需要为模型预留足够磁盘、为选定上下文提供足够内存,并为偏好的后端准备兼容硬件。MLX 路径尤其依赖 Apple Silicon;模型质量仍取决于模型和 Prompt,而不是推理发生在哪里。

同样,回环 API 也不能替代权限策略。它需要显式开启、妥善处理 Token,并保持狭窄的接口面。隐私、可靠性和互操作性都来自具体的运行时策略。

实用的架构分层

最终,系统可以理解为五层:

产品界面与配套应用

对话、请求与资源策略

模型注册表与提供商抽象

GGUF / llama.cpp 运行时     MLX 运行时

        Metal 与本地存储

这套分层使同一个产品可以在 LocalEngine、Ollama、LM Studio 或云端提供商间切换,而不会将产品逻辑绑定到某一个推理框架。但本地提供商应拥有一等的契约:模型所有权、硬件状态、用户可控的隐私,以及本地集成能力,正是它的定义性特征。

延伸阅读