本地 AI 应成常态
在现代软件领域,当前存在一个趋势,开发者会在应用中添加对 OpenAI 或 Anthropic 的 API 调用以实现某些功能。对于这些功能是否真能为用户带来价值,理性的人或许看法不同,但这里要探讨的是应用依赖云托管 AI 模型这一基本概念。
这种做法催生了一代脆弱、侵犯隐私且存在缺陷的软件。开发的应用,一旦服务器崩溃或信用卡过期就会停止工作。
人们需要重拾让本地设备承担工作的软件开发习惯。如今口袋里设备芯片的运算速度比十年前快很多,设备里有专门的神经引擎,大部分时间却闲置,而人们却在等待弗吉尼亚州服务器群传来的 JSON 响应,这很荒谬。
即便初衷是好的,但将用户内容传输给第三方 AI 提供商,产品性质就变了,会面临数据保留相关问题及一系列麻烦(如用户同意、审计、数据泄露、政府请求、模型训练等)。
此外,技术栈会变得极为复杂,因为该功能依赖于网络状况、外部供应商的正常运行时间、速率限制、账户计费以及自己后端的健康状况。这把一个用户体验功能变成了一个让开发者花钱的分布式系统。
如果某个功能可以在本地实现,却陷入这种麻烦,那就是自找麻烦。“AI 无处不在”并非目标,打造有用的软件才是目标。
具体示例:Brutalist Report 的设备端摘要功能
几年前启动了一个副业项目 The Brutalist Report,这是一个受 20 世纪 90 年代风格网页启发的新闻聚合服务。
最近,为其开发了一个原生 iOS 客户端,设计目标是确保它能保持高密度的新闻阅读体验。客户端有简洁的标题列表、去除了网页冗余内容的阅读模式,还有(可选的)能生成文章摘要的“智能”视图。
关键在于,摘要功能是在设备端利用苹果的本地模型 API 生成的。无需绕道服务器,没有提示信息或用户日志,无需供应商账户,也无需“我们会将你的内容存储 30 天”这样的备注。
如今,人们习惯了所有 AI 应用都在服务器端运行。作为一个行业,要扭转这种局面还有很多工作要做。并非没有意识到,有时某些用例确实需要云托管模型才能提供的智能,但并非所有用例都是如此,需要深思熟虑。
可用工具
由于最初的开发工作主要集中在苹果生态系统,所以只能谈谈该生态系统内可用的工具。过去一年,苹果在这方面投入巨大,让开发者能够轻松使用内置的本地 AI 模型。
核心流程大致如下:
import FoundationModels
let model = SystemLanguageModel.default
guard model.availability == .available else { return }
let session = LanguageModelSession {
"""
以 Markdown 格式提供简洁、信息密集的摘要。
- 用 **粗体** 突出关键概念。
- 用项目符号列出事实。
- 不废话,只讲事实。
"""
}
let response = try await session.respond(options: .init(maximumResponseTokens: 1_000)) {
articleText
}
let markdown = response.content
对于较长的内容,可以将纯文本进行分块(每块约 10000 个字符),为每块生成简洁的“只包含事实”的笔记,然后再进行二次处理,将这些笔记合并成最终摘要。
这正是本地模型的完美用武之地。输入数据已经在设备上(因为用户正在阅读),输出也很轻量。它速度快且保护隐私。即使它没有达到超人般的博士级智能水平也没关系,因为它只是在总结你刚加载的页面,而不是创造世界知识。
当模型的任务是转换用户自有数据,而不是充当宇宙搜索引擎时,本地 AI 就能大放异彩。
有很多 AI 功能是人们想要但又不信任的,比如总结电子邮件、从笔记中提取行动项、对文档进行分类等。
通常的云服务方式会让这些功能都变成一场信任考验:“请将你的数据发送到我们的服务器,我们保证会妥善处理。”
本地 AI 改变了这一切。设备已经有了数据,就在本地完成工作。开发者不是靠写一篇 2000 字的隐私政策来赢得用户信任,而是从一开始就不需要这样的政策。
该平台上的工具功能更强大。苹果最近将“AI 输出”从无结构的文本块转向类型化数据。
不再是“向模型请求 JSON 数据,然后祈祷能得到想要的结果”,更好的新模式是定义一个 Swift `struct` 来表示想要的数据结构。用自然语言为模型提供每个字段的指导,让模型生成该类型的实例。
从概念上讲,代码如下:
import FoundationModels
@Generable
struct ArticleIntel {
@Guide(description: "一句话,不夸张。") var tldr: String
@Guide(description: "3 - 7 个项目符号,只列事实。") var bullets: [String]
@Guide(description: "逗号分隔的关键词。") var keywords: [String]
}
let session = LanguageModelSession()
let response = try await session.respond(
to: "从文章中提取结构化笔记。",
generating: ArticleIntel.self
) {
articleText
}
let intel = response.content
这样,UI 就无需从 Markdown 中提取项目符号,也不用指望模型能记住 JSON 架构。能得到一个带有真实字段的实际类型,并能一致地进行渲染。它能生成应用真正能用的结构化输出,而且一切都在本地运行!
这不仅使用起来更方便,也是一项工程上的改进。如果正在开发以本地优先的应用,这就是“将 AI 作为新奇事物”和“将 AI 作为可靠子系统”的区别。
“但本地模型没那么智能”
没错。但那又怎样?
大多数应用功能并不需要一个能写出莎士比亚作品、解释量子力学并通过律师资格考试的模型。它们只需要一个能可靠完成以下任务之一的模型:总结、分类、提取、改写或规范化。
对于这些任务,本地模型可以表现得非常出色。
如果试图用本地模型替代整个互联网,会失望。但如果把它当作应用内部的“数据转换器”,会疑惑自己为什么曾经要把这些数据发送到服务器。
只在真正必要时使用云模型。把用户数据留在它该在的地方。当使用 AI 时,不要只是把它当作一个聊天框,要把它作为一个有类型输出和可预测行为的真正子系统来使用。
不要本意是发布一个功能,结果却发布了一个分布式系统。