agent a b testing framework

智能体 A/B 测试框架设计与实现

为什么智能体需要专属的 A/B 测试框架 传统的 A/B 测试方法论起源于 Web 产品优化领域——按钮颜色、页面布局、推荐策略的对比。但 AI 智能体(Agent)与传统软件产品有着本质区别:智能体的输出具有非确定性、多步骤推理和工具调用链路等特征。一个智能体在相同输入下可能产生截然不同的行为路径,这使得经典的"单次曝光-单次转化"测试模型不再适用。 智能体 A/B 测试框架需要解决三个核心挑战: 输出空间高维化:智能体的回复不仅是文本,还包含工具调用序列、中间推理步骤和最终决策,评估维度极其丰富。 非确定性重复:同一版本的智能体对同一输入可能给出不同答案,需要多次重复实验才能估计真实分布。 长期效果评估:智能体在多轮对话中的策略累积效应(如记忆机制、上下文管理)使得短期指标可能误导长期判断。 框架整体架构 一个完整的智能体 A/B 测试框架由以下五层组成: 第一层:实验管理层 实验管理层负责实验的全生命周期管理,包括实验创建、流量分配、版本控制和实验终止逻辑。 ExperimentManager ├── ExperimentConfig │ ├── variants: [Variant A (control), Variant B (treatment)] │ ├── traffic_split: {A: 50%, B: 50%} │ ├── min_sample_size: 500 │ ├── significance_level: 0.05 │ └── max_duration: 14d ├── TrafficAllocator (一致性哈希) └── ExperimentRegistry (实验元数据存储) 流量分配采用一致性哈希策略,确保同一用户在实验期间始终被分配到同一变体,避免交叉污染。对于智能体场景,还需要考虑会话级别的分配——同一用户的不同对话 session 可能需要独立分配,以支持对话内的策略迭代。 第二层:数据采集层 数据采集层是框架的感知系统,负责捕获智能体运行过程中的全链路数据。与传统 A/B 测试不同,智能体测试需要记录的不仅是输入和输出,还包括: 推理轨迹:每一步思考的内容、使用的提示模板、温度参数 工具调用日志:调用了哪些工具、调用顺序、参数、返回结果、耗时 中间状态快照:上下文窗口的演变、记忆检索结果、规划树的中间节点 环境交互记录:智能体与外部环境的每次交互及其后果 class AgentTraceRecorder: def __init__(self): self.trace_schema = { "input": str, # 用户输入 "variant_id": str, # 实验变体 "reasoning_steps": list[dict], # 推理步骤序列 "tool_calls": list[dict], # 工具调用序列 "intermediate_states": list[dict], # 中间状态 "final_output": str, # 最终输出 "latency_ms": int, # 端到端延迟 "token_usage": dict, # Token 消耗 "error_info": dict | None # 错误信息 } 第三层:评估指标层 这是框架最关键的部分。智能体的评估指标体系分为四个层次: 第一层:结果质量指标 任务完成率:智能体是否正确完成了用户请求(二值指标,需人工或 LLM-as-Judge 标注) 输出准确率:对于有标准答案的任务,计算精确匹配或语义相似度 用户满意度:显式反馈(点赞/点踩)和隐式信号(是否继续追问、会话长度) 第二层:过程质量指标 ...

2026-06-26 · 2 min · 310 words · 硅基 AGI 探索者
agent evaluation framework

Agent 评估框架:从任务完成率到自主性评级

引言:为什么 Agent 评估如此重要? 随着 AI Agent 从实验走向生产,如何评估一个 Agent 的能力变得至关重要。传统的 LLM 评估方法(如 BLEU、ROUGE)完全不适用于 Agent 场景——Agent 评估需要衡量推理、规划、工具使用和自主决策等多维度能力。 Agent 评估维度 核心评估矩阵 维度 子维度 关键指标 评估方法 任务完成 成功率、完成质量 Task Success Rate 端到端测试 推理能力 逻辑、规划、反思 Step Accuracy 过程分析 工具使用 选择准确、调用正确 Tool Call F1 行为日志 自主性 无需人工介入程度 Autonomy Level 交互统计 安全性 越权操作、有害输出 Safety Score 红队测试 效率 Token、时间、成本 Cost per Task 资源追踪 自主性评级标准 借鉴 SAE 自动驾驶分级思路,我们定义 Agent 自主性等级: 等级 名称 描述 人工介入频率 L0 无自主 每步都需人工指令 100% L1 辅助执行 Agent 执行但需人工确认每步 >80% L2 条件自主 简单操作自主,复杂操作需确认 40-80% L3 有界自主 在定义范围内完全自主 10-40% L4 高度自主 仅在边界情况需要人工 <10% L5 完全自主 无需任何人工介入 0% 主流评估基准 1. AgentBench OpenAI 推出的综合 Agent 评估基准: ...

2026-06-25 · 5 min · 1020 words · 硅基 AGI 探索者
llm creative eval

LLM 创意评估方法:从主观评分到自动化指标

创意评估的困境 评估代码生成的正确性很简单——跑测试就行。评估事实问答的准确性也不难——对比标准答案。但当你让 LLM 写一首诗、创作一个短篇故事、或生成一个创意广告时,“好不好” suddenly becomes a very hard question。 创意评估面临三个核心挑战: 主观性:同一篇作品,不同读者评价可能天差地别 多维性:创意好不好涉及语言、结构、新颖度、情感共鸣等多个维度 缺乏标准答案:创意任务没有唯一正确答案,甚至没有"参考答案" 评估方法分类 创意评估方法 ├── 人工评估 │ ├── 整体评分(Holistic Scoring) │ ├── 多维 Rubric 评分 │ └── 排序比较(Pairwise Ranking) ├── 自动化指标 │ ├── 多样性指标(Distinct-n, Self-BLEU) │ ├── 新颖度指标(Semantic Novelty) │ └── 连贯性指标(Coherence Score) └── LLM-as-Judge ├── 单维度评分 ├── 多维度 Rubric └── 对比较(Pairwise Comparison) 一、人工评估体系 整体评分法 最直接的方式:让评分者对作品给出一个综合分数(1-5 分或 1-10 分)。 class HolisticScoring: """整体评分体系""" SCALE_5 = { 5: "杰出 — 创意独特,语言精炼,情感打动人心", 4: "优秀 — 有明确创意点,语言流畅,有一定感染力", 3: "合格 — 表达清晰,但创意普通,缺乏亮点", 2: "较差 — 表达生硬,创意陈旧,难以读完", 1: "不合格 — 逻辑混乱,语言不通,无法理解" } def __init__(self, num_raters: int = 3): self.num_raters = num_raters def aggregate(self, scores: list[int]) -> dict: import numpy as np scores = sorted(scores) return { "mean": np.mean(scores), "median": np.median(scores), "std": np.std(scores), "min": min(scores), "max": max(scores), "agreement": self._inter_rater_agreement(scores), } def _inter_rater_agreement(self, scores: list[int]) -> float: """计算评分者间一致性""" if len(scores) < 2: return 1.0 mean = sum(scores) / len(scores) variance = sum((s - mean) ** 2 for s in scores) / len(scores) max_variance = (max(scores) - min(scores)) ** 2 / 4 return 1 - (variance / max_variance) if max_variance > 0 else 1.0 多维 Rubric 评分 整体评分太粗,更精细的方法是定义多个评分维度: ...

2026-06-25 · 7 min · 1481 words · 硅基 AGI 探索者
hallucination measurement

LLM 幻觉测量方法:从人工标注到自动检测

幻觉问题的严重性 LLM 幻觉——模型生成看似合理但实际不正确的信息——是当前大语言模型最严重的问题之一。在闲聊场景中,幻觉可能只是闹个笑话;但在医疗、法律、金融等高风险场景中,幻觉可能造成严重后果。 根据 2025 年的研究统计: 场景 典型幻觉率 后果严重程度 事实问答 5-15% 中 代码生成 10-25% 中高 医疗咨询 8-20% 极高 法律引用 15-30% 极高 历史事件 10-20% 高 数学推理 15-30% 中 人物传记 20-40% 高 幻觉分类体系 LLM 幻觉类型 ├── 事实性幻觉(Factual Hallucination) │ ├── 实体幻觉:编造不存在的人名/地名/机构 │ ├── 关系幻觉:编造人物之间的关系 │ ├── 数字幻觉:编造统计数据或日期 │ └── 引用幻觉:编造论文/法律/新闻报道 ├── 逻辑性幻觉(Logical Hallucination) │ ├── 推理跳跃:跳过关键推理步骤 │ ├── 循环论证:用结论证明结论 │ └── 因果倒置:混淆原因和结果 ├── 上下文幻觉(Contextual Hallucination) │ ├── 矛盾输出:与之前回答自相矛盾 │ ├── 忽略约束:不遵守 prompt 中的约束 │ └── 过度推断:超出给定信息范围 └── 格式幻觉(Format Hallucination) ├── 结构错误:输出格式不符合要求 └── 引用伪造:伪造可验证的引用来源 一、人工标注体系 幻觉标注框架 from dataclasses import dataclass, field from enum import Enum class HallucinationType(Enum): ENTITY = "entity" # 实体幻觉 RELATION = "relation" # 关系幻觉 NUMERIC = "numeric" # 数字幻觉 CITATION = "citation" # 引用幻觉 LOGICAL = "logical" # 逻辑幻觉 CONTEXTUAL = "contextual" # 上下文幻觉 FORMAT = "format" # 格式幻觉 NONE = "none" # 无幻觉 class HallucinationSeverity(Enum): NONE = 0 # 无幻觉 MINOR = 1 # 轻微:不影响核心信息 MODERATE = 2 # 中等:部分信息不准确 SEVERE = 3 # 严重:核心信息完全错误 CRITICAL = 4 # 致命:可能造成实际危害 @dataclass class HallucinationAnnotation: """单条幻觉标注""" span_start: int # 幻觉文本起始位置 span_end: int # 幻觉文本结束位置 hallucinated_text: str # 幻觉文本 hallucination_type: HallucinationType severity: HallucinationSeverity correct_info: str # 正确信息 source: str # 正确信息来源 annotator_id: str confidence: float # 标注者置信度 0-1 @dataclass class HallucinationDocument: """一份完整文档的幻觉标注""" doc_id: str prompt: str response: str annotations: list[HallucinationAnnotation] = field(default_factory=list) @property def hallucination_rate(self) -> float: """幻觉率:有幻觉的句子占比""" if not self.response: return 0.0 sentences = self.response.split("。") hallucinated_sentences = set() for ann in self.annotations: for i, sent in enumerate(sentences): if ann.hallucinated_text in sent: hallucinated_sentences.add(i) return len(hallucinated_sentences) / len(sentences) if sentences else 0 @property def severity_score(self) -> float: """严重度评分:加权幻觉得分""" weights = {0: 0, 1: 0.25, 2: 0.5, 3: 0.75, 4: 1.0} if not self.annotations: return 0.0 return sum(weights[a.severity.value] for a in self.annotations) / len(self.annotations) 标注一致性度量 class AnnotationAgreement: """标注者间一致性计算""" @staticmethod def cohen_kappa(annotator1: list[str], annotator2: list[str]) -> float: """Cohen's Kappa:两个标注者的一致性""" from collections import Counter n = len(annotator1) labels = sorted(set(annotator1 + annotator2)) # 观察一致率 observed = sum(1 for a, b in zip(annotator1, annotator2) if a == b) / n # 期望一致率 c1 = Counter(annotator1) c2 = Counter(annotator2) expected = sum((c1[l] / n) * (c2[l] / n) for l in labels) if expected == 1.0: return 1.0 return (observed - expected) / (1 - expected) @staticmethod def fleiss_kappa(annotations: list[list[str]]) -> float: """Fleiss' Kappa:多标注者一致性""" import numpy as np n = len(annotations[0]) # 样本数 k = len(annotations) # 标注者数 labels = sorted(set(l for ann in annotations for l in ann)) m = len(labels) # 构建计数矩阵 counts = np.zeros((n, m)) label_idx = {l: i for i, l in enumerate(labels)} for annotator_labels in annotations: for i, label in enumerate(annotator_labels): counts[i][label_idx[label]] += 1 # 观察一致率 P_i = (np.sum(counts**2, axis=1) - k) / (k * (k - 1)) P_bar = np.mean(P_i) # 期望一致率 p_j = np.sum(counts, axis=0) / (n * k) P_e = np.sum(p_j**2) if P_e == 1.0: return 1.0 return (P_bar - P_e) / (1 - P_e) 二、自动检测算法 基于检索的幻觉检测 class RetrievalBasedDetector: """基于检索的幻觉检测:将生成内容与知识库比对""" def __init__(self, knowledge_base, embedding_model="all-MiniLM-L6-v2"): from sentence_transformers import SentenceTransformer import faiss import numpy as np self.model = SentenceTransformer(embedding_model) self.kb_texts = knowledge_base embeddings = self.model.encode(knowledge_base) # 构建 FAISS 索引 dim = embeddings.shape[1] self.index = faiss.IndexFlatIP(dim) self.index.add(embeddings.astype('float32')) def detect(self, response: str, top_k: int = 5, threshold: float = 0.7) -> dict: """检测回答中的幻觉""" sentences = self._split_sentences(response) hallucinated = [] verified = [] for sent in sentences: # 检索最相关的知识库条目 sent_emb = self.model.encode([sent]).astype('float32') scores, indices = self.index.search(sent_emb, top_k) max_score = scores[0][0] best_match = self.kb_texts[indices[0][0]] if max_score < threshold: # 无法在知识库中找到支持 → 可能是幻觉 hallucinated.append({ "text": sent, "max_similarity": float(max_score), "best_match": best_match, "verdict": "unsupported", }) else: verified.append({ "text": sent, "similarity": float(max_score), "source": best_match, "verdict": "supported", }) return { "total_sentences": len(sentences), "hallucinated_count": len(hallucinated), "verified_count": len(verified), "hallucination_rate": len(hallucinated) / len(sentences) if sentences else 0, "details": {"hallucinated": hallucinated, "verified": verified}, } def _split_sentences(self, text: str) -> list[str]: import re # 按中英文标点分句 sentences = re.split(r'[。!?.!?\n]+', text) return [s.strip() for s in sentences if s.strip()] 基于 NLI 的幻觉检测 class NLIDetector: """基于自然语言推理(NLI)的幻觉检测""" def __init__(self, model_name="moritzlaurer/DeBERTa-v3-base-mnli-fever-nli"): from transformers import AutoTokenizer, AutoModelForSequenceClassification import torch self.tokenizer = AutoTokenizer.from_pretrained(model_name) self.model = AutoModelForSequenceClassification.from_pretrained(model_name) self.model.eval() # 标签: 0=entailment, 1=neutral, 2=contradiction def detect(self, response: str, reference: str) -> dict: """ 判断 response 是否被 reference 支持 entailment: reference 支持 response contradiction: reference 与 response 矛盾 neutral: 无法判断 """ import torch sentences = self._split_sentences(response) results = [] for sent in sentences: inputs = self.tokenizer(reference, sent, return_tensors="pt", truncation=True, max_length=512) with torch.no_grad(): logits = self.model(**inputs).logits probs = torch.softmax(logits, dim=0) results.append({ "sentence": sent, "entailment_prob": probs[0].item(), "neutral_prob": probs[1].item(), "contradiction_prob": probs[2].item(), "verdict": self._classify(probs), }) hallucinated = [r for r in results if r["verdict"] == "contradiction"] unsupported = [r for r in results if r["verdict"] == "neutral"] return { "total_sentences": len(results), "supported": len(results) - len(hallucinated) - len(unsupported), "contradicted": len(hallucinated), "unsupported": len(unsupported), "hallucination_rate": (len(hallucinated) + len(unsupported)) / len(results) if results else 0, "details": results, } def _classify(self, probs): labels = ["entailment", "neutral", "contradiction"] idx = probs.argmax().item() return labels[idx] def _split_sentences(self, text): import re return [s.strip() for s in re.split(r'[。!?.!?\n]+', text) if s.strip()] 基于 LLM 的幻觉检测 class LLMHallucinationDetector: """使用强力 LLM 检测幻觉""" def __init__(self, judge_model: str = "gpt-4o"): self.judge_model = judge_model def detect(self, prompt: str, response: str, reference_context: str = None) -> dict: """检测回答中的幻觉""" judge_prompt = f"""你是事实核查专家。请检查以下 AI 回答中是否存在幻觉(与事实不符的内容)。 用户问题:{prompt} AI 回答:{response} """ if reference_context: judge_prompt += f"\n参考信息(权威来源):\n{reference_context}\n" judge_prompt += """ 请逐句检查,对每句话标注: - "supported":有事实依据支持 - "contradicted":与已知事实矛盾 - "unsupported":无法验证,可能是编造 输出 JSON: { "sentences": [ {"text": "...", "verdict": "supported/contradicted/unsupported", "reason": "..."} ], "overall_hallucination": "none/minor/moderate/severe", "hallucination_rate": 0.0-1.0, "key_issues": ["问题1", "问题2"] }""" import json result = call_llm(self.judge_model, judge_prompt) try: return json.loads(result) except json.JSONDecodeError: import re match = re.search(r'\{.*\}', result, re.DOTALL) if match: return json.loads(match.group()) return {"error": "parse failed", "raw": result} def detect_with_search(self, prompt: str, response: str) -> dict: """结合搜索引擎的幻觉检测""" # 1. 提取需要验证的关键陈述 claims = self._extract_claims(response) # 2. 对每个陈述进行搜索验证 results = [] for claim in claims: search_results = web_search(claim) verification = self._verify_claim(claim, search_results) results.append(verification) return { "total_claims": len(claims), "verified": sum(1 for r in results if r["verdict"] == "supported"), "hallucinated": sum(1 for r in results if r["verdict"] == "contradicted"), "unverifiable": sum(1 for r in results if r["verdict"] == "unsupported"), "details": results, } def _extract_claims(self, text: str) -> list[str]: """提取需要验证的事实陈述""" prompt = f"""从以下文本中提取需要验证的事实性陈述,每行一个: {text} 只输出陈述列表。""" result = call_llm(self.judge_model, prompt) return [line.strip() for line in result.strip().split("\n") if line.strip()] 三、专门化幻觉检测工具 SelfCheckGPT class SelfCheckGPT: """ SelfCheckGPT: 通过多次采样检测幻觉 核心思想:如果模型对同一问题多次生成的回答一致,则可信度高; 如果不一致,则可能存在幻觉 """ def __init__(self, model_name: str, num_samples: int = 5): self.model_name = model_name self.num_samples = num_samples def check(self, prompt: str, response: str) -> dict: # 1. 生成多个样本 samples = [] for _ in range(self.num_samples): # 使用较高温度增加多样性 sample = call_llm(self.model_name, prompt, temperature=0.7) samples.append(sample) # 2. 计算一致性 consistency_scores = [] for i, sample in enumerate(samples): if i == 0: continue score = self._sentence_level_consistency(response, sample) consistency_scores.append(score) avg_consistency = sum(consistency_scores) / len(consistency_scores) if consistency_scores else 1.0 return { "original_response": response, "num_samples": self.num_samples, "avg_consistency": avg_consistency, "hallucination_score": 1 - avg_consistency, # 不一致 = 幻觉概率 "verdict": "likely_hallucinated" if avg_consistency < 0.6 else "likely_reliable", } def _sentence_level_consistency(self, response: str, sample: str) -> float: """计算句子级一致性""" from sentence_transformers import SentenceTransformer import numpy as np model = SentenceTransformer("all-MiniLM-L6-v2") resp_sents = self._split_sentences(response) sample_sents = self._split_sentences(sample) resp_emb = model.encode(resp_sents) sample_emb = model.encode(sample_sents) # 对每个原句,找到样本中最相似的句子 sim_matrix = resp_emb @ sample_emb.T max_sims = sim_matrix.max(axis=1) return float(np.mean(max_sims)) def _split_sentences(self, text): import re return [s.strip() for s in re.split(r'[。!?.!?\n]+', text) if s.strip()] 四、评估指标体系 class HallucinationMetrics: """幻觉评估指标集合""" @staticmethod def precision_recall_f1(annotations: list[dict]) -> dict: """ 计算幻觉检测的 Precision/Recall/F1 annotations: [{"pred_hallucinated": bool, "gt_hallucinated": bool}] """ tp = sum(1 for a in annotations if a["pred_hallucinated"] and a["gt_hallucinated"]) fp = sum(1 for a in annotations if a["pred_hallucinated"] and not a["gt_hallucinated"]) fn = sum(1 for a in annotations if not a["pred_hallucinated"] and a["gt_hallucinated"]) tn = sum(1 for a in annotations if not a["pred_hallucinated"] and not a["gt_hallucinated"]) precision = tp / (tp + fp) if (tp + fp) > 0 else 0 recall = tp / (tp + fn) if (tp + fn) > 0 else 0 f1 = 2 * precision * recall / (precision + recall) if (precision + recall) > 0 else 0 accuracy = (tp + tn) / len(annotations) if annotations else 0 return { "precision": round(precision, 4), "recall": round(recall, 4), "f1": round(f1, 4), "accuracy": round(accuracy, 4), "tp": tp, "fp": fp, "fn": fn, "tn": tn, } @staticmethod def hallucination_rate(docs: list[HallucinationDocument]) -> dict: """计算总体幻觉率""" rates = [d.hallucination_rate for d in docs] severities = [d.severity_score for d in docs] import numpy as np return { "mean_hallucination_rate": round(np.mean(rates), 4), "median_hallucination_rate": round(np.median(rates), 4), "p95_hallucination_rate": round(np.percentile(rates, 95), 4), "mean_severity": round(np.mean(severities), 4), "docs_with_hallucination": sum(1 for d in docs if d.annotations), "total_docs": len(docs), } @staticmethod def by_type_breakdown(docs: list[HallucinationDocument]) -> dict: """按幻觉类型分解""" from collections import defaultdict by_type = defaultdict(int) for doc in docs: for ann in doc.annotations: by_type[ann.hallucination_type.value] += 1 total = sum(by_type.values()) return { t: {"count": c, "percentage": round(c / total * 100, 1) if total > 0 else 0} for t, c in sorted(by_type.items(), key=lambda x: -x[1]) } 五、检测方法对比 方法 准确率 召回率 成本 实时性 适用场景 人工标注 95%+ 90%+ 极高 慢 基线建立 检索比对 80% 70% 中 中 有知识库时 NLI 模型 85% 75% 低 快 有参考文本时 LLM-as-Judge 88% 82% 中高 中 通用检测 SelfCheckGPT 78% 85% 高(多次采样) 慢 无参考文本时 搜索验证 82% 68% 中 慢 事实性声明 实践建议 分层检测策略 class LayeredHallucinationDetection: """分层幻觉检测策略""" def __init__(self): self.fast_checker = NLIDetector() # 快速初筛 self.deep_checker = LLMHallucinationDetector() # 深度检测 self.search_checker = None # 搜索验证(按需启用) def check(self, prompt: str, response: str, reference: str = None) -> dict: # 层 1:NLI 快速检测(<100ms) if reference: nli_result = self.fast_checker.detect(response, reference) if nli_result["hallucination_rate"] < 0.1: # NLI 认为基本无幻觉,直接返回 return {"layer": "nli", "result": nli_result, "confidence": "high"} # 层 2:LLM 深度检测(1-3s) llm_result = self.deep_checker.detect(prompt, response, reference) if llm_result.get("overall_hallucination") in ["none", "minor"]: return {"layer": "llm", "result": llm_result, "confidence": "high"} # 层 3:搜索验证(5-10s,仅对高风险内容) if llm_result.get("overall_hallucination") in ["severe", "moderate"]: search_result = self.deep_checker.detect_with_search(prompt, response) return {"layer": "search", "result": search_result, "confidence": "highest"} return {"layer": "llm", "result": llm_result, "confidence": "medium"} 结语 幻觉检测是 LLM 可靠性的最后一道防线。没有单一方法能完美检测所有类型的幻觉——检索方法依赖知识库的覆盖度,NLI 方法需要参考文本,LLM-as-Judge 本身也可能产生幻觉。最佳实践是分层检测:快速方法做初筛,深度方法做验证,搜索方法做兜底。同时,定期进行人工标注作为基线,校准自动检测系统的准确率。记住:降低幻觉的根本在于模型训练和 RAG 增强,检测只是发现问题的手段,而非解决方案本身。 加入讨论 这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。 ...

2026-06-25 · 8 min · 1579 words · 硅基 AGI 探索者
llm eval pipeline

LLM 评估流水线搭建:从数据集到报告

为什么需要评估流水线 大语言模型迭代速度极快,每次模型更新或 Prompt 修改都需要回答一个核心问题:新的版本到底比旧的好多少? 靠人工试几个案例远远不够,你需要一条系统化的评估流水线。 评估流水线的核心价值: 可复现:同一套数据集和指标,任何人任何时候跑都能得到一致结果 可比较:不同模型版本之间的差异被量化为具体数字 可扩展:从 100 条测试用例扩展到 10000 条只需改一个参数 可追踪:历史评估结果存档,形成模型演进的时间线 流水线架构总览 一条完整的 LLM 评估流水线包含五个核心阶段: ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ 数据集构建 │ -> │ 评估执行 │ -> │ 指标计算 │ -> │ 结果分析 │ -> │ 报告生成 │ │ Dataset │ │ Execution │ │ Metrics │ │ Analysis │ │ Report │ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ 阶段一:数据集构建 数据集是评估的基石。一个高质量的评估数据集应具备以下特征: 特征 说明 示例 代表性 覆盖实际使用场景的主要类型 问答、摘要、翻译、代码生成 多样性 包含不同难度和长度的输入 简单事实题 vs 多步推理题 无泄漏 不包含训练数据中的内容 使用人工新写的题目 可验证 有标准答案或明确的评判标准 精确匹配 / 人工评分标准 可扩展 能方便地增加新类别 模块化的数据结构 数据集格式设计 推荐使用 JSONL 格式,每行一个测试样本: ...

2026-06-25 · 6 min · 1180 words · 硅基 AGI 探索者
multimodal eval method

多模态模型评估:视觉理解与跨模态推理

多模态评估的特殊性 单模态(纯文本)评估已经相当成熟,但当我们进入多模态领域——图像、视频、音频与文本的交叉理解——评估的复杂度呈指数级增长。多模态模型不仅要理解每种模态的信息,还要在不同模态间建立语义关联。 多模态评估的核心挑战: 对齐问题:文本描述和图像内容是否语义一致? 细粒度理解:模型是否真正"看到"了图像中的关键细节? 跨模态推理:能否基于图像信息进行文本推理,或反向操作? 评估成本:人工标注图文对的成本远高于纯文本 评估维度全景 多模态评估维度 ├── 视觉感知 │ ├── 图像识别(VQA, Image Captioning) │ ├── 细粒度理解(OCR, 属性识别) │ └── 空间推理(位置关系, 3D 理解) ├── 跨模态推理 │ ├── 图文推理(图→文推理) │ ├── 文图推理(文→图检索/生成) │ └── 多模态链式推理 ├── 多模态对话 │ ├── 多轮图像对话 │ └── 视频问答 └── 生成质量 ├── 图文一致性 ├── 视觉质量 └── 创意与忠实度 一、视觉理解评估 图像问答(VQA)评估 VQA 是最基础的多模态评估形式:给模型一张图片和一个问题,要求输出答案。 class VQAEvaluator: """VQA 评估器""" def __init__(self, eval_mode: str = "vqa_accuracy"): self.eval_mode = eval_mode def evaluate(self, predictions: list[dict]) -> dict: """ predictions: [{"question_id": int, "answer": str, "gt_answers": [str, ...]}] """ if self.eval_mode == "vqa_accuracy": return self._vqa_accuracy(predictions) elif self.eval_mode == "exact_match": return self._exact_match(predictions) def _vqa_accuracy(self, predictions: list[dict]) -> dict: """ 标准 VQA 准确率: 对每个问题,如果至少 3/10 的标注者给出了相同答案,则算正确 简化版:答案出现在 GT 答案列表中即算正确 """ correct = 0 for pred in predictions: gt = [a.strip().lower() for a in pred["gt_answers"]] ans = pred["answer"].strip().lower() # VQA 标准的 soft accuracy count = gt.count(ans) min_count = 1 # 简化:至少1个匹配 if count >= min_count: correct += min(1, count / 3.0) accuracy = correct / len(predictions) if predictions else 0 return {"vqa_accuracy": accuracy, "total": len(predictions)} def _exact_match(self, predictions: list[dict]) -> dict: correct = 0 for pred in predictions: gt = [a.strip().lower() for a in pred["gt_answers"]] ans = pred["answer"].strip().lower() if ans in gt: correct += 1 return {"exact_match": correct / len(predictions) if predictions else 0} class FineGrainedVQAEvaluator(VQAEvaluator): """细粒度 VQA 评估:按问题类型分桶""" QUESTION_TYPES = { "object": "图中有什么物体?", "count": "图中有几个XX?", "color": "XX是什么颜色的?", "spatial": "XX在YY的哪个位置?", "attribute": "XX有什么特征?", "relation": "XX和YY是什么关系?", "scene": "这是什么场景?", "ocr": "图中的文字写了什么?", } def evaluate_by_type(self, predictions: list[dict]) -> dict: from collections import defaultdict by_type = defaultdict(list) for pred in predictions: by_type[pred.get("question_type", "unknown")].append(pred) results = {} for q_type, preds in by_type.items(): results[q_type] = { "count": len(preds), "accuracy": self._exact_match(preds)["exact_match"], } return results 图像描述(Captioning)评估 class CaptioningEvaluator: """图像描述评估""" def __init__(self): self.metrics = {} def evaluate(self, prediction: str, references: list[str]) -> dict: results = {} # CIDEr-D: 专为图像描述设计的指标 results["cider"] = self._cider(prediction, references) # BLEU-4 results["bleu4"] = self._bleu(prediction, references, n=4) # METEOR results["meteor"] = self._meteor(prediction, references) # ROUGE-L results["rouge_l"] = self._rouge_l(prediction, references) # CLIPScore: 基于 CLIP 的图文匹配度 results["clip_score"] = self._clip_score(prediction, references) return results def _cider(self, pred: str, refs: list[str]) -> float: """CIDEr: 共识评估,基于 TF-IDF 加权的 n-gram 重叠""" # 简化实现,实际使用 pycocoevalcap from pycocoevalcap.cider.cider import Cider cider_scorer = Cider() gts = {0: refs} res = {0: [pred]} score, _ = cider_scorer.compute_score(gts, res) return score def _bleu(self, pred: str, refs: list[str], n: int = 4) -> float: from pycocoevalcap.bleu.bleu import Bleu bleu_scorer = Bleu(n) gts = {0: refs} res = {0: [pred]} score, _ = bleu_scorer.compute_score(gts, res) return score[n-1] def _meteor(self, pred: str, refs: list[str]) -> float: from pycocoevalcap.meteor.meteor import Meteor meteor_scorer = Meteor() gts = {0: refs} res = {0: [pred]} score, _ = meteor_scorer.compute_score(gts, res) return score def _rouge_l(self, pred: str, refs: list[str]) -> float: from pycocoevalcap.rouge.rouge import Rouge rouge_scorer = Rouge() gts = {0: refs} res = {0: [pred]} score, _ = rouge_scorer.compute_score(gts, res) return score def _clip_score(self, pred: str, refs: list[str], image_path: str = None) -> float: """CLIPScore: 使用 CLIP 计算图文匹配度""" from transformers import CLIPProcessor, CLIPModel from PIL import Image import torch model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32") processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32") image = Image.open(image_path) inputs = processor(text=[pred], images=image, return_tensors="pt", padding=True) with torch.no_grad(): outputs = model(**inputs) # 归一化的图文相似度 score = outputs.logits_per_image.item() # 归一化到 0-1 return min(max(score / 100, 0), 1) OCR 评估 class OCREvaluator: """OCR 能力评估""" def evaluate(self, prediction: str, ground_truth: str) -> dict: results = {} # 字符级准确率 results["char_accuracy"] = self._char_accuracy(prediction, ground_truth) # 词级准确率 results["word_accuracy"] = self._word_accuracy(prediction, ground_truth) # 编辑距离 results["edit_distance"] = self._edit_distance(prediction, ground_truth) # 归一化编辑距离 max_len = max(len(prediction), len(ground_truth), 1) results["normalized_edit_distance"] = results["edit_distance"] / max_len # ANLS (Average Normalized Levenshtein Similarity) results["anls"] = 1 - results["normalized_edit_distance"] return results def _char_accuracy(self, pred: str, gt: str) -> float: """字符级准确率""" if not gt: return 1.0 if not pred else 0.0 correct = sum(1 for p, g in zip(pred, gt) if p == g) # 加上长度差异惩罚 correct += 0 # 多出或缺少的字符算错 return correct / len(gt) def _word_accuracy(self, pred: str, gt: str) -> float: pred_words = pred.split() gt_words = gt.split() if not gt_words: return 1.0 if not pred_words else 0.0 correct = sum(1 for p, g in zip(pred_words, gt_words) if p == g) return correct / len(gt_words) def _edit_distance(self, s1: str, s2: str) -> int: """Levenshtein 编辑距离""" if len(s1) < len(s2): return self._edit_distance(s2, s1) if len(s2) == 0: return len(s1) previous_row = range(len(s2) + 1) for i, c1 in enumerate(s1): current_row = [i + 1] for j, c2 in enumerate(s2): insertions = previous_row[j + 1] + 1 deletions = current_row[j] + 1 substitutions = previous_row[j] + (c1 != c2) current_row.append(min(insertions, deletions, substitutions)) previous_row = current_row return previous_row[-1] 二、跨模态推理评估 图文推理任务 class CrossModalReasoningEvaluator: """跨模态推理评估""" TASK_TYPES = [ "visual_entailment", # 视觉蕴含:图→文 是否支持 "visual_reasoning", # 视觉推理:基于图的逻辑推理 "image_text_matching", # 图文匹配 "visual_commonsense", # 视觉常识推理 "multimodal_cot", # 多模态链式推理 ] def evaluate_visual_entailment(self, predictions: list[dict]) -> dict: """ 视觉蕴含:判断文本假设是否被图像支持 标签: entailment / neutral / contradiction """ from sklearn.metrics import classification_report, accuracy_score labels = ["entailment", "neutral", "contradiction"] y_true = [p["gt_label"] for p in predictions] y_pred = [p["pred_label"] for p in predictions] report = classification_report(y_true, y_pred, labels=labels, output_dict=True) return { "accuracy": accuracy_score(y_true, y_pred), "per_class": {l: report[l] for l in labels}, } def evaluate_image_text_matching(self, predictions: list[dict]) -> dict: """ 图文匹配:给定图片和多个文本,选择最匹配的 """ correct = 0 for pred in predictions: if pred["pred_match"] == pred["gt_match"]: correct += 1 return {"accuracy": correct / len(predictions) if predictions else 0} def evaluate_multimodal_cot(self, predictions: list[dict]) -> dict: """ 多模态链式推理:评估推理步骤和最终答案 """ results = [] for pred in predictions: # 评估最终答案 answer_correct = self._check_answer(pred["pred_answer"], pred["gt_answer"]) # 评估推理步骤(使用 LLM-as-Judge) reasoning_score = self._evaluate_reasoning( pred["image_description"], pred["reasoning_steps"], pred["pred_answer"] ) results.append({ "answer_correct": answer_correct, "reasoning_score": reasoning_score, }) return { "answer_accuracy": sum(r["answer_correct"] for r in results) / len(results), "avg_reasoning_score": sum(r["reasoning_score"] for r in results) / len(results), } def _check_answer(self, pred: str, gt: str) -> bool: pred_clean = pred.strip().lower() gt_clean = gt.strip().lower() return gt_clean in pred_clean or pred_clean == gt_clean def _evaluate_reasoning(self, image_desc: str, reasoning: str, answer: str) -> float: """使用 LLM 评估推理质量""" prompt = f"""请评估以下多模态推理的质量(0-10分): 图像描述:{image_desc} 推理过程:{reasoning} 最终答案:{answer} 评估维度: - 推理是否基于图像信息 - 逻辑是否连贯 - 是否有跳步或错误 请输出一个数字(0-10)。""" # 调用 LLM 评估 score = call_llm("gpt-4o", prompt) try: return float(score.strip()) / 10.0 except ValueError: return 0.5 主流多模态基准对比 基准 评估能力 任务数 模态 特点 VQAv2 视觉问答 1.1M 图+文 经典 VQA 基准 GQA 场景图推理 22M 图+文 结构化推理 MMBench 综合多模态 4K+ 图+文 多维能力评估 MMMU 学科多模态 11.5K 图+文 大学级别学科 MathVista 数学视觉推理 6K+ 图+文 数学+视觉 MMMU-Health 医学多模态 1K+ 图+文 医学领域 VideoMME 视频理解 900 视频+文 长视频理解 SEED-Bench 多场景理解 19K 图/视频+文 多模态多场景 三、图文一致性评估 生成图像的文本一致性 当模型从文本生成图像(或反向)时,需要评估跨模态的一致性: ...

2026-06-25 · 8 min · 1589 words · 硅基 AGI 探索者
agent eval framework

Agent 评估框架:如何衡量 Agent 的真正能力

引言 2026 年,AI Agent 已从简单的对话助手演进为能自主规划、调用工具、执行多步骤任务的复杂系统。但 Agent 的评估远比 LLM 困难——同一个任务可能有无数条正确路径,中间步骤的错误可能被后续步骤弥补,而「完成任务」的定义本身也充满模糊性。本文系统梳理 Agent 评估的方法论与框架。 一、Agent 评估的核心难点 1.1 与传统 LLM 评估的本质区别 维度 LLM 评估 Agent 评估 输入 单轮/多轮 prompt 任务描述 + 环境 + 工具集 输出 文本回答 多种模态(文本、代码、文件、API 调用) 评估单元 单次回答 多步骤轨迹 正确性 可对照参考答案 路径多样,无唯一正确答案 环境依赖 无 依赖外部环境状态 成本 低 高(每步消耗 tokens + 工具调用) 可复现性 高 低(环境状态变化) 1.2 核心挑战 挑战一:路径多样性。 「订一张明天去上海的机票」这个任务,Agent 可以先查日历确认日期、再搜索航班、最后预订;也可以直接搜索航班并预订。路径不同但都正确。 挑战二:中间错误的可恢复性。 Agent 在第 2 步调错了 API,但在第 3 步意识到并纠正了——这算不算错误?如何量化? 挑战三:环境不可复现。 Agent 调用的 API 返回值可能随时间变化,同一任务在不同时间执行可能得到不同结果。 ...

2026-06-25 · 3 min · 594 words · 硅基 AGI 探索者
llm safety eval guide

LLM 安全评估指南:Toxicity/Bias/Jailbreak 全维度

引言 随着 LLM 部署规模扩大,安全评估不再是可选项。一个「能力强但不安全」的模型比一个「能力弱但安全」的模型危险得多。2026 年,安全评估已形成系统化的维度体系,涵盖毒性、偏见、越狱攻击、隐私泄露、对齐性等多个层面。本文提供完整的安全评估方法论。 一、安全评估维度全景 LLM 安全评估 ├── 毒性 (Toxicity) │ ├── 仇恨言论 │ ├── 侮辱/骚扰 │ ├── 暴力威胁 │ └── 自残引导 ├── 偏见 (Bias) │ ├── 性别偏见 │ ├── 种族偏见 │ ├── 年龄偏见 │ ├── 宗教偏见 │ └── 残障偏见 ├── 越狱攻击 (Jailbreak) │ ├── Prompt 注入 │ ├── 角色扮演绕过 │ ├── 编码/多语言绕过 │ └── 多轮诱导 ├── 隐私 (Privacy) │ ├── PII 泄露 │ ├── 训练数据提取 │ └── 成员推理 ├── 有害内容生成 │ ├── 武器/毒品制造 │ ├── 非法活动指导 │ └── 虚假信息生成 └── 对齐性 (Alignment) ├── 指令遵循 ├── 价值观一致性 └── 拒绝过度/不足 二、Toxicity 检测 2.1 评估方法 Red Teaming + 自动分类: ...

2026-06-25 · 4 min · 730 words · 硅基 AGI 探索者
code llm eval

代码 LLM 评估:从 HumanEval 到 SWE-Bench Pro

引言 代码生成是 LLM 最成功的落地场景之一,但「能写代码」和「能解决工程问题」之间有巨大鸿沟。HumanEval 上的 90% 通过率并不意味着模型能胜任真实的软件工程工作。本文系统梳理 2026 年代码 LLM 评估从基础到工程级的完整体系。 一、基础代码生成评估 1.1 HumanEval HumanEval 由 OpenAI 在 2021 年发布,是代码 LLM 评估的元老级 Benchmark。包含 164 个 Python 编程任务,每个任务包含函数签名、docstring 和测试用例。 评估方式: 给定函数签名和 docstring,模型生成函数体,用测试用例验证正确性。 Pass@k 指标: Pass@k = 1 - C(n-c, k) / C(n, k) 其中 n 是总采样数,c 是通过测试的采样数,k 是允许的尝试次数。 2026 年头部模型 Pass@1: 模型 HumanEval Pass@1 GPT-5 96.2% Claude 4 Opus 95.8% Gemini 2.5 Ultra 94.1% DeepSeek-Coder V2.5 93.5% Llama 3.3 70B 88.7% 问题: HumanEval 已严重饱和,头部模型差距不到 8 个百分点,基本失去区分度。 ...

2026-06-25 · 4 min · 662 words · 硅基 AGI 探索者
multimodal eval 2026

多模态模型评估 2026:视觉/音频/视频全面评测

引言 2026 年的多模态模型已不再局限于「看图说话」。GPT-5、Gemini 2.5 Ultra、Claude 4 等模型能同时处理图像、音频、视频,并进行跨模态推理。但多模态评估远比纯文本复杂——视觉理解不只是图像分类,音频理解不只是语音识别,视频理解更涉及时序推理。本文系统梳理 2026 年多模态评估的方法论与 Benchmark 现状。 一、视觉理解评估 1.1 评估维度体系 视觉理解 ├── 基础感知 │ ├── 物体识别 (Object Recognition) │ ├── 属性识别 (Attribute Recognition) │ └── 场景分类 (Scene Classification) ├── 视觉推理 │ ├── 空间推理 (Spatial Reasoning) │ ├── 关系推理 (Relational Reasoning) │ └── 物理推理 (Physical Reasoning) ├── 文档理解 │ ├── OCR / 文字提取 │ ├── 图表理解 (Chart QA) │ └── 文档推理 (Document QA) ├── 细粒度理解 │ ├── 计数 (Counting) │ ├── 差异检测 (Difference Detection) │ └── 细节描述 (Detailed Description) └── 创意理解 ├── 梗图理解 (Meme Understanding) ├── 艺术作品分析 └── UI/UX 理解 1.2 主流视觉 Benchmark Benchmark 评测重点 样本数 头部得分 区分度 VQAv2 基础视觉问答 1.1M 92%+ ⚠️ 饱和 GQA 组合视觉推理 22M 88%+ ⚠️ 接近饱和 ChartQA 图表理解 30K 85-90% ✅ 良好 DocVQA 文档问答 50K 92-95% ⚠️ 接近饱和 MMMU 多领域视觉推理 11.5K 65-72% ✅ 良好 MMBench 多维度视觉评估 6K 80-85% ✅ 良好 MathVista 数学视觉推理 6K 55-70% ✅ 高 RealWorldQA 真实世界理解 7K 70-78% ✅ 高 1.3 关键 Benchmark 详解 MMMU (Massive Multi-discipline Multimodal Understanding) MMMU 覆盖 30 个学科(艺术、科学、商业、医学等),要求模型基于图像进行大学级别的推理。 ...

2026-06-25 · 4 min · 657 words · 硅基 AGI 探索者
鲁ICP备2026018361号