1. 内网部署 one-api 时 tiktoken-go 报错的真实场景如果你正在内网、隔离网或者没有外网出口的服务器上部署 one-api大概率会遇到这个报错failed to get gpt-3.5-turbo token encoder。这个报错不是 one-api 本身的问题而是它依赖的 tiktoken-go 库在初始化编码器时需要从公网下载cl100k_base.tiktoken这类编码文件内网环境下载失败于是整个 token 计数功能直接挂掉。先说清楚 tiktoken-go 是什么。它是 OpenAI 官方 tiktoken 的 Go 语言移植版作用是把你输入的文本按模型对应的编码规则切成 token然后算出 token 数量。one-api 用它来做额度预扣、用量统计、上下文长度校验。gpt-3.5-turbo、gpt-4、text-embedding-ada-002 这些模型用的都是cl100k_base编码而gpt-4o、o1系列用的是o200k_base。这些编码文件本质上是 BPE 词表和合并规则的二进制映射体积不大但必须存在本地才能工作。问题出在 tiktoken-go 的默认行为上。它内部有一个BpeLoader默认实现是NewDefaultBpeLoader()会去https://openaipublic.blob.core.windows.net/encodings/这个地址拉文件。内网服务器没有外网路由DNS 解析不了TCP 也连不上于是返回错误one-api 启动或首次调用时就抛出failed to get gpt-3.5-turbo token encoder。这个场景适合谁适合在企业内网、政务隔离网、实验室封闭环境里跑 one-api 做统一 API 网关的运维和开发。你不需要改 one-api 的核心逻辑只需要把 tiktoken-go 的缓存目录和下载地址改掉让它从本地读文件。下面我会给出可复制的目录结构、Go 源码修改点、编译命令以及一次本地 token 计数请求来验证编码器加载成功。需要提前说明的是本文的验证环节会用到 TaoToken 的 API 端点来做一次真实的 token 计数请求这样你能确认编码器不只是编译通过而是运行时真的能算出 token 数。TaoToken 在这里扮演的是兼容 OpenAI 协议的接入层Base URL 和 Key 的配置方式我会在第三节写清楚。2. TaoToken 前置准备与 tiktoken-go 缓存目录定位在动手改代码之前先把两件事准备好一是找到 tiktoken-go 在 Go module 缓存里的真实路径二是准备好 TaoToken 的接入信息用于后续验证。2.1 定位 tiktoken-go 的 module 目录Go 项目依赖下载后会放在GOPATH/pkg/mod下面。默认GOPATH是~/go所以路径通常是cd ~/go/pkg/mod/github.com/pkoukk/ ls你会看到类似tiktoken-gov0.1.5这样的目录。注意包作者是pkoukk不是pkouukk网上有些文章拼错了照着错路径 cd 会找不到目录。进去之后重点看两个文件cd tiktoken-gov0.1.5 ls # 你会看到 encoding.go bpe_loader.go tiktoken.go 等encoding.go里定义了各个模型对应的编码名称映射bpe_loader.go里定义了默认的下载器。我们要改的就是这两个地方涉及 URL 的部分。2.2 准备编码文件需要下载的编码文件有三个对应不同的模型族文件名对应编码适用模型cl100k_base.tiktokencl100k_basegpt-3.5-turbo、gpt-4、text-embedding-ada-002o200k_base.tiktokeno200k_basegpt-4o、gpt-4o-mini、o1 系列p50k_base.tiktokenp50k_base老版 text-davinci 系列你可以在任意一台能访问外网的机器上用浏览器或 curl 把这三个文件下载下来。文件名必须保持原样因为 tiktoken-go 是按编码名去拼文件名的。下载完成后通过内网文件传输工具上传到服务器建议统一放在一个固定目录比如mkdir -p /data/tiktoken-cache # 把三个 .tiktoken 文件放进去 ls /data/tiktoken-cache # cl100k_base.tiktoken o200k_base.tiktoken p50k_base.tiktoken2.3 TaoToken 接入信息准备验证阶段我们要发一次真实的 token 计数请求。TaoToken 兼容 OpenAI 的接口协议所以你需要准备三样东西Base URL、API Key、Model ID。Base URL 用https://taotoken.net/api注意这里不加任何查询参数。API Key 在控制台的 API Keys 页面创建路径是https://taotoken.net/console/api-keys。Model ID 填gpt-3.5-turbo即可因为我们要验证的正是这个模型的编码器。如果你还没创建 Key可以先去控制台生成一个。创建时给它起个容易识别的名字比如tiktoken-offline-test权限按最小化原则只勾选需要的模型范围。生成后立刻复制保存页面刷新后就看不到完整 Key 了。提示TaoToken 的 API 端点https://taotoken.net/api是标准 OpenAI 兼容路径one-api 里配置渠道时 Base URL 填这个不要带/v1后缀one-api 会自己拼接。到这里前置准备就完成了。接下来进入核心的代码修改环节。3. 可复制配置修改 encoding.go 与 bpe_loader.go 指向本地缓存这一节是全文最关键的部分我会给出完整的修改步骤和可复制的代码片段。改动的核心思路是把 tiktoken-go 默认从公网下载的行为改成从本地绝对路径读取。3.1 修改 encoding.go 中的下载地址进入 module 目录cd ~/go/pkg/mod/github.com/pkoukk/tiktoken-gov0.1.5 vim encoding.go在文件里搜索openaipublic.blob.core.windows.net你会找到类似这样的常量定义const ( cl100k_base https://openaipublic.blob.core.windows.net/encodings/cl100k_base.tiktoken o200k_base https://openaipublic.blob.core.windows.net/encodings/o200k_base.tiktoken p50k_base https://openaipublic.blob.core.windows.net/encodings/p50k_base.tiktoken )把这三个 URL 全部替换成本地绝对路径const ( cl100k_base /data/tiktoken-cache/cl100k_base.tiktoken o200k_base /data/tiktoken-cache/o200k_base.tiktoken p50k_base /data/tiktoken-cache/p50k_base.tiktoken )注意路径要用绝对路径不要用~或相对路径因为 one-api 运行时的工作目录不一定是 module 目录。3.2 修改 bpe_loader.go 的加载逻辑只改 URL 还不够因为默认的BpeLoader会判断字符串是不是以http开头如果是就走 HTTP 下载如果不是才走本地文件读取。我们改成绝对路径后它应该会走本地读取分支。但为了保险检查一下bpe_loader.govim bpe_loader.go找到LoadTiktokenBpe方法确认逻辑大致是func (l *defaultBpeLoader) LoadTiktokenBpe(tiktokenBpeFile string) (map[string]int, error) { if strings.HasPrefix(tiktokenBpeFile, http) { // HTTP 下载逻辑 } // 本地文件读取逻辑 file, err : os.Open(tiktokenBpeFile) // ... }如果你的版本里没有这个判断而是无条件走 HTTP那就手动加上本地读取分支。改完后保存退出。3.3 处理 go.sum 校验问题直接改 module 缓存里的文件go build时可能会因为哈希校验不通过而报错。有两个办法第一个办法是关闭校验在项目根目录执行export GOFLAGS-modmod export GONOSUMCHECK1 export GONOSUMDB* export GOSUMDBoff第二个办法更彻底用replace指令把 tiktoken-go 指向本地副本。先把整个 module 目录复制出来cp -r ~/go/pkg/mod/github.com/pkoukk/tiktoken-gov0.1.5 /data/tiktoken-go-local chmod -R uw /data/tiktoken-go-local然后在 one-api 项目的go.mod末尾加上replace github.com/pkoukk/tiktoken-go /data/tiktoken-go-local这样编译时就会用你改过的本地版本不再去校验远程哈希。3.4 重新编译 one-api回到 one-api 项目根目录cd /path/to/one-api go mod tidy go build -o one-api如果编译过程中报missing go.sum entry执行go mod download再编译。编译成功后你会得到一个新的one-api二进制文件。把它替换掉原来的可执行文件重启服务。注意如果你用的是 Docker 部署需要把/data/tiktoken-cache目录挂载进容器并且把改过的 tiktoken-go 一起打进镜像否则容器里读不到本地文件。到这里配置就完成了。下一节我们发一次真实请求来验证。4. 验证请求用本地 token 计数确认编码器加载成功编译通过不代表运行时编码器能正常加载必须发一次真实请求验证。这里我用 TaoToken 的 API 端点来做 token 计数因为 one-api 的 token 计数逻辑和 OpenAI 的/v1/chat/completions返回的 usage 字段是一致的。4.1 启动 one-api 并配置渠道先启动编译好的 one-api./one-api --port 3000 --log-dir ./logs浏览器打开http://你的服务器IP:3000默认账号root密码123456首次登录会要求改密码。登录后进入「渠道」页面新建一个渠道类型OpenAI名称taotoken-testBase URLhttps://taotoken.net/api密钥你从https://taotoken.net/console/api-keys创建的 Key模型勾选gpt-3.5-turbo保存后在「令牌」页面创建一个访问令牌用于调用 one-api 自己的接口。4.2 发送 token 计数请求用 curl 发一次 chat completions 请求观察返回的 usage 字段curl -X POST http://localhost:3000/v1/chat/completions \ -H Authorization: Bearer 你的one-api令牌 \ -H Content-Type: application/json \ -d { model: gpt-3.5-turbo, messages: [{role: user, content: Hello, count my tokens please.}], max_tokens: 10 }如果编码器加载成功你会看到类似这样的返回{ id: chatcmpl-xxx, object: chat.completion, created: 1700000000, model: gpt-3.5-turbo, choices: [{index: 0, message: {role: assistant, content: Hello}, finish_reason: stop}], usage: { prompt_tokens: 8, completion_tokens: 2, total_tokens: 10 } }重点看usage.prompt_tokens有没有正常数值。如果编码器没加载成功这里会是 0或者请求直接报错failed to get gpt-3.5-turbo token encoder。4.3 用模型对话页面交叉验证除了 curl你也可以直接在 TaoToken 的模型对话页面发一条消息观察返回的 token 统计。路径是https://taotoken.net/models。在页面里选gpt-3.5-turbo输入一段文本发送后看响应里的 usage 信息。如果显示正常的 token 数说明整条链路是通的。这一步的意义在于one-api 的 token 计数和上游返回的 usage 应该基本一致。如果 one-api 本地算出来是 8上游返回也是 8说明 tiktoken-go 的本地编码器工作正常。4.4 检查日志确认无下载行为最后看一眼 one-api 的日志tail -f ./logs/one-api.log搜索openaipublic或blob.core.windows.net如果没有任何下载相关的日志说明所有编码文件都是从本地读的。如果还有下载尝试说明某处 URL 没改干净回去检查encoding.go和bpe_loader.go。5. 本篇常见错排查401、local proxy failed、reading choices 等报错对照改代码的过程中会遇到各种报错这一节我把常见的几个列出来对照着排查。5.1 401 Unauthorized这个报错通常不是 tiktoken 的问题而是 TaoToken 的 Key 配置错了。检查三点Key 是否复制完整有没有多余空格、Base URL 是否填成了https://taotoken.net/api不要带/v1、渠道里选的模型是否包含gpt-3.5-turbo。如果 Key 是在https://taotoken.net/console/api-keys创建的确认它没有被删除或禁用。5.2 local proxy failed 或 connection refused这个报错说明 one-api 尝试连接上游失败。如果你在内网确认服务器能访问taotoken.net。如果完全隔离那验证环节需要换一台能出网的机器或者用内网自建的 mock 服务。注意这里不要配置任何网络代理工具直接检查路由和 DNS 即可。5.3 failed to get gpt-3.5-turbo token encoder 仍然出现如果改完代码重新编译后还是报这个错按顺序检查第一确认encoding.go里的三个 URL 都改成了绝对路径没有遗漏。第二确认/data/tiktoken-cache/下的文件名和代码里写的一致大小写敏感。第三确认 one-api 进程有权限读取这些文件用ls -l看权限必要时chmod 644。第四确认go.mod里的replace生效了执行go list -m github.com/pkoukk/tiktoken-go看指向的路径。5.4 error reading choices 或 invalid response这个报错说明上游返回的 JSON 结构不对。检查 TaoToken 的 Base URL 是否写成了https://taotoken.net/api/v1多写/v1会导致路径拼接错误。正确的写法是https://taotoken.net/apione-api 会自动补/v1/chat/completions。5.5 OAuth 相关报错如果你用的是 Claude Code 或者 Codex 这类需要 OAuth 的工具报错可能和 tiktoken 无关。这类工具的认证走的是另一套流程需要单独配置。tiktoken-go 只负责 token 计数不参与认证。排查时先把认证和计数分开看。5.6 编译报错 missing go.sum entry这是 Go module 校验问题。执行go clean -modcache go mod download go build -o one-api如果还不行确认GOSUMDBoff和GOFLAGS-modmod都设置了。6. 长期编码与 Agent 场景的接入建议把 tiktoken-go 缓存改到本地只是第一步。如果你打算在内网长期跑 one-api 做统一网关还有几件事值得做。第一把编码文件纳入配置管理。/data/tiktoken-cache/下的三个文件不要手动散落写进你的部署脚本或 Ansible playbook每次新机器上线自动拷贝。文件不大加起来几百 KB但缺一个就会导致对应模型不可用。第二如果你用 Docker把 tiktoken-go 的本地副本和编码文件一起打进镜像。Dockerfile 里加两行COPY --frombuilder /data/tiktoken-go-local /data/tiktoken-go-local COPY tiktoken-cache /data/tiktoken-cache然后在go.mod里用replace指向镜像内的路径。第三如果你要跑 Coding Plan 或者 Agent 类应用token 计数的准确性直接影响成本控制。建议在 one-api 里开启用量日志定期对比本地计数和上游返回的 usage偏差超过 5% 就检查编码文件版本是否和模型匹配。TaoToken 的 Coding Plan 页面在https://taotoken.net/coding-plan适合需要长期稳定调用的场景。第四Claude Code 这类工具接入时Base URL 填https://taotoken.net/apiKey 用控制台创建的Model ID 按工具要求填。如果遇到 OAuth 报错先确认工具版本再检查网络出口。tiktoken 的问题和 OAuth 是两回事不要混在一起排查。最后说一个我踩过的坑改完 module 缓存后如果之后执行了go clean -modcache所有修改都会被清掉需要重新改一遍。所以强烈建议用replace指向本地副本的方式而不是直接改缓存目录。这样即使清了缓存本地副本还在重新go mod tidy就能恢复。整套流程走下来核心就是三件事编码文件放本地、URL 改绝对路径、用 replace 固定版本。做完之后内网 one-api 的 token 计数就能正常工作了。