1. 从“凭感觉选模型”到四维打分:LLM 模型选型框架到底解决什么问题
团队做 LLM 应用落地时,最常听到的一句话是“先接最强的那个试试”。这句话在 Demo 阶段没问题,一旦进入真实业务就会暴露三个坑:账单不可控、延迟不可控、合规边界不可控。我见过一个刷题类产品,题解生成一开始直接上最贵的旗舰模型,单次调用成本约 0.03 美元,一天 100 次就是 3 美元,一个月接近 90 美元;后来把简单题的思路提示换成轻量模型,成本直接砍到三分之一,用户几乎无感知。
这就是 LLM 模型选型框架要解决的问题:把“哪个模型好”这种主观判断,拆成能力(Capability)、延迟(Latency)、成本(Cost)、合规(Compliance)四个可量化维度,再用同一批 prompt 跑出可复现的数据,最后按业务场景加权打分。它适合三类人:一是正在做 AI 功能选型的后端/全栈工程师;二是需要向老板解释“为什么不用最贵模型”的技术负责人;三是想把手里的多模型调用统一管理起来的独立开发者。
四维之间天然存在权衡。能力强的模型往往参数大、推理慢、单价高;延迟低的模型通常能力有限;能本地部署的模型合规性最好,但能力上限受硬件约束。所以“最优模型”不是“最强模型”,而是在当前场景约束下综合得分最高的那个。本文会给出可复制的评测脚本配置、四维打分表模板,以及用统一 Key 跑通多模型并记录延迟与 token 成本的完整验证动作。核心检索词就三个:LLM 模型选型、四维评估、统一 Key 多模型对比。
2. 用 TaoToken 统一 Key 接入多模型:前置准备与四维打分表设计
2.1 为什么选型阶段需要统一 Key 通道
做横向对比时,最麻烦的不是写评测脚本,而是每换一个模型就要换一套 SDK、换一个 Key、换一种鉴权方式。OpenAI 兼容协议、Anthropic 原生协议、各家自己的参数命名,光适配就能耗掉半天。更现实的问题是:如果每个模型单独申请 Key,额度、账单、限流都分散在不同后台,你根本没法在同一张表里对比成本和延迟。
TaoToken 在这里的作用是提供一个统一的 API 通道:你只需要一个 Key、一个 Base URL,就能用 OpenAI 兼容的方式调用多个模型。对选型评测来说,这意味着同一份脚本、同一批 prompt、同一套计时逻辑,可以跑遍所有候选模型,数据天然可比。官网入口是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 地址是 https://taotoken.net/api (注意 API 地址不加 UTM 参数)。
2.2 四维打分表模板
先设计打分表,再写脚本。下面这张表是我实际用的模板,每个维度 1–10 分,分数越高代表该维度表现越好(注意:延迟维度是“越快分越高”,成本维度是“越便宜分越高”,合规维度是“越可控分越高”)。
| 维度 | 评分含义 | 数据来源 | 权重示例(精度优先) |
|---|---|---|---|
| 能力 Capability | 正确率/指令遵循/代码通过率 | 同一批 prompt 的人工或自动评分 | 0.6 |
| 延迟 Latency | 首 token 时间 + 总耗时,越快分越高 | 脚本实测 P50/P95 | 0.1 |
| 成本 Cost | 每千 token 综合单价,越便宜分越高 | 实测 token 用量 × 单价 | 0.2 |
| 合规 Compliance | 数据是否可本地/私有化,越可控分越高 | 部署方式与数据流向 | 0.1 |
权重不是固定的。实时对话场景把延迟权重提到 0.6;批量离线生成把成本权重提到 0.6;处理敏感数据时合规权重提到 0.7,并且直接过滤掉不可本地部署的模型。这张表的价值在于:每次选型决策都能回溯到具体分数和权重,而不是“我觉得这个好”。
2.3 环境准备
你需要准备 Python 3.9+,安装 openai 官方 SDK(因为走 OpenAI 兼容协议)和 python-dotenv:
pip install openai python-dotenv然后在项目根目录建一个.env文件,把 Key 和 Base URL 放进去,避免硬编码:
TAOTOKEN_API_KEY=你的Key TAOTOKEN_BASE_URL=https://taotoken.net/apiKey 在控制台的 API Keys 页面创建,地址是 https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。创建后先别急着跑大批量,用一条最小请求确认通道可用。
3. 可复制的评测脚本配置:同一批 prompt 跑通多模型
3.1 评测脚本的目录结构
我习惯把评测拆成三个文件,方便复用:
llm-eval/ ├── .env ├── models.json # 候选模型清单与单价 ├── prompts.jsonl # 评测用 prompt 集 └── eval.py # 主评测脚本models.json里记录每个模型的 Model ID 和单价,这是成本维度计算的基础。注意 Model ID 必须和通道里实际可用的名称一致,写错会直接报 model not found。
{ "models": [ { "name": "gpt-4o", "model_id": "gpt-4o", "input_price_per_1k": 0.005, "output_price_per_1k": 0.015, "local_deployable": false }, { "name": "claude-3-5-sonnet", "model_id": "claude-3-5-sonnet-20241022", "input_price_per_1k": 0.003, "output_price_per_1k": 0.015, "local_deployable": false }, { "name": "gpt-4o-mini", "model_id": "gpt-4o-mini", "input_price_per_1k": 0.00015, "output_price_per_1k": 0.0006, "local_deployable": false } ] }3.2 主评测脚本
脚本的核心逻辑是:对每个模型、每条 prompt,记录开始时间、结束时间、token 用量,然后算出延迟和成本。这里用time.perf_counter()而不是time.time(),因为前者精度更高。
import json import os import time from openai import OpenAI from dotenv import load_dotenv load_dotenv() client = OpenAI( api_key=os.getenv("TAOTOKEN_API_KEY"), base_url=os.getenv("TAOTOKEN_BASE_URL"), ) def load_models(path="models.json"): with open(path, "r", encoding="utf-8") as f: return json.load(f)["models"] def load_prompts(path="prompts.jsonl"): prompts = [] with open(path, "r", encoding="utf-8") as f: for line in f: line = line.strip() if line: prompts.append(json.loads(line)) return prompts def run_one(model, prompt_text, max_tokens=512): start = time.perf_counter() resp = client.chat.completions.create( model=model["model_id"], messages=[{"role": "user", "content": prompt_text}], max_tokens=max_tokens, temperature=0, ) elapsed_ms = (time.perf_counter() - start) * 1000 usage = resp.usage input_tokens = usage.prompt_tokens output_tokens = usage.completion_tokens cost = ( input_tokens / 1000 * model["input_price_per_1k"] + output_tokens / 1000 * model["output_price_per_1k"] ) return { "model": model["name"], "latency_ms": round(elapsed_ms, 1), "input_tokens": input_tokens, "output_tokens": output_tokens, "cost_usd": round(cost, 6), "answer": resp.choices[0].message.content, } def main(): models = load_models() prompts = load_prompts() results = [] for model in models: for item in prompts: try: r = run_one(model, item["prompt"]) r["prompt_id"] = item["id"] results.append(r) print(f"[OK] {model['name']} / {item['id']} / {r['latency_ms']}ms / ${r['cost_usd']}") except Exception as e: print(f"[FAIL] {model['name']} / {item['id']} / {e}") with open("eval_results.json", "w", encoding="utf-8") as f: json.dump(results, f, ensure_ascii=False, indent=2) if __name__ == "__main__": main()prompts.jsonl每行一条,建议覆盖三类任务:简单分类、中等推理、复杂代码生成。这样能力维度的区分度才够。
{"id": "cls-01", "prompt": "判断下面这句话的情感倾向,只输出 positive/negative/neutral:这家店的服务态度很好,但上菜太慢了。"} {"id": "reason-01", "prompt": "一个水池有甲乙两个进水管,甲管单独注满需要 6 小时,乙管单独注满需要 4 小时。两管同时开,多久注满?给出计算过程。"} {"id": "code-01", "prompt": "用 Python 写一个函数,输入一个整数列表,返回其中所有偶数的平方和,要求处理空列表。"}3.3 把结果汇总成四维打分表
跑完eval.py后,你会得到eval_results.json。延迟维度直接取每个模型的 P50 和 P95;成本维度把每条 prompt 的cost_usd求和再除以条数,得到平均单次成本;能力维度需要人工或自动评分,简单做法是给每条 prompt 设标准答案,用字符串匹配或再调一个裁判模型打分。合规维度不来自脚本,而是来自部署方式:走公有 API 的记低分,可本地部署的记高分。
4. 验证请求与成功结果:确认通道可用并跑出第一份对比数据
4.1 最小验证请求
在跑完整评测前,先用一条 curl 确认 Key 和 Base URL 没问题。这一步能帮你排除 90% 的低级错误。
curl https://taotoken.net/api/chat/completions \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o-mini", "messages": [{"role": "user", "content": "只回复两个字:可用"}], "max_tokens": 16 }'如果返回的 JSON 里choices[0].message.content是“可用”,说明通道正常。如果返回 401,先检查 Key 是否复制完整、有没有多余空格;如果返回 model not found,检查 Model ID 拼写。
4.2 跑通评测并观察输出
执行python eval.py,终端会逐条打印每个模型每条 prompt 的延迟和成本。实测下来,轻量模型在简单分类任务上的延迟通常在 400–900ms,旗舰模型在复杂推理任务上可能到 3–8 秒。这个差距在交互式场景里非常关键。
跑完后打开eval_results.json,你会看到类似这样的记录:
[ { "model": "gpt-4o-mini", "prompt_id": "cls-01", "latency_ms": 612.4, "input_tokens": 48, "output_tokens": 3, "cost_usd": 0.000009, "answer": "neutral" }, { "model": "gpt-4o", "prompt_id": "reason-01", "latency_ms": 4210.7, "input_tokens": 62, "output_tokens": 210, "cost_usd": 0.00346, "answer": "2.4 小时..." } ]4.3 汇总成打分表
写一个小的汇总脚本,把eval_results.json按模型聚合:
import json from collections import defaultdict with open("eval_results.json", "r", encoding="utf-8") as f: results = json.load(f) agg = defaultdict(lambda: {"latency": [], "cost": 0.0, "count": 0}) for r in results: agg[r["model"]]["latency"].append(r["latency_ms"]) agg[r["model"]]["cost"] += r["cost_usd"] agg[r["model"]]["count"] += 1 for model, data in agg.items(): lat = sorted(data["latency"]) p50 = lat[len(lat) // 2] p95 = lat[int(len(lat) * 0.95) - 1] avg_cost = data["cost"] / data["count"] print(f"{model}: P50={p50:.0f}ms P95={p95:.0f}ms 平均成本=${avg_cost:.6f}")把输出填进第 2 节的四维打分表,能力维度用人工评分补上,合规维度按部署方式补上,一张可复现的选型矩阵就成型了。这套流程跑一次大约 10 分钟,但能省下后面几个月的反复纠结。
5. 本篇常见错排查:401、local proxy failed、reading choices、OAuth 报错怎么处理
5.1 401 Unauthorized
最常见的原因是 Key 没读到。如果你用.env,确认load_dotenv()在创建 client 之前调用;如果 Key 是从环境变量读的,确认 shell 里echo $TAOTOKEN_API_KEY有输出。还有一种情况是 Key 前后带了引号或空格,复制时容易带上。排查顺序:先 curl 验证,再检查 Python 读取逻辑。
5.2 local proxy failed / connection error
这个报错通常和网络环境有关。先确认base_url写的是https://taotoken.net/api,没有多余路径,也没有拼错。如果你在公司内网,检查是否有出站限制。注意不要在任何配置里写代理相关的字段,保持直连即可。如果 curl 能通但 Python 不通,多半是 SDK 版本问题,升级到最新版pip install -U openai。
5.3 reading choices 报错 / KeyError: 'choices'
这个报错说明返回的 JSON 结构里没有choices字段,通常是请求本身失败了,但脚本没检查异常就直接取字段。正确做法是在run_one里先判断resp.choices是否存在,或者用 try/except 包住。另一个原因是max_tokens设得太小,模型还没输出就被截断,某些通道会返回空 choices。把max_tokens调到 64 以上再试。
5.4 OAuth / 鉴权方式不匹配
如果你之前用的是 Anthropic 原生 SDK,切到 OpenAI 兼容协议时容易把x-api-key和Authorization: Bearer搞混。走 TaoToken 的 OpenAI 兼容通道时,统一用Authorization: Bearer <Key>。如果你在用 Claude Code 这类工具,它的配置项里需要同时填 Base URL、Key、Model ID 三件套,缺一个都会鉴权失败。Base URL 填https://taotoken.net/api,Key 填控制台创建的 Key,Model ID 填你要用的模型名。
5.5 成本算出来是 0
检查models.json里的单价字段名和脚本里读取的字段名是否一致。另一个可能是usage返回的 token 数为 0,这通常发生在请求被缓存或直接失败的情况下。加一行日志打印resp.usage就能定位。
6. 把选型变成可复现流程:统一 Key 通道下的持续评估
四维评估框架真正的价值不在于跑一次,而在于它能持续跑。模型在更新、价格在调整、新模型在不断出现,今天的最优解三个月后可能就不是了。有了统一 Key 通道和固定脚本,你只需要更新models.json里的模型清单和单价,重新执行一次python eval.py,就能得到新一期的对比数据。
我的实际做法是:主力模型处理 80% 的常规请求,轻量模型处理 15% 的简单任务,本地可部署模型处理 5% 的敏感数据。这个组合的月度成本能控制在很低的水平,同时核心任务的质量不打折。当调用量增长到需要精细化管理成本时,再把评测脚本接进 CI,每次模型更新自动跑一轮回归,用数据决定要不要切换。
如果你还没开始,建议先做三件事:在控制台创建一个 Key(https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ),用模型对话页面(https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= )手动试几条 prompt 感受差异,然后照着本文的脚本跑一遍完整评测。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,里面有各语言的调用示例。长期做编码类 Agent 的团队,可以了解 Coding Plan(https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ),把评测和日常开发放在同一条通道上管理。