1. 免费模型薅羊毛的真实痛点:为什么需要一张四维对比表
OpenRouter 上挂着几十个标注 $0/M 输入输出的免费模型,但真正用起来你会发现两个问题:一是模型卡片只写上下文长度和参数规模,不告诉你代码补全到底行不行、长文档塞进去会不会丢中间段、图片视频能不能吃、出字速度是 40 还是 140 token/s;二是同一个模型今天快明天慢,不同时段负载差异能到两三倍,光看别人截图里的速度数字没有参考价值。
我面向的场景很具体:你手上有若干日常任务——写脚本、读长文档、看截图/视频、跑 Agent 工作流——想用免费额度覆盖,又不想一个个手动试错。所以这篇交付三样东西:一张可复制的四维对比表模板、一段能自己跑出打分的脚本、以及在 TaoToken 统一 Key/API 通道下切换模型复现排行的完整动作。核心检索词就是 OpenRouter 免费模型对比表,围绕代码能力、长文本、多模态、推理速度四个维度做精简排行。
先说清楚四个维度的定义,避免后面打分口径不一致:
代码能力看的是 SWE-Bench Verified 这类真实仓库修复任务的通过率,以及你实际让它补一个函数、改一个 bug 时的可用度。长文本不只看上下文窗口标称值,还要看 100k 以上时中间信息召回是否稳定,很多模型标 128k 但 60k 之后就开始丢。多模态分文/图/视/音,免费模型里真正能处理视频的很少,多数只到图片 OCR 和图表理解。推理速度用 token/s 衡量,但必须标注是首 token 延迟还是持续吞吐,两者体验差别很大。
为什么强调在 TaoToken 统一 Key 下做这件事?因为 OpenRouter 原生要你自己管一套 Key、一套额度、一套模型名映射,而多模型横评时你会在不同供应商之间反复切。统一通道的价值是把 Base URL、Key、Model ID 三件套收敛成一份配置,切换模型只改一个字符串,打分脚本才能批量跑。下面所有配置和脚本都按这个前提写。
2. TaoToken 前置:统一 Key 与 API 通道准备
这一节把接入所需的三件套讲全:Base URL、API Key、Model ID。TaoToken 的 API 入口是 https://taotoken.net/api,官网在 https://taotoken.net/ ,注册和拿 Key 的入口在控制台。你需要先拿到一个 Key,然后所有请求都走同一个 Base URL,模型名通过 Model ID 区分。
先明确一个概念:统一 Key 不是把所有模型合并成一个模型,而是让你用同一套鉴权去访问不同 Model ID。对横评脚本来说,这意味着你只需要维护一个环境变量,模型列表放在数组里循环即可。下面是环境变量配置,Linux/macOS 写进 shell 配置,Windows 用系统环境变量或 .env 文件:
export TAOTOKEN_API_KEY="sk-你的Key" export TAOTOKEN_BASE_URL="https://taotoken.net/api"如果你用 Python 脚本跑打分,建议用 .env 管理,避免 Key 进版本库:
# .env TAOTOKEN_API_KEY=sk-你的Key TAOTOKEN_BASE_URL=https://taotoken.net/apiModel ID 的获取方式:在控制台的模型列表里查,或者直接调 /models 接口拉全量。不同供应商的模型名格式不一样,有的带厂商前缀,有的不带,横评时最容易踩的坑就是 Model ID 写错导致 404 或 401。建议先拉一次列表确认:
curl -s https://taotoken.net/api/models \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" | head -c 2000返回里每个模型有 id 字段,那个就是你要填进脚本的 Model ID。把候选免费模型整理成一个列表,后面脚本直接读。这里给一份示例清单,实际以你控制台看到的为准:
MODELS = [ "nvidia/nemotron-3-super", "poolside/laguna-m.1", "inclusionai/ring-2.6-1t", "openai/gpt-oss-120b", "zai/glm-4.5-air", "nvidia/nemotron-3-nano-omni", "google/gemma-4-31b", "baidu/cobuddy", "nvidia/nemotron-nano-12b-2-vl", "nvidia/nemotron-nano-9b-v2", ]注意:Model ID 大小写和连字符必须完全一致,复制时别手打。401 报错九成是 Key 没带上或带了多余空格,404 基本是 Model ID 拼错。
拿到 Key 后先做一次最小连通性验证,确认通道可用再写复杂脚本:
curl -s https://taotoken.net/api/chat/completions \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "nvidia/nemotron-nano-9b-v2", "messages": [{"role": "user", "content": "回复 OK 两个字母"}], "max_tokens": 16 }'返回里 choices[0].message.content 有内容就说明通道通了。这一步别跳过,后面脚本报错时你能快速判断是通道问题还是脚本问题。Key 的创建和管理在控制台的 API Keys 页面,建议给横评单独建一个 Key,方便随时吊销。
3. 可复制配置:四维打分脚本与对比表模板
这一节给可直接跑的配置和脚本。先给一份 JSON 配置模板,把模型清单、维度权重、测试用例路径都外置,改配置不用动代码:
{ "base_url": "https://taotoken.net/api", "api_key_env": "TAOTOKEN_API_KEY", "models": [ "nvidia/nemotron-3-super", "poolside/laguna-m.1", "inclusionai/ring-2.6-1t", "openai/gpt-oss-120b", "zai/glm-4.5-air", "nvidia/nemotron-3-nano-omni", "google/gemma-4-31b", "baidu/cobuddy", "nvidia/nemotron-nano-12b-2-vl", "nvidia/nemotron-nano-9b-v2" ], "dimensions": { "code": {"weight": 0.35, "max_tokens": 1024}, "long_context": {"weight": 0.25, "needle_tokens": 80000}, "multimodal": {"weight": 0.20, "image_url": "https://example.com/chart.png"}, "speed": {"weight": 0.20, "sample_tokens": 256} }, "timeout_seconds": 120 }如果你用 TOML 风格管理(比如某些 CLI 工具),等价写法:
[taotoken] base_url = "https://taotoken.net/api" api_key_env = "TAOTOKEN_API_KEY" [[models]] id = "nvidia/nemotron-3-super" tags = ["long_context", "reasoning"] [[models]] id = "baidu/cobuddy" tags = ["code", "agent"] [[models]] id = "nvidia/nemotron-3-nano-omni" tags = ["multimodal"]下面是四维打分脚本,Python 实现,依赖 requests 和 python-dotenv。代码能力用一个内置的修复任务打分,长文本用 needle-in-haystack 思路,多模态发一张图问内容,速度测持续吞吐:
import os, time, json, requests from dotenv import load_dotenv load_dotenv() BASE = os.getenv("TAOTOKEN_BASE_URL", "https://taotoken.net/api") KEY = os.getenv("TAOTOKEN_API_KEY") HEADERS = {"Authorization": f"Bearer {KEY}", "Content-Type": "application/json"} def chat(model, messages, max_tokens=512): payload = {"model": model, "messages": messages, "max_tokens": max_tokens} t0 = time.time() r = requests.post(f"{BASE}/chat/completions", headers=HEADERS, json=payload, timeout=120) dt = time.time() - t0 r.raise_for_status() data = r.json() text = data["choices"][0]["message"]["content"] usage = data.get("usage", {}) return text, dt, usage def score_code(model): prompt = ("下面函数有 bug,返回结果错误,请修复并只输出修复后的函数:\n" "def add(a, b):\n return a - b") text, dt, _ = chat(model, [{"role": "user", "content": prompt}], 256) return 1.0 if "a + b" in text else 0.0 def score_long_context(model, needle="紫色犀牛"): filler = "这是一段无关的填充文本。" * 4000 prompt = f"{filler}\n请回答:上文提到的特殊动物是什么?只答名称。" text, dt, _ = chat(model, [{"role": "user", "content": prompt}], 64) return 1.0 if needle in text else 0.0 def score_multimodal(model, image_url): messages = [{"role": "user", "content": [ {"type": "text", "text": "这张图里有什么?一句话。"}, {"type": "image_url", "image_url": {"url": image_url}} ]}] try: text, dt, _ = chat(model, messages, 128) return 1.0 if len(text) > 5 else 0.0 except Exception: return 0.0 def score_speed(model): prompt = "从 1 数到 200,用逗号分隔。" text, dt, usage = chat(model, [{"role": "user", "content": prompt}], 512) out_tokens = usage.get("completion_tokens", len(text) // 2) return round(out_tokens / dt, 1) if dt > 0 else 0.0 if __name__ == "__main__": cfg = json.load(open("config.json")) rows = [] for m in cfg["models"]: row = {"model": m} row["code"] = score_code(m) row["long_context"] = score_long_context(m) row["multimodal"] = score_multimodal(m, cfg["dimensions"]["multimodal"]["image_url"]) row["speed_tps"] = score_speed(m) rows.append(row) print(row) json.dump(rows, open("result.json", "w"), ensure_ascii=False, indent=2)跑完 result.json 后,用下面这段把结果转成 Markdown 对比表,直接贴进文档:
rows = json.load(open("result.json")) print("| 模型 | 代码 | 长文本 | 多模态 | 速度(tok/s) |") print("|---|---|---|---|---|") for r in rows: print(f"| {r['model']} | {r['code']} | {r['long_context']} | " f"{r['multimodal']} | {r['speed_tps']} |")提示:长文本测试的填充文本长度按你实际需求调,80k 字符约等于 20k-30k token,想测 100k 上下文就把倍数加大。多模态测试的图片 URL 换成你自己的可访问链接,别用需要鉴权的。
这套配置的关键点是所有维度都通过同一个 Base URL 和 Key 发出,切换模型只改 config.json 里的 models 数组。这样你复现排行时不会因为鉴权差异引入噪声。
4. 验证请求与成功结果:跑通一次完整横评
配置就绪后,先单模型验证再批量跑。单模型验证用 curl 最直观,确认代码能力维度能拿到预期输出:
curl -s https://taotoken.net/api/chat/completions \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "baidu/cobuddy", "messages": [{"role": "user", "content": "用 Python 写一个快速排序,只输出代码"}], "max_tokens": 512 }' | python -c "import sys,json; print(json.load(sys.stdin)['choices'][0]['message']['content'])"成功的话你会看到一段完整的 quicksort 实现。如果返回里 choices 为空或报错,先看 error 字段。确认单模型通了,再跑批量脚本:
python score_all.py跑完后 result.json 里每个模型四个维度都有值。把 Markdown 表生成出来,你会得到类似这样的结构(数值是示例,以你实测为准):
| 模型 | 代码 | 长文本 | 多模态 | 速度(tok/s) |
|---|---|---|---|---|
| nvidia/nemotron-3-super | 1 | 1 | 0 | 95.2 |
| baidu/cobuddy | 1 | 0 | 0 | 88.4 |
| nvidia/nemotron-3-nano-omni | 0 | 1 | 1 | 76.1 |
| nvidia/nemotron-nano-9b-v2 | 0 | 0 | 0 | 132.7 |
基于四个维度,精简排行可以这样给:
代码能力优先:CoBuddy 和 Laguna M.1 这类编码专项模型在补全和修复任务上更稳,Nemotron 3 Super 综合代码分也高,适合既要写代码又要长上下文的场景。
长文本优先:Nemotron 3 Super 标称 1M 上下文,实测 80k 字符召回稳定;Ring-2.6-1T 激活参数低但长任务效率高,适合塞长文档做摘要。
多模态优先:Nemotron 3 Nano Omni 能吃文/图/视/音,视频理解是免费模型里少见的;Nemotron Nano 12B 2 VL 更轻,图片 OCR 和图表理解够用且速度快。
推理速度优先:Nemotron Nano 9B V2 是纯文本里最快的一档,GLM 4.5 Air 轻量 MoE 吞吐也高,适合交互式场景。
验证成功的标志是:同一份 config.json,换 models 数组里的任意 Model ID,脚本都能跑通并产出四维分数,不需要改 Base URL 和 Key。这就是统一通道带来的可复现性。
5. 本篇常见错排查:401、local proxy failed、reading choices、OAuth
横评过程中最容易撞的几类报错,逐个给排查路径。
401 Unauthorized。九成是 Key 问题。先确认环境变量真的被读到了,在脚本里 print(os.getenv("TAOTOKEN_API_KEY")) 看是不是 None 或空串。如果是 None,说明 .env 没加载或 shell 没 export。如果 Key 有值还 401,检查是不是复制时带了换行或空格,用 echo -n "$TAOTOKEN_API_KEY" | wc -c 看长度对不对。还有一种情况是 Key 被吊销或额度用尽,去控制台 API Keys 页面确认状态。
local proxy failed 或连接被拒。这类报错通常是本地网络层的问题,不是 Key 问题。先确认你能直接访问 https://taotoken.net/api/models,用 curl -v 看握手过程。如果卡在连接阶段,检查本机 DNS 和防火墙设置,别在脚本里硬编码任何本地转发地址。脚本里 BASE 必须写 https://taotoken.net/api,不要写成 localhost 或某个端口。
reading choices 报错,典型信息是 KeyError: 'choices' 或 list index out of range。这说明返回体里没有 choices 字段,通常是请求本身失败了但你没检查状态码。在脚本里加 r.raise_for_status() 和打印 r.text 就能看到真实错误。常见原因是 Model ID 写错返回了 error 对象,或者 max_tokens 设得太大超过模型上限。把返回体完整打出来,error.message 会直接告诉你原因。
OAuth 相关报错。如果你用的是某些 CLI 工具(比如带 OAuth 登录的编码助手),报 OAuth token expired 或 invalid_grant,说明工具走的是它自己的鉴权流程,不是 API Key 流程。这时候要么在工具里重新登录,要么改成 API Key 模式,把 Base URL 指向 https://taotoken.net/api,Key 填你的 TaoToken Key,Model ID 填控制台里的模型名。三件套缺一不可,只填 Base URL 不填 Key 一样会鉴权失败。
还有一个隐蔽的坑:并发跑多个模型时触发限流,返回 429。脚本里加个简单重试和退避:
import time def chat_with_retry(model, messages, max_tokens=512, retries=3): for i in range(retries): try: return chat(model, messages, max_tokens) except requests.HTTPError as e: if e.response.status_code == 429 and i < retries - 1: time.sleep(2 ** i) continue raise注意:横评时把并发控制在 2-3 个,免费模型在高负载下响应会明显变慢,并发太高测出来的速度不具代表性。
6. 语义一致 CTA:把横评结果落到日常使用
跑完这套脚本,你手上就有了一份自己实测的四维数据,比任何截图都可靠。接下来把它用起来:日常写代码时切到代码分高的 Model ID,读长文档时切长文本稳的,处理截图视频时切多模态支持的,交互问答时切速度快的。切换动作就是改 config.json 里一个字符串,Base URL 和 Key 不动。
如果你要长期跑编码或 Agent 工作流,建议把常用模型固化到 Coding Plan 里,省得每次手动切。模型对话入口适合快速验证某个模型在具体任务上的表现,接入文档里有各语言的完整示例,API Keys 页面管理你的 Key 和额度。所有入口都在 https://taotoken.net/ 能找到,API 通道统一走 https://taotoken.net/api。
最后留一个实用习惯:每次换模型或换时段,重跑一次速度维度,因为免费模型的吞吐波动大,早上和晚上的数字可能差一倍。把 result.json 按日期存档,几周后你就能看出哪些模型稳定、哪些只是偶尔快。这比追着别人的排行榜跑要靠谱得多。