hf本地下载解决方案

结论:宿主机直接跑 data/hf-dl.sh(bash + aria2c),不经过 docker、不需要 python。模型文件放在 /s500data/docker/llama/data/models/<作者>/<仓库>/<文件>,与 ini 里 model 路径机械对应。

一、为什么是这个方案(走过的弯路)

先说被否掉的路线,避免以后重蹈:

尝试结果根因
hf.yml + container python huggingface_hub失败单文件 >50GB 直接被拒
装 hf_xet 再试失败hf-mirror 只做 302 转发,无 xet 后端
HF_HUB_DISABLE_XET=1失败同上,仍是普通 HTTP 路径
自己写 Range 分块下载可用但不采纳重复实现 aria2c 已有能力,续传/校验都得自己扛
官方站 huggingface.co + hf_xet能下但慢约 160KB/s,且中断不续传(重试另开 .incomplete)
宿主机 aria2c + hf-mirror采纳Range 分块天然支持任意大小,自带续传

关键事实:huggingface_hub 源码 file_download.py 里 MAX_HTTP_DOWNLOAD_SIZE = 50000000000(50GB 硬阈值),超过就抛 "The file is too large"。而上游 issue #3868 至今 open,即 >50GB 走 HTTP 这条路在镜像站上无解。hf_transfer 后端在该版本已被移除,只剩 http_get(拒绝大文件)和 xet_get 两条路。

aria2c 用 HTTP Range 请求,不受这个阈值约束 —— 实测 58.5GB 文件正常下载。

二、脚本用法

cd /s500data/docker/llama

# 列出所有可用的 env 文件及其仓库/文件
./data/hf-dl.sh -l

# 按 env 文件下载(不带路径,.env 后缀可省)
./data/hf-dl.sh mradermacher_Qwen3.8-4B-Agentic-distill.Q4_K_M.gguf

# 直接给完整 URL,下单个文件
./data/hf-dl.sh https://hf-mirror.com/mradermacher/Qwen3.8-4B-Agentic-distill-GGUF/resolve/main/Qwen3.8-4B-Agentic-distill.Q4_K_M.gguf

# 干跑:只打印将下什么,不实际下载
./data/hf-dl.sh -n <同上参数>

# 中断后续传:重跑同一条命令即可

三、脚本设计

项说明
文件/s500data/docker/llama/data/hf-dl.sh(283 行,纯 bash)
依赖aria2c、curl(宿主已装)。不需要 jq、不需要 python、不需要 docker
路径基准脚本目录的上一级即项目根,不写死绝对路径
下载引擎aria2c --continue=true,带 .aria2 控制文件
默认线程-x4 -s4 -k1M,并发 -j2
默认镜像HF_ENDPOINT=https://hf-mirror.com
默认代理HF_PROXY=http://127.0.0.1:7890
断连重试--max-tries=0(无限重试)--retry-wait=10 --timeout=60 --connect-timeout=30
退出码映射0=完成;13=文件已存在(跳过,算成功);7=收 TERM/INT 仍有未完成下载 → 报「中断」并返回非 0
幂等目标已存在且大小一致则跳过;大小不符则删掉重下

可覆盖的环境变量:HF_ENDPOINT(换镜像)、HF_PROXY(换代理,设空串则不走代理)、HF_THREADS、HF_JOBS。

两种参数模式的分支逻辑

参数以 http(s):// 开头
  └─ URL 模式
       ├─ 路径含 resolve/ 或 blob/  → 下该单文件(落点 <作者>/<仓库>/<文件名>)
       └─ 只给到仓库             → 走 tree API 列全部文件逐个下

否则
  └─ env 模式
       └─ 读 data/models/hf_down_env/<参数>.env
            取 HF_REPO / HF_FILES / HF_TOKEN
            逐个拼 Hf_ENDPOINT/<repo>/resolve/main/<file> 下载

四、env 文件格式

位置:data/models/hf_down_env/<名>.env,一模型一文件。

HF_REPO=mradermacher/Qwen3.8-4B-Agentic-distill-GGUF
HF_FILES=Qwen3.8-4B-Agentic-distill.Q4_K_M.gguf
HF_TOKEN=

要点:

  • HF_FILES 空格分隔,可列多个(35B 列 3 个、DeepSeek 列 5 个分片)
  • HF_TOKEN 每模型各写一份,公开仓留空。不再集中放 .env
  • 文件名建议 <作者>_<主文件名>.env,便于 -l 识别

五、当前 5 个模型

env 文件仓库文件数状态
mradermacher_Qwen3.8-4B-Agentic-distill.Q4_K_M.ggufmradermacher/Qwen3.8-4B-Agentic-distill-GGUF1已下好
mradermacher_qwen3.8-9b-hermes-fc-tooluse.Q4_K_M.ggufmradermacher/qwen3.8-9b-hermes-fc-tooluse-GGUF1已下好
IsValorum_Qwen3.8-35B-A3B-Distill.APEX-I-MiniPlus-V2.1-Abliterated.ggufIsValorum/Qwen3.8-35B-A3B-Distill-MTP-APEX-I-MiniPlus-V2.1-Abliterated-GGUF3已下好
agentionai_Qwen3.8-Flash-Next-Gyro-S-TQ1_0.ggufagentionai/Qwen3.8-Flash-Next-Gyro-GGUF1下载中(54.5 GiB)
ISTA-DASLab_DeepSeek-V4.1-Flash-1017-GSQ-RCO-IQ2_XXS.ggufISTA-DASLab/DeepSeek-V4.1-Flash-1017-GSQ-RCO-GGUF5待下(231.7 GiB)

六、踩坑记录

现象原因处理
aria2c 报「权限不够」errNum=13目录被早先的 docker 容器建成 root:rootchown -R cat:users,并清掉容器遗留的 .cache
URL 模式把整个仓库全下了判断写成 *"/resolve/"*,匹配不到以 resolve/ 开头的 tail改为匹配 resolve/* 与 */resolve/* 两种
4 线程被 429 限流hf-mirror 对大文件并发限流降并发;脚本默认 -x4 -j2,实测约 630KiB/s 稳定
日志看着不动,像卡住tee 有块缓冲,落盘滞后判断进度看磁盘写入量 du,不看日志时间戳
du 显示 41G 但才下一个多小时aria2 预分配的稀疏文件,表观大小≠实际写入用 du -sh 看实际占用(非 --apparent-size)
下到 2.0GiB 时 4 条连接同时「Got EOF from the server」hf-mirror 主动断连(非本地网络问题),脚本原先失败即退出加 aria2 原生 --max-tries=0 --retry-wait=10;重跑同命令从断点续,实测从 2.0GiB 接着下未重来
被 kill 后脚本却打印「已有相同文件,跳过」并返回成功把 aria2 退出码 7 当成「文件已存在」。实际 7 = 收到 TERM/INT 时仍有未完成下载(中断);13 才是文件已存在改为 13→跳过(返回 0)、7→报「中断」并返回非 0。已用假 aria2c 造 7/13 两个退出码实测两个分支

七、速度预期

实测 500–630 KiB/s(hf-mirror 经代理;断连重试期间会短时下降)。据此:

  • Gyro 54.5 GiB ≈ 25–30 小时
  • DeepSeek 231.7 GiB ≈ 4–5 天

想提速可调 HF_THREADS,但有触发 429 的风险,需实测。

八、已废弃的东西

以下文件/镜像均已删除,不要再用:

  • hf.yml — 容器方案,>50GB 走不通
  • Dockerfile.hfd、镜像 llama-hfd:latest
  • data/python/hf_download.py — 已由 hf-dl.sh 取代
  • data/python/hfd.sh — 未采用(脚本自实现,不依赖外部 sh)
  • .env 里的 HF_ENV 段 — 已清理,改为指向 hf-dl.sh 的用法注释