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 探索者
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 · AI 实战派
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 · AI 实战派
llm eval pipeline

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

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

2026-06-25 · 6 min · 1180 words · AI 实战派
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 · AI 实战派
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 · AI 实战派
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 · AI 实战派
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 · AI 实战派
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 · AI 实战派
openai evals guide

OpenAI Evals 框架指南:标准化 LLM 评估

为什么需要标准化评估框架 大语言模型能力飞速迭代,但"模型到底好不好"这个问题却越来越难回答。手工测试覆盖面有限, ad-hoc 脚本难以复现,不同模型之间的对比缺乏统一基准。OpenAI Evals 框架正是为解决这一痛点而生——它提供了一套标准化的评估流程,让模型评估变得可复现、可对比、可扩展。 Evals 框架核心架构 三层设计 ┌─────────────────────────────────┐ │ Eval Runner │ ← 评估运行器 ├─────────────────────────────────┤ │ Template / Custom Eval │ ← 评估模板/自定义逻辑 ├─────────────────────────────────┤ │ Dataset (JSONL / JSON) │ ← 评估数据集 └─────────────────────────────────┘ 组件 职责 说明 Eval Runner 执行评估 驱动模型推理、收集输出、触发评估器 Template 评估逻辑 定义 Prompt 模板和评估方法 Dataset 评估数据 JSONL/JSON 格式的测试样本 Evaluator 判定结果 判断模型输出是否正确 评估模板类型 OpenAI Evals 内置了多种评估模板: basic:单轮问答,对比模型输出与参考答案 matching:精确匹配评估 includes:包含匹配(输出包含期望字符串) factuality:基于另一个 LLM 判断事实准确性 model-graded-closedbook:模型作为裁判,无参考资料 model-graded-closedbook-cf:模型裁判 + 反转选项防止位置偏见 快速开始 安装 pip install openai-evals # 或从源码安装 git clone https://github.com/openai/evals.git cd evals && pip install -e . 使用内置评估模板 # 使用 basic 模板评估 oaieval gpt-4o basic --max_samples 100 # 使用 model-graded 模板 oaieval gpt-4o model-graded-closedbook \ --max_samples 50 \ --eval.evaluator gpt-4o 数据集格式 评估数据集采用 JSONL 格式,每行一个样本: ...

2026-06-24 · 3 min · 487 words · AI 实战派
鲁ICP备2026018361号