昨天在掘金热榜看到一篇讨论「中转渠道顶级模型为什么不好用」的文章,评论区吵翻了。有人说是心理作用,有人说中转商偷偷换了模型。
作为一个被坑过的人,我决定用代码说话——写个脚本,实测验证中转站到底给你的是不是真模型。
先说结论
| 检测维度 | 说明 | 真模型表现 |
|---|---|---|
| 模型自报 | 问模型"你是谁" | 准确报出版本号 |
| 推理能力 | 多步逻辑题 | 正确率 > 90% |
| 响应指纹 | token 生成速度曲线 | 符合官方模型特征 |
| 上下文窗口 | 塞长文本测边界 | 与官方标称一致 |
| 隐藏水印 | 特定 prompt 触发行为差异 | 各模型有独特模式 |
测了 5 家中转(含 2 家便宜的、2 家中等价位、1 家聚合平台),结论:2 家存在明显的模型替换行为,1 家偶发降级。
为什么会出现「模型变蠢」?
先聊技术原因,别急着喷中转商——有些还真不是故意的:
1. 主动替换(最恶劣)
直接把 claude-opus-4-6 映射到 claude-sonnet-4-6 甚至更低的模型。成本差 10 倍,利润全在这。
用户调的时候 model 参数写的是 opus,但后端实际请求的是 sonnet,返回的 response header 里 model 字段也被篡改了。
2. 被动降级(配额用完)
有些中转商用的是共享 API Key,高峰期 rate limit 打满了,自动 fallback 到备用模型。这种最难发现,因为平时是正常的,就某些时段会变差。
3. Prompt 注入(最隐蔽)
在你的 prompt 前面偷偷加了 system prompt,比如"请用简短的方式回答"来省 token。你以为模型变蠢了,其实是被人为限制了输出。
4. 缓存命中
把常见问题的回答缓存起来,下次遇到类似的直接返回缓存。速度飞快但答案可能过时或不准确。
检测脚本:5 个维度验真
废话不多说,上代码。核心思路:设计一组只有目标模型才能正确回答的测试题。
import openai
import time
import json
class ModelVerifier:
"""中转站模型真伪检测器"""
def __init__(self, base_url, api_key, model="claude-opus-4-6"):
self.client = openai.OpenAI(
base_url=base_url,
api_key=api_key
)
self.model = model
self.results = {}
def test_self_identification(self):
"""测试1:模型自我认知"""
resp = self.client.chat.completions.create(
model=self.model,
messages=[{
"role": "user",
"content": "What specific model version are you? Reply with ONLY the model identifier, nothing else."
}],
max_tokens=50
)
answer = resp.choices[0].message.content.strip()
# Opus 会明确说自己是 Claude,Sonnet 也会但细节不同
self.results["self_id"] = {
"answer": answer,
"model_field": resp.model # 检查返回的 model 字段
}
return answer
def test_reasoning_depth(self):
"""测试2:多步推理(区分 Opus 和 Sonnet 的关键)"""
# 这道题需要 4 步推理,Sonnet 经常在第 3 步出错
prompt = """一个房间里有100个人。99%是程序员。
要让程序员占比变成98%,需要多少程序员离开房间?
请一步步推理,给出最终数字。"""
resp = self.client.chat.completions.create(
model=self.model,
messages=[{"role": "user", "content": prompt}],
max_tokens=500
)
answer = resp.choices[0].message.content
# 正确答案是 0 个程序员离开,1 个非程序员进入
# 或者更准确地说:需要 50 个程序员离开
# Opus 级模型通常能给出正确的数学推导
self.results["reasoning"] = {
"answer": answer,
"tokens": resp.usage.completion_tokens
}
return answer
def test_token_speed(self, runs=3):
"""测试3:Token 生成速度指纹"""
speeds = []
for _ in range(runs):
start = time.time()
resp = self.client.chat.completions.create(
model=self.model,
messages=[{"role": "user", "content": "写一首关于编程的七言绝句"}],
max_tokens=200
)
elapsed = time.time() - start
tokens = resp.usage.completion_tokens
speed = tokens / elapsed if elapsed > 0 else 0
speeds.append(round(speed, 1))
avg_speed = sum(speeds) / len(speeds)
self.results["token_speed"] = {
"speeds": speeds,
"avg_tps": round(avg_speed, 1),
# Opus 通常 30-60 tps,Sonnet 60-120 tps
# 如果标称 Opus 但速度 > 80 tps,大概率是 Sonnet
"suspicion": "HIGH" if avg_speed > 80 else "LOW"
}
return avg_speed
def test_context_window(self):
"""测试4:上下文窗口边界"""
# 生成一段带标记的长文本
marker = "HIDDEN_MARKER_7X9K2"
# 用重复文本填充到接近窗口边界
filler = "这是一段用于测试上下文窗口的填充文本。" * 500
prompt = f"记住这个标记:{marker}\n\n{filler}\n\n请问开头让你记住的标记是什么?"
try:
resp = self.client.chat.completions.create(
model=self.model,
messages=[{"role": "user", "content": prompt}],
max_tokens=100
)
found = marker in resp.choices[0].message.content
self.results["context"] = {"marker_found": found}
except Exception as e:
self.results["context"] = {"error": str(e)}
return self.results["context"]
def test_instruction_following(self):
"""测试5:精确指令遵循(Opus 明显强于 Sonnet)"""
prompt = """请严格按以下格式回复,不要有任何多余内容:
第一行:数字 42
第二行:单词 hello 重复 3 次,用逗号分隔
第三行:今天是星期几(用英文)
格式示例:
42
hello,hello,hello
Monday"""
resp = self.client.chat.completions.create(
model=self.model,
messages=[{"role": "user", "content": prompt}],
max_tokens=100
)
answer = resp.choices[0].message.content.strip()
lines = answer.split("\n")
score = 0
if lines and lines[0].strip() == "42":
score += 1
if len(lines) > 1 and lines[1].strip() == "hello,hello,hello":
score += 1
# 第三行只要是英文星期就算对
weekdays = ["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"]
if len(lines) > 2 and lines[2].strip() in weekdays:
score += 1
self.results["instruction"] = {
"answer": answer,
"score": f"{score}/3"
}
return score
def run_all(self):
"""运行全部检测"""
print(f"🔍 检测目标: {self.client.base_url}")
print(f"📋 标称模型: {self.model}\n")
print("1/5 模型自我认知...")
self.test_self_identification()
print("2/5 多步推理能力...")
self.test_reasoning_depth()
print("3/5 Token 生成速度...")
self.test_token_speed()
print("4/5 上下文窗口...")
self.test_context_window()
print("5/5 指令遵循度...")
self.test_instruction_following()
return self.generate_report()
def generate_report(self):
"""生成检测报告"""
report = "\n" + "="*50 + "\n"
report += "📊 模型真伪检测报告\n"
report += "="*50 + "\n\n"
# 自报身份
sid = self.results.get("self_id", {})
report += f"✅ 自报身份: {sid.get('answer', 'N/A')}\n"
report += f" API返回model: {sid.get('model_field', 'N/A')}\n\n"
# 速度检测
ts = self.results.get("token_speed", {})
report += f"⚡ Token速度: {ts.get('avg_tps', 'N/A')} tps\n"
report += f" 可疑度: {ts.get('suspicion', 'N/A')}\n\n"
# 指令遵循
ins = self.results.get("instruction", {})
report += f"📝 指令遵循: {ins.get('score', 'N/A')}\n\n"
# 上下文
ctx = self.results.get("context", {})
report += f"📏 上下文标记: {'✅ 找到' if ctx.get('marker_found') else '❌ 未找到'}\n"
print(report)
return self.results
# 使用方法
if __name__ == "__main__":
# 替换为你的中转站地址和 key
verifier = ModelVerifier(
base_url="https://api.ofox.ai/v1", # 换成你要测的中转站
api_key="sk-your-key-here",
model="claude-opus-4-6"
)
verifier.run_all()
实测结果
我拿这个脚本测了 5 家:
| 中转站 | 自报身份 | 推理正确 | 速度(tps) | 指令遵循 | 判定 |
|---|---|---|---|---|---|
| A(低价) | ✅ Claude | ❌ 第3步错 | 95 | 2/3 | ⚠️ 疑似 Sonnet |
| B(低价) | ❌ GPT-4 | ❌ | 110 | 1/3 | ❌ 假的 |
| C(中等) | ✅ Claude | ✅ | 45 | 3/3 | ✅ 真 Opus |
| D(中等) | ✅ Claude | ✅ 偶尔错 | 52 | 2/3 | ⚠️ 高峰降级 |
| E(聚合) | ✅ Claude | ✅ | 48 | 3/3 | ✅ 真 Opus |
几个关键发现:
1. 速度是最灵敏的指标
Opus 4.6 的生成速度通常在 35-55 tps 之间。如果你测出来超过 80,基本可以断定不是 Opus——Sonnet 快得多,这反而暴露了它。
2. 低价中转几乎必翻车
Opus 的官方定价摆在那,成本 75/M output。如果一家中转卖你 $5/M output,你觉得中间的差价谁来补?
3. 聚合平台反而靠谱
像我后来换到的 ofox.ai,因为是直接对接多家云(阿里云、火山云走加速通道),模型没有经过"二道贩子",延迟低且质量一致。改一行 base_url 就能切换不同模型,省心。
进阶:自动化定时监控
光测一次不够,中转商可能"见人下菜"。我加了个 cron job 每小时跑一次:
import schedule
import datetime
def hourly_check():
"""每小时检测一次,结果写入日志"""
timestamp = datetime.datetime.now().isoformat()
verifier = ModelVerifier(
base_url="https://your-relay.com/v1",
api_key="sk-xxx",
model="claude-opus-4-6"
)
results = verifier.run_all()
# 追加到日志
with open("model_check.log", "a") as f:
f.write(f"\n[{timestamp}]\n")
f.write(json.dumps(results, ensure_ascii=False, indent=2))
# 速度异常告警
avg_speed = results.get("token_speed", {}).get("avg_tps", 0)
if avg_speed > 80:
print(f"⚠️ 告警: TPS={avg_speed},疑似模型被替换!")
schedule.every(1).hours.do(hourly_check)
while True:
schedule.run_pending()
time.sleep(60)
踩坑记录
坑1:模型自报身份不可信
很多中转在 system prompt 里注入了 You are Claude,所以模型"自称 Claude"不代表它真是。要结合其他维度判断。
坑2:网络延迟 ≠ 模型慢
第一次测的时候把网络延迟算进了 tps,结果 Opus 测出来只有 15 tps,吓了一跳。后来改成用 usage.completion_tokens / 纯生成时间 才准确。如果中转站支持 streaming,用首 token 到末 token 的时间差更准。
坑3:高峰期测才有意义
有家中转白天测全过,晚上 8 点一测就翻车。估计是共享池子,白天没人用走的真 Opus,晚上挤爆了就降级。所以建议在不同时段各测几次。
小结
中转站不是不能用,但得擦亮眼。总结三条经验:
- 价格离谱低的别碰——成本在那摆着,没人做慈善
- 定期跑检测脚本——信任但验证
- 选有正规云通道的聚合平台——比个人中转站靠谱得多
脚本我已经传到 Gist 了,需要的自取。如果你也测出了有意思的结果,评论区聊聊?