能登录进 LibreChat 界面,说明会话本身是通的,但模型下拉为空是另一条链路上的事:后端有没有把配置文件读进来、读进来的配置里密钥和端点声明是否成立、以及前端去取模型列表的那个接口到底返回了什么。这三件事可以分开验证,不必靠反复重启碰运气。建议的排查顺序是先看容器日志确认配置文件加载情况,再核对环境变量与配置文件的引用关系,然后回到浏览器网络面板看接口状态码和响应体,最后用一个只留单端点的最小配置收敛干扰。判断依据只取日志行、环境变量取值和接口返回状态,不去猜服务端内部实现。
模型列表为空,先用日志排除「配置文件没被读到」,用环境变量取值排除「密钥为空或变量名不匹配」,再用浏览器网络面板区分 401/403(鉴权或凭据问题)与 200 空数组(配置里没有可用模型)。三者指向不同修法,混在一起重启只会掩盖线索。若接口是 5xx,回到日志看堆栈,而不是继续改 yaml。变量名与引用处必须逐字一致,具体字段名以你所用版本的配置说明为准。
在容器日志里确认配置文件是否被加载
这一步的目标是先把「文件根本没挂进去」和「挂进去了但解析失败」这两类硬性原因排除掉。如果配置文件压根没被读到,后面查密钥和接口都是白费力气。
先定位容器日志,Docker 部署常用 docker logs -f <容器名>,Kubernetes 部署常用 kubectl logs -f deploy/librechat。启动阶段那条和配置有关的行是你真正要看的:通常会打印正在加载的配置路径,例如 librechat.yaml 的绝对路径,或者一条解析失败的错误。关注点只有两个——它读的是不是你挂载的那个路径,以及有没有报解析错误。
路径错误时的典型表现是找不到文件的错误(类似 ENOENT 或 no such file or directory),同时你本地确实改了配置但行为毫无变化。常见原因是宿主机的挂载路径写错、挂载点对不上,或者容器内的工作目录和你想的不是同一个。格式错误时通常会出现 YAML 解析类报错,带上行号,类似 yaml: line 12: did not find expected key,这类错误往往导致整段配置被丢弃。
# 只筛启动阶段和配置相关的行,缩小阅读范围
docker logs <容器名> 2>&1 | grep -i -E 'config|yaml|endpoint'
# 确认容器内那个路径是否真的存在、内容是否是你要的
docker exec -it <容器名> sh -c 'ls -l /app/librechat.yaml && head -n 20 /app/librechat.yaml'
如果日志里能看到配置路径、也没有解析报错,但容器内 head 出来的内容和本地不一致,那问题在挂载而不是在配置语法,应先把挂载修对再继续往下走。
核对环境变量里的密钥与端点声明
配置文件的常见写法是引用环境变量,而不是把密钥写死。这里有两种容易混淆的失败方式:变量存在但值为空,和变量名与引用处对不上。前者往往会在调用上游时拿到未授权响应,后者经常让引用处展开成一个空串或字面量,于是端点看起来声明了、实际没有可用凭据。
对照时的关键是「变量名逐字一致」,包括大小写和下划线。下面是一个骨架,键名请替换成你实际在用的名字,字段结构以你所装版本的配置说明为准。
# 环境变量侧(.env 或容器 env)
OPENAI_API_KEY=你的密钥
OPENAI_BASE_URL=https://your-endpoint.example.com/v1
# librechat.yaml 侧的引用
endpoints:
custom:
- name: "my-endpoint"
apiKey: "${OPENAI_API_KEY}"
baseURL: "${OPENAI_BASE_URL}"
models:
default:
- "your-model-name"
验证环境变量是否真的进了容器,不要只看本地的 .env。可以用 docker exec -it <容器名> env | grep -i openai 确认变量是否存在,并确认取值不为空。空值的表现通常是变量在列表里但等号后面什么都没有;名字不匹配的表现是容器里根本查不到那个变量,而配置文件里对应的引用位置仍然是未展开的状态。这两种情况都不要靠改一行 yaml 猜,先把变量名对齐。
看前端请求的接口返回是 401 还是空数组
前三步是在服务端侧找原因,这一步回到浏览器,用实际返回把「鉴权失败」和「配置为空」区分开。打开开发者工具的 Network 面板,刷新页面或重新打开一次新建对话,在过滤框里输入 models 或 endpoints,找到负责拉取模型列表的那个请求(通常是 GET,路径里带 api)。
看两处就够:状态码和响应体。如果是 401 或 403,方向偏向会话或凭据问题,例如请求没带上有效会话、或者服务端在取模型时用的凭据被上游拒绝,这时应该回到上一步核对密钥取值。如果是 200 但响应体是空数组(形如 [])或对象里 models 为空,方向偏向配置本身没有可用模型,重点回去检查 yaml 里的 endpoints 与 models 是否被正确读到。如果是 5xx,说明服务端在读取配置或请求上游时出错了,回到容器日志看对应时间点的堆栈更有效。
# 在 Network 面板里可以对照的三种典型结果
# 200 + [] -> 配置读到了但没有可用模型条目
# 401 / 403 -> 鉴权或凭据问题
# 500 及以上 -> 服务端读取配置或调用上游时报错,需回日志
用只留一个端点的最小配置复现并排除干扰
多个自定义端点同时存在时,一个端点配置写错可能影响你对整体结果的判断,容易让人误以为「全都失效」。把所有端点收敛到一个,能最快地判断链路是不是通的。改之前建议先备份原配置。
version: 1.0
endpoints:
custom:
- name: "only-one"
apiKey: "${OPENAI_API_KEY}"
baseURL: "https://your-endpoint.example.com/v1"
models:
default:
- "your-model-name"
fetch: false
这里把模型名显式写进 default,避免依赖服务端向上游动态拉取模型列表,从而把「拉取失败」这个变量先摘出去;字段名与是否支持该开关以你的版本为准。保存后按你当前的部署方式重新加载配置,然后新开一个对话页面。
验证标准很直接:模型下拉里应当出现 your-model-name 这一个条目。如果它出现了,说明配置文件加载、变量引用、接口返回这条链路是通的,之前的空列表来自其他端点的配置问题,可以逐个加回并每加一个就复看一次。如果仍然为空,就回到前面三步:日志里有没有解析错误、容器内变量取值是否为空、Network 面板里那个请求是 401 还是 200 空数组,用这三条把范围继续缩小。改动前保留一份可回退的配置,是本轮排查里成本最低的一步。