你每个月可能花费 20 到 200 美元来访问 LLM(大型语言模型)。Claude、GPT、Gemini,无论哪一个都在烧你的钱。而这些模型从你的脚本和开发工具调用的大多数场景都可归结如下几类:
- “把这个错误分类到这五个类别之一。”
- “给这个变量起个名字。”
- “告诉我这个 commit 是 fix、feat 还是 refactor。”
- “用两句话总结这段文字。”
与此同时,你的 Apple Silicon Mac 上还坐着一个拥有 30 亿参数的语言模型:全系统集成、无需支付费用、无需网络连接、无需 API 密钥、甚至不需要网络延迟。而你大概根本没用过。
基础模型框架(Foundation Models Framework)
自 macOS 26 (Tahoe) 起,Apple 推出了 Foundation Models Framework,允许访问 Apple Intelligence 支持的语言模型。这是一个原生的 Swift 框架,适用于 macOS 26、iOS 26 和 iPadOS 26,兼容所有支持 Apple Intelligence 的 Apple Silicon 设备。
值得注意的不仅仅是它免费——这的确很棒,更特别的是它生成 Swift 类型化输出。它不会返回需要你用正则表达式和上帝保佑才能解析的 String,而是直接输出一个结构体(struct)。
import FoundationModels
@Generable
struct CommitClassification {
@Guide(description: "The type of change")
@Guide(.anyOf(["fix", "feat", "refactor", "test", "docs", "chore"]))
let type: String
@Guide(description: "One-line summary of the change, max 72 chars")
let summary: String
}
---
`@Generable` 宏告诉框架在编译时生成架构(schema)。语言模型利用这个架构生成结构化输出。`@Guide` 则限制了可能的值——通俗地说,你给模型铺好了轨道,它就不会出轨。
以下是用法示例:
```swift
let session = LanguageModelSession(instructions: """
You are a commit message classifier. Given a git diff,
classify the change and write a summary.
""")
let diff = "..." // 你的 git diff 数据
let result = try await session.respond(
to: "Classify this diff:\n\(diff)",
generating: CommitClassification.self
)
print("\(result.type): \(result.summary)")
// "fix: handle nil response in auth flow"
就是这样。没有 URLSession(网络请求),也没有 API 密钥,也不需要解析 JSON,不需要 try? JSONDecoder().decode(SomeType.self, from: data)。整个模型在设备内部运行,依托于你 Mac 的 Neural Engine(神经引擎),并返回 Swift 类型数据,编译器将会为它进行类型检查。
它适合做什么?不适合做什么?
必须如实讲,这是一个大约 3B 参数的模型,优化目标是能耗效率和低延迟,而不是搞复杂推理。Apple 在其官方文档中也明确指出:这是为 分类、提取、摘要生成等任务 而设计的模型,不适合复杂推理或百科知识。
在公开的 MMLU 基准测试中,Apple 的模型得分大约为 44%,低于像 Llama 3.2 3B 或 Gemma 2 2B 这样的模型。为什么呢?因为 Apple 优先考虑了模型的运行效率和电池续航能力,而不是要赢得知识竞赛。
但这对开发工具来说并不重要。大量的开发工具任务并不需要深度推理能力,而更需要 快速的分类能力和受控的词汇:
| 任务 | 需要 GPT-4 吗? | Apple 模型适用吗? |
|---|---|---|
| 将 commit 分类为 fix/feat/refactor | 不需要 | 可以 |
| 从上下文生成变量名称 | 不需要 | 可以 |
| 总结编译错误 | 不需要 | 可以 |
| 判断某个 issue 是 bug 还是 feature | 不需要 | 可以 |
| 判断拉取请求(PR)的语气 | 不需要 | 小心使用可以 |
| 设计分布式系统架构 | 需要 | 不适合 |
| 解释并发的复杂 bug | 需要 | 不适合 |
| 从零开始写一个复杂算法 | 需要 | 不适合 |
分界线很明确:如果任务有一个有限的答案集,且上下文足够简短,那么 Apple 的模型可能可以胜任。如果涉及对数百行代码及依赖项的深度分析,那你确实需要一个更强大的模型。
可复制的示例
以下是几个示例,你可以立即上手(等你的 macOS 26 可用时)。
1. 错误优先级分类(Triage)
@Generable
struct ErrorTriage {
@Guide(.anyOf(["critical", "warning", "info", "noise"]))
let severity: String
@Guide(description: "Which team should handle this")
@Guide(.anyOf(["backend", "frontend", "infra", "ignore"]))
let owner: String
@Guide(description: "One sentence explaining the issue")
let summary: String
}
let session = LanguageModelSession(instructions: """
You triage error messages from a CI pipeline.
Classify severity and assign to the right team.
""")
let error = "FATAL: column 'user_id' does not exist"
let triage = try await session.respond(
to: "Triage: \(error)",
generating: ErrorTriage.self
)
// severity: "critical", owner: "backend",
// summary: "Missing column in database schema"
2. 命名助手
@Generable
struct NamingSuggestion {
@Guide(description: "camelCase name for the variable or function")
let name: String
@Guide(description: "Why this name is appropriate")
let reasoning: String
}
let session = LanguageModelSession(instructions: """
You suggest variable and function names following
Swift naming conventions (camelCase, descriptive,
no abbreviations except standard ones like URL, ID).
""")
let context = "A function that takes a list of timestamps and returns the average interval between consecutive entries"
let suggestion = try await session.respond(
to: "Suggest a name for: \(context)",
generating: NamingSuggestion.self
)
// name: "averageIntervalBetweenTimestamps"
3. 提交信息生成器
@Generable
struct CommitMessage {
@Guide(.anyOf(["fix", "feat", "refactor", "test", "docs", "chore"]))
let type: String
@Guide(description: "Scope of the change, e.g. auth, ui, db")
let scope: String
@Guide(description: "Imperative summary, max 50 chars")
let subject: String
}
let session = LanguageModelSession(instructions: """
Generate a conventional commit message from a git diff.
Use imperative mood. Be concise.
""")
let diff = try String(contentsOfFile: "/tmp/current.diff")
let msg = try await session.respond(
to: "Generate commit message:\n\(diff)",
generating: CommitMessage.self
)
print("\(msg.type)(\(msg.scope)): \(msg.subject)")
// "fix(auth): handle expired token in refresh flow"
完整翻译文档。