
1. one-api 分詞器緩存為什么總在重復(fù)下載如果你用 Docker Compose 部署過 one-api大概率遇到過這個場景容器起來了日志里卻在反復(fù)請求openaipublic.blob.core.windows.net網(wǎng)絡(luò)一抖就卡住接口調(diào)用報(bào)tiktoken相關(guān)錯誤甚至整個網(wǎng)關(guān)啟動超時。這不是 one-api 本身的 bug而是它依賴的 tiktoken 分詞器在首次使用時需要下載編碼文件而默認(rèn)緩存目錄在容器里是臨時的容器一重建緩存就沒了于是又得重新下載一遍。one-api 是一個把多家大模型 API 統(tǒng)一成 OpenAI 兼容格式的自建網(wǎng)關(guān)適合想在自己服務(wù)器上聚合多個模型渠道、給團(tuán)隊(duì)或應(yīng)用提供統(tǒng)一入口的開發(fā)者。它內(nèi)部用 tiktoken 做 token 計(jì)數(shù)用來做額度統(tǒng)計(jì)和請求預(yù)估。tiktoken 在初始化某個編碼比如cl100k_base時會先查本地緩存目錄沒有就去官方地址拉取拉完存到TIKTOKEN_CACHE_DIR指向的位置。問題就在于這個環(huán)境變量如果不顯式設(shè)置緩存路徑可能落在容器可寫層docker-compose down再up之后就丟了。我試過最直接的解法就是把緩存目錄掛到宿主機(jī)上讓分詞器文件持久化。這樣第一次下載完之后后續(xù)無論怎么重建容器都直接讀本地文件不再依賴外網(wǎng)。下面按「問題定位 → 前置準(zhǔn)備 → 可復(fù)制配置 → 驗(yàn)證生效 → 排錯」的順序把整套落地過程寫清楚你可以直接照著改自己的docker-compose.yml。2. 前置準(zhǔn)備TaoToken 渠道與 API Keyone-api 本身只是網(wǎng)關(guān)要真正跑通一次對話驗(yàn)證分詞器是否生效你還需要一個可用的上游模型渠道。這里我用 TaoToken 作為上游接入它的接口是 OpenAI 兼容的填進(jìn) one-api 的渠道配置里很順。先去控制臺拿一個 API Key地址是 https://taotoken.net/api-keys 登錄后新建一個 Key復(fù)制出來備用。這個 Key 后面會填到 one-api 的「渠道」里作為調(diào)用上游模型的憑證。如果你還沒注冊從 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 進(jìn)官網(wǎng)注冊即可。拿到 Key 之后one-api 的渠道配置大致是這樣渠道類型選 OpenAIBase URL 填https://taotoken.net/api模型可以填gpt-4o-mini這類常用名密鑰就是剛才復(fù)制的 Key。保存后點(diǎn)「測試」能返回成功就說明上游通了。這一步通了后面驗(yàn)證分詞器緩存才有意義否則你分不清是網(wǎng)絡(luò)問題還是緩存問題。注意Base URL 用https://taotoken.net/api不要帶多余的路徑后綴one-api 會自動拼接/v1/chat/completions。3. 可復(fù)制的 docker-compose 配置與 TIKTOKEN_CACHE_DIR 骨架核心思路一句話把 one-api 容器里的/data掛到宿主機(jī)目錄再把TIKTOKEN_CACHE_DIR指向/data/cache讓分詞器文件落在掛載卷里。下面是一份可以直接改的docker-compose.yml片段我保留了 one-api 和它常用的 mysql、redis 依賴。version: 3.8 services: one-api: image: justsong/one-api:latest container_name: one-api restart: always ports: - 3000:3000 environment: - TZAsia/Shanghai - TIKTOKEN_CACHE_DIR/data/cache volumes: - ./oneapi:/data depends_on: - mysql - redis mysql: image: mysql:8.0 container_name: one-api-mysql restart: always environment: - MYSQL_ROOT_PASSWORDoneapi123 - MYSQL_DATABASEoneapi volumes: - ./mysql:/var/lib/mysql redis: image: redis:7-alpine container_name: one-api-redis restart: always volumes: - ./redis:/data關(guān)鍵點(diǎn)有三個。第一TIKTOKEN_CACHE_DIR/data/cache寫在environment里容器啟動時就會帶上這個變量。第二volumes把宿主機(jī)的./oneapi掛到容器的/data所以/data/cache實(shí)際就是宿主機(jī)的./oneapi/cache。第三目錄要提前建好否則容器可能因?yàn)闄?quán)限或路徑不存在而寫入失敗。在宿主機(jī)上執(zhí)行mkdir -p ./oneapi/cache chmod 755 ./oneapi/cache然后啟動docker-compose up -d啟動后進(jìn)容器確認(rèn)變量生效docker exec -it one-api env | grep TIKTOKEN正常應(yīng)該輸出TIKTOKEN_CACHE_DIR/data/cache。如果沒輸出說明環(huán)境變量沒寫進(jìn) compose 或者容器沒重建先docker-compose down再up -d。4. 手動預(yù)置分詞器文件徹底擺脫外網(wǎng)依賴即使配了緩存目錄第一次啟動時 one-api 還是要去外網(wǎng)拉一次cl100k_base.tiktoken。如果你的服務(wù)器出網(wǎng)不穩(wěn)定這一步照樣會卡。更穩(wěn)的做法是手動把文件放進(jìn)去讓容器啟動時直接命中緩存。tiktoken 的緩存文件名不是原始文件名而是對下載 URL 做 SHA1 得到的哈希值。cl100k_base對應(yīng)的兩個常見哈希文件名是9b5ad71b2ce5302211f9c61530b329a4922fc6a4fb374d419588a4632f3f557e76b4b70aebbca790你可以先下載原始文件cd ./oneapi/cache curl -O https://openaipublic.blob.core.windows.net/encodings/cl100k_base.tiktoken然后復(fù)制成兩個哈希名cp cl100k_base.tiktoken 9b5ad71b2ce5302211f9c61530b329a4922fc6a4 cp cl100k_base.tiktoken fb374d419588a4632f3f557e76b4b70aebbca790放好之后目錄結(jié)構(gòu)應(yīng)該是./oneapi/ ├── cache/ │ ├── 9b5ad71b2ce5302211f9c61530b329a4922fc6a4 │ ├── fb374d419588a4632f3f557e76b4b70aebbca790 │ └── cl100k_base.tiktoken └── one-api.db重啟容器docker-compose down docker-compose up -d這樣容器啟動時tiktoken 查緩存直接命中不會再發(fā)起外網(wǎng)請求。如果你用的是其他編碼比如o200k_base哈希名不同需要按同樣方式處理但cl100k_base覆蓋了 GPT-3.5/4 系列日常夠用。提示哈希文件名必須完全一致多一個字符少一個字符都會導(dǎo)致緩存未命中tiktoken 會重新去下載。5. 驗(yàn)證分詞器緩存是否真正生效配置完不能只看「沒報(bào)錯」要確認(rèn)它確實(shí)讀了本地緩存。有三種驗(yàn)證方式從簡到繁。第一種看容器日志有沒有下載請求。啟動后執(zhí)行docker logs -f one-api如果日志里沒有出現(xiàn)openaipublic.blob.core.windows.net或Downloading字樣基本說明緩存命中了。反之如果還在刷下載日志說明路徑或文件名不對。第二種進(jìn)容器直接跑一段 Python 驗(yàn)證 tiktoken 讀取路徑。one-api 鏡像里帶了 Python 環(huán)境可以這樣測docker exec -it one-api python3 -c import tiktoken, os print(cache dir:, os.environ.get(TIKTOKEN_CACHE_DIR)) enc tiktoken.get_encoding(cl100k_base) print(tokens:, enc.encode(hello one-api)) 如果輸出cache dir: /data/cache和一段 token 列表且執(zhí)行很快沒有卡頓等待下載說明緩存生效。如果卡了幾秒才出結(jié)果多半還是在聯(lián)網(wǎng)下載。第三種最貼近真實(shí)業(yè)務(wù)在 one-api 后臺建好渠道后發(fā)一次對話請求看額度統(tǒng)計(jì)里的 token 數(shù)是否正常累加。token 計(jì)數(shù)正常說明分詞器工作正常。你可以用模型對話頁面直接測https://taotoken.net/api-keys 拿到的 Key 配好渠道后在 one-api 的「對話」里發(fā)一條消息觀察返回和用量。curl http://localhost:3000/v1/chat/completions \ -H Authorization: Bearer sk-你的one-api令牌 \ -H Content-Type: application/json \ -d { model: gpt-4o-mini, messages: [{role: user, content: 你好}] }返回正常且后臺用量有變化整條鏈路就通了。6. 本篇常見錯誤排查報(bào)錯一PermissionError: [Errno 13] Permission denied: /data/cache/xxx宿主機(jī)./oneapi/cache權(quán)限不夠容器內(nèi)進(jìn)程寫不進(jìn)去。執(zhí)行chmod -R 777 ./oneapi/cache臨時放開或者確認(rèn)容器運(yùn)行用戶對掛載目錄有寫權(quán)限。生產(chǎn)環(huán)境建議用chown指定 uid而不是直接 777。報(bào)錯二日志一直刷下載緩存目錄里沒文件先確認(rèn)TIKTOKEN_CACHE_DIR是否真的進(jìn)了容器用第 3 節(jié)的env | grep檢查。再確認(rèn)掛載路徑對不對docker exec -it one-api ls /data/cache看目錄是否存在。如果目錄不存在說明宿主機(jī)./oneapi/cache沒建或者掛載點(diǎn)寫錯了。報(bào)錯三文件名對了但還是重新下載哈希名必須和 tiktoken 內(nèi)部計(jì)算的完全一致。不同版本的 tiktoken 對同一編碼的 URL 可能不同哈希也會變。最穩(wěn)的辦法是讓容器先聯(lián)網(wǎng)下載一次然后去/data/cache里看實(shí)際生成的文件名把它備份下來下次直接復(fù)用。這樣比死記哈希名可靠。報(bào)錯四docker-compose up卡在拉鏡像這跟分詞器無關(guān)是鏡像源問題??梢苑珠_拉docker pull justsong/one-api:latest docker pull mysql:8.0 docker pull redis:7-alpine一個個拉失敗概率低拉完再docker-compose up -d。報(bào)錯五渠道測試通過但對話報(bào) token 相關(guān)錯誤多半是分詞器編碼和模型不匹配。one-api 會根據(jù)模型名選編碼如果你填了非常規(guī)模型名可能選到未緩存的編碼。此時要么補(bǔ)對應(yīng)編碼的緩存文件要么換成cl100k_base覆蓋的模型名測試。7. 長期編碼與 Agent 場景的接入建議如果你不只是拿 one-api 做臨時網(wǎng)關(guān)而是要長期跑編碼助手、Agent 工作流這類高頻調(diào)用場景建議把渠道配置和額度策略一起規(guī)劃好。TaoToken 的 Coding Plan 適合這種持續(xù)調(diào)用的需求地址是 https://taotoken.net/coding-plan 按套餐走比單次計(jì)費(fèi)更可控。接入文檔在 https://taotoken.net/doc 里面有 one-api 渠道配置的詳細(xì)字段說明遇到 Base URL 或模型名不確定的時候可以直接查?;氐椒衷~器這件事核心就一句把TIKTOKEN_CACHE_DIR指到掛載卷再手動預(yù)置哈希文件之后無論怎么重建容器都不再依賴外網(wǎng)。這套配置我放在自己的docker-compose.yml里跑了很久docker-compose down up -d循環(huán)多次日志里再沒出現(xiàn)過下載請求。你可以先把第 3 節(jié)的片段抄進(jìn)去跑通第 5 節(jié)的驗(yàn)證再按第 4 節(jié)補(bǔ)文件順序反了容易在排錯時分不清是網(wǎng)絡(luò)還是緩存的問題。