# llama-cpp-docker-stack **Repository Path**: aizuda/llama-cpp-docker-stack ## Basic Information - **Project Name**: llama-cpp-docker-stack - **Description**: llama.cpp Docker 本地模型服务栈 本项目使用 Docker Compose 在 NVIDIA GPU 上运行多个 llama.cpp 服务,并通过一个 Nginx 网关提供统一 HTTP 入口。聊天和 Embedding 接口采用 OpenAI 兼容格式,/v1/rerank 是 llama.cpp 提供的扩展接口。 - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 3 - **Forks**: 0 - **Created**: 2026-07-10 - **Last Updated**: 2026-07-16 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # llama.cpp Docker 本地模型服务栈 本项目使用 Docker Compose 在 NVIDIA GPU 上运行多个 llama.cpp 服务,并通过 Python 网关(`nginx/gateway.py`)提供统一 HTTP 入口。聊天和 Embedding 接口采用 OpenAI 兼容格式,`/v1/rerank` 是 llama.cpp 提供的扩展接口。文本向量与图文向量分别由独立服务处理,避免 `models-max=1` 下的互相踢出。 当前部署同时提供: - 聊天、推理和多模态模型服务; - 文本 Embedding 服务; - 图文 / VL Embedding 独立服务; - 文本 Rerank 服务; - 占用监控页与模型卸载接口; - 基于 Embedding + Rerank 的最小 RAG 检索示例。 项目默认入口为 `http://127.0.0.1:8023`。浏览器打开后先进入登录页,使用 `secrets/llama-api-key.txt` 中的 API Key 登录后可查看占用监控。模型权重和 API Key 只保存在本机,不纳入 Git。 ## 架构 ```text 客户端 │ ▼ Python gateway(宿主机 8023,`nginx/gateway.py`) ├─ /login ──────────────────────────────► 监控登录页 ├─ / 、/dashboard ──────────────────────► 占用监控页(需登录) ├─ /usage 、/api/unload ────────────────► 占用查询 / 卸载模型(需 API Key) ├─ /mineru/* ───────────────────────────► mineru-api-cpu(文档解析,可选) ├─ /mineru-gpu/* ───────────────────────► mineru-api-gpu(可选) ├─ /v1/embeddings │ ├─ model 含 VL/vision/multimodal ──► embedding-vl │ └─ 其他文本 embedding 模型 ───────► embedding ├─ /v1/rerank ──────────────────────────► reranker └─ 其他路径 ────────────────────────────► llama-cpp(聊天 / 推理 / OCR) │ 四个后端共同只读挂载 ./models 四个后端共同读取 Docker Secret gateway 额外加入 mineru-api_default 网络 ``` | Compose 服务 | 容器名 | 作用 | 对外端口 | | --- | --- | --- | --- | | `gateway` | `llama-cpp-gateway` | Python 统一入口;按 `model` 分流 embedding;提供登录监控页、`/usage`、`/api/unload` | `8023` | | `llama-cpp` | `llama-cpp` | 聊天、推理、工具调用和多模态请求 | 仅容器网络 `8080` | | `embedding` | `embedding` | 文本 `/v1/embeddings`(bge / Qwen3-Embedding) | 仅容器网络 `8080` | | `embedding-vl` | `embedding-vl` | 图文 `/v1/embeddings`(Qwen3-VL-Embedding) | 仅容器网络 `8080` | | `reranker` | `reranker` | 处理 `/v1/rerank` | 仅容器网络 `8080` | 四个 llama.cpp 后端都使用 `llamacpp:server-cuda` 镜像和 `--models-max 1`,但各自加载不同的角色 preset: | 服务 | preset 文件 | | --- | --- | | `llama-cpp` | `models/presets/chat.ini` | | `embedding` | `models/presets/embedding.ini` | | `embedding-vl` | `models/presets/embedding-vl.ini` | | `reranker` | `models/presets/reranker.ini` | 这样文本向量与 VL 向量不会在同一个 router 里互相 LRU 踢掉。`models/models.ini` 仅作全量目录参考,运行时不直接挂载给服务。 ## 目录结构 ```text . ├─ docker-compose.yml # 五个服务的 Compose 配置 ├─ nginx/ │ ├─ gateway.py # 运行时统一入口:分流 / 流式透传 / 占用与卸载 │ ├─ gateway.conf # 纯 Nginx 路径分流参考(未默认启用) │ └─ static/ │ ├─ login.html # 监控登录页 │ └─ dashboard.html # 占用监控页面 ├─ models/ │ ├─ models.ini # 全量模型目录参考(运行时不直接加载) │ ├─ models.ini.example # 可复制的最小模型配置示例 │ ├─ presets/ │ │ ├─ chat.ini # llama-cpp 聊天/OCR preset │ │ ├─ embedding.ini # 文本 embedding preset │ │ ├─ embedding-vl.ini # 图文 embedding preset │ │ └─ reranker.ini # rerank preset │ └─ *.gguf # 本地模型文件,不提交 Git ├─ secrets/ │ └─ llama-api-key.txt # 本地 API Key,不提交 Git ├─ tests/ │ ├─ __init__.py # 测试包标记 │ ├─ retrieval_demo.py # Embedding + Rerank 检索示例 │ ├─ test_retrieval_demo.py # 检索示例单元测试 │ ├─ vl_embedding_smoke_test.py # VL Embedding 冒烟测试脚本 │ └─ test_vl_embedding_smoke_test.py # VL 脚本单元测试 ├─ MODEL_GUIDE.md # 本地模型目录、选型与调用指南 ├─ TEST_REPORT.md # Qwythos GGUF v2 上游验证记录 └─ SHA256SUMS # 当前本地模型文件校验清单 ``` `.gitignore` 会忽略 `models/` 下的模型权重和 `secrets/` 下的凭据,仅保留 `models/models.ini`、`models/models.ini.example` 与 `models/presets/`。 ## 环境要求 - Docker Engine 或 Docker Desktop; - Docker Compose v2; - NVIDIA 显卡和可用的 NVIDIA 驱动; - Linux 环境需安装 NVIDIA Container Toolkit; - 本机已存在名为 `llamacpp:server-cuda` 的 CUDA 版 llama.cpp server 镜像; - `models/presets/*.ini`(以及目录参考 `models/models.ini`)引用的 GGUF 文件已放入 `models/`; - 网关镜像使用本机已有的 `python:3.12-slim`,并需要能访问 GPU 以便 `/usage` 调用 `nvidia-smi`; - 运行检索示例时需要 Python 3.9 或更高版本。 可以从 llama.cpp 官方 GHCR 镜像创建 Compose 所需的本地标签: ```bash docker pull ghcr.io/ggml-org/llama.cpp:server-cuda docker tag ghcr.io/ggml-org/llama.cpp:server-cuda llamacpp:server-cuda docker run --rm --entrypoint /app/llama-server llamacpp:server-cuda --version ``` 当前本机验证使用 llama.cpp `b9917`,源码 revision 为 `4a7ee3126d529618b330269f7a68c53d1ca012c1`,本地镜像摘要为 `sha256:b58e2ecb2b3964080f1ca9662237ed92c2d267b0b0211c5eacfe3417ff1c20a1`。`server-cuda` 是可变标签,升级后应重新运行本文末尾的验证项。 如果使用其他镜像名称,需要同步修改 `docker-compose.yml` 中四个 llama.cpp 服务(`llama-cpp`、`embedding`、`embedding-vl`、`reranker`)的 `image`。 ## 快速启动 ### 1. 创建 API Key 在项目根目录创建 `secrets/llama-api-key.txt`,文件中只放一行足够长的随机字符串。 PowerShell: ```powershell New-Item -ItemType Directory -Force secrets | Out-Null $bytes = New-Object byte[] 32 $rng = [System.Security.Cryptography.RandomNumberGenerator]::Create() $rng.GetBytes($bytes) $rng.Dispose() $key = [Convert]::ToBase64String($bytes) Set-Content -Encoding ascii -NoNewline ` -LiteralPath secrets/llama-api-key.txt -Value $key Remove-Variable bytes, key ``` Bash(需要 OpenSSL): ```bash mkdir -p secrets openssl rand -hex 32 | tr -d '\n' > secrets/llama-api-key.txt ``` 没有 OpenSSL 但已安装 Python 3 时: ```bash python3 -c 'import secrets; print(secrets.token_hex(32), end="")' \ > secrets/llama-api-key.txt ``` 不要把真实密钥提交到 Git。 ### 2. 准备模型 把所需 `.gguf` 文件放到 `models/`。文件名必须与 `models/models.ini` 中的 `model` 和 `mmproj` 路径完全一致。 首次从干净目录部署时,可参考 [models/models.ini.example](models/models.ini.example)。示例默认启用一个 Qwen3 文本聊天模型、`bge-m3` 文本 Embedding 和 bge Reranker;Qwythos 图片聊天、PaddleOCR 文档识别与 Qwen3-VL 图文 Embedding 以注释块给出。确认权重齐全后再复制或取消注释,避免 API 模型列表出现无法加载的 preset。 PowerShell: ```powershell if (Test-Path -LiteralPath models/models.ini) { throw 'models/models.ini 已存在;不会覆盖现有配置。' } Copy-Item -LiteralPath models/models.ini.example -Destination models/models.ini ``` Linux、macOS、WSL 或 Git Bash: ```bash if [ -e models/models.ini ]; then printf '%s\n' 'models/models.ini 已存在;不会覆盖现有配置。' >&2 exit 1 fi cp models/models.ini.example models/models.ini ``` 当前配置引用的所有模型文件在本机均存在,但这些大文件被 Git 忽略,克隆仓库后需要自行准备。 完成聊天、Embedding 和 Rerank 的最小部署,只需要: - 一个聊天模型,例如 `Qwen3-4B-Q4_K_M.gguf`; - 一个 Embedding 模型,例如 `bge-m3-Q8_0.gguf`; - 一个 Reranker 模型,例如 `bge-reranker-v2-m3-Q8_0.gguf`; - `models.ini` 中与上述三个模型对应的 preset。 如果不准备全部模型,应删除或注释 `models.ini` 中未下载的 preset,避免模型列表展示无法加载的条目。视觉模型必须同时具备严格匹配的主 GGUF 和 `mmproj`;当前包括 Qwythos、PaddleOCR-VL 和 Qwen3-VL Embedding 三组投影文件。 本仓库不提供模型下载脚本,也不分发模型权重。可从模型发布者页面确认来源和许可,再自行取得对应 GGUF 或按 llama.cpp 文档转换: | 模型系列 | 上游模型 / 发布页 | | --- | --- | | Qwythos GGUF | [empero-ai/Qwythos-9B-Claude-Mythos-5-1M-GGUF](https://huggingface.co/empero-ai/Qwythos-9B-Claude-Mythos-5-1M-GGUF) | | bge-m3 | [BAAI/bge-m3](https://huggingface.co/BAAI/bge-m3) | | bge-reranker-v2-m3 | [BAAI/bge-reranker-v2-m3](https://huggingface.co/BAAI/bge-reranker-v2-m3) | | Qwen3 Embedding / Reranker | [Qwen3-Embedding-0.6B](https://huggingface.co/Qwen/Qwen3-Embedding-0.6B)、[Qwen3-Reranker-0.6B](https://huggingface.co/Qwen/Qwen3-Reranker-0.6B) | | PaddleOCR-VL-1.6 | [主模型](https://huggingface.co/PaddlePaddle/PaddleOCR-VL-1.6)、[官方 GGUF](https://huggingface.co/PaddlePaddle/PaddleOCR-VL-1.6-GGUF) | | Qwen3 聊天模型 | [Qwen3-1.7B](https://huggingface.co/Qwen/Qwen3-1.7B)、[Qwen3-4B](https://huggingface.co/Qwen/Qwen3-4B)、[Qwen3-8B](https://huggingface.co/Qwen/Qwen3-8B) | 除 Qwythos 外,当前仓库没有记录本机 GGUF 转换文件的具体发布仓库。`SHA256SUMS` 只能校验文件完整性,不能证明来源。模型许可和使用限制以实际下载页面为准。 ### 3. 校验并启动 ```bash docker compose config --quiet docker compose up -d docker compose ps ``` 正常情况下,以下四个容器都会显示为 `Up` 或 `healthy`: - `llama-cpp-gateway` - `llama-cpp` - `embedding` - `embedding-vl` - `reranker` - `gateway` 健康检查: ```bash curl http://127.0.0.1:8023/health ``` 预期响应: ```text ok ``` 查看 GPU 与各后端已加载模型占用: ```bash export LLAMA_API_KEY="$(cat secrets/llama-api-key.txt)" curl -H "Authorization: Bearer $LLAMA_API_KEY" http://127.0.0.1:8023/usage # 等价别名 curl -H "Authorization: Bearer $LLAMA_API_KEY" http://127.0.0.1:8023/v1/usage ``` 浏览器实时监控界面(自动刷新,支持卸载模型): ```text http://127.0.0.1:8023/ # 未登录会进入登录页 http://127.0.0.1:8023/login http://127.0.0.1:8023/dashboard ``` 监控页、`/usage`、`/api/unload` **默认需要** `secrets/llama-api-key.txt` 授权: - 浏览器:打开页面后输入 API Key 登录(写入 HttpOnly Cookie) - API:请求头 `Authorization: Bearer ` ```bash export LLAMA_API_KEY="$(cat secrets/llama-api-key.txt)" # 占用快照 curl -H "Authorization: Bearer $LLAMA_API_KEY" http://127.0.0.1:8023/usage # 卸载指定模型(可带 backend) curl -X POST http://127.0.0.1:8023/api/unload \ -H "Authorization: Bearer $LLAMA_API_KEY" \ -H "Content-Type: application/json" \ -d '{"backend":"embedding-vl","model":"Qwen3-VL-Embedding-2B-Q8_0"}' # 卸载全部已加载模型 curl -X POST http://127.0.0.1:8023/api/unload \ -H "Authorization: Bearer $LLAMA_API_KEY" \ -H "Content-Type: application/json" \ -d '{"all":true}' ``` `/usage` 返回 JSON,包含: - `gpu`:显存总量/已用/空闲、利用率、温度等(来自容器内 `nvidia-smi`) - `backends`:chat / embedding / embedding-vl / reranker 是否健康、当前 loaded 模型 - `loaded_models`:跨服务汇总的已加载模型列表 - `summary`:一眼可读的占用摘要 运维鉴权默认使用与后端相同的 `llama_api_key` secret。若要单独指定运维密钥,可设置 gateway 环境变量 `USAGE_API_KEY`;仅当显式设为空字符串时才关闭运维鉴权。 ## API 鉴权 当前 llama.cpp 镜像的实际鉴权边界如下: | 端点 | 未携带或携带错误 API Key | | --- | --- | | `GET /health`、`GET /v1/health` | 返回 `200`,不鉴权 | | `GET /login` | 登录页,不鉴权 | | `POST /api/login`、`POST /api/logout` | 登录/退出;登录成功设置 Cookie | | `GET /`、`GET /dashboard` | 监控页,需 API Key(Cookie 或 Bearer) | | `GET /usage`、`GET /v1/usage` | 需 API Key(Cookie 或 Bearer) | | `POST /api/unload` | 需 API Key(Cookie 或 Bearer) | | `GET /models`、`GET /v1/models` | 返回 `200`,不鉴权,会公开 preset、模型文件路径、启动参数、加载状态和模型元数据 | | `POST /v1/chat/completions` | 返回 `401` | | `POST /v1/embeddings` | 返回 `401` | | `POST /v1/rerank` | 返回 `401` | 聊天、Embedding 和 Rerank 请求应携带 `secrets/llama-api-key.txt` 中的密钥: ```http Authorization: Bearer ``` 网关对监控页与运维接口默认使用 `secrets/llama-api-key.txt` 鉴权。`/health` 与 `/v1/models` 仍可能公开服务状态;若不能对外暴露,请用防火墙或把端口绑定改为 `127.0.0.1:8023:8080`。 以下 Bash、Git Bash 或 WSL 示例先把密钥读入环境变量: ```bash export LLAMA_API_KEY="$(cat secrets/llama-api-key.txt)" ``` PowerShell: ```powershell $env:LLAMA_API_KEY = (Get-Content -Raw secrets/llama-api-key.txt).Trim() ``` ## API 调用示例 ### 查看模型列表 ```bash curl http://127.0.0.1:8023/v1/models ``` 该端点当前不要求 API Key;`/models` 是返回相同详细信息的免鉴权别名。 ### 聊天补全 ```bash curl http://127.0.0.1:8023/v1/chat/completions \ -H "Authorization: Bearer $LLAMA_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "Qwen3-4B-Q4_K_M", "messages": [ {"role": "user", "content": "用一句话介绍 llama.cpp。"} ], "temperature": 0.6, "max_tokens": 128 }' ``` 也可以把 `model` 换成 `models/models.ini` 中配置的 Qwythos 或其他聊天模型。 ### PaddleOCR-VL 图片文字识别 `PaddleOCR-VL-1.6` 是专用文档识别模型,调用 `/v1/chat/completions`。文本 OCR 使用官方提示词 `OCR:`,并设置 `temperature` 为 `0`: 完整的公网/本机调用、入参、出参、Python 和 PowerShell 示例见 [IHONGHU_PADDLEOCR_VL_GUIDE.md](IHONGHU_PADDLEOCR_VL_GUIDE.md)。 ```bash IMAGE_B64="$(base64 -w 0 ./document.png)" curl http://127.0.0.1:8023/v1/chat/completions \ -H "Authorization: Bearer $LLAMA_API_KEY" \ -H "Content-Type: application/json" \ -d "{ \"model\": \"PaddleOCR-VL-1.6\", \"messages\": [{ \"role\": \"user\", \"content\": [ {\"type\": \"image_url\", \"image_url\": {\"url\": \"data:image/png;base64,$IMAGE_B64\"}}, {\"type\": \"text\", \"text\": \"OCR:\"} ] }], \"temperature\": 0, \"max_tokens\": 2048 }" ``` 其他官方任务提示词:`Formula Recognition:`、`Table Recognition:`、`Chart Recognition:`、`Seal Recognition:` 和 `Spotting:`。可运行内置测试图的端到端检查: ```powershell py -3 -B tests\paddleocr_vl_smoke_test.py ``` 测试自己的图片: ```powershell py -3 -B tests\paddleocr_vl_smoke_test.py --image .\document.png ``` ### 文本 Embedding ```bash curl http://127.0.0.1:8023/v1/embeddings \ -H "Authorization: Bearer $LLAMA_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "bge-m3-Q8_0", "input": ["第一段文本", "第二段文本"] }' ``` ### 图文 VL Embedding `Qwen3-VL-Embedding-2B-Q8_0` 用于把文字、图片、截图或图文混合内容转换为向量,调用端点仍是 `/v1/embeddings`。它不是图片聊天模型;要获取图片描述或回答图片问题,请使用带 Qwythos `mmproj` 的聊天模型和 `/v1/chat/completions`。 图片请求使用 llama.cpp 的扩展 `input` 对象:`prompt_string` 中的 `<__media__>` 数量必须与 `multimodal_data` 中的 Base64 项数相同。`multimodal_data` 传入的是**原始 Base64**,不要加 `data:image/...;base64,` 前缀。 Linux、WSL 或 Git Bash(GNU `base64`): ```bash IMAGE_B64="$(base64 -w 0 ./photo.jpg)" curl http://127.0.0.1:8023/v1/embeddings \ -H "Authorization: Bearer $LLAMA_API_KEY" \ -H "Content-Type: application/json" \ -d "{ \"model\": \"Qwen3-VL-Embedding-2B-Q8_0\", \"input\": [ { \"prompt_string\": \"<__media__>\", \"multimodal_data\": [\"$IMAGE_B64\"] }, \"白色背景上的红色正方形\" ], \"encoding_format\": \"float\" }" ``` macOS 可把第一行替换为: ```bash IMAGE_B64="$(base64 ./photo.jpg | tr -d '\n')" ``` 上例返回两个向量:`data[0].embedding` 对应图片,`data[1].embedding` 对应候选文本。当前 `Qwen3-VL-Embedding-2B-Q8_0` preset 返回 2048 维向量。可运行 `py -3 -B tests\vl_embedding_smoke_test.py` 进行端到端验证。 ### 文本 Rerank ```bash curl http://127.0.0.1:8023/v1/rerank \ -H "Authorization: Bearer $LLAMA_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "bge-reranker-v2-m3-Q8_0", "query": "什么是向量检索?", "documents": [ "向量检索通过比较向量距离查找语义相近内容。", "Docker Compose 用于编排多个容器。", "Reranker 会对初步召回结果重新排序。" ], "top_n": 2 }' ``` ### Windows PowerShell Windows PowerShell 5.1 中的 `curl` 默认是 `Invoke-WebRequest` 别名,不能直接照搬上面的 Bash 参数。可以使用下面的原生命令;如果坚持使用 curl,请显式调用 `curl.exe`。 ```powershell $baseUrl = "http://127.0.0.1:8023" $env:LLAMA_API_KEY = (Get-Content -Raw secrets/llama-api-key.txt).Trim() $headers = @{ Authorization = "Bearer $env:LLAMA_API_KEY" } # 模型列表:当前不鉴权 Invoke-RestMethod -Uri "$baseUrl/v1/models" # 聊天 $chatBody = @{ model = "Qwen3-4B-Q4_K_M" messages = @(@{ role = "user"; content = "用一句话介绍 llama.cpp。" }) temperature = 0.6 max_tokens = 128 } | ConvertTo-Json -Depth 5 Invoke-RestMethod -Method Post -Uri "$baseUrl/v1/chat/completions" ` -Headers $headers -ContentType "application/json" -Body $chatBody # Embedding $embeddingBody = @{ model = "bge-m3-Q8_0" input = @("第一段文本", "第二段文本") } | ConvertTo-Json -Depth 5 Invoke-RestMethod -Method Post -Uri "$baseUrl/v1/embeddings" ` -Headers $headers -ContentType "application/json" -Body $embeddingBody # 图文 VL Embedding:图片对应 data[0],候选文本对应 data[1] $imagePath = ".\photo.jpg" $imageBase64 = [Convert]::ToBase64String([IO.File]::ReadAllBytes($imagePath)) $vlEmbeddingBody = @{ model = "Qwen3-VL-Embedding-2B-Q8_0" input = @( @{ prompt_string = "<__media__>" multimodal_data = @($imageBase64) }, "白色背景上的红色正方形" ) encoding_format = "float" } | ConvertTo-Json -Depth 8 $vlEmbeddingResponse = Invoke-RestMethod -Method Post ` -Uri "$baseUrl/v1/embeddings" ` -Headers $headers -ContentType "application/json" -Body $vlEmbeddingBody $vlEmbeddingResponse.data | Sort-Object index | Select-Object index, embedding # Rerank $rerankBody = @{ model = "bge-reranker-v2-m3-Q8_0" query = "什么是向量检索?" documents = @( "向量检索通过比较向量距离查找语义相近内容。", "Docker Compose 用于编排多个容器。", "Reranker 会对初步召回结果重新排序。" ) top_n = 2 } | ConvertTo-Json -Depth 5 Invoke-RestMethod -Method Post -Uri "$baseUrl/v1/rerank" ` -Headers $headers -ContentType "application/json" -Body $rerankBody ``` ## Python 检索示例 `tests/retrieval_demo.py` 使用 Python 标准库完成一个最小两阶段检索流程: 1. 调用 `/v1/embeddings` 生成查询和文档向量; 2. 使用余弦相似度进行初步召回; 3. 调用 `/v1/rerank` 对候选结果重新排序。 脚本只使用 Python 标准库。Windows PowerShell: ```powershell py -3 -B tests\retrieval_demo.py ``` Linux、macOS、WSL 或 Git Bash: ```bash python3 -B tests/retrieval_demo.py ``` 从**项目根目录**运行对应单元测试: ```powershell py -3 -B -m unittest tests.test_retrieval_demo -v ``` 如果当前目录已经是 `tests\`,去掉 `tests.` 前缀: ```powershell py -3 -B -m unittest test_retrieval_demo -v ``` 脚本默认配置: | 环境变量 | 默认值 | 说明 | | --- | --- | --- | | `LLAMA_BASE_URL` | `http://127.0.0.1:8023` | 网关地址 | | `LLAMA_API_KEY` | 无 | 未设置时读取 `secrets/llama-api-key.txt` | | `EMBEDDING_MODEL` | `bge-m3-Q8_0` | Embedding 模型 | | `RERANKER_MODEL` | `bge-reranker-v2-m3-Q8_0` | Rerank 模型 | 当前环境已验证该示例能够正常完成 Embedding 召回和 Rerank 排序。 ## Python VL Embedding 冒烟测试 `tests/vl_embedding_smoke_test.py` 使用 Python 标准库测试 `Qwen3-VL-Embedding-2B-Q8_0` 的图片向量和图文相似度。无参数运行时,脚本会在内存中生成一张“白底红色方块”PNG,并确认对应文本在三个候选描述中以至少 `0.02` 的相似度优势排名第一: ```powershell py -3 -B tests\vl_embedding_smoke_test.py ``` 如果 Windows PowerShell 5.1 显示中文乱码,可先执行 `chcp 65001` 再运行脚本。 从**项目根目录**运行对应单元测试: ```powershell py -3 -B -m unittest tests.test_vl_embedding_smoke_test -v ``` 如果当前目录已经是 `tests\`,运行: ```powershell py -3 -B -m unittest test_vl_embedding_smoke_test -v ``` 测试自己的图片时,可以重复传入 `--candidate` 比较图文相似度: ```powershell py -3 -B tests\vl_embedding_smoke_test.py ` --image .\photo.jpg ` --candidate "一只猫的照片" ` --candidate "风景照" ``` 脚本默认读取 `LLAMA_BASE_URL`、`LLAMA_API_KEY`、`VL_EMBEDDING_MODEL`、 `VL_EMBEDDING_DIMENSION`(默认 `2048`)和 `LLAMA_MEDIA_MARKER`;未设置 `LLAMA_API_KEY` 时会读取 `secrets/llama-api-key.txt`。它还会先检查 `/v1/models`,确认目标 preset 明确声明了 `文本 + 图片` 输入能力。 ## 当前模型配置 `models/models.ini` 当前定义 18 个 API preset。 | 类型 | 已配置模型 | | --- | --- | | Qwythos 聊天 / 推理 / 多模态 | BF16、MTP-BF16、MTP-Q4_K_M、MTP-Q5_K_M、MTP-Q6_K、MTP-Q8_0、Q4_K_M、Q6_K、Q8_0 | | Qwen3 聊天 | `Qwen3-1.7B-Q8_0`、`Qwen3-4B-Q4_K_M`、`Qwen3-8B-Q4_K_M` | | 文档 OCR | `PaddleOCR-VL-1.6` | | Embedding | `bge-m3-Q8_0`、`Qwen3-Embedding-0.6B-Q8_0` | | 多模态 Embedding | `Qwen3-VL-Embedding-2B-Q8_0` | | Reranker | `bge-reranker-v2-m3-Q8_0`、`Qwen3-Reranker-0.6B-Q8_0` | `models/` 中全部权重、视觉投影文件、API preset、选型与调用边界见 [MODEL_GUIDE.md](MODEL_GUIDE.md)。 ### 上下文长度 项目实际使用的上下文长度以 `models/models.ini` 中的 `ctx-size` 为准: - Qwythos BF16:`16384`; - PaddleOCR-VL:`16384`; - 其他已配置聊天模型通常为 `32768`; - bge-m3 Embedding / Reranker:`8192`; - Qwen3 Embedding / Reranker:`32768`。 模型理论上支持的最大上下文不等于当前部署值。调高 `ctx-size` 会显著增加 KV Cache 和显存占用。 ### Qwythos 多模态 所有 Qwythos preset 都配置了: ```text /models/mmproj-Qwythos-9B-Claude-Mythos-5-1M-F16_2.gguf ``` 因此这些 preset 已具备图片输入所需的视觉投影文件。客户端仍需使用 llama.cpp 支持的 OpenAI 多模态消息格式。 ### MTP 说明 仓库包含多个 `-MTP-` preset,但当前 `docker-compose.yml` 没有设置 `--spec-type draft-mtp` 等 speculative decoding 参数。因此,选择 MTP 模型并不代表当前部署已经启用 MTP 推测加速。 如果只追求兼容性和稳定性,优先使用不带 `-MTP-` 的普通量化版本。 ## 模型文件校验 `SHA256SUMS` 记录了当前本地部署所使用和保留的模型文件哈希。在 Linux、WSL 或 Git Bash 中可运行: ```bash sha256sum -c SHA256SUMS ``` `TEST_REPORT.md` 是 Qwythos GGUF v2 文件的发布验证记录,不代表本项目所有 Compose 服务、所有模型组合都完成了同等范围的测试。 ## 独立 MinerU API 仓库在 `mineru/` 下提供 MinerU `3.4.4` 的 CPU / GPU 两个独立版本,并已接入统一网关 `8023`: | 版本 | 直连 | 统一网关 | 默认 backend | compose profile | | --- | --- | --- | --- | --- | | CPU | `http://127.0.0.1:8024` | `http://127.0.0.1:8023/mineru` | `pipeline` | `mineru` | | GPU | `http://127.0.0.1:8025` | `http://127.0.0.1:8023/mineru-gpu` | `vlm-engine` | `mineru-gpu` | 一键启动主栈 + MinerU CPU: ```powershell docker compose --profile mineru up -d curl http://127.0.0.1:8023/mineru/health py -3 -B tests\mineru_api_smoke_test.py --variant cpu ``` 仅主栈(不含 MinerU): ```powershell docker compose up -d ``` 主栈 + MinerU CPU + GPU(需额外显存): ```powershell docker compose --profile mineru --profile mineru-gpu up -d ``` 也可继续用独立文件:`docker compose -f mineru/compose.yaml up -d mineru-api-cpu`。 GPU 需要约 8GB+ 可用显存。完整文档见: - [mineru/README.md](mineru/README.md) — 目录总览 - [mineru/docs/USAGE_MODES.md](mineru/docs/USAGE_MODES.md) — **什么时候用什么模式** - [mineru/docs/DEPLOY.md](mineru/docs/DEPLOY.md) — 部署运维 - [mineru/docs/API.md](mineru/docs/API.md) — API 调用(同步/异步/结构化返回) ## 常用运维命令 查看状态: ```bash docker compose ps ``` 查看全部日志: ```bash docker compose logs -f ``` 查看单个服务日志: ```bash docker compose logs -f llama-cpp docker compose logs -f embedding docker compose logs -f reranker docker compose logs -f gateway ``` 重启服务: ```bash docker compose restart ``` 停止并删除 Compose 创建的服务容器和网络: ```bash docker compose down ``` 更新配置后重建容器: ```bash docker compose up -d --force-recreate ``` ## 常见问题 ### 网关没有启动或一直等待 `gateway` 要求四个 llama.cpp 后端达到 healthy 状态。先检查: ```bash docker compose ps docker compose logs llama-cpp embedding embedding-vl reranker gateway ``` ### 提示模型不存在或无法加载 确认请求中的 `model` 与对应角色 preset(`models/presets/*.ini`)的段名一致;`models/models.ini` 仅作目录参考。同时确认 GGUF 文件名与 `models/` 中实际文件名完全一致。 ### 文本与 VL Embedding 并发 500 / failed to load 旧方案把文本向量和 VL 向量放在同一个 `embedding` 进程里,且 `models-max=1`,会互相 LRU 踢掉并触发约 10 秒加载超时。当前已拆成 `embedding` 与 `embedding-vl`;若仍看到该错误,确认 compose 已用新配置重建,且请求走统一入口 `8023`。 ### 监控页能看占用但卸载失败 确认 gateway 已挂载 `llama_api_key` secret(用于调用后端 `/models/unload`)。`GET /usage` 中 `gateway.unload_ready` 应为 `true`,且请求已登录或携带正确 Bearer。卸载成功后,下次请求该模型会因 `--models-autoload` 重新冷启动。 ### 监控页一直停在登录或提示 unauthorized 确认输入的是 `secrets/llama-api-key.txt` 当前内容(不要带引号或多余空格)。修改密钥后执行: ```bash docker compose up -d --force-recreate gateway ``` API 调用请使用: ```bash curl -H "Authorization: Bearer $LLAMA_API_KEY" http://127.0.0.1:8023/usage ``` ### CUDA、驱动或容器启动失败 确认宿主机驱动可用、Docker 已启用 NVIDIA GPU,并确认 `llamacpp:server-cuda` 是支持当前 GPU 架构的 CUDA 镜像。 ### 显存不足 可以优先: 1. 选择更小的量化模型; 2. 降低对应 `models/presets/*.ini` 中的 `ctx-size`(并同步目录参考 `models/models.ini`); 3. 降低 Embedding / Reranker 的 `batch-size` 和 `ubatch-size`; 4. 16GB 显存时仍避免同时加载多个大聊天/OCR 模型;文本 embedding 与 VL embedding 已拆服务,可并发调用; 5. 登录 `http://127.0.0.1:8023/`,卸载暂时不用的模型释放显存。 ### API 返回未授权 确认请求头使用了正确格式: ```http Authorization: Bearer ``` 监控页未登录、Cookie 失效,或 `/usage`、`/api/unload` 未带 Bearer 时也会返回 `401`。浏览器可打开 `/login` 重新登录。 修改密钥后需要重新创建相关容器: ```bash docker compose up -d --force-recreate ``` ## 安全提示 - 当前端口映射是 `8023:8080`,默认绑定宿主机所有网卡;`127.0.0.1` 只是本机访问地址,并不表示服务仅限本机。 - 如果只允许本机访问,可把端口映射改为 `127.0.0.1:8023:8080`。 - `/health` 与 `/v1/models` 当前不校验 API Key;模型列表会公开 preset 与加载状态。 - `/`、`/dashboard`、`/usage`、`/api/unload` 默认需要 `secrets/llama-api-key.txt`(浏览器登录 Cookie 或 `Authorization: Bearer`)。 - 网关当前没有配置 TLS。需要跨机器访问时,应配合防火墙、可信反向代理和 HTTPS。 - API Key 文件、模型权重和任何私有数据都不应提交到 Git。 - 模型输出不应直接用于医疗、法律、金融或其他高风险决策,关键结果需要人工核验。 ## 当前验证状态 在当前本机环境中已确认: - `docker compose config --quiet` 通过; - 五个容器(含 `embedding-vl` 与 gateway)正常运行并处于 healthy 状态; - llama.cpp 镜像版本为 `b9917`,revision 为 `4a7ee3126`; - `GET /health` 返回 `200` 和纯文本 `ok`; - `GET /` 未登录时进入登录页;登录后可打开占用监控页; - `GET /usage` 与 `POST /api/unload` 需要 API Key; - `GET /models` 和 `GET /v1/models` 聚合四个后端的 preset 列表; - 聊天、Embedding 和 Rerank 端点不带 API Key 时返回 `401`; - 文本 embedding 与 VL embedding 可并发调用,不再因同进程 LRU 互踢导致 500; - 聊天 `stream: true` 可按 SSE 分块返回; - `tests/retrieval_demo.py` 能完成 Embedding 检索和 Rerank 排序; - `tests/vl_embedding_smoke_test.py` 能生成 2048 维图片向量,并完成内置图文语义排序检查; - `tests/paddleocr_vl_smoke_test.py` 能识别内置 PNG 中的 `OCR 12345`(首次调用会触发模型切换和加载)。