上周有个读者问我:“现在国产大模型这么多,DeepSeek、通义千问、GLM-4、Kimi、豆包,到底该用哪个?”我愣了一下,说实话,我自己也纠结。平时写代码用DeepSeek挺顺手,但写文案的时候总觉得差点意思。写文案换Kimi?逻辑推理换GLM-4?每个都打开网页手动试一遍,太折磨人了。
干脆写个脚本,把5个模型拉出来统一跑一遍。同一组测试题,同时记录耗时和花费,最后输出一张对比表格。跑完你就知道,哪个模型适合干什么,钱花在哪里最值。
思路
整体思路很直接。我给每个模型写一个统一的调用适配器,不管底层API长什么样,对外都暴露同一个接口。然后准备一组测试题,涵盖代码生成、文案写作、逻辑推理、中文理解四个维度。每道题发给每个模型,记录响应时间和token用量,最后算出费用。
测试结果用pandas整理成DataFrame,再用tabulate输出成好看的表格。整个过程大概需要跑两分钟,取决于网络速度。
完整代码
先把依赖装好:
pip install openai pandas tabulate
这里用openai库是因为国产模型基本都兼容OpenAI的API格式,一个库就能调所有模型。
# pip install openai pandas tabulate# 各大模型的API key需要自己去对应平台申请,下面用占位符代替import timeimport jsonfrom dataclasses import dataclass, fieldfrom openai import OpenAIimport pandas as pdfrom tabulate import tabulate# ============ 第一步:定义模型配置 ============# 每个模型的基础信息,包括API地址、模型名、价格等# 价格单位:元/百万token(输入和输出分开算)MODEL_CONFIGS = {"DeepSeek": {"base_url": "https://api.deepseek.com/v1","model": "deepseek-chat","api_key": "YOUR_DEEPSEEK_API_KEY","input_price": 1.0, # 元/百万token"output_price": 2.0, },"通义千问": {"base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1","model": "qwen-plus","api_key": "YOUR_QWEN_API_KEY","input_price": 0.8,"output_price": 2.0, },"GLM-4": {"base_url": "https://open.bigmodel.cn/api/paas/v4","model": "glm-4-flash","api_key": "YOUR_GLM_API_KEY","input_price": 0.1, # flash版本很便宜"output_price": 0.1, },"Kimi": {"base_url": "https://api.moonshot.cn/v1","model": "moonshot-v1-8k","api_key": "YOUR_KIMI_API_KEY","input_price": 12.0,"output_price": 12.0, },"豆包": {"base_url": "https://ark.cn-beijing.volces.com/api/v3","model": "doubao-pro-4k","api_key": "YOUR_DOUBAO_API_KEY","input_price": 0.8,"output_price": 2.0, },}# ============ 第二步:统一调用适配器 ============# 不管哪个模型,调用的方式都一样# 这样后面就不用为每个模型写不同的调用逻辑了class ModelAdapter:"""统一的模型调用接口"""def __init__(self, name: str, config: dict):self.name = nameself.config = config# 用openai库创建客户端,指定base_url就能对接不同平台self.client = OpenAI(api_key=config["api_key"],base_url=config["base_url"], )def chat(self, prompt: str) ->dict:""" 发送一条消息,返回结果和统计信息 返回值包含:回复内容、耗时、输入token数、输出token数、费用 """start = time.time()try:response = self.client.chat.completions.create(model=self.config["model"],messages=[{"role": "user", "content": prompt}],temperature=0.7,max_tokens=2048, )elapsed = time.time() -start# 从返回里拿token用量usage = response.usageinput_tokens = usage.prompt_tokensoutput_tokens = usage.completion_tokens# 算费用:token数 × 单价 / 100万cost = (input_tokens*self.config["input_price"] /1_000_000+output_tokens*self.config["output_price"] /1_000_000 )return {"content": response.choices[0].message.content,"time": round(elapsed, 2),"input_tokens": input_tokens,"output_tokens": output_tokens,"cost": round(cost, 6), # 单次调用费用很便宜,保留6位小数"error": None, }except Exception as e:elapsed = time.time() -startreturn {"content": "","time": round(elapsed, 2),"input_tokens": 0,"output_tokens": 0,"cost": 0,"error": str(e), }# ============ 第三步:准备测试用例 ============# 四个维度,每个维度两到三道题# 题目设计尽量贴近实际使用场景TEST_CASES = [ {"category": "代码生成","prompt": "用Python写一个LRU缓存装饰器,要求支持设置最大容量和过期时间(TTL),过期时间单位是秒。给出完整代码和使用示例。", }, {"category": "代码生成","prompt": "用Python实现一个简单的异步HTTP请求限流器,限制每秒最多发10个请求,超出排队等待。给出代码和使用示例。", }, {"category": "文案写作","prompt": "帮我写一篇关于'远程办公的利与弊'的公众号文章开头,300字左右,风格轻松幽默,带点自嘲。", }, {"category": "文案写作","prompt": "给一款面向程序员的机械键盘写一段产品宣传文案,200字以内,要突出手感和静音两个卖点。", }, {"category": "逻辑推理","prompt": "一个房间里有100个人,每人随机戴一顶红色或蓝色的帽子。每个人能看到其他人的帽子颜色但看不到自己的。不允许任何人交流。现在要求所有人同时猜测自己帽子的颜色。请设计一个策略,使得猜对的人数期望值最大,并解释为什么。", }, {"category": "逻辑推理","prompt": "有5个海盗抢到100枚金币。他们按编号1-5依次提出分配方案,提出方案后所有人投票,如果赞成票数大于等于反对票数则通过,否则提出者被扔海里。每个海盗都足够聪明且贪婪,并且在不影响自己收益的情况下更倾向于把人扔海里。请问1号海盗应该怎么分配?", }, {"category": "中文理解","prompt": "解释以下三个成语的含义和区别:'不以为然'、'不以为意'、'不足为训'。分别造句说明用法。", }, {"category": "中文理解","prompt": "下面这句话有歧义,请分析所有可能的理解方式并解释原因:'咬死了猎人的狗'", },]# ============ 第四步:跑测试并收集结果 ============def run_benchmark():"""把所有测试题发给所有模型,收集结果"""# 先创建所有模型的适配器adapters = []fo rname, config in MODEL_CONFIGS.items():adapters.append(ModelAdapter(name, config))all_results = []for i, case in enumerate(TEST_CASES):print(f"\n--- 测试题 {i+1}/{len(TEST_CASES)} [{case['category']}] ---")print(f"题目:{case['prompt'][:50]}...")for adapter in adapters:print(f" 调用 {adapter.name}...", end=" ")result = adapter.chat(case["prompt"])if result["error"]:print(f"出错: {result['error']}")else:print(f"耗时{result['time']}s, 费用{result['cost']:.6f}元")# 把每条结果记录下来all_results.append({"测试类别": case["category"],"题目编号": i+1,"模型": adapter.name,"回复长度": len(result["content"]),"耗时(秒)": result["time"],"输入token": result["input_tokens"],"输出token": result["output_tokens"],"费用(元)": result["cost"],"是否出错": "是"ifresult["error"] else"否","回复内容": result["content"][:200], # 只保留前200字用于预览 })return all_results# ============ 第五步:生成对比报告 ============def generate_report(results: list):"""把结果整理成表格,输出到终端"""df = pd.DataFrame(results)# 按模型汇总统计summary = df.groupby("模型").agg(平均耗时=("耗时(秒)", "mean"),总费用=("费用(元)", "sum"),平均回复长度=("回复长度", "mean"),出错次数=("是否出错", lambdax: (x == "是").sum()), ).round(3)# 给列名加点说明summary["平均耗时"] = summary["平均耗时"].apply(lambdax: f"{x:.1f}s")summary["总费用"] = summary["总费用"].apply(lambdax: f"¥{x:.4f}")summary["平均回复长度"] = summary["平均回复长度"].apply(lambdax: f"{x:.0f}字")print("\n"+"="*60)print("📊 模型综合对比报告")print("="*60)print(tabulate(summary, headers="keys", tablefmt="github"))# 按类别细分print("\n"+"="*60)print("📋 分类别对比")print("="*60)for category in df["测试类别"].unique():cat_df = df[df["测试类别"] == category]cat_summary = cat_df.groupby("模型").agg(平均耗时=("耗时(秒)", "mean"),费用=("费用(元)", "sum"),平均长度=("回复长度", "mean"), ).round(3)print(f"\n【{category}】")print(tabulate(cat_summary, headers="keys", tablefmt="github"))# 保存详细数据到CSV,方便后续分析df.to_csv("llm_benchmark_results.csv", index=False, encoding="utf-8-sig")print("\n\n详细数据已保存到 llm_benchmark_results.csv")if __name__ == "__main__":print("开始跑大模型对比测试...")print(f"共 {len(TEST_CASES)} 道测试题 × {len(MODEL_CONFIGS)} 个模型")print(f"预计总请求数: {len(TEST_CASES) * len(MODEL_CONFIGS)}\n")results = run_benchmark()generate_report(results)运行效果
跑完之后终端会输出这样的表格:
===================================================模型综合对比报告===================================================| 模型 | 平均耗时 | 总费用 | 平均回复长度 | 出错次数 ||----------|----------|-----------|-------------|----------|| DeepSeek | 3.2s | ¥0.0134 | 680字 | 0 || 通义千问 | 2.8s | ¥0.0112 | 720字 | 0 || GLM-4 | 1.9s | ¥0.0008 | 550字 | 0 || Kimi | 4.5s | ¥0.0890 | 810字 | 0 || 豆包 | 2.5s | ¥0.0105 | 640字 | 0 |
从我的测试数据来看,几个发现挺有意思。GLM-4-flash是最便宜的,几乎不花钱,响应速度也快,但回复偏短,有时候会漏掉一些细节。Kimi回复最长最详细,但价格也是最贵的,差不多是其他模型的5到10倍。DeepSeek在代码生成方面表现确实好,逻辑清晰,基本能直接跑。通义千问比较均衡,没什么明显短板。豆包的性价比不错,速度和通义差不多,价格略低一点。
当然这只是我的一组测试题,你的场景可能不一样。代码里的测试用例可以随便换,加上你自己工作中常遇到的问题,跑出来的结果才更有参考价值。
几个使用建议
API key去各家官网申请就行,大部分都有免费额度,跑一遍测试花不了几分钱。如果你想加上更多模型,直接在MODEL_CONFIGS字典里加一项就行,其他代码不用动。
测试的时候建议多跑几轮取平均值,因为单次调用受网络波动影响比较大。你可以把run_benchmark()套个循环,跑3到5轮,结果会更稳定。
小结
一个脚本搞定5个模型的横向对比,统一接口、统一测试题、统一统计口径。选大模型这事,别看广告,跑一遍数据比什么都靠谱。
“无他,惟手熟尔”!有需要的用起来!关注微信公众号「Nicholas与Pypi」获取更多Python实战!------加入知识库与更多人一起学习------https://ima.qq.com/wiki/?shareId=f2628818f0874da17b71ffa0e5e8408114e7dbad46f1745bbd1cc1365277631c