在实际技术社区和开发者讨论中,我们经常会遇到关于不同大语言模型(LLM)在特定任务上表现的对比。这类讨论往往聚焦于模型的“战斗模式”(Battle Mode)或“提示词工程”(Prompt Engineering)的较量。虽然输入材料中的标题“Battle Mode - Kimi K3 vs GPT-5.6 Prompt”可能指向一个非正式的、带有娱乐性质的对比,但它背后反映了一个严肃且对开发者至关重要的技术议题:如何通过精心设计的提示词(Prompt)来最大化特定模型的能力,以及如何客观评估不同模型在特定场景下的表现。
对于开发者而言,无论是使用闭源的 GPT 系列模型,还是开源的、国产的 Kimi 等模型,核心目标都是高效、稳定地将其集成到自己的应用中,解决实际问题。因此,本文不会进行任何非正式的“对战”或排名,而是将从一个工程实践者的角度出发,深入探讨如何为不同的大语言模型设计有效的提示词,如何构建一个可复现的评估框架来测试模型在特定任务上的表现,以及在实际项目中如何根据需求进行模型选型。我们将以构建一个“代码审查助手”任务为例,贯穿全文,展示从提示词设计、环境准备、代码实现、结果评估到问题排查的完整流程。
1. 理解提示词工程与模型评估的核心概念
在深入实操之前,我们需要明确几个关键概念,这有助于理解后续所有步骤的设计动机。
1.1 什么是提示词(Prompt)及其工程化?
提示词是用户输入给大语言模型的指令或问题文本。模型的输出质量极大程度上依赖于提示词的清晰度、具体性和结构性。提示词工程,就是系统化地设计、优化和测试提示词,以引导模型产生更准确、更相关、更符合格式要求的输出。
一个糟糕的提示词可能是:“检查代码”。而一个工程化后的提示词则会包含角色定义、任务上下文、具体指令、输出格式约束和示例:
你是一个经验丰富的Java后端架构师。请审查以下Java方法的代码,重点关注性能、线程安全性和异常处理。 【代码开始】 public List<User> fetchUsers(List<Integer> ids) { List<User> result = new ArrayList<>(); for (Integer id : ids) { User user = userDao.getById(id); // 假设这是一个数据库查询 result.add(user); } return result; } 【代码结束】 请按以下格式提供审查报告: 1. **潜在问题**:列出发现的问题,每个问题附带简要说明。 2. **风险等级**:对每个问题标注【高】、【中】、【低】。 3. **改进建议**:针对每个问题提供具体的代码修改建议。1.2 为何需要系统化的模型评估?
直接对比“Kimi K3”和“GPT-5.6”这样的版本标签没有意义,因为:
- 模型能力多维性:一个模型可能在创意写作上优秀,但在代码生成上薄弱。评估必须在具体任务上进行。
- 提示词敏感性:同一个模型,使用不同的提示词,表现可能天差地别。公平对比要求使用优化过的、任务对应的提示词。
- 输出非确定性:大多数LLM的输出具有随机性(由
temperature参数控制),单次测试结果偶然性大,需要多次采样统计。 - 评估标准主观性:什么是“更好”的代码建议?需要将其转化为可量化的指标,如“建议采纳率”、“问题检出率”、“格式符合度”等。
因此,我们的目标不是给出“谁赢谁输”的结论,而是建立一套方法,让开发者能为自己关心的任务,找到最合适的“模型-提示词”组合。
1.3 典型评估框架组件
一个可复现的评估框架通常包含:
- 任务数据集:一组具有标准输入和期望输出的测试用例(例如,100个需要审查的代码片段)。
- 提示词模板:可参数化插入具体测试用例的提示词结构。
- 模型调用层:封装对不同模型API(如OpenAI API、Kimi API、本地模型)的调用,统一接口。
- 评估函数:将模型输出与期望输出(或评估规则)进行比较,生成量化分数(如0-1分)。
- 结果分析与可视化:汇总分数,计算平均值、标准差,并生成对比图表。
2. 环境准备与依赖配置
我们将使用Python来构建这个评估框架,因为它有丰富的LLM SDK和科学计算库。以下环境假设你正在进行一个全新的项目。
2.1 创建项目目录与虚拟环境
首先,隔离项目环境以避免依赖冲突。
# 创建项目目录 mkdir llm-battle-eval && cd llm-battle-eval # 创建虚拟环境(以Python 3.10为例) python3.10 -m venv venv # 激活虚拟环境 # Linux/macOS source venv/bin/activate # Windows # venv\Scripts\activate # 升级pip pip install --upgrade pip2.2 安装核心依赖
我们需要安装调用模型API的SDK、用于评估的辅助库以及结果处理的库。创建requirements.txt文件:
# 模型API客户端 (示例:OpenAI, Kimi) openai>=1.0.0 # 用于GPT系列模型 # 注意:Kimi的官方SDK可能不同,这里假设有一个`moonshot`包,请根据实际API提供商安装 # pip install moonshot 或通过其他方式安装 # 用于HTTP请求和配置管理 requests>=2.28.0 python-dotenv>=0.19.0 # 用于数据处理和评估 pandas>=1.5.0 numpy>=1.23.0 scikit-learn>=1.2.0 # 用于一些高级评估指标 # 用于结果可视化 matplotlib>=3.5.0 seaborn>=0.12.0 # 用于进度显示 tqdm>=4.65.0然后安装依赖:
pip install -r requirements.txt注意:
moonshot包名仅为示例。国内如Kimi、通义千问、文心一言等模型,需查阅其官方文档获取正确的Python SDK安装方式。通常它们会提供自己的pip包或GitHub仓库。
2.3 配置API密钥与环境变量
永远不要将API密钥硬编码在代码中。使用.env文件管理敏感信息。
在项目根目录创建
.env文件:touch .env在
.env文件中填入你的API密钥(请从对应平台获取):# OpenAI GPT (如果评估GPT模型) OPENAI_API_KEY=sk-your-openai-api-key-here OPENAI_API_BASE=https://api.openai.com/v1 # 如果使用代理或特定端点,可修改 # Kimi (示例,变量名和格式需按官方SDK要求) MOONSHOT_API_KEY=your-kimi-api-key-here MOONSHOT_API_BASE=https://api.moonshot.cn/v1 # 其他模型... # QWEN_API_KEY=... # BAIDU_API_KEY=...创建
.gitignore文件,确保.env不会被提交到版本库:venv/ .env __pycache__/ *.pyc .DS_Store results/ logs/
2.4 项目结构设计
一个清晰的项目结构有助于维护。建议如下:
llm-battle-eval/ ├── .env # 环境变量(密钥) ├── .gitignore ├── requirements.txt ├── config/ # 配置文件目录 │ └── prompts.yaml # 提示词模板 ├── data/ # 数据目录 │ ├── raw/ # 原始测试数据 │ └── processed/ # 处理后的数据 ├── src/ # 源代码 │ ├── __init__.py │ ├── evaluator.py # 评估框架核心逻辑 │ ├── models/ # 模型调用封装 │ │ ├── __init__.py │ │ ├── openai_client.py │ │ └── kimi_client.py # 或其他国产模型客户端 │ ├── prompts/ # 提示词管理 │ │ ├── __init__.py │ │ └── code_review.py │ └── utils/ # 工具函数 │ ├── __init__.py │ └── metrics.py # 评估指标计算 ├── tests/ # 单元测试 ├── scripts/ # 执行脚本 │ └── run_evaluation.py └── results/ # 评估结果输出(自动生成) ├── figures/ └── tables/3. 构建代码审查任务评估框架
我们将以“代码审查”作为评估任务。本节将逐步实现框架的各个组件。
3.1 准备测试数据集
在data/raw/下创建code_review_samples.jsonl文件(JSON Lines格式,每行一个独立JSON对象)。这里提供3个示例,实际项目可能需要上百个。
{"id": 1, "code": "public int sum(List<Integer> list) {\n int sum = 0;\n for (int i = 0; i < list.size(); i++) {\n sum += list.get(i);\n }\n return sum;\n}", "language": "java", "expected_issues": ["使用索引遍历List效率低于增强for循环或迭代器", "未处理list为null的情况"]} {"id": 2, "code": "def fetch_data(url):\n response = requests.get(url)\n return response.json()", "language": "python", "expected_issues": ["未处理网络请求异常(如requests.exceptions.RequestException)", "未处理HTTP错误状态码(如404, 500)", "未设置请求超时"]} {"id": 3, "code": "async function getUser(id) {\n const user = await db.query('SELECT * FROM users WHERE id = ?', [id]);\n return user[0];\n}", "language": "javascript", "expected_issues": ["未处理数据库查询可能返回空数组的情况", "未进行SQL注入防御(虽然参数化查询已部分解决,但依赖的db.query实现需确认)"]}每个样本包含:
id: 唯一标识。code: 待审查的代码片段。language: 编程语言。expected_issues: 期望模型能发现的已知问题列表(作为评估的参考答案之一)。
3.2 设计并管理提示词模板
在config/prompts.yaml中定义提示词模板,便于管理和迭代。
code_review: system_message: | 你是一个严谨、专业的{language}开发专家,擅长代码审查和性能优化。 user_message_template: | 请仔细审查以下{language}代码,找出其中的潜在问题,包括但不限于:性能瓶颈、线程安全隐患、空指针异常、资源泄露、代码风格问题、逻辑错误、安全漏洞等。 请严格按照以下格式输出: ### 审查报告 **代码摘要**:简要说明该函数/代码块的目的。 **发现问题**: 1. [问题1描述]。【风险等级:高/中/低】。**改进建议**:[具体修改建议]。 2. [问题2描述]。【风险等级:高/中/低】。**改进建议**:[具体修改建议]。 **总结**:对代码质量的整体评价。 待审查代码: ```{language} {code} ```在src/prompts/code_review.py中编写加载和渲染提示词的函数:
import yaml from pathlib import Path def load_prompt_template(template_name: str, variables: dict) -> tuple[str, str]: """ 加载并渲染提示词模板。 返回 (system_message, user_message) """ prompt_config_path = Path(__file__).parent.parent.parent / "config" / "prompts.yaml" with open(prompt_config_path, 'r', encoding='utf-8') as f: config = yaml.safe_load(f) if template_name not in config: raise ValueError(f"Prompt template '{template_name}' not found in config.") template = config[template_name] system_msg = template['system_message'].format(**variables) user_msg = template['user_message_template'].format(**variables) return system_msg, user_msg # 示例用法 if __name__ == "__main__": vars = {"language": "Java", "code": "public void test() {}"} sys_msg, user_msg = load_prompt_template("code_review", vars) print("System:", sys_msg) print("User:", user_msg)3.3 封装模型调用客户端
为不同模型创建统一的调用接口。首先定义基础类src/models/base_client.py:
from abc import ABC, abstractmethod import logging from typing import Optional, Dict, Any logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) class BaseLLMClient(ABC): """大语言模型客户端抽象基类""" def __init__(self, model_name: str, api_key: str, base_url: Optional[str] = None): self.model_name = model_name self.api_key = api_key self.base_url = base_url @abstractmethod def generate( self, system_message: str, user_message: str, temperature: float = 0.2, # 评估时建议较低温度,减少随机性 max_tokens: int = 2000, **kwargs ) -> str: """ 调用模型生成内容。 返回模型输出的文本。 """ pass def _log_call(self, prompt_preview: str, response: str): """记录调用日志(生产环境可接入更专业的日志系统)""" logger.debug(f"Model: {self.model_name}, Prompt Preview: {prompt_preview[:200]}...") logger.debug(f"Response Preview: {response[:200]}...")然后实现具体的客户端,例如src/models/openai_client.py:
import openai from openai import OpenAI from .base_client import BaseLLMClient import os from dotenv import load_dotenv load_dotenv() # 加载.env文件中的环境变量 class OpenAIClient(BaseLLMClient): def __init__(self, model_name: str = "gpt-4-turbo-preview"): api_key = os.getenv("OPENAI_API_KEY") base_url = os.getenv("OPENAI_API_BASE", "https://api.openai.com/v1") if not api_key: raise ValueError("OPENAI_API_KEY not found in environment variables.") super().__init__(model_name, api_key, base_url) self.client = OpenAI(api_key=api_key, base_url=base_url) def generate(self, system_message: str, user_message: str, temperature: float = 0.2, max_tokens: int = 2000, **kwargs) -> str: try: response = self.client.chat.completions.create( model=self.model_name, messages=[ {"role": "system", "content": system_message}, {"role": "user", "content": user_message} ], temperature=temperature, max_tokens=max_tokens, **kwargs ) content = response.choices[0].message.content self._log_call(user_message[:100], content) return content.strip() if content else "" except Exception as e: logger.error(f"OpenAI API call failed: {e}") # 根据业务需求,可以选择重试、返回空字符串或抛出异常 return ""类似地,创建src/models/kimi_client.py(假设使用moonshotSDK,具体需按官方文档调整):
# 示例结构,实际SDK调用方式请查阅对应模型平台文档 # from moonshot import MoonshotClient # 假设的导入 import os from dotenv import load_dotenv from .base_client import BaseLLMClient load_dotenv() class KimiClient(BaseLLMClient): def __init__(self, model_name: str = "moonshot-v1-8k"): # 示例模型名 api_key = os.getenv("MOONSHOT_API_KEY") base_url = os.getenv("MOONSHOT_API_BASE", "https://api.moonshot.cn/v1") if not api_key: raise ValueError("MOONSHOT_API_KEY not found in environment variables.") super().__init__(model_name, api_key, base_url) # 初始化官方客户端,这里为示例 # self.client = MoonshotClient(api_key=api_key, base_url=base_url) def generate(self, system_message: str, user_message: str, temperature: float = 0.2, max_tokens: int = 2000, **kwargs) -> str: # 实际调用代码需替换为官方SDK方式 # 例如: # response = self.client.chat.completions.create(...) # return response.choices[0].message.content # 此处为保逻辑完整,模拟一个调用 print(f"[Simulating Kimi Call] Model: {self.model_name}") print(f"System: {system_message[:50]}...") print(f"User: {user_message[:100]}...") # 模拟返回 return "### 审查报告\n**代码摘要**:模拟审查。\n**发现问题**:\n1. 示例问题。【风险等级:中】。**改进建议**:示例建议。\n**总结**:模拟总结。"3.4 实现评估器核心逻辑
在src/evaluator.py中,构建评估流程。
import json import pandas as pd from pathlib import Path from typing import List, Dict, Any, Callable from tqdm import tqdm import logging from src.models.base_client import BaseLLMClient from src.prompts.code_review import load_prompt_template logger = logging.getLogger(__name__) class CodeReviewEvaluator: def __init__(self, llm_client: BaseLLMClient, prompt_template_name: str = "code_review"): self.llm_client = llm_client self.prompt_template_name = prompt_template_name def load_dataset(self, file_path: Path) -> List[Dict[str, Any]]: """加载JSON Lines格式的数据集""" samples = [] with open(file_path, 'r', encoding='utf-8') as f: for line in f: samples.append(json.loads(line.strip())) logger.info(f"Loaded {len(samples)} samples from {file_path}") return samples def run_evaluation(self, dataset: List[Dict[str, Any]], max_samples: int = None) -> pd.DataFrame: """在数据集上运行评估,返回包含输入、输出、评分的结果DataFrame""" if max_samples: dataset = dataset[:max_samples] # 用于快速测试 results = [] for sample in tqdm(dataset, desc=f"Evaluating with {self.llm_client.model_name}"): # 1. 准备提示词 system_msg, user_msg = load_prompt_template( self.prompt_template_name, {"language": sample["language"], "code": sample["code"]} ) # 2. 调用模型 try: model_output = self.llm_client.generate(system_msg, user_msg) except Exception as e: logger.error(f"Failed to generate for sample {sample['id']}: {e}") model_output = "" # 3. 评估输出(此处为简化,实际需要更复杂的解析和匹配) score = self._evaluate_output(model_output, sample) # 4. 记录结果 results.append({ "sample_id": sample["id"], "language": sample["language"], "input_code": sample["code"], "expected_issues": sample.get("expected_issues", []), "model_output": model_output, "score": score, "model_name": self.llm_client.model_name }) return pd.DataFrame(results) def _evaluate_output(self, model_output: str, sample: Dict[str, Any]) -> float: """ 评估模型输出。 这是一个简化的评估函数,实际项目需要更复杂的NLP匹配或规则判断。 此处我们简单检查: 1. 输出是否包含关键部分(如“审查报告”)。 2. 是否提到了我们期望的部分问题(基于expected_issues)。 返回一个0-1之间的分数。 """ score = 0.0 # 检查格式基本符合度 if "### 审查报告" in model_output and "**发现问题**" in model_output: score += 0.3 # 检查是否提到期望的问题(非常简单的关键词匹配,生产环境需用更智能的方法) expected_issues = sample.get("expected_issues", []) if expected_issues: found_count = 0 for issue in expected_issues: # 简单判断:如果模型输出中包含问题的核心词(这里简化处理) # 实际应用应使用更精确的文本相似度计算 if any(keyword in model_output.lower() for keyword in issue.lower().split()[:3]): # 取前三个词作为关键词 found_count += 1 if found_count > 0: score += 0.7 * (found_count / len(expected_issues)) return round(score, 2) def save_results(self, df: pd.DataFrame, output_dir: Path): """保存评估结果到CSV和JSON文件""" output_dir.mkdir(parents=True, exist_ok=True) timestamp = pd.Timestamp.now().strftime("%Y%m%d_%H%M%S") model_safe_name = self.llm_client.model_name.replace("/", "_") csv_path = output_dir / f"results_{model_safe_name}_{timestamp}.csv" json_path = output_dir / f"results_{model_safe_name}_{timestamp}.json" df.to_csv(csv_path, index=False, encoding='utf-8-sig') df.to_json(json_path, orient='records', lines=True, force_ascii=False) logger.info(f"Results saved to {csv_path} and {json_path}")3.5 编写评估执行脚本
创建scripts/run_evaluation.py作为入口点。
#!/usr/bin/env python3 import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).parent.parent)) from src.evaluator import CodeReviewEvaluator from src.models.openai_client import OpenAIClient from src.models.kimi_client import KimiClient # 假设已实现 import pandas as pd import matplotlib.pyplot as plt import seaborn as sns def main(): # 1. 初始化模型客户端 # 评估GPT-4 # gpt_client = OpenAIClient(model_name="gpt-4-turbo-preview") # 评估Kimi (示例) kimi_client = KimiClient(model_name="moonshot-v1-8k") # 2. 初始化评估器 evaluator = CodeReviewEvaluator(llm_client=kimi_client, prompt_template_name="code_review") # 3. 加载数据集 data_path = Path(__file__).parent.parent / "data" / "raw" / "code_review_samples.jsonl" dataset = evaluator.load_dataset(data_path) # 4. 运行评估(这里先用Kimi示例,实际可循环多个客户端) results_df = evaluator.run_evaluation(dataset, max_samples=5) # 先用5个样本测试 # 5. 保存结果 output_dir = Path(__file__).parent.parent / "results" evaluator.save_results(results_df, output_dir) # 6. 简单分析与可视化 print("\n=== 评估结果摘要 ===") print(results_df[['sample_id', 'model_name', 'score']]) print(f"\n平均分: {results_df['score'].mean():.2f}") print(f"标准差: {results_df['score'].std():.2f}") # 可视化(如果有多模型结果,可以对比) plt.figure(figsize=(8,5)) sns.barplot(data=results_df, x='sample_id', y='score', hue='model_name', palette='viridis') plt.title('Code Review Evaluation Scores by Model') plt.ylabel('Score') plt.xlabel('Sample ID') plt.ylim(0, 1) plt.tight_layout() fig_path = output_dir / f"score_comparison.png" plt.savefig(fig_path) print(f"\n评分对比图已保存至: {fig_path}") # plt.show() # 在无GUI环境可注释掉 if __name__ == "__main__": main()4. 运行、验证与结果分析
4.1 执行评估脚本
在项目根目录下运行:
python scripts/run_evaluation.py如果一切配置正确,你将看到进度条,并最终在results/目录下生成CSV、JSON结果文件和一个评分对比图。
4.2 验证输出与排查常见问题
运行后,首先检查控制台输出和日志。常见问题及排查路径如下:
| 问题现象 | 可能原因 | 检查方式 | 处理建议 |
|---|---|---|---|
ModuleNotFoundError: No module named 'openai' | 依赖未安装或虚拟环境未激活 | pip list | grep openai | 激活虚拟环境,运行pip install -r requirements.txt |
ValueError: OPENAI_API_KEY not found | .env文件未创建或密钥未正确设置 | 检查项目根目录下是否存在.env文件,并确认变量名与代码中os.getenv()读取的键名一致 | 创建/修改.env文件,确保密钥正确,重启终端或IDE |
openai.AuthenticationError | API密钥无效、过期或额度不足 | 登录对应平台控制台检查密钥状态和余额 | 更换有效密钥,或检查API Base URL是否正确(如使用代理) |
| 请求超时或网络错误 | 网络连接问题,或国内访问国际API不稳定 | 使用curl或ping测试API端点连通性 | 配置网络代理,或考虑使用国内可稳定访问的模型API |
| 模型输出为空或格式完全不符合 | 提示词渲染错误,或模型未理解指令 | 打印出实际发送的system_message和user_message | 检查prompts.yaml格式和变量替换逻辑,简化提示词进行测试 |
| 评估分数全部为0 | _evaluate_output函数逻辑过于严格或与输出不匹配 | 手动查看几个model_output字段,对比expected_issues | 调整评估函数,例如使用更宽松的字符串匹配,或引入相似度计算(如difflib或rouge) |
4.3 分析评估结果
打开生成的CSV文件,你可以看到类似以下的数据:
| sample_id | language | model_name | score | model_output (摘要) |
|---|---|---|---|---|
| 1 | java | moonshot-v1-8k | 0.85 | 发现了未处理null和遍历效率问题... |
| 2 | python | moonshot-v1-8k | 0.90 | 发现了未处理异常和超时设置问题... |
| 3 | javascript | moonshot-v1-8k | 0.60 | 发现了空数组问题,但未提及SQL注入... |
关键分析点:
- 平均分与标准差:反映模型在该任务上的整体表现和稳定性。
- 分样本查看:模型在哪些代码样例上得分高/低?原因是什么?是提示词问题、模型能力问题,还是评估标准问题?
- 输出质量人工复核:分数只是量化参考,必须人工阅读
model_output,判断建议是否正确、可行、有深度。例如,模型是否给出了具体的代码修改示例? - 格式符合度:模型是否严格遵守了提示词中要求的输出格式?这对于后续自动化处理至关重要。
5. 深入优化:从简单评估到生产级评测
上述框架是一个起点。要得到可靠、有指导意义的结论,还需要在以下几个方面进行深化:
5.1 设计更科学的评估指标
简单的关键词匹配评分太粗糙。可以考虑:
- 精确匹配(Exact Match):模型提出的问题列表是否与专家标注的列表完全一致?(要求过高)
- F1分数:将问题发现视为分类任务,计算精确率(Precision)和召回率(Recall)的调和平均。
- 基于LLM的评估:使用另一个更强大的LLM(如GPT-4)作为“裁判”,根据一套标准对回答进行评分。这需要设计详细的评分规则(Rubric)。
- 人工评估:黄金标准,但成本高。可以抽样进行。
在src/utils/metrics.py中实现更复杂的评估函数:
import difflib from typing import List def calculate_semantic_similarity(text1: str, text2: str) -> float: """计算两个文本的语义相似度(简化版:使用序列匹配器)""" seq = difflib.SequenceMatcher(None, text1.lower(), text2.lower()) return seq.ratio() def evaluate_issues_found(model_issues: List[str], expected_issues: List[str], threshold: float = 0.6) -> dict: """ 评估模型发现的问题。 返回包含精确率、召回率、F1的字典。 """ if not expected_issues: return {"precision": None, "recall": None, "f1": None} tp = 0 # 真正例:模型发现且匹配上的问题 fp = 0 # 假正例:模型发现但未匹配上的问题 fn = 0 # 假负例:期望有但模型未发现的问题 matched_expected = set() for m_issue in model_issues: matched = False for e_issue in expected_issues: if calculate_semantic_similarity(m_issue, e_issue) > threshold: tp += 1 matched_expected.add(e_issue) matched = True break if not matched: fp += 1 fn = len([e for e in expected_issues if e not in matched_expected]) precision = tp / (tp + fp) if (tp + fp) > 0 else 0.0 recall = tp / (tp + fn) if (tp + fn) > 0 else 0.0 f1 = 2 * precision * recall / (precision + recall) if (precision + recall) > 0 else 0.0 return {"precision": round(precision, 3), "recall": round(recall, 3), "f1": round(f1, 3)}5.2 实现多模型并行评估与对比
修改run_evaluation.py,使其能循环测试多个模型,并生成对比报告。
# 在 scripts/run_evaluation.py 中扩展 def compare_models(model_configs: List[dict], dataset: List[Dict], output_dir: Path): """比较多个模型在同一数据集上的表现""" all_results = [] for config in model_configs: client_class = config['client_class'] client = client_class(model_name=config['model_name']) evaluator = CodeReviewEvaluator(llm_client=client, prompt_template_name="code_review") results_df = evaluator.run_evaluation(dataset, max_samples=None) # 使用全部数据 all_results.append(results_df) evaluator.save_results(results_df, output_dir / config['model_name'].replace("/", "_")) # 聚合分析 comparison_data = [] for df in all_results: comparison_data.append({ 'model': df['model_name'].iloc[0], 'avg_score': df['score'].mean(), 'std_score': df['score'].std(), 'samples': len(df) }) comparison_df = pd.DataFrame(comparison_data) print("\n=== 模型对比 ===") print(comparison_df.sort_values('avg_score', ascending=False)) # ... 生成更丰富的对比图表 ...5.3 提示词迭代与A/B测试
提示词对结果影响巨大。应系统化地进行A/B测试。
- 创建提示词变体:在
prompts.yaml中为同一任务定义多个模板(如code_review_v1,code_review_v2)。 - 设计实验:固定模型和数据集,轮流使用不同提示词模板进行评估。
- 分析结果:比较不同提示词带来的平均分、标准差、格式符合率等差异。
- 分析原因:为什么某个提示词效果更好?是因为角色定义更清晰?指令更具体?还是格式约束更有效?
5.4 处理速率限制、重试与缓存
生产级评估需要处理API限制和网络波动。
- 速率限制:在客户端中加入请求间隔(
time.sleep)或使用令牌桶算法。 - 自动重试:对可重试的错误(如网络超时、速率限制)实现带退避策略的重试机制。
- 结果缓存:将
(model, prompt, input)三元组哈希后作为键,将输出缓存到本地文件或数据库,避免重复调用产生费用和浪费时间。
6. 项目总结与最佳实践
通过构建这样一个评估框架,我们能够超越主观的“Battle Mode”对比,进行客观、可量化的模型能力评估。以下是关键收获和最佳实践:
- 评估始于清晰的任务定义:不要泛泛地比较模型,要明确你要用它做什么(代码审查、文本摘要、数据提取等)。
- 数据是评估的基石:构建一个高质量、有代表性的测试数据集(含参考答案)比选择模型更重要。
- 提示词是模型的“编程语言”:投入时间系统化地设计和测试提示词,其回报可能超过切换一个更强大的模型。
- 量化指标与人工复核相结合:自动化评分提供效率和大规模对比,但关键样本的人工深度分析不可或缺,它能发现自动化评估的盲点。
- 环境隔离与配置外置:使用虚拟环境、
.env文件、配置文件,确保项目可复现、可协作。 - 为生产环境做好准备:如果评估结果用于指导生产选型,必须考虑模型的稳定性(API可用性)、延迟、成本以及合规性(数据是否出境、是否符合监管要求)。
对于想深入此领域的开发者,下一步可以:
- 探索更复杂的评估任务,如多轮对话、复杂推理、长文本生成。
- 集成更多开源和闭源模型(如 Claude, Gemini, 国内各大厂商模型)。
- 构建Web界面,方便非技术同学提交测试用例和查看评估报告。
- 将评估框架CI/CD化,在模型或提示词更新后自动运行回归测试。
最终,选择哪个模型或哪个提示词,取决于你在特定任务上的评估结果、项目预算、技术栈整合难度以及合规要求。没有绝对的“胜者”,只有最适合你当前场景的“解决方案”。