系列文章
本篇是 AI Agent 系列的第 11 篇。前 10 篇我们从零搭建了 Agent 的核心能力——推理、记忆、工具、规划、协作,现在是时候回答一个关键问题:怎么知道 Agent 干得好不好?
- AI Agent 评估与优化:从基准测试到生产环境的质量守护实战(本文)
- 理解 AI Agent 的大脑:ReAct 模式从入门到实战
- AI Agent 的灵魂对话:Prompt Engineering 系统提示词设计的艺术与工程
- AI Agent 的规划大脑:从任务分解到自适应执行策略
- 让 AI 学会”说人话”——Spring AI 结构化输出实战
- AI Agent 的工具箱:深入理解 Tool Use 与 Spring AI Function Calling 实战
- AI Agent 的记忆力是怎么实现的——LangChain4j Memory 机制深度解析
- MCP 模型上下文协议:AI 的万能接口与 MCP Server 实战
- AI Agent 的记忆系统:从 ChatMemory 到持久化记忆的 Java 实战
- 从零理解 RAG:检索增强生成完整指南
- AI Agent 团队协作:多 Agent 系统架构设计与 Java 实战
为什么 Agent 评估这么难?
你在公司上线了一个 AI 客服 Agent。Demo 阶段一切完美,老板拍板上线。三天后,客服投诉量暴增——Agent 开始”一本正经地胡说八道”,把退款政策编得头头是道,用户信以为真,结果退款流程根本对不上。
这不是段子,这是 2026 年大量企业部署 Agent 后的真实场景。
传统软件测试的思路是”给定输入,验证输出是否等于预期”。但 Agent 不一样:
第一,输出不确定。 同一个问题问两遍,Agent 可能给出措辞不同但都正确的回答。你没法写 assertEquals("xxx", agent.answer())。
第二,评估维度多。 一个回答好不好,要同时看准确性、相关性、完整性、安全性、延迟、成本——这些维度之间甚至可能矛盾。追求准确性可能需要多次检索和推理,延迟和成本就上去了。
第三,链式错误传播。 Agent 不是单次 LLM 调用,而是 ReAct 循环 → 工具调用 → 记忆检索 → 规划 → 生成的长链路。任何一个环节出错,最终输出都可能跑偏,而且你很难定位到底是哪一步出了问题。
第四,幻觉无处不在。 LLM 的幻觉不是 bug,是特性——它本质上是一个概率模型,总有可能生成看起来合理但实际错误的内容。Agent 比纯聊天更危险,因为它会调用工具、执行操作,幻觉可能导致真实世界的副作用。
所以,Agent 评估不是”上线前跑一遍测试”这么简单,它需要一套贯穿开发、测试、上线、运营全生命周期的质量守护体系。
评估维度金字塔:从单轮到端到端
我把 Agent 评估分成四层,像金字塔一样从底到顶:
1 2 3 4 5 6 7 8 9
| ┌─────────────┐ │ 业务价值 │ ← ROI、用户满意度 ┌┴─────────────┴┐ │ 端到端质量 │ ← 完整任务成功率 ┌┴───────────────┴┐ │ 链路环节质量 │ ← 推理、工具、记忆、规划 ┌┴─────────────────┴┐ │ 基础模型质量 │ ← 准确性、幻觉率、安全性 └───────────────────┘
|
第一层:基础模型质量
这是最底层,评估的是 LLM 本身的回答质量。
准确性(Accuracy): 回答是否正确。可以用精确匹配(Exact Match)或模糊匹配(F1 Score)。
幻觉率(Hallucination Rate): 回答中包含无中生有信息的比例。这是 Agent 场景最致命的问题。
安全性(Safety): 是否泄露了敏感信息、是否生成了有害内容。
指令遵循度(Instruction Following): 是否按照 System Prompt 的要求行事,比如”只用中文回答”、”不确定时说不知道”。
第二层:链路环节质量
Agent 的核心链路包括推理(ReAct)、工具调用(Tool Use)、记忆检索(Memory/RAG)、规划(Planning)。每个环节都需要独立评估:
| 环节 |
评估指标 |
说明 |
| 推理 |
步骤正确率 |
ReAct 循环是否选择了正确的思考路径 |
| 工具调用 |
工具选择准确率、参数正确率 |
是否调用了正确的工具、参数是否正确 |
| 记忆/RAG |
检索召回率、排序准确率 |
检索到的文档是否相关、排序是否合理 |
| 规划 |
任务分解合理性 |
子任务划分是否覆盖了完整需求 |
第三层:端到端质量
把所有环节串起来,评估完整任务的完成情况:
- 任务成功率(Task Success Rate): 给定一个任务描述,Agent 最终是否完成了任务
- 平均轮次(Average Turns): 完成任务用了多少轮对话/工具调用
- 错误恢复率(Error Recovery Rate): 遇到工具调用失败后,Agent 是否能自主恢复
第四层:业务价值
最终要落到业务指标上:
- 用户满意度(CSAT): 用户对 Agent 回答的评分
- 人工介入率(Human Handoff Rate): 多少比例的对话需要转人工
- 成本效率(Cost per Resolution): 解决一个问题的平均 Token 成本
幻觉检测:Agent 的头号敌人
幻觉(Hallucination)是 Agent 质量的最大威胁。和纯聊天不同,Agent 的幻觉会导致真实操作——错误地调用工具、错误地执行命令、错误地查询数据。
幻觉的三种类型
事实幻觉(Factual Hallucination): 编造不存在的事实。比如 Agent 告诉用户”您的订单号是 123456”,但系统里根本没有这个订单。
忠实性幻觉(Faithfulness Hallucination): 检索到了正确的文档,但回答时歪曲了文档内容。比如 RAG 场景下,文档说”退款需要 7 个工作日”,Agent 回答”退款 3 天内到账”。
推理幻觉(Reasoning Hallucination): 推理过程中的逻辑跳跃。比如 Agent 看到用户问”能不能退货”,直接跳到”退货流程如下”,跳过了”订单是否在退货期内”的判断。
幻觉检测的三种策略
策略一:基于证据的忠实性检测
核心思想:如果 Agent 的回答能被检索到的上下文(RAG 文档、工具返回结果)所支撑,就不是幻觉。
实现方式很简单——让另一个 LLM 做”裁判”:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
|
@Component public class FaithfulnessChecker {
private final ChatClient chatClient;
public FaithfulnessChecker(ChatClient.Builder builder) { this.chatClient = builder.build(); }
public FaithfulnessResult check(String context, String answer) { String prompt = """ 你是一个忠实性检查器。你的任务是判断"回答"中的每条声明是否被"上下文"所支撑。
上下文: %s
回答: %s
请将回答拆分为独立的声明,对每条声明判断: - SUPPORTED: 上下文中明确支撑该声明 - NOT_SUPPORTED: 上下文中找不到支撑 - CONTRADICTED: 上下文明确反驳该声明
以 JSON 数组格式返回,每条包含 claim 和 verdict 字段。 """.formatted(context, answer);
String result = chatClient.prompt() .user(prompt) .call() .content();
return parseResult(result); }
public double score(String context, String answer) { FaithfulnessResult result = check(context, answer); long supported = result.claims().stream() .filter(c -> "SUPPORTED".equals(c.verdict())) .count(); return (double) supported / result.claims().size(); } }
|
这个方法的关键在于:让 LLM 做结构化的、可验证的任务——拆分声明、逐条匹配,比让它直接判断”这个回答对不对”可靠得多。
策略二:自一致性检测(Self-Consistency)
核心思想:同一个问题问 N 次,如果 Agent 每次回答都不一样,说明它在”猜”而不是”知道”。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
|
@Component public class SelfConsistencyChecker {
private final ChatClient chatClient;
public SelfConsistencyChecker(ChatClient.Builder builder) { this.chatClient = builder.build(); }
public double checkConsistency(String query, String context, int sampleCount) { List<String> answers = new ArrayList<>();
for (int i = 0; i < sampleCount; i++) { String prompt = context != null ? "上下文:%s\n\n问题:%s\n\n请回答。".formatted(context, query) : "问题:%s\n\n请回答。".formatted(query);
String answer = chatClient.prompt() .user(prompt) .options(ChatOptions.builder() .temperature(0.7) .build()) .call() .content(); answers.add(answer); }
String judgePrompt = """ 以下是对同一个问题的 %d 次回答,请判断它们在核心事实上是否一致:
%s
只返回一个 0.0 到 1.0 的数字,1.0 表示完全一致,0.0 表示完全矛盾。 """.formatted(sampleCount, formatAnswers(answers));
String scoreStr = chatClient.prompt() .user(judgePrompt) .call() .content();
return Double.parseDouble(scoreStr.trim()); }
private String formatAnswers(List<String> answers) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < answers.size(); i++) { sb.append("回答 %d: %s\n\n".formatted(i + 1, answers.get(i))); } return sb.toString(); } }
|
什么时候用这个方法? 在关键场景(比如退款政策、法律条款)下,如果一致性分数低于阈值(比如 0.8),就应该触发人工审核,而不是直接返回给用户。
策略三:工具结果验证
Agent 和纯聊天的最大区别是——它会调用工具。幻觉的一个常见来源是 Agent “编造”了工具返回的结果,或者错误地解读了结果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
|
@Component public class ToolResultVerifier {
private final ChatClient chatClient;
public ToolResultVerifier(ChatClient.Builder builder) { this.chatClient = builder.build(); }
public VerificationResult verify( String toolCall, String toolResult, String agentClaim) {
String prompt = """ 你是一个工具调用结果验证器。
工具调用:%s 工具返回:%s Agent 陈述:%s
请判断: 1. Agent 的陈述是否被工具返回的数据支撑? 2. Agent 是否遗漏了工具返回中的重要信息? 3. Agent 是否编造了工具返回中不存在的数据?
返回 JSON:{"supported": true/false, "missing_info": [...], "fabricated_info": [...]} """.formatted(toolCall, toolResult, agentClaim);
String result = chatClient.prompt() .user(prompt) .call() .content();
return parseVerification(result); } }
|
成本优化:Agent 的第二大挑战
上线 Agent 后你会发现,成本增长曲线远超预期。一个客服对话可能涉及 5-10 次 LLM 调用(ReAct 循环、工具调用、总结),每次调用都带着 System Prompt + 历史消息 + 检索结果。单次对话的 Token 消耗轻松突破 10K。
成本的四大来源
1 2 3 4 5
| 单次对话成本 ≈ Prompt Tokens × $单价 + Completion Tokens × $单价
其中: - Prompt Tokens = System Prompt + 历史消息 + RAG 上下文 + 工具描述 - Completion Tokens = 推理步骤 + 工具调用 + 最终回答
|
优化策略一:分层模型调度
不是所有任务都需要最强的模型。一个成熟的 Agent 系统应该有模型分层:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
|
@Component public class ModelRouter {
private final ChatClient fastModel; private final ChatClient smartModel; private final ChatClient proModel;
public ModelRouter( @Qualifier("fast") ChatClient.Builder fastBuilder, @Qualifier("smart") ChatClient.Builder smartBuilder, @Qualifier("pro") ChatClient.Builder proBuilder) { this.fastModel = fastBuilder.build(); this.smartModel = smartBuilder.build(); this.proModel = proBuilder.build(); }
public ChatClient route(String userMessage, List<ToolDefinition> availableTools) { TaskComplexity complexity = classifyComplexity(userMessage);
return switch (complexity) { case SIMPLE -> { yield fastModel; } case MODERATE -> { yield smartModel; } case COMPLEX -> { yield proModel; } }; }
private TaskComplexity classifyComplexity(String message) { if (message.length() < 20 && !message.contains("代码") && !message.contains("分析")) { return TaskComplexity.SIMPLE; }
int complexityScore = 0; if (message.contains("代码") || message.contains("实现")) complexityScore += 2; if (message.contains("架构") || message.contains("设计")) complexityScore += 2; if (message.contains("分析") || message.contains("对比")) complexityScore += 1; if (message.length() > 200) complexityScore += 1;
if (complexityScore >= 3) return TaskComplexity.COMPLEX; if (complexityScore >= 1) return TaskComplexity.MODERATE; return TaskComplexity.SIMPLE; }
enum TaskComplexity { SIMPLE, MODERATE, COMPLEX } }
|
实际效果参考: 在一个客服场景中,70% 的查询是简单问答(退换货政策、营业时间),用轻量模型处理,成本下降约 60%,响应速度提升 3 倍,用户满意度几乎不受影响。
优化策略二:Prompt 压缩
System Prompt 和历史消息是 Token 消耗的大头。优化手段包括:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
|
@Component public class PromptCompressor {
private final ChatClient chatClient;
public PromptCompressor(ChatClient.Builder builder) { this.chatClient = builder.build(); }
public List<Message> compressHistory(List<Message> history, int maxTokens) { int currentTokens = estimateTokens(history); if (currentTokens <= maxTokens) { return history; }
int keepRecent = 4; List<Message> recent = history.subList( Math.max(0, history.size() - keepRecent), history.size()); List<Message> older = history.subList( 0, Math.max(0, history.size() - keepRecent));
String olderText = older.stream() .map(m -> m.getMessageType() + ": " + m.getText()) .collect(Collectors.joining("\n"));
String summary = chatClient.prompt() .user("请用 3-5 句话概括以下对话的关键信息:\n" + olderText) .call() .content();
List<Message> compressed = new ArrayList<>(); compressed.add(new SystemMessage("之前的对话摘要:" + summary)); compressed.addAll(recent); return compressed; }
public String compressContext(String query, String fullContext, int maxChars) { if (fullContext.length() <= maxChars) { return fullContext; }
String[] paragraphs = fullContext.split("\n\n"); return chatClient.prompt() .user(""" 从以下文档中,提取与问题最相关的关键信息,不超过 %d 字符:
问题:%s
文档: %s """.formatted(maxChars, query, fullContext)) .call() .content(); }
private int estimateTokens(List<Message> messages) { return messages.stream() .mapToInt(m -> m.getText().length() * 2) .sum(); } }
|
优化策略三:缓存与语义去重
很多用户的问题本质上是一样的,只是措辞不同。用语义缓存可以避免重复调用 LLM:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
|
@Component public class SemanticCache {
private final EmbeddingModel embeddingModel; private final CacheEntryStore store; private final double similarityThreshold;
public SemanticCache( EmbeddingModel embeddingModel, CacheEntryStore store, @Value("${agent.cache.similarity-threshold:0.92}") double threshold) { this.embeddingModel = embeddingModel; this.store = store; this.similarityThreshold = threshold; }
public String get(String query) { float[] queryEmbedding = embeddingModel.embed(query).getContent();
CacheEntry best = store.findMostSimilar(queryEmbedding);
if (best != null && best.similarity() >= similarityThreshold) { return best.answer(); } return null; }
public void put(String query, String answer) { float[] embedding = embeddingModel.embed(query).getContent(); store.save(new CacheEntry(query, answer, embedding)); }
record CacheEntry(String query, String answer, float[] embedding, double similarity) { CacheEntry(String query, String answer, float[] embedding) { this(query, answer, embedding, 0.0); } } }
|
阈值选择的经验法则:
- 0.95+:非常保守,只缓存几乎一模一样的问题,命中率低但安全
- 0.90-0.95:平衡模式,推荐大多数场景
- 0.85-0.90:激进模式,命中率高但可能返回不完全匹配的缓存
Benchmark 框架:系统化评估 Agent
手动测试几个 case 不够——你需要系统化的基准测试框架。
评估数据集设计
一个好的评估数据集应该包含:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
public record EvalCase( String id, String category, // 分类:qa / tool_use / multi_turn / edge_case String query, // 用户输入 String expectedAnswer, // 期望的回答(或关键信息) List<String> expectedTools, // 期望调用的工具列表 EvalRubric rubric // 评分标准 ) {}
public record EvalRubric( double accuracyWeight, // 准确性权重 double completenessWeight, // 完整性权重 double safetyWeight, // 安全性权重 double efficiencyWeight // 效率权重(轮次、Token 消耗) ) { public static EvalRubric defaultRubric() { return new EvalRubric(0.4, 0.3, 0.2, 0.1); } }
|
自动化评估流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
|
@Service public class AgentEvalEngine {
private final ChatClient judgeModel; private final AgentService agentService;
public AgentEvalEngine( @Qualifier("smart") ChatClient.Builder builder, AgentService agentService) { this.judgeModel = builder.build(); this.agentService = agentService; }
public EvalReport runSuite(List<EvalCase> cases) { List<EvalResult> results = new ArrayList<>();
for (EvalCase evalCase : cases) { long startTime = System.currentTimeMillis(); AgentResponse response = agentService.chat(evalCase.query()); long latency = System.currentTimeMillis() - startTime;
ScoredResult scored = score(evalCase, response, latency); results.add(new EvalResult(evalCase, response, scored)); }
return generateReport(results); }
private ScoredResult score(EvalCase evalCase, AgentResponse response, long latency) { String prompt = """ 你是一个 AI Agent 评估专家。请对以下回答进行多维度评分。
【问题】%s 【期望回答】%s 【Agent 实际回答】%s 【Agent 调用的工具】%s 【期望调用的工具】%s 【响应延迟】%d ms
请分别对以下维度打分(0-10 分): 1. 准确性:回答的事实是否正确 2. 完整性:是否覆盖了所有关键信息 3. 安全性:是否有有害/误导性内容 4. 工具使用:是否正确调用了合适的工具
以 JSON 格式返回:{"accuracy": N, "completeness": N, "safety": N, "tool_use": N, "reasoning": "..."} """.formatted( evalCase.query(), evalCase.expectedAnswer(), response.answer(), String.join(", ", response.toolsUsed()), String.join(", ", evalCase.expectedTools()), latency );
String result = judgeModel.prompt().user(prompt).call().content(); return parseScoredResult(result, latency); }
private EvalReport generateReport(List<EvalResult> results) { double avgAccuracy = results.stream() .mapToDouble(r -> r.scored().accuracy()) .average().orElse(0);
double avgCompleteness = results.stream() .mapToDouble(r -> r.scored().completeness()) .average().orElse(0);
double avgSafety = results.stream() .mapToDouble(r -> r.scored().safety()) .average().orElse(0);
double avgLatency = results.stream() .mapToDouble(EvalResult::latencyMs) .average().orElse(0);
double toolAccuracy = results.stream() .filter(r -> !r.evalCase().expectedTools().isEmpty()) .mapToDouble(r -> r.scored().toolUse()) .average().orElse(0);
Map<String, List<EvalResult>> byCategory = results.stream() .collect(Collectors.groupingBy(r -> r.evalCase().category()));
return new EvalReport( results.size(), avgAccuracy, avgCompleteness, avgSafety, toolAccuracy, avgLatency, byCategory.entrySet().stream() .collect(Collectors.toMap( Map.Entry::getKey, e -> calculateCategoryStats(e.getValue()))) ); } }
|
与 CI/CD 集成
评估不应该只在开发阶段做,应该集成到 CI/CD 流程中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| name: Agent Evaluation
on: pull_request: paths: - 'src/main/java/**/agent/**' - 'src/main/resources/prompts/**'
jobs: eval: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Run Agent Eval Suite run: | mvn test -Dtest=AgentEvalTest env: OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
- name: Check Quality Gates run: | # 准确性必须 >= 8.0 ACCURACY=$(cat eval-report.json | jq '.avgAccuracy') if (( $(echo "$ACCURACY < 8.0" | bc -l) )); then echo "❌ Accuracy $ACCURACY below threshold 8.0" exit 1 fi echo "✅ All quality gates passed"
|
生产环境的可观测性
上线不是终点,是起点。你需要持续监控 Agent 的表现。
关键监控指标
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
|
@Component public class AgentMetrics {
private final MeterRegistry registry; private final Counter hallucinationCounter; private final Counter toolFailureCounter; private final Timer responseTimer; private final DistributionSummary tokenUsage;
public AgentMetrics(MeterRegistry registry) { this.registry = registry; this.hallucinationCounter = Counter.builder("agent.hallucination.detected") .description("检测到的幻觉次数") .register(registry); this.toolFailureCounter = Counter.builder("agent.tool.failure") .description("工具调用失败次数") .tag("tool", "all") .register(registry); this.responseTimer = Timer.builder("agent.response.time") .description("Agent 响应时间") .register(registry); this.tokenUsage = DistributionSummary.builder("agent.token.usage") .description("每次对话的 Token 消耗") .register(registry); }
public void recordConversation(ConversationMetrics metrics) { responseTimer.record(metrics.duration()); tokenUsage.record(metrics.totalTokens());
if (metrics.hallucinationDetected()) { hallucinationCounter.increment(); }
metrics.toolFailures().forEach(toolName -> { Counter.builder("agent.tool.failure") .tag("tool", toolName) .register(registry) .increment(); }); } }
|
红线告警
设置自动告警,当以下指标异常时立即通知:
- 幻觉率突增:5 分钟窗口内幻觉率 > 5%
- 延迟飙升:P95 延迟 > 阈值的 2 倍
- 成本异常:单小时 Token 消耗超过日均的 3 倍
- 工具失败率:某个工具连续失败 3 次以上
生产环境最佳实践
1. 渐进式灰度发布
不要一次性把新版本推给所有用户。用 A/B 测试验证效果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
@Component public class GrayReleaseRouter {
@Value("${agent.gray.new-version-ratio:0.1}") private double newVersionRatio;
public AgentVersion route(String userId) { int hash = Math.abs(userId.hashCode()); return (hash % 100) < (newVersionRatio * 100) ? AgentVersion.NEW : AgentVersion.STABLE; } }
|
2. 安全兜底策略
Agent 不确定时,宁可说”我不知道”也不要瞎编:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
|
@Component public class SafetyNet {
private final FaithfulnessChecker faithfulnessChecker; private final SelfConsistencyChecker consistencyChecker; private final double faithfulnessThreshold; private final double consistencyThreshold;
public SafetyNet( FaithfulnessChecker faithfulnessChecker, SelfConsistencyChecker consistencyChecker, @Value("${agent.safety.faithfulness-threshold:0.7}") double faithThreshold, @Value("${agent.safety.consistency-threshold:0.75}") double consistencyThreshold) { this.faithfulnessChecker = faithfulnessChecker; this.consistencyChecker = consistencyChecker; this.faithfulnessThreshold = faithThreshold; this.consistencyThreshold = consistencyThreshold; }
public String checkAndFallback(String context, String answer, String originalQuery) { double faithfulness = faithfulnessChecker.score(context, answer); if (faithfulness < faithfulnessThreshold) { return "抱歉,关于这个问题我目前没有足够的信息来给出准确回答,建议您联系人工客服获取帮助。"; }
return answer; } }
|
3. 持续评估闭环
评估不是一次性的事,而是一个持续的闭环:
1
| 生产日志 → 采样 → 人工标注 → 更新评估集 → 重新评估 → 优化 Prompt/模型 → 灰度验证 → 全量发布
|
每周从生产日志中随机采样 50-100 条对话,人工标注质量分数,持续积累评估数据集。这个数据集的价值会随时间指数增长。
总结:Agent 评估的”不可能三角”
Agent 评估存在一个”不可能三角”——准确性、成本、速度三者很难同时最优:
- 追求准确性 → 多次 LLM 调用做交叉验证 → 成本高、速度慢
- 追求成本低 → 用轻量模型、减少验证 → 准确性下降
- 追求速度快 → 减少推理步骤、跳过验证 → 质量风险
实际落地的关键是分场景制定策略:
| 场景 |
准确性要求 |
推荐策略 |
| 金融交易 |
极高 |
忠实性检测 + 人工审核 |
| 客服问答 |
高 |
忠实性检测 + 安全兜底 |
| 内容创作 |
中 |
自一致性检测 |
| 闲聊娱乐 |
低 |
基本安全过滤即可 |
最后,回到系列文章的视角——Agent 评估不是独立的能力,而是对前面所有能力的守护:
没有评估的 Agent 就像没有仪表盘的汽车——你不知道它跑多快、油还剩多少、发动机温度是否正常。也许它看起来跑得很好,但哪天突然抛锚了你都不知道为什么。
给你的 Agent 装上仪表盘,让它在生产环境中安全、可控、可持续地运行。