在常见框架里配置中转 API:从 SDK 到 Dify
接入中转 API 的核心只有三个值:接口地址、密钥、模型名。难的是每个框架给这三个值起的名字不一样,有的叫 base_url,有的叫 api_base,Dify 里干脆是一个表单。这篇文章把它们对照着列出来,每段代码都能直接运行,最后附一份按顺序排查的清单。
要点
- 统一把地址和密钥放进环境变量 API_BASE 与 API_KEY,代码里不出现明文。
- 接口地址要带 /v1 后缀,模型名固定写 uncensored。
- LangChain 用 base_url,LlamaIndex 的 OpenAILike 用 api_base,名字不同含义相同。
- Dify 里选“OpenAI-API-compatible”供应商,手工填写模型名、地址和上下文长度即可。
先备齐三个值
不管用哪个框架,先确认手里有这三样东西,后面的配置都是在填空。
- 接口地址:
https://api.llmzhongzhuan.com/v1。注意末尾的/v1要保留,但不要再加/chat/completions,SDK 会自己拼接。 - 密钥:在 获取密钥页 用邮箱和密码注册后立即显示,每个账号一把,可以重置,重置后旧的立即作废。
- 模型名:只有一个,
uncensored。你可以用GET /v1/models自己确认。
另外记住几个边界:上下文总长 100,000 token,单次 max_tokens 默认 2048、最大 16,000,每个密钥每分钟 300 次请求。这些数字在后面的参数设置里会反复用到。
环境变量的写法
把密钥写死在代码里是最常见的事故源头。推荐的做法是只读环境变量,部署时由容器或密钥管理系统注入。下面用 API_KEY 和 API_BASE 两个变量,各个框架的示例都沿用这两个名字。
# Linux / macOS:写进 ~/.zshrc 或 .env 加载脚本
export API_KEY="替换为你的密钥"
export API_BASE="https://api.llmzhongzhuan.com/v1"
# Windows PowerShell(仅当前会话)
$env:API_KEY = "替换为你的密钥"
$env:API_BASE = "https://api.llmzhongzhuan.com/v1"
如果你用 .env 文件,别忘了把它加进 .gitignore。另外,官方 Python SDK 默认会读取 OPENAI_API_KEY 和 OPENAI_BASE_URL,你既可以沿用这两个变量名,也可以像下面那样在构造函数里显式传参。显式传参的好处是不会被机器上残留的旧变量干扰,排错时少一个变量。
OpenAI Python SDK 与 Node SDK
Python 需要 v1 以上的 openai 包,Node 需要 v4 以上。两者的差别只有语法,构造客户端时传入地址和密钥即可。先看 Python 的非流式调用,同时打印 usage,方便核对扣费:
import os
from openai import OpenAI
client = OpenAI(
base_url=os.environ.get("API_BASE", "https://api.llmzhongzhuan.com/v1"),
api_key=os.environ["API_KEY"],
)
resp = client.chat.completions.create(
model="uncensored",
messages=[{"role": "user", "content": "写一条 20 字以内的发布公告标题。"}],
max_tokens=100,
)
print(resp.choices[0].message.content)
print(resp.usage)
Node 示例改用流式输出,这是前端打字机效果最常用的写法。流式请求结束时服务端会自动补一个带 usage 的分片,不需要额外参数:
import OpenAI from "openai";
const client = new OpenAI({
baseURL: process.env.API_BASE ?? "https://api.llmzhongzhuan.com/v1",
apiKey: process.env.API_KEY,
});
const stream = await client.chat.completions.create({
model: "uncensored",
messages: [{ role: "user", content: "列出三个命名服务端日志字段的好习惯。" }],
stream: true,
max_tokens: 300,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
process.stdout.write("\n");
Node 示例使用顶层 await,需要 .mjs 文件或在 package.json 里设置 "type": "module"。如果你的运行时不支持,把它包进 async 函数即可。
LangChain:ChatOpenAI 的 base_url
LangChain 里不需要任何专门的适配器,直接用 langchain_openai 包里的 ChatOpenAI,把 base_url 指过去就行。
import os
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="uncensored",
base_url=os.environ.get("API_BASE", "https://api.llmzhongzhuan.com/v1"),
api_key=os.environ["API_KEY"],
temperature=0.7,
max_tokens=500,
timeout=60,
)
print(llm.invoke("把“服务已降级”改写成对用户友好的一句话。").content)
两个小提醒。第一,max_tokens 和 timeout 最好显式设置,默认值未必符合你的业务。第二,如果你在链里用工具调用,本站支持 OpenAI 格式的 tools,LangChain 的 bind_tools 可以正常使用;流式时在 stream() 里按块消费即可。
LlamaIndex:OpenAILike
LlamaIndex 自带的 OpenAI 类会校验模型名是否在官方清单内,遇到自定义模型名会报错。这时应该改用 llama-index-llms-openai-like 里的 OpenAILike,它不做这种校验,参数名也不同,地址叫 api_base。
import os
from llama_index.llms.openai_like import OpenAILike
llm = OpenAILike(
model="uncensored",
api_base=os.environ.get("API_BASE", "https://api.llmzhongzhuan.com/v1"),
api_key=os.environ["API_KEY"],
is_chat_model=True,
context_window=100000,
max_tokens=500,
)
print(llm.complete("用一句话解释什么是幂等请求。"))
这里的 is_chat_model=True 很关键,它让 LlamaIndex 走对话接口而不是旧的补全接口。context_window 设为 100000,LlamaIndex 在切分上下文时就不会误按默认的几千 token 截断你的文档。
Dify:OpenAI-API-compatible 供应商
Dify 这类可视化平台没有代码,而是通过表单配置。不同版本的界面文案略有出入,但步骤基本一致:
- 进入“设置”,找到“模型供应商”页面,在列表里选择“OpenAI-API-compatible”,点击添加模型。
- 模型类型选“LLM”,模型名称填
uncensored。 - API Key 填你的密钥,API endpoint URL 填
https://api.llmzhongzhuan.com/v1。 - 模型上下文长度填 100000,最大 token 上限填 16000。
- 如果要在工作流里使用工具调用,把函数调用支持选项打开;流式输出保持开启。
- 保存后新建一个简单的聊天应用,选中刚添加的模型发一句话,确认能正常返回。
平台有时会在保存时发一次探测请求,如果这一步失败,通常是地址多写了路径,或者密钥前后多了空格。如果你的 Dify 部署在容器里,还要确认容器能访问外网域名。
上线前要核对的参数与部署写法
框架能跑通只是第一步,下面几个参数在上线前最好逐一核对,它们决定了成本和失败率。
- max_tokens:默认 2048,需要长文输出就显式调大,上限 16,000。注意输入和输出加起来不能超过 100,000,否则会收到 400。
- timeout:长输出的请求耗时更久,流式场景建议把读超时设到 60 秒以上,非流式则按最长输出估算。
- temperature / top_p / stop:这些标准采样参数会被透传,框架里设置的值会直接生效,不需要额外开关。
- 并发:每个密钥每分钟 300 次请求,多个服务实例共用同一把密钥时,限流是合并计算的,别让每个实例都按 300 次来规划。
- 重试:框架自带的重试往往只针对网络错误,对 429 和 503 要自己加退避,写法见稳定性那一篇。
部署到容器时,思路同样是“镜像里不放密钥”,由编排工具在启动时注入。下面是 compose 里的最小写法:
# docker-compose.yml 片段
services:
app:
image: your-app:latest
environment:
API_BASE: https://api.llmzhongzhuan.com/v1
API_KEY: ${API_KEY} # 从宿主机环境或 .env 读取,不写进镜像
在 Kubernetes 里就换成 Secret 引用,原则不变。另外建议给不同环境准备不同的账号或密钥,例如开发、预发和生产各一套,这样某个环境的密钥泄露或余额耗尽,不会连带影响线上。因为每个账号只有一把密钥,所以多环境最好对应多个账号,并分别预充值适量余额。
最后是日志。很多框架在调试模式下会打印完整的请求头,里面就包含 Authorization,务必在生产环境关闭这类调试输出,或者在日志过滤器里把密钥脱敏。把 usage 字段记录下来则是好习惯,它能帮你对照账单,也能提前发现某个功能的提示词突然变长。
报错时的排查顺序
接入阶段的问题 90% 集中在几处,按下面的顺序查最快:
- 401:密钥为空、复制时带了空格,或者重置后仍在用旧密钥。
- 404:地址写成了不带
/v1的根路径,或者多拼了一次/chat/completions。 - 402:错误码为 no_credit,说明余额用完或试用已过期,需要充值预付费余额。
- 400:常见原因是输入加
max_tokens超过了 100,000,或请求体超过 8 MB。 - 429 / 503:前者是每分钟 300 次的限流,后者是 upstream_busy,稍等几秒重试,具体做法见 稳定性实践。
还有一类容易被忽略的问题是网络环境。公司内网、代理软件或者安全组规则,可能让域名解析成功却无法建立 HTTPS 连接,表现为长时间卡住后超时,而不是明确的错误码。遇到这种情况,先在同一台机器上用 curl 访问 /v1/models;能通就说明问题在应用层,不通则去查代理和防火墙。把排查过程记在团队文档里,下次新同事接入时就能少走一遍弯路。
三个值都正确却仍然失败时,先用 curl 直接请求一次,排除框架自身的问题。如果想先了解中转到底做了什么,可以回看 中转原理 这一篇。
常见问题
base_url 到底要不要带 /v1?
要带。写成 https://api.llmzhongzhuan.com/v1 即可,SDK 会在后面自动拼上 /chat/completions,不要重复添加。
LlamaIndex 为什么要用 OpenAILike 而不是 OpenAI?
OpenAI 类会检查模型名是否属于官方清单,自定义模型名会报错。OpenAILike 不做校验,更适合接入兼容接口。
Dify 里填的模型名可以随便起吗?
不可以,模型名必须是 uncensored,因为请求会原样带上这个 id。显示名称可以另外起,但模型字段要对。
环境变量改了为什么没生效?
多数是终端或进程没有重启。建议在代码里显式传 base_url 和 api_key,并打印一次实际使用的地址来确认。
只需填写表单即可获取密钥
创建账户,复制密钥,修改 Base URL。配置就是这么简单。