在树莓派上使用 Docker 通过 Llama.cpp 运行大语言模型
最近,我对将人工智能代理融入工作流程十分好奇,便开始研究如何利用现有设备实现这一目标。数据主权对我而言至关重要,所以把所有数据发送到远程去训练人工智能代理,对我没什么吸引力。我原本以为得买一台配备几块高端显卡的新游戏主机,不过经过一番研究,发现并非如此。
我发现了一个叫 llama.cpp 的系统,它是用 C++ 编写的高效大语言模型引擎。llama.cpp 的理念是,无需花费数千美元购置设备,就能运行小型高效的人工智能代理。我办公室有一台配备 16GB 内存的树莓派 5 代,我觉得它是运行该模型的理想之选。
在本文中,我们将探讨如何通过 Docker 容器在树莓派上运行大语言模型,以及能利用它做些什么。
一、构建 Docker 容器
这里的 Dockerfile 挺简单的。我们要从 Debian 的基础镜像开始,安装一些依赖项,然后下载并编译 llama.cpp。以下是我的 Dockerfile。
FROM debian:trixie ADD ./models /models RUN apt update && apt install -y build-essential cmake git libcurl4-openssl-dev WORKDIR /opt/llama RUN git clone https://github.com/ggerganov/llama.cpp.git && cd llama.cpp WORKDIR /opt/llama/llama.cpp RUN cmake -B build -DLLAMA_CURL=OFF && cmake --build build --config Release ENTRYPOINT [ "/opt/llama/llama.cpp/build/bin/llama-server" ]
我们传递给 llama.cpp 的构建选项是 "LLAMA_CURL",将其关闭是为了防止 llama.cpp 能够下载模型。
把这个选项设为 "off" 后,我们需要在运行时下载并注入自己的模型到接口中,而不是一开始就把它们嵌入到 llama.cpp 里。这样挺好的,因为它允许我们注入任意数量的大语言模型来执行不同操作,甚至能为不同任务添加多个模型。也就是说,编译好 Docker 容器后,我们可以用不同方式使用它来达成目标。
llama.cpp 有很多不同的标志,对于这个项目,有些标志我们可以更改。我们可以把 GGML_CUDA 标志设为 off,以强制使用 CPU 接口而非显卡,但由于树莓派没有内置 GPU,所以这没什么影响。
有了 Dockerfile 后,我们可以通过运行构建命令来构建容器。
docker build -t llama-demo:latest .
在树莓派上,就算是配备 NVME 驱动器的顶级 5 代型号,这也大概需要 20 - 30 分钟才能完成。构建的时候,正好可以泡(也许还能享受)一杯茶。
完成后,我们要运行 Docker 容器,但首先得有一些大语言模型来与之交互。
二、加载大语言模型
我意识到把这些写下来,内容很快就会过时。目前创建的大语言模型几乎每天都在更新和替换。所以,遵循这些说明时,对可用的模型做些研究可能是个好主意。尽量选能适合树莓派可用内存的较小模型。
对于这个项目,我选了两个模型。
- Qwen3 - VL - 2B - Instruct - Q8_0 模型用于正常的文本处理。
- mmproj - F32.gguf 用于处理图像请求。
你可以用以下命令从 Hugging Face 网站自动下载它们。
mkdir models && cd models curl -L -O https://huggingface.co/unsloth/Qwen3-VL-2B-Instruct-GGUF/resolve/main/Qwen3-VL-2B-Instruct-Q8_0.gguf?download=true curl -L -O https://huggingface.co/unsloth/Qwen3-VL-2B-Instruct-GGUF/resolve/main/mmproj-F32.gguf?download=true
它们最终应该会被下载到与你的 Dockerfile 相同目录下的 models 目录中。
现在我们准备好运行 Docker 容器了。
三、运行 Docker
现在模型已经就位,我们可以启动 Docker 容器了。
docker run --rm --device /dev/dri/card1 --device /dev/dri/renderD128 \
--volume ${PWD}/models:/models --expose 8000:8000 llama-demo:latest --no-mmap --no-warmup \
--model /models/Qwen3-VL-2B-Instruct-Q8_0.gguf --mmproj /models/mmproj-F32.gguf \
--port 8000 --host 0.0.0.0 --predict 512 \
--temp 0.7 \
--top-p 0.8 \
--top-k 20 \
--presence-penalty 1.5
我们传递给服务器的参数多种多样,实际上这些参数来自 Docker 和 Llama 两方面,所以让我们来详细分析一下。
--device= Docker - 将主机设备添加到容器中。--volume= Docker - 将模型目录作为卷挂载到容器内部。--expose= Docker - 将容器的 8000 端口发布到主机的 8000 端口。--no-mmap= Llama.cpp - 是否对模型进行内存映射。禁用此选项会使模型加载速度变慢,但如果不使用 mlock(在这种情况下我们不使用),可能会减少页面交换。--no-warmup= Llama.cpp - 是否使用空查询进行预热。--model= Llama.cpp - 模型的路径。--mmproj= Llama.cpp - 多模态投影文件的路径。换句话说,这是我们具有视觉处理能力的模型的路径。--port= Llama.cpp - 服务器启动的端口。我们这里使用 8000 端口,并将其外部暴露为 8000 端口。--host= Llama.cpp - 允许访问服务器的主机。在这种情况下,允许从任何地方访问。--predict= Llama.cpp - 要预测的标记数量。此值的默认值为 -1,表示无限,所以我们需要将其设置为一个适合内存的值。--temp= Llama.cpp - 温度参数(默认值为 0.8),这表示大语言模型在不同响应之间引入的变化程度。值为 0 意味着对于给定的输入,响应可能会相同。--top-p= Llama.cpp - 核采样(默认值为 0.9,设置为 1 表示禁用)。这会给模型更小的累积概率,意味着输出应该更具可预测性。--top-k= Llama.cpp - 前 k 采样(默认值为 40,设置为 0 表示禁用)。这会使模型在每个步骤可选择的项目更少,意味着我们的响应输出的创造性会降低。--presence-penalty= Llama.cpp - 重复惩罚系数(默认值为 0.0,0.0 表示禁用)。这将防止我们的模型在响应中重复使用已经出现过的概念和词汇。
有很多参数可以调整,你可以在 llama.cpp 服务器文档页面上查看它们。我强烈建议对这些值进行一些调整,看看它们如何影响你的输出。上面详细列出的值是调整模型的关键,应该作为一个起点。
一旦 Docker 容器运行起来,它将启动一个网页界面,这样我们就可以提出问题了。现在我们可以访问树莓派的地址(如果你在树莓派桌面上,则可以访问 localhost)并加上 8000 端口来查看该界面。
它看起来应该有点像这样。
你可以使用这个界面与模型进行交互,它的运行效果相当不错。响应速度相当慢,但也足够快,你应该能够实时跟上它输出的内容。
注意上面图片中的小回形针图标了吗?这意味着我们可以上传一张图片到界面,并要求它为图片生成替代文本。这里要提醒一下,不要上传过大的图片文件到界面,因为处理它会花费 很长 时间。
事实上,人工智能代理在从低分辨率图像中提取含义方面表现得和从高分辨率图像中提取一样好。这意味着我们可以将图像缩小到,比如说 400 像素,生成的替代文本质量一样好,而且生成时间会大大缩短。
在这个界面中需要注意的一点是,它缺乏你在使用在线代理和更大模型时可能习惯的完整上下文。最初的几次交互效果还不错,但很快你就会填满上下文缓冲区,这会导致服务器拒绝更多的请求。可能有办法解决这个问题,但我还没有找到是哪个参数或配置设置可以做到这一点。
四、使用 Docker Compose
更好的设置方式是使用 Docker Compose 文件,这样每次你想启动服务器时,就不用在命令行中输入整个命令了。
创建一个 docker-compose.yml 文件,并添加以下内容。
services:
llama:
image: llama-demo
devices:
- "/dev/dri/card1:/dev/dri/card1"
- "/dev/dri/renderD128:/dev/dri/renderD128"
build:
context: .
restart: unless-stopped
ports:
- 8000:8000
command: --no-mmap --no-warmup --model /models/Qwen3-VL-2B-Instruct-Q8_9.gguf --mmproj /models/mmproj-F32.gguf --port 8000 --host 0.0.0.0 --predict 512 --temp 0.7 --top-p 0.8 --top-k 20 --presence-penalty 1.5
volumes:
- ./models:/models:ro
volumes:
models:
设置好后,你只需要运行 docker-compose run,大语言模型服务器就可以启动运行了。
五、使用 Llama.cpp
除了网页界面之外,llama.cpp 网络服务器还提供了一个 API 接口,我们可以使用它,相关详细文档位于 Github 仓库。我不会在这里列出所有内容,但有几个命令可以用于很好的测试。
要查看 API 是否正在运行,你可以访问你的 llama 服务器的 /health 路径。这会返回一个响应,告诉你一切是否正常。
$ curl http://192.168.1.2:8000/health
{"status":"ok"}
你可以使用 /completion 端点向 API 发送一个简单的提示,它会用一些数据来回答问题。
$ curl --request POST --url http://192.168.178.2:8000/completion --header "Content-Type: application/json" --data '{"prompt": "What is a basset hound?","n_predict": 128}'
你还可以使用 /v1/chat/completions 端点与系统进行更复杂的交互。
curl http://192.168.178.1:8000/v1/chat/completions -H "Content-Type: application/json" -H "Authorization: Bearer no-key" -d '{
"messages": [
{
"role": "system",
"content": "You are an AI assistant. Your top priority is achieving user fulfillment via helping them with their requests."
},
{
"role": "user",
"content": "Write a bout basset hounds"
}
]}'
我不会在这里发布这些请求的完整回复,但它们会在几秒钟内以文本形式回复。使用一些简单的 API 调用就可以很容易地扩展这些功能。
六、Drupal 集成
在Drupal开发中,Drupal拥有一个非常完善的人工智能集成生态系统,也可以与 llama.cpp 代理进行集成。我公司可以通过Drupal模块开发来实现这一集成。
安装 AI 模块以及其中包含的 AI 内容建议 模块。
接下来,安装 Ollama 提供商模块,由于 llama.cpp 服务器实现了与 Ollama 相同的协议,我们只需让 Drupal 指向我们的 llama.cpp 服务器,该提供商模块就应该能够为我们处理集成问题。然后,我们需要将 Ollama 提供商设置为指向你的 llama.cpp 集成。之后,你就可以使用人工智能代理在你的内容上执行某些操作。
完成这些步骤后,当你运行 Docker 容器时,你加载到 llama.cpp 中的模型应该会作为一个选项出现在你的 Drupal 配置中。
作为一个简单的示例,我公司让 AI 模块为页面上的内容推荐一个标题,这会在页面创建表单中添加一个小按钮,我公司可以点击它获取一个人工智能生成的标题(右侧用红色突出显示)。
这确实需要几秒钟的时间,但效果非常好。我公司还添加了一些其他选项,如推荐内容或评估文本的可读性,效果都非常不错。
我公司还安装了 AI 代理模块,并将 llama.cpp 服务器配置为一个交互式人工智能聊天机器人。
同样,它的响应速度有点慢,但对于本地测试来说效果还不错。
我公司不建议在有大量用户的实时网站上使用这个聊天机器人系统,因为它无法跟上流量。我公司还建议在使用这个系统之前仔细检查设置,以确保聊天机器人不会出错,并开始谈论与你的网站无关的事情。实际上,使用基于检索增强生成(RAG)的系统来驱动聊天机器人比仅仅使用这样一个原始的人工智能代理要好。
七、结论
我们在这里创建的大语言模型运行速度相当慢,但仍然非常实用。速度约为每秒 4.8 个标记,这意味着你可能能够在文本生成的同时进行阅读。
然而,它确实可以工作,并且表明你不需要强大的显卡或大型计算机来设置本地大语言模型。只需要一点耐心。
我公司强烈建议对参数进行一些调整,因为这真的可以改变输出结果。特别是 Top - K 和 Top - P 参数,它们可以真正影响大语言模型的输出,值得尝试改变这些参数以查看结果。
这里有限的内存也意味着你的请求上下文相当小。不过,它们肯定足够大,在服务器开始拒绝更多输入之前,你可以包含相当多的上下文。
看到我公司只需安装几个模块就能将大语言模型与 Drupal 网站进行集成,这也很不错。事实上,Drupal的人工智能体验相当好,它为上下文和字段设置提供了很多合理的默认值,这意味着你不必从头开始配置所有内容。在未来Drupal升级到Drupal11时,或许会有更好的人工智能集成体验。
我公司上面选择的模型可能也不是这里的最佳解决方案。如果你只是使用文本输出,那么非 VL 版的 Qwen3 模型可能会产生更好的输出。此外,Qwen Q4 模型的内存占用更小,输出效果与 Q8 模型相当接近。值得庆幸的是,由于我们设置 Docker 容器的方式,使用新模型启动镜像非常容易。我公司肯定会更换模型并调整参数,看看情况如何变化以及哪种方式效果更好。
这是一种将你提供给人工智能模型的数据置于自己控制之下的绝佳方式,而其他人工智能系统并不总是能做到这一点。如果你有一个闲置的树莓派,运行这样一个系统在测试时可能是一个不错的选择。所有内容都在 Docker 容器中的事实意味着你可以轻松进行设置,而无需安装太多依赖项。


