已实现

Agent 端到端自动化评估体系

覆盖 6 个核心质量维度,通过真实 LLM 调用 + LLM-as-Judge 评分,量化证明 Agent 架构设计决策的正确性。

2026-06-01 feat/multi-agent 18 条用例 6 维度
01

目标

为 dawn-ai 构建一套端到端自动化评估体系,覆盖 AI Agent 系统的 6 个核心质量维度。通过真实 LLM 调用 + LLM-as-Judge 评分,量化证明 Agent 架构的设计决策正确性,并将评估结果自动写入 Langfuse Dashboard 形成可观测闭环。

🎯
一键展示
面试场景下展示 Agent 技术深度
🔄
自动回归
每次变更后量化对比效果
📊
可观测闭环
结果可追溯、可对比、可可视化
02

非目标

  • 不替代现有单元测试(37 个测试类保持不动,评估体系是补充而非替代)
  • 不做线上 A/B 测试(本方案聚焦开发阶段的离线评估)
  • 不引入 Langfuse Java SDK(延续现有纯 OTel 集成风格,评估打分通过 REST API 实现)
  • 不做人工标注评估(纯自动化,LLM-as-Judge 替代人工)
03

已锁定决策

1
评估维度
全部 6 个
覆盖面试高频拷问领域
2
LLM 策略
纯真实 LLM
Agent 核心是 LLM 行为,Mock 太假
3
评分方式
Langfuse LLM-as-Judge
v3 内置 evals,零额外依赖
4
数据集管理
双写(本地 JSON + Langfuse)
git 版本控制 + 可视化
5
运行模式
JUnit @Tag("evaluation")
CI 原生支持,最小侵入
6
Judge 模型
独立 ChatModel bean
避免自评偏见
7
数据集结构
统一 JSON Schema
6 维度共用,维护成本低
8
评分方式
混合 Binary + Likert
行为类确定性强,质量类细粒度
9
测试环境
本地 Docker Compose
最接近真实环境
10
Langfuse API
封装 REST Client
零额外依赖
11
Judge Prompt
代码内 .txt 文件
git 版本控制,测试自包含
04

架构

ToolSelectionBinary
RagRecallLikert
AnswerCompleteLikert
PromptAssemblyBinary
SubAgentIsol.Binary
MultiTurnLikert
AbstractEvaluationTest基类 · 报告汇总 · Langfuse 推送
JudgeServiceLLM Judge
DatasetLoaderJSON 加载
LangfuseClientREST API
Judge ChatModel独立 LLM
Langfuse Dashboard评分可视化

数据流

1
加载数据集

EvaluationDatasetLoader.loadByDimension() 从 JSON 加载用例

2
执行 Agent

agentOrchestrator.chat() 调用真实 LLM + 工具执行,返回 AgentResult

3
提取结果

从 AgentResult.steps() 提取 actualTools / retrievedDocs / answer

4
Judge 评分

JudgeService 加载 Prompt 模板 → 填充变量 → 调用 Judge LLM → 解析 JSON

5
断言 + 写入

assertThat(passed) + LangfuseScoringClient.score() 写入 Dashboard

05

评估维度

🔧

工具选择正确性

Binary 阈值: 1.0

评什么:给定用户 query,Agent 是否调用了正确的 tool 组合。

面试价值:证明 ReAct 范式的工具调度可靠性。

Query期望工具验证点
"今天北京天气怎么样?"weatherTool天气查询 → 天气工具
"帮我算 (15*23)+47"calculatorTool数学计算 → 计算器
"公司2024年营收增长率?"knowledgeSearchTool知识查询 → RAG
"上海多少度?超30度算电费"weather + calc多工具编排
🔍

RAG 召回相关性

Likert 1-5 阈值: ≥ 3.5

评什么:检索到的文档是否与 query 语义相关,是否召回了正确的文档。

5
所有期望文档被召回,无无关文档
4
所有期望文档被召回,混入少量无关文档
3
大部分期望文档被召回(≥70%)
2
少量期望文档被召回(<50%)
1
无期望文档被召回,或完全无关
💬

Answer 完整性

Likert 1-5 阈值: ≥ 3.5

评什么:最终回答是否覆盖了 query 要求的所有关键信息点。

5
完整覆盖所有关键信息点,结构清晰
4
覆盖大部分信息点(≥80%)
3
覆盖核心信息点(≥60%)
2
覆盖部分信息(<50%)
1
回答与问题无关或完全错误
📝

Prompt 拼装正确性

Binary 阈值: 1.0

评什么AgentOrchestrator.buildSystemPrompt() 是否正确拼装了所有必要的 prompt 段落。

验证方式:通过反射调用 private 方法,将实际输出与期望段落对比。

System Prompt 拼装顺序(8 段)

1
baseSystemPrompt

基础角色定义

2
User Profile

UserProfileService.formatForSystemPrompt()

3
Topic Section

话题约束(如有 topicId)

4
Skills Catalog

formatSkills() — 技能目录

5
Sub-Agent Catalog

formatSubAgents() — 子 Agent 目录

6
Execution Plan

TaskPlanner 输出

7
Plan Enforcement

计划强制指令

8
Max-Steps

最大步数限制

🤖

子 Agent 上下文隔离

Binary 阈值: 1.0

评什么:子 Agent 的执行是否与主 Agent 完全隔离。

步骤隔离
子 Agent 步骤不进入主 Agent 的 StepCollector
超时降级
超时后返回 PARTIAL_SUCCESS,主 Agent 不崩溃
派发限制
超过 max-dispatches 的派发被拦截
实现说明:GenericReActSubAgentExecutor 为每个子 Agent 创建独立的 StepCollectorContext,通过 CompletableFuture.get(timeoutSeconds, SECONDS) 强制超时。
💬

多轮对话连贯性

Likert 1-5 阈值: ≥ 3.5

评什么:Agent 是否正确利用对话历史(memory snapshot)提供连贯的回答。

5
完美引用对话历史,理解上下文
4
正确使用大部分上下文
3
感知部分上下文,遗漏重要细节
2
几乎忽略对话历史
1
完全忽略历史或与先前事实矛盾
记忆机制:MemoryService 使用 Redis 列表(20 条滑动窗口),getHistory() 从 Redis 加载历史,buildHistory() 转换为 Spring AI Message 对象。
06

数据模型

统一 JSON Schema

JSON { "id": "tool_select_001", "dimension": "tool_selection", "query": "今天北京天气怎么样?", "context": { "availableTools": ["weatherTool", "calculatorTool", "knowledgeSearchTool"], "memorySnapshot": null, "ragDocuments": [] }, "expected": { "tools": ["weatherTool"], "answerCriteria": "应调用 weatherTool 查询天气", "docIds": null } }

各维度字段使用矩阵

字段工具选择RAGAnswerPrompt子Agent多轮
query
availableTools
memorySnapshot
ragDocuments
promptSegments
subAgentBehavior
expected.tools
answerCriteria
docIds

✓ 必填   ○ 可选   – 不适用

数据集规模

18
总用例数
6
评估维度
3 + 3
Binary + Likert
07

Judge 系统

为什么需要独立 Judge 模型

自评偏见
独立模型避免"自己给自己打高分"
面试加分
可回答"如何避免评估偏见"的拷问
职责分离
Judge Prompt 和业务 Prompt 独立调优
成本可控
Judge 仅在测试时调用,不影响线上

Judge Prompt 设计原则

1
结构化输出

要求返回 {"score": N, "reasoning": "..."} JSON

2
明确评分规则

每个 Prompt 内嵌评分 rubric,消除歧义

3
容错解析

JudgeService.extractJson() 自动剥离 markdown 代码围栏

4
模板变量

{{variable}} 占位符,运行时填充

评分阈值

≥ 1.0
Binary 通过阈值
≥ 3.5
Likert 通过阈值
08

Langfuse 集成

双写策略

本地 JSON(source of truth)
  • git 版本控制
  • CI 原生加载
  • 离线可用
Langfuse(可视化)
  • UI 可视化
  • Experiment 对比
  • 评分趋势

REST API 端点

端点用途调用时机
POST /api/public/scores写入评分每个 case 评估完成后
POST /api/public/datasets创建/更新 Dataset评估开始前
POST /api/public/dataset-items添加 Dataset Item同步数据集时
POST /api/public/dataset-run-items关联 trace 到 Dataset评估完成后

认证方式

Java String auth = Base64.getEncoder().encodeToString( (publicKey + ":" + secretKey).getBytes(UTF_8)); // Header: Authorization: Basic {auth}
09

测试执行

前置条件

Shell # 1. 启动基础设施 docker compose up -d postgres redis # 2. (可选)启动 Langfuse docker compose -f docker-compose.yml -f docker-compose.observe.yml --profile observe up -d # 3. 配置 LLM API export OPENAI_API_KEY=your-key export BASE_URL=your-base-url export CHAT_MODEL=your-model

运行命令

Shell # 全量评估(6 维度,18 用例) mvn test -Dgroups=evaluation # 单维度评估 mvn test -Dtest=ToolSelectionEvaluationTest mvn test -Dtest=RagRecallEvaluationTest # 跳过评估测试(正常开发时) mvn test -DexcludedGroups=evaluation

输出

输出位置格式
控制台报告stdoutscore + reasoning + 总结
本地 JSONtarget/evaluation-reports/结构化 JSON
Langfuse 评分Dashboard → Scores评分趋势图
Langfuse DatasetDashboard → DatasetsExperiment 对比
10

文件结构

src/test/java/com/dawn/ai/evaluation/ base/ AbstractEvaluationTest.java # 基类 EvaluationCase.java # 数据模型 EvaluationDatasetLoader.java # 加载器 EvaluationReport.java # 报告 LangfuseScoringClient.java # REST Client judge/ JudgeChatModelConfig.java # 独立 ChatModel JudgeDimension.java # 6 维度枚举 JudgeResult.java # 评分结果 JudgeService.java # 统一调用 dimensions/ ToolSelectionEvaluationTest.java # Binary RagRecallEvaluationTest.java # Likert AnswerCompletenessEvaluationTest.java # Likert PromptAssemblyEvaluationTest.java # Binary SubAgentIsolationEvaluationTest.java # Binary MultiTurnCoherenceEvaluationTest.java # Likert src/test/resources/evaluation/ evaluation-dataset.json # 18 条用例 judge-prompts/ tool_selection.txt # 工具选择 rag_recall.txt # RAG 召回 answer_completeness.txt # Answer 完整性 prompt_assembly.txt # Prompt 拼装 subagent_isolation.txt # 子 Agent 隔离 multi_turn_coherence.txt # 多轮对话
11

测试体系对比

单元测试 (37)
评估测试 (6) ← 新增
LLM 调用
Mock
真实 LLM
断言方式
assertEquals
LLM-as-Judge
运行频率
每次 commit
需要时
运行时间
秒级
分钟级
成本
LLM API 费用
12

面试叙事线

开场(30 秒)

"我搭了一套 6 维度的 Agent 评估体系,用 LLM-as-Judge 做自动化评分。数据集 git 版本控制,评分自动写入 Langfuse Dashboard。mvn test -Dgroups=evaluation 一键跑完。"

技术深度(2-3 分钟)

逐维度展示:工具选择准确率 → RAG 召回 MRR → Answer 完整性 → Prompt 拼装 snapshot 回归 → 子 Agent 隔离证明 → 多轮对话记忆利用。每个维度用 30 秒,展示评分结果和 Judge reasoning。

工程化能力(1 分钟)

"评估结果双写:本地 JSON 做 git 版本控制,Langfuse Dashboard 做可视化。Judge 模型独立于业务模型,避免自评偏见。整个评估体系零 Langfuse SDK 依赖,通过 REST API 实现。"

迭代能力(30 秒)

"每次 prompt 变更后跑一次评估,Langfuse Experiment 对比不同版本的评分趋势。数据驱动迭代,不是凭感觉调 prompt。"
13

后续扩展

项目优先级说明
扩充数据集每个维度扩充到 20+ 用例
CI 集成GitHub Actions 运行评估,PR 评论评分
Langfuse Experiment不同版本的评分趋势对比
人工标注校准人工标注用例,校准 Judge 准确率
成本监控统计评估运行的 LLM API 费用