← 返回 关于

Ollama

2026-07-21 · 原文链接

"Only Apple can do this" —— 据传出自 Tim Cook 之口

Apple 在 WWDC 2024 上推出了 Apple Intelligence。在等待了将近一年,等 Craig Federighi 所说的 "把它做对" 之后,其"为普通人打造的 AI"的承诺仍然遥不可及。

在我们等待 Apple Intelligence 降临设备的同时,你的 Mac 上其实已经在运行着一些了不起的东西了。把它想象成一种本地的 AI 方法论:自产自用、可持续、全年无休。

本周的 NSHipster,我们将看看如何用 Ollama 在你的 Mac 上本地运行大语言模型——既从终端用户角度,也从开发者角度。


什么是 Ollama?

Ollama 是在 Mac 上运行大语言模型最简单的方式。你可以把它看作是"LLM 界的 Docker"——像管理容器一样拉取、运行和管理 AI 模型。

Homebrew 或者直接从 官网 下载 Ollama,然后拉取并运行 llama3.2(2GB)。

$ brew install --cask ollama
$ ollama run llama3.2
>>> 讲一个关于 Swift 编程的笑话。
一个 Apple 开发者最喜欢的饮料是什么?
酷爱(Kool-Aid)。

底层上,Ollama 由 llama.cpp 驱动。但如果说 llama.cpp 提供了引擎,那么 Ollama 就是一辆你真正想开的车——它处理了模型管理、优化和推理的全部复杂性。

类似于 Dockerfile 定义容器镜像,Ollama 使用 Modelfile 来配置模型行为:

FROM mistral:latest
PARAMETER temperature 0.7
TEMPLATE """
{{- if .First }}
你是一个有用的助手。
{{- end }}

用户:{{.Prompt}}
助手:"""

Ollama 使用 开放容器倡议(OCI) 标准来分发模型。每个模型被拆分为多个层,并用清单(manifest)来描述——与 Docker 容器使用相同的方式:

{
  "mediaType": "application/vnd.oci.image.manifest.v1+json",
  "config": {
    "mediaType": "application/vnd.ollama.image.config.v1+json",
    "digest": "sha256:..."
  },
  "layers": [
    {
      "mediaType": "application/vnd.ollama.image.layer.v1+json",
      "digest": "sha256:...",
      "size": 4019248935
    }
  ]
}

总的来说,Ollama 的做法考虑周全、工程优良。最重要的是,开箱即用

本地运行模型有什么大不了的?

杰文斯悖论指出,当某件事变得更高效时,我们往往会使用更多,而不是更少。

在你的设备上拥有 AI 会改变一切。当计算基本上变成免费的,你开始以不同的方式看待智能。

虽然 GPT-4 和 Claude 这样的前沿模型毫无疑问是神迹,但本地运行开源模型这个小小的奇迹同样值得一提。

用 Ollama 构建 macOS 应用

Ollama 还在 11434 端口上暴露了一个 HTTP APIleet 语里的 llama 🦙)。这让它可以轻松地与任何编程语言或工具集成。

为此,我们创建了 Ollama Swift 包,帮助开发者将 Ollama 集成到他们的应用中。

文本补全

使用语言模型最简单的方式就是给定提示词生成文本:

import Ollama

let client = Client.default
let response = try await client.generate(
    model: "llama3.2",
    prompt: "Tell me a joke about Swift programming.",
    options: ["temperature": 0.7]
)
print(response.response)
// 需要多少 Apple 工程师才能写完一个 API 的文档?
// 一个都不用——WWDC 视频就是文档。

聊天补全

对于更结构化的交互,你可以使用聊天 API 来维持包含多条消息和不同角色的对话:

let initialResponse = try await client.chat(
    model: "llama3.2",
    messages: [
        .system("你是一个有用的助手。"),
        .user("Apple 在哪个城市?")
    ]
)
print(initialResponse.message.content)
// Apple 的总部 Apple Park 位于加利福尼亚州的库比蒂诺(Cupertino)。
// 该公司最初成立于加利福尼亚州的洛斯阿尔托斯,后来于 1997 年搬到了库比蒂诺。

let followUp = try await client.chat(
    model: "llama3.2",
    messages: [
        .system("你是一个有用的助手。"),
        .user("Apple 在哪个城市?"),
        .assistant(initialResponse.message.content),
        .user("请用一个词总结")
    ]
)
print(followUp.message.content)
// Cupertino

生成文本嵌入

嵌入(Embeddings) 将文本转换为捕捉语义高维向量。这些向量可用于查找相似内容或进行语义搜索。

例如,如果你想查找与用户查询相似的文档:

let documents: [String] = …

// 将文本转换为可比较相似度的向量
let embeddings = try await client.embeddings(
    model: "nomic-embed-text", 
    texts: documents
)

/// 查找相关文档
func findRelevantDocuments(
    for query: String, 
    threshold: Float = 0.7, // 匹配阈值,可调
    limit: Int = 5
) async throws -> [String] {
    // 获取查询的嵌入
    let [queryEmbedding] = try await client.embeddings(
        model: "llama3.2",
        texts: [query]
    )
 
    // 参见:https://en.wikipedia.org/wiki/Cosine_similarity
    func cosineSimilarity(_ a: [Float], _ b: [Float]) -> Float {
        let dotProduct = zip(a, b).map(*).reduce(0, +)
        let magnitude = { sqrt($0.map { $0 * $0 }.reduce(0, +)) }
        return dotProduct / (magnitude(a) * magnitude(b))
    }
    
    // 查找超过相似度阈值的文档
    let rankedDocuments = zip(embeddings, documents)
        .map { embedding, document in
            (similarity: cosineSimilarity(embedding, queryEmbedding),
             document: document)
        }
        .filter { $0.similarity >= threshold }
        .sorted { $0.similarity > $1.similarity }
        .prefix(limit)
    
    return rankedDocuments.map(\.document)
}

构建 RAG 系统

嵌入在与文本生成结合的 RAG(检索增强生成)工作流中才能真正大放异彩。我们不再要求模型从训练数据中生成信息,而是将回答基于我们自己的文档:

  1. 将文档转换为嵌入
  2. 根据查询找出相关文档
  3. 将这些文档作为生成的上下文

下面是一个简单示例:

let query = "AAPL 在 2024 年第三季度的收益如何?"
let relevantDocs = try await findRelevantDocuments(query: query)
let context = """
    请使用以下文档来回答问题。
    如果答案不在文档中,请如实说明。
    
    文档:
    \(relevantDocs.joined(separator: "\n---\n"))
    
    问题:\(query)
    """

let response = try await client.generate(
    model: "llama3.2",
    prompt: context
)

总结一下:不同的模型有不同的能力。

有了 Ollama,你可以无限访问这些以及更多开源语言模型的丰富资源。


那么,用这些你能构建什么呢?这里举一个例子:

Nominate.app

Nominate 是一款 macOS 应用,它使用 Ollama 根据 PDF 文件的内容智能地重命名文件。

和许多追求无纸化生活的人一样,你可能发现自己扫描的文档最终以 Scan2025-02-03_123456.pdf 这种神秘名称命名。Nominate 通过将 AI 与传统 NLP 技术相结合,根据文档内容自动生成描述性的文件名来解决这个问题。

该应用利用了我们在本文中讨论的几种技术:

展望未来

"未来已经来临——只是分布得还不均匀而已。"——William Gibson

想想这些时间线:

如果你等着 Apple 兑现它的承诺,你将会错过一代人中最重大的技术变革

未来已经到来。你不需要等待。有了 Ollama,你现在就可以开始构建下一波 AI 驱动的应用。