单进程常驻 HTTP 服务,在 Apple Silicon 上同时提供:
fastino/gliner2-privacy-filter-PII-multi(GLiNER2 / mDeBERTa-v3,PyTorch + MPS)mlx-community/* 模型(mlx_lm)注意:这不是 MLX 移植的 PII 模型。PII 走 PyTorch 的 Metal(MPS) 后端;MLX 仅用于 LLM 生成。两者在同一个 venv、同一个进程里,互不冲突。
localai(已加入 PATH)localai start # 启动 服务 + Docker + Open WebUI, 并打开 http://localhost:3000
localai stop # 停服务和前端容器 (保留 Colima, 下次启动更快)
localai down # 全部关闭 (含 Colima)
localai restart # 仅重启本服务 (改了 server.py / 换模型后用)
localai status # 查看各组件状态 + 已加载的 MLX 模型
localai logs # 跟踪服务日志
脚本本体:~/local-ai-service/localai,已软链到 /opt/homebrew/bin/localai。
所有操作幂等(已在运行不会重复启动)。日常只需 localai start 一条。
| 路径 | 说明 | 是否可删 |
|---|---|---|
~/local-ai-service/server.py | 常驻 HTTP 服务(核心) | 否 |
~/local-ai-service/pii.py | 命令行/库版 PII 工具,可脱离服务单用 | 否 |
~/local-ai-service/test_run.py | 冒烟测试脚本 | 可 |
~/local-ai-service/server.log | 运行日志 | 可(会重建) |
~/local-ai-service/pyproject.toml | uv 项目定义 + 依赖声明 | 否 |
~/local-ai-service/uv.lock | 锁定的精确依赖版本(可复现) | 否 |
~/local-ai-service/.python-version | 固定 Python 版本(3.14) | 否 |
~/local-ai-service/.venv/ | uv 管理的虚拟环境(~1G) | 可,uv sync 重建 |
~/.cache/huggingface/hub/ | 模型权重缓存(~1.5G) | 删了会重新下载 |
环境由 uv 管理:Python 3.14 · gliner2 1.3.1 · torch 2.12.0 · mlx_lm 0.31.3。
依赖声明在 pyproject.toml、精确版本锁在 uv.lock,全部隔离在 .venv,不污染系统 Python。
clone 后一键就绪:
cd ~/local-ai-service && uv sync # 按 uv.lock 还原完全一致的环境
所有命令默认 cd ~/local-ai-service。
# 启动(后台常驻,默认 127.0.0.1:8000)
nohup uv run python server.py > server.log 2>&1 &
# 自定义端口/对外开放/默认 MLX 模型
HOST=0.0.0.0 PORT=8080 MLX_MODEL=mlx-community/Qwen2.5-3B-Instruct-4bit \
nohup uv run python server.py > server.log 2>&1 &
# 查看是否在跑
pgrep -fl local-ai-service/server.py
# 看日志
tail -f server.log
# 停止
pkill -f local-ai-service/server.py
# 重启
pkill -f local-ai-service/server.py; sleep 1; \
nohup uv run python server.py > server.log 2>&1 &
# 健康检查
curl -s http://127.0.0.1:8000/health
可配置环境变量:HOST(默认 127.0.0.1)、PORT(8000)、PII_REPO、MLX_MODEL。
| 方法 | 路径 | body |
|---|---|---|
| GET | /health | — |
| GET | / | 用法 JSON |
| POST | /pii/extract | {"text","labels"?,"threshold"?} |
| POST | /pii/redact | {"text","labels"?,"threshold"?} |
| POST | /mlx/generate | {"prompt","model"?,"max_tokens"?,"temperature"?,"top_p"?,"chat"?} |
# PII 脱敏
curl -s -X POST http://127.0.0.1:8000/pii/redact \
-H 'Content-Type: application/json' \
-d '{"text":"Email John at john@acme.com, call +1 415 555 0199"}'
# LLM 生成
curl -s -X POST http://127.0.0.1:8000/mlx/generate \
-H 'Content-Type: application/json' \
-d '{"prompt":"你好","max_tokens":80}'
行为:后端懒加载(首次请求才载入,启动 2s 即就绪);共享一把 GPU 锁串行推理;
MLX 按 model 字段多模型缓存,传不同 repo 即可并存。
不经过服务,直接命令行用 PII:
uv run python pii.py --redact "Marie au 06 12 34 56 78"
换 / 加 MLX 模型:请求里带 "model":"mlx-community/<repo>" 即可,首次自动下载到 HF 缓存;
或设 MLX_MODEL 改默认。
改默认 PII 标签:编辑 server.py 顶部的 DEFAULT_LABELS(模型支持 42 种 PII 类型,标签是自由文本)。
漏检/误报:调 threshold(默认 0.5;漏检调低到 0.3,误报调高)。
升级依赖(uv):
uv lock --upgrade # 重新解析最新版本并更新 uv.lock
uv sync # 应用到 .venv
# 或加单个依赖: uv add <包名>
清理空间:删某个模型缓存 rm -rf ~/.cache/huggingface/hub/models--mlx-community--Qwen2.5-0.5B-Instruct-4bit(下次用会重下)。
重建整个环境(.venv 损坏时):
cd ~/local-ai-service && rm -rf .venv && uv sync
127.0.0.1 仅本机可访问;设 HOST=0.0.0.0 暴露到局域网前请自行加防护,切勿直接挂公网。nohup ... & 启动的进程在关机/注销后不会自动拉起。需要开机自启/掉线重启,用 launchd(见下)。把服务交给 macOS 守护,开机自动启动、崩溃自动重启。创建
~/Library/LaunchAgents/com.local.local-ai-service.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict>
<key>Label</key><string>com.local.local-ai-service</string>
<key>ProgramArguments</key>
<array>
<string>/Users/ryantao/local-ai-service/.venv/bin/python</string>
<string>/Users/ryantao/local-ai-service/server.py</string>
</array>
<key>WorkingDirectory</key><string>/Users/ryantao/local-ai-service</string>
<key>EnvironmentVariables</key>
<dict><key>PORT</key><string>8000</string></dict>
<key>RunAtLoad</key><true/>
<key>KeepAlive</key><true/>
<key>StandardOutPath</key><string>/Users/ryantao/local-ai-service/server.log</string>
<key>StandardErrorPath</key><string>/Users/ryantao/local-ai-service/server.log</string>
</dict></plist>
launchctl load ~/Library/LaunchAgents/com.local.local-ai-service.plist # 启用
launchctl unload ~/Library/LaunchAgents/com.local.local-ai-service.plist # 停用
服务自带 OpenAI 兼容接口(GET /v1/models、POST /v1/chat/completions,支持流式),
因此可接任何 OpenAI 客户端。这里用 Open WebUI(开源、类 ChatGPT)。
PII 也能在 Open WebUI 里直接用:模型下拉里除了 LLM,还有两个 PII 伪模型
pii-redact(回脱敏文本)和pii-extract(回检测清单)——选中后把文本发过去即可。 本机 Python 3.14 装不了 pip 版 Open WebUI(要求 <3.13),所以走 Docker(Colima 运行时)。
前提:本服务必须绑 0.0.0.0,容器才能从 host.docker.internal 访问到它:
cd ~/local-ai-service
HOST=0.0.0.0 nohup uv run python server.py > server.log 2>&1 &
(launchd 自启同理:在 plist 的 EnvironmentVariables 里加 <key>HOST</key><string>0.0.0.0</string>)
启动 Docker 运行时 + Open WebUI 容器(已用环境变量预配好后端、关掉登录):
colima start # 启动 Docker 运行时(Linux VM),开机后只需跑一次
docker run -d -p 3000:8080 \
--add-host=host.docker.internal:host-gateway \
-v open-webui:/app/backend/data \
--name open-webui \
-e OPENAI_API_BASE_URL=http://host.docker.internal:8000/v1 \
-e OPENAI_API_KEY=local \
-e WEBUI_AUTH=False \
--restart unless-stopped \
ghcr.io/open-webui/open-webui:main
访问:浏览器打开 http://localhost:3000 —— 模型下拉里会出现
mlx-community/Qwen2.5-0.5B-Instruct-4bit,直接对话即可(首条消息会触发 MLX 懒加载,稍慢)。
容器运维:
docker ps # 看是否在跑
docker logs -f open-webui # 看日志
docker stop open-webui # 停
docker start open-webui # 启
docker rm -f open-webui # 删容器(数据在 named volume open-webui 里, 不丢)
colima stop # 关掉整个 Docker 运行时(省资源)
换模型给前端用:先随便发一条带新模型的请求让服务加载它,或改 MLX_MODEL 重启服务;
Open WebUI 的模型下拉来自 GET /v1/models,会自动反映已加载/默认模型。
连不上排查:① 服务是否绑了 0.0.0.0(不是 127.0.0.1);② curl http://127.0.0.1:8000/v1/models 本机是否通;
③ 容器内 docker exec open-webui curl -s http://host.docker.internal:8000/v1/models 是否通。
3 commits
Python
80.6%
Shell
19.4%
单进程常驻 HTTP 服务,在 Apple Silicon 上同时提供:
fastino/gliner2-privacy-filter-PII-multi(GLiNER2 / mDeBERTa-v3,PyTorch + MPS)mlx-community/* 模型(mlx_lm)注意:这不是 MLX 移植的 PII 模型。PII 走 PyTorch 的 Metal(MPS) 后端;MLX 仅用于 LLM 生成。两者在同一个 venv、同一个进程里,互不冲突。
localai(已加入 PATH)localai start # 启动 服务 + Docker + Open WebUI, 并打开 http://localhost:3000
localai stop # 停服务和前端容器 (保留 Colima, 下次启动更快)
localai down # 全部关闭 (含 Colima)
localai restart # 仅重启本服务 (改了 server.py / 换模型后用)
localai status # 查看各组件状态 + 已加载的 MLX 模型
localai logs # 跟踪服务日志
脚本本体:~/local-ai-service/localai,已软链到 /opt/homebrew/bin/localai。
所有操作幂等(已在运行不会重复启动)。日常只需 localai start 一条。
| 路径 | 说明 | 是否可删 |
|---|---|---|
~/local-ai-service/server.py | 常驻 HTTP 服务(核心) | 否 |
~/local-ai-service/pii.py | 命令行/库版 PII 工具,可脱离服务单用 | 否 |
~/local-ai-service/test_run.py | 冒烟测试脚本 | 可 |
~/local-ai-service/server.log | 运行日志 | 可(会重建) |
~/local-ai-service/pyproject.toml | uv 项目定义 + 依赖声明 | 否 |
~/local-ai-service/uv.lock | 锁定的精确依赖版本(可复现) | 否 |
~/local-ai-service/.python-version | 固定 Python 版本(3.14) | 否 |
~/local-ai-service/.venv/ | uv 管理的虚拟环境(~1G) | 可,uv sync 重建 |
~/.cache/huggingface/hub/ | 模型权重缓存(~1.5G) | 删了会重新下载 |
环境由 uv 管理:Python 3.14 · gliner2 1.3.1 · torch 2.12.0 · mlx_lm 0.31.3。
依赖声明在 pyproject.toml、精确版本锁在 uv.lock,全部隔离在 .venv,不污染系统 Python。
clone 后一键就绪:
cd ~/local-ai-service && uv sync # 按 uv.lock 还原完全一致的环境
所有命令默认 cd ~/local-ai-service。
# 启动(后台常驻,默认 127.0.0.1:8000)
nohup uv run python server.py > server.log 2>&1 &
# 自定义端口/对外开放/默认 MLX 模型
HOST=0.0.0.0 PORT=8080 MLX_MODEL=mlx-community/Qwen2.5-3B-Instruct-4bit \
nohup uv run python server.py > server.log 2>&1 &
# 查看是否在跑
pgrep -fl local-ai-service/server.py
# 看日志
tail -f server.log
# 停止
pkill -f local-ai-service/server.py
# 重启
pkill -f local-ai-service/server.py; sleep 1; \
nohup uv run python server.py > server.log 2>&1 &
# 健康检查
curl -s http://127.0.0.1:8000/health
可配置环境变量:HOST(默认 127.0.0.1)、PORT(8000)、PII_REPO、MLX_MODEL。
| 方法 | 路径 | body |
|---|---|---|
| GET | /health | — |
| GET | / | 用法 JSON |
| POST | /pii/extract | {"text","labels"?,"threshold"?} |
| POST | /pii/redact | {"text","labels"?,"threshold"?} |
| POST | /mlx/generate | {"prompt","model"?,"max_tokens"?,"temperature"?,"top_p"?,"chat"?} |
# PII 脱敏
curl -s -X POST http://127.0.0.1:8000/pii/redact \
-H 'Content-Type: application/json' \
-d '{"text":"Email John at john@acme.com, call +1 415 555 0199"}'
# LLM 生成
curl -s -X POST http://127.0.0.1:8000/mlx/generate \
-H 'Content-Type: application/json' \
-d '{"prompt":"你好","max_tokens":80}'
行为:后端懒加载(首次请求才载入,启动 2s 即就绪);共享一把 GPU 锁串行推理;
MLX 按 model 字段多模型缓存,传不同 repo 即可并存。
不经过服务,直接命令行用 PII:
uv run python pii.py --redact "Marie au 06 12 34 56 78"
换 / 加 MLX 模型:请求里带 "model":"mlx-community/<repo>" 即可,首次自动下载到 HF 缓存;
或设 MLX_MODEL 改默认。
改默认 PII 标签:编辑 server.py 顶部的 DEFAULT_LABELS(模型支持 42 种 PII 类型,标签是自由文本)。
漏检/误报:调 threshold(默认 0.5;漏检调低到 0.3,误报调高)。
升级依赖(uv):
uv lock --upgrade # 重新解析最新版本并更新 uv.lock
uv sync # 应用到 .venv
# 或加单个依赖: uv add <包名>
清理空间:删某个模型缓存 rm -rf ~/.cache/huggingface/hub/models--mlx-community--Qwen2.5-0.5B-Instruct-4bit(下次用会重下)。
重建整个环境(.venv 损坏时):
cd ~/local-ai-service && rm -rf .venv && uv sync
127.0.0.1 仅本机可访问;设 HOST=0.0.0.0 暴露到局域网前请自行加防护,切勿直接挂公网。nohup ... & 启动的进程在关机/注销后不会自动拉起。需要开机自启/掉线重启,用 launchd(见下)。把服务交给 macOS 守护,开机自动启动、崩溃自动重启。创建
~/Library/LaunchAgents/com.local.local-ai-service.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict>
<key>Label</key><string>com.local.local-ai-service</string>
<key>ProgramArguments</key>
<array>
<string>/Users/ryantao/local-ai-service/.venv/bin/python</string>
<string>/Users/ryantao/local-ai-service/server.py</string>
</array>
<key>WorkingDirectory</key><string>/Users/ryantao/local-ai-service</string>
<key>EnvironmentVariables</key>
<dict><key>PORT</key><string>8000</string></dict>
<key>RunAtLoad</key><true/>
<key>KeepAlive</key><true/>
<key>StandardOutPath</key><string>/Users/ryantao/local-ai-service/server.log</string>
<key>StandardErrorPath</key><string>/Users/ryantao/local-ai-service/server.log</string>
</dict></plist>
launchctl load ~/Library/LaunchAgents/com.local.local-ai-service.plist # 启用
launchctl unload ~/Library/LaunchAgents/com.local.local-ai-service.plist # 停用
服务自带 OpenAI 兼容接口(GET /v1/models、POST /v1/chat/completions,支持流式),
因此可接任何 OpenAI 客户端。这里用 Open WebUI(开源、类 ChatGPT)。
PII 也能在 Open WebUI 里直接用:模型下拉里除了 LLM,还有两个 PII 伪模型
pii-redact(回脱敏文本)和pii-extract(回检测清单)——选中后把文本发过去即可。 本机 Python 3.14 装不了 pip 版 Open WebUI(要求 <3.13),所以走 Docker(Colima 运行时)。
前提:本服务必须绑 0.0.0.0,容器才能从 host.docker.internal 访问到它:
cd ~/local-ai-service
HOST=0.0.0.0 nohup uv run python server.py > server.log 2>&1 &
(launchd 自启同理:在 plist 的 EnvironmentVariables 里加 <key>HOST</key><string>0.0.0.0</string>)
启动 Docker 运行时 + Open WebUI 容器(已用环境变量预配好后端、关掉登录):
colima start # 启动 Docker 运行时(Linux VM),开机后只需跑一次
docker run -d -p 3000:8080 \
--add-host=host.docker.internal:host-gateway \
-v open-webui:/app/backend/data \
--name open-webui \
-e OPENAI_API_BASE_URL=http://host.docker.internal:8000/v1 \
-e OPENAI_API_KEY=local \
-e WEBUI_AUTH=False \
--restart unless-stopped \
ghcr.io/open-webui/open-webui:main
访问:浏览器打开 http://localhost:3000 —— 模型下拉里会出现
mlx-community/Qwen2.5-0.5B-Instruct-4bit,直接对话即可(首条消息会触发 MLX 懒加载,稍慢)。
容器运维:
docker ps # 看是否在跑
docker logs -f open-webui # 看日志
docker stop open-webui # 停
docker start open-webui # 启
docker rm -f open-webui # 删容器(数据在 named volume open-webui 里, 不丢)
colima stop # 关掉整个 Docker 运行时(省资源)
换模型给前端用:先随便发一条带新模型的请求让服务加载它,或改 MLX_MODEL 重启服务;
Open WebUI 的模型下拉来自 GET /v1/models,会自动反映已加载/默认模型。
连不上排查:① 服务是否绑了 0.0.0.0(不是 127.0.0.1);② curl http://127.0.0.1:8000/v1/models 本机是否通;
③ 容器内 docker exec open-webui curl -s http://host.docker.internal:8000/v1/models 是否通。
3 commits
Python
80.6%
Shell
19.4%