에이전트 평가
adk-eval crate는 에이전트 동작을 테스트하고 검증하기 위한 포괄적인 도구를 제공합니다. 기존 소프트웨어 테스트와 달리 에이전트 평가는 LLM의 확률적 특성을 고려해야 하며, 동시에 의미 있는 품질 신호를 제공해야 합니다.
개요
ADK-Rust의 에이전트 평가는 여러 평가 전략을 지원합니다:
- Trajectory Evaluation: 에이전트가 예상되는 도구를 올바른 순서로 호출하는지 검증합니다.
- Response Similarity: 다양한 알고리즘(Jaccard, Levenshtein, ROUGE)을 사용하여 에이전트 응답을 비교합니다.
- LLM-Judged Evaluation: 다른 LLM을 사용하여 의미론적 유사성과 품질을 평가합니다.
- Rubric-Based Scoring: 가중치 부여 점수와 함께 사용자 지정 기준에 따라 평가합니다.
빠른 시작
use adk_eval::{Evaluator, EvaluationConfig, EvaluationCriteria};
use std::sync::Arc;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create your agent
let agent = create_my_agent()?;
// Configure evaluator with criteria
let config = EvaluationConfig::with_criteria(
EvaluationCriteria::exact_tools()
.with_response_similarity(0.8)
);
let evaluator = Evaluator::new(config);
// Run evaluation against test file
let report = evaluator
.evaluate_file(agent, "tests/my_agent.test.json")
.await?;
// Check results
if report.all_passed() {
println!("All {} tests passed!", report.summary.total);
} else {
println!("{}", report.format_summary());
}
Ok(())
}
테스트 파일 형식
테스트 케이스는 .test.json 확장자를 사용하는 JSON 파일에 정의됩니다.
{
"eval_set_id": "weather_agent_tests",
"name": "Weather Agent Tests",
"description": "Test weather agent functionality",
"eval_cases": [
{
"eval_id": "test_current_weather",
"conversation": [
{
"invocation_id": "inv_001",
"user_content": {
"parts": [{"text": "What's the weather in NYC?"}],
"role": "user"
},
"final_response": {
"parts": [{"text": "The weather in NYC is 65°F and sunny."}],
"role": "model"
},
"intermediate_data": {
"tool_uses": [
{
"name": "get_weather",
"args": {"location": "NYC"}
}
]
}
}
]
}
]
}
평가 기준
도구 궤적 일치
에이전트가 예상된 도구를 올바른 순서로 호출하는지 검증합니다.
let criteria = EvaluationCriteria {
tool_trajectory_score: Some(1.0), // Require 100% match
tool_trajectory_config: Some(ToolTrajectoryConfig {
strict_order: true, // Tools must be called in exact order
strict_args: false, // Allow extra arguments in tool calls
}),
..Default::default()
};
옵션:
strict_order: 정확한 순서 일치를 요구합니다.strict_args: 정확한 인자 일치를 요구합니다 (추가 인자 허용 안 함).- 구성 가능한 임계값을 통한 부분 일치
응답 유사성
다양한 알고리즘을 사용하여 응답 텍스트를 비교합니다.
let criteria = EvaluationCriteria {
response_similarity: Some(0.8), // 80% similarity required
response_match_config: Some(ResponseMatchConfig {
algorithm: SimilarityAlgorithm::Jaccard,
ignore_case: true,
normalize: true,
..Default::default()
}),
..Default::default()
};
사용 가능한 알고리즘:
| Algorithm | 설명 |
|---|---|
Exact | 정확한 문자열 일치 |
Contains | 부분 문자열 확인 |
Levenshtein | 편집 거리 |
Jaccard | 단어 중첩 (기본값) |
Rouge1 | 유니그램 중첩 |
Rouge2 | 바이그램 중첩 |
RougeL | 최장 공통 부분 수열 |
LLM 평가 의미론적 일치
LLM을 사용하여 의미론적 동등성을 평가합니다.
use adk_eval::{Evaluator, EvaluationConfig, EvaluationCriteria, LlmJudge};
use adk_model::GeminiModel;
// Create evaluator with LLM judge
let judge_model = Arc::new(GeminiModel::new(&api_key, "gemini-2.0-flash")?);
let config = EvaluationConfig::with_criteria(
EvaluationCriteria::semantic_match(0.85)
);
let evaluator = Evaluator::with_llm_judge(config, judge_model);
LLM 평가자는 다음을 평가합니다.
- 의미론적 동등성 (의미는 같으나 단어가 다름)
- 사실적 정확성
- 응답의 완전성
루브릭 기반 평가
가중치 점수화를 통해 사용자 지정 기준에 따라 평가합니다.
use adk_eval::{Rubric, EvaluationCriteria};
let criteria = EvaluationCriteria::default()
.with_rubrics(0.7, vec![
Rubric::new("Accuracy", "Response is factually correct")
.with_weight(0.5),
Rubric::new("Helpfulness", "Response addresses user's needs")
.with_weight(0.3),
Rubric::new("Clarity", "Response is clear and well-organized")
.with_weight(0.2),
]);
각 루브릭은 LLM 평가자에 의해 0-1점으로 점수가 매겨진 다음, 가중치를 사용하여 결합됩니다.
안전 및 환각 감지
응답에서 안전 문제 및 환각을 확인합니다.
let criteria = EvaluationCriteria {
safety_score: Some(0.95), // Require high safety score
hallucination_score: Some(0.9), // Require low hallucination rate
..Default::default()
};
결과 보고
평가 보고서는 상세한 결과를 제공합니다.
let report = evaluator.evaluate_file(agent, "tests/agent.test.json").await?;
// 요약 통계
println!("Total: {}", report.summary.total);
println!("Passed: {}", report.summary.passed);
println!("Failed: {}", report.summary.failed);
println!("Pass Rate: {:.1}%", report.summary.pass_rate * 100.0);
// 상세 실패 내역
for result in report.failures() {
println!("Failed: {}", result.eval_id);
for failure in &result.failures {
println!(" - {}: {} (expected: {}, actual: {})",
failure.criterion,
failure.message,
failure.expected,
failure.actual
);
}
}
// CI/CD를 위한 JSON 내보내기
let json = report.to_json()?;
std::fs::write("eval_results.json", json)?;
배치 평가
병렬 평가
여러 테스트 케이스를 동시에 평가합니다:
let results = evaluator
.evaluate_cases_parallel(agent, &cases, 4) // 4개의 동시 평가
.await;
디렉토리 평가
디렉토리의 모든 테스트 파일을 평가합니다:
let reports = evaluator
.evaluate_directory(agent, "tests/eval_cases")
.await?;
for (file, report) in reports {
println!("{}: {} passed, {} failed",
file,
report.summary.passed,
report.summary.failed
);
}
cargo test와의 통합
표준 Rust 테스트에서 평가를 사용합니다:
#[tokio::test]
async fn test_weather_agent() {
let agent = create_weather_agent().unwrap();
let evaluator = Evaluator::new(EvaluationConfig::with_criteria(
EvaluationCriteria::exact_tools()
));
let report = evaluator
.evaluate_file(agent, "tests/weather_agent.test.json")
.await
.unwrap();
assert!(report.all_passed(), "{}", report.format_summary());
}
예시
# 기본 평가
cargo run --example eval_basic
# 트랙젝토리 유효성 검사
cargo run --example eval_trajectory
# LLM으로 판단하는 의미론적 일치
cargo run --example eval_semantic
# 루브릭 기반 채점
cargo run --example eval_rubric
# 응답 유사성 알고리즘
cargo run --example eval_similarity
# 보고서 생성
cargo run --example eval_report
모범 사례
- 간단하게 시작: 의미론적 검사를 추가하기 전에 트랙젝토리 유효성 검사부터 시작하세요.
- 대표적인 케이스 사용: 테스트 파일은 엣지 케이스와 일반적인 시나리오를 모두 다뤄야 합니다.
- 임계값 조정: 관대한 임계값으로 시작하여 Agent가 개선됨에 따라 조여나가세요.
- 기준 결합: 포괄적인 평가를 위해 여러 기준을 사용하세요.
- 테스트 파일 버전 관리: Agent 코드와 함께 테스트 파일을 버전 관리 시스템에 유지하세요.
- CI/CD 통합: 회귀를 감지하기 위해 CI에서 평가를 실행하세요.
이전: ← A2A Protocol | 다음: Access Control →