Wu Xianzhi API迁移指南
API 中转站迁移指南:从 OpenAI、OpenRouter 切换到无审查 API
如果你的项目现在走的是 OpenAI、OpenRouter 或者某个 API 中转站,想换到一个不会拒答合法需求的无审查 API,其实只需要改三项配置:base_url、密钥和模型名。本文先讲清中转站和专用无审查模型的区别,再给出参数对照表、用环境变量并行运行新旧接口的写法、上线前的切换清单,以及迁移时最容易踩的几个坑。
更新于
要点
- 中转站转售的仍是原厂模型,内容策略不变;专用无审查模型才能解决拒答问题
- 迁移只改三处:base_url 为 https://api.wuxianzhiapi.com/v1,密钥,模型名 uncensored
- 不支持 embeddings、图片、音频和微调,这些能力继续用原来的服务
- 用环境变量做灰度切换,出问题改一个变量即可回退
API 中转站和专用无审查模型有什么区别
先把概念说清楚,否则迁移的时候容易选错方向。常见的 API 中转站,本质上是把大厂模型的调用额度转售或聚合起来,对外提供一个 OpenAI 兼容的地址,让你用同一个 SDK 切换不同的模型。它解决的是「访问和付费」的问题,比如统一入口、统一账单,但模型本身还是原来那一个,原厂的内容策略一条不少:该拒绝的话题照样拒绝,换个中转地址并不会改变这一点。
专用的无审查模型是另一回事。它不是转发别人的模型,而是一个单独提供的模型,合法的成人内容、虚构创作和有争议的话题不会被拒答。Wu Xianzhi API 只提供一个模型,模型名是 uncensored,接口兼容 OpenAI 格式,所以迁移成本很低,但它也有清楚的边界:只做文本,不支持图片、音频、向量和微调;涉及未成年人的性内容无论是否虚构都会被拦截,返回 403。
所以迁移前先问自己:我遇到的问题是「接口不稳、价格贵」,还是「模型总是拒绝我的合法需求」?如果是后者,换中转站意义不大,换到专用无审查 API 才对症。很多团队的做法是两者并存:通用任务继续走原来的接口,需要无审查输出的请求单独路由到这里,后文会讲具体怎么做。
从 OpenAI 或 OpenRouter 迁移,要改的三个地方
不管你原来用的是 OpenAI 官方、OpenRouter 这类聚合接口,还是某个中转站,只要代码用的是 OpenAI 兼容的 SDK,需要改的就是三项:base_url 改成 https://api.wuxianzhiapi.com/v1;api_key 换成在 /get-api-key/ 拿到的密钥;model 统一写 uncensored。没有别的模型可选,GET /v1/models 里也只有这一个。
import os
from openai import OpenAI
messages = [{"role": "user", "content": "你好"}]
# 迁移前(示意):
# client = OpenAI(api_key=os.environ["OLD_API_KEY"], base_url="旧地址")
# resp = client.chat.completions.create(model="旧模型名", messages=messages)
# 迁移后:只动 base_url、api_key、model 这三处
client = OpenAI(
base_url="https://api.wuxianzhiapi.com/v1",
api_key=os.environ["WUXIANZHI_API_KEY"],
)
resp = client.chat.completions.create(model="uncensored", messages=messages, max_tokens=100)
print(resp.choices[0].message.content)Node.js 同理,把 new OpenAI({...}) 里的 baseURL 和 apiKey 换掉。如果你的项目是直接用 HTTP 请求的,把请求地址改为 https://api.wuxianzhiapi.com/v1/chat/completions,请求头保持 Authorization: Bearer <密钥> 即可。完整的 Python、Node.js、cURL 写法可以对照 代码示例。
curl https://api.wuxianzhiapi.com/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $WUXIANZHI_API_KEY" \
-d '{"model":"uncensored","messages":[{"role":"user","content":"你好"}],"max_tokens":50}'
参数对照:哪些能用,哪些不适用
下面这张表把迁移时最常碰到的字段过了一遍。原则是:对话相关的、OpenAI 格式的核心字段都可以照常使用;和「别的模型、别的模态」相关的功能,这里没有。
| 原来的用法 | 在这里怎么处理 |
|---|---|
model(如各种 gpt 型号) | 必须改成 uncensored |
messages(system / user / assistant / tool) | 格式一致,直接使用 |
max_tokens | 默认 2048,最大 16,000;超过会报 400 |
stream: true | 支持,最后自动追加一个用量块 |
tools / tool_choice | 支持,OpenAI 格式 |
| 上下文长度 | 提示词加输出合计 64,000 tokens |
| 请求体大小 | 不超过 8 MB |
| 速率 | 每个密钥每分钟 300 次请求 |
| 向量 embeddings | 不支持 |
| 图片生成 / 识图、语音、视频 | 不支持,只处理文本 |
| 微调 fine-tuning | 不支持 |
| 切换多个模型 | 只有一个模型,没有可切换的列表 |
表里没有列出的其他可选字段,不要默认它们都会按原厂的方式生效。稳妥的做法是在测试环境里单独跑一遍,确认行为符合预期再上线,具体支持情况以 接口文档 为准。
没有的能力怎么办:向量、图片、语音的替代思路
如果你原来的项目同时用了对话和向量检索,迁移时不要想着「全部换过来」。这里只提供文本对话,所以 embeddings 相关的代码,比如知识库检索、语义去重,需要继续使用你原来的向量服务,或者换成自己部署的向量方案。对话部分换到这里,检索部分保持不动,两边互不影响,这是最省事的拆法。
图片、语音同理。比如你的产品是「文字加配图」的形态,文字生成可以走这里,配图继续用原来的图片接口;需要语音播报的,文本生成之后再交给你已有的语音服务。把「生成文字」这一步单独抽成一个函数,后面无论怎么拼装其他能力,改动面都会很小。
还有一种情况是你的代码里用了多个模型分工,比如便宜的模型做分类、贵的模型做创作。这里只有一个模型,分类任务也要由它来做。好在输入单价是 $0.25 / 百万 tokens,做分类这种输出很短的任务成本很低,把 max_tokens 设小,成本基本可以忽略。价格细节见 价格页。
并行运行:用环境变量在两套接口间切换
迁移最怕的是「一刀切」。更稳的做法是在代码里做一层很薄的封装,用环境变量决定走哪一个接口,这样可以先让一小部分流量或者一部分功能走新接口,出了问题改一个变量就能退回去。因为两边都是 OpenAI 兼容格式,封装非常简单。
import os
from openai import OpenAI
PROVIDERS = {
"old": {
"base_url": os.environ.get("OLD_BASE_URL", ""),
"api_key": os.environ.get("OLD_API_KEY", ""),
"model": os.environ.get("OLD_MODEL", ""),
},
"wuxianzhi": {
"base_url": "https://api.wuxianzhiapi.com/v1",
"api_key": os.environ.get("WUXIANZHI_API_KEY", ""),
"model": "uncensored",
},
}
def get_client(name=None):
name = name or os.environ.get("LLM_PROVIDER", "old")
cfg = PROVIDERS[name]
return OpenAI(base_url=cfg["base_url"], api_key=cfg["api_key"]), cfg["model"]
def chat(messages, provider=None, **kwargs):
client, model = get_client(provider)
return client.chat.completions.create(model=model, messages=messages, **kwargs)
# 通用任务走旧接口,需要无审查输出的请求显式指定新接口
resp = chat([{"role": "user", "content": "写一个黑色幽默的短故事"}],
provider="wuxianzhi", max_tokens=800)
print(resp.choices[0].message.content)切换粒度可以有三层:按环境(测试环境先切)、按功能(只把创作类接口切过来)、按用户(灰度一部分账号)。无论哪一层,都建议把日志里记录的 provider 字段留下,出现差异时才能对照排查。对话历史也建议统一存成标准的 messages 数组,这样同一段会话可以在两边之间无缝接着聊。
切换清单
上线前按下面的顺序过一遍,基本不会漏项:
- 在 /get-api-key/ 注册,拿到密钥,用 $0.50 试用额度(7 天内有效)做验证,不需要先充值。
- 用
curl /v1/models确认密钥有效、网络可达。 - 把
base_url、api_key、model三项改成环境变量驱动,密钥不进代码仓库。 - 搜一遍代码里写死的模型名、
max_tokens数值和 embeddings 调用。 - 检查流式处理代码,兼容最后一个
choices为空的用量块。 - 给 429 和 503 加指数退避重试,给 402 和 403 加明确的提示分支。
- 用你真实的提示词跑一批回归样例,重点看原来被拒答的那部分是否正常输出。
- 先灰度小比例流量,对比用量和延迟,没问题再扩大。
- 确认产品面向的是成年用户,并且用途合法,这是使用本接口的前提。
迁移中最常见的几个坑
模型名忘了改。旧代码里的 gpt 型号或某个聚合平台的模型路径原样发过来,会得到错误响应。全局搜索模型名,确保最终发出去的是 uncensored。
max_tokens 超限。有些项目为了让模型写得长,把 max_tokens 设到 32000 甚至更大,在这里单次最大是 16,000,超过会返回 400。另外,提示词加 max_tokens 不能超过 64,000 tokens,输入很长的请求要相应调小输出上限。
流式用量块。流结束前服务器会自动追加一个带 usage 的块,它的 choices 是空数组。如果你的解析代码写成 chunk.choices[0] 直接取值,就会在最后一步报错。有些旧代码还会手动传 stream_options 去要用量,在这里不需要。
把「无审查」当成「无边界」。合法的成人内容、虚构和争议话题不会被拒绝,但涉及未成年人的性内容始终拦截,包括虚构和角色扮演,返回 403 content_blocked。产品需要自己做好成年用户的准入。
余额与试用到期。试用额度 7 天后失效,余额用完后会收到 402,错误码是 no_credit。在应用里把这个错误翻译成用户能看懂的提示,而不是笼统的「服务出错」。具体场景设计可以参考 应用场景 一文。
常见问题
迁移之后原来的提示词还要重写吗?
格式不用改,messages 结构完全一致。但原来为了绕过拒答而写的那些「越狱式」铺垫可以删掉,直接写清楚角色和任务,既省 token 也更稳定。
迁移期间能不能新旧接口同时保留?
可以,而且推荐这样做。用环境变量决定 base_url、密钥和模型名,先让部分功能或部分用户走新接口,出问题改一个变量就能退回。
旧接口里的 embeddings 调用换过来会怎样?
这里没有 embeddings 接口,请求会返回 404。向量检索部分请继续使用原来的服务,只把对话请求切过来。
怎么判断迁移后效果有没有变好?
拿一批原来被拒答或被改写的真实提示词做回归对比,记录拒答率、回复长度和 usage 里的 token 数。样本要来自你自己的业务,而不是网上的通用测试集。