تقييم الوكيل

توفر حزمة adk-eval أدوات شاملة لاختبار سلوك الوكلاء والتحقق منه. وعلى خلاف اختبار البرمجيات التقليدي، يجب أن يراعي تقييم الوكلاء الطبيعة الاحتمالية لـ LLMs، مع الاستمرار في توفير مؤشرات جودة مفيدة.

نظرة عامة

يدعم تقييم الوكلاء في ADK-Rust استراتيجيات تقييم متعددة:

  • تقييم المسار: التحقق من أن الوكلاء يستدعون الأدوات المتوقعة بالتسلسل الصحيح
  • تشابه الاستجابة: مقارنة استجابات الوكيل باستخدام خوارزميات متنوعة (Jaccard وLevenshtein وROUGE)
  • تقييم محكوم بواسطة LLM: استخدام LLM آخر لتقييم التشابه الدلالي والجودة
  • التقييم القائم على سلم التقييم: التقييم وفق معايير مخصصة باستخدام أوزان للدرجات

البدء السريع

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(())
}

تنسيق ملف الاختبار

تُعرَّف حالات الاختبار في ملفات JSON بامتداد .test.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()
};

الخوارزميات المتاحة:

الخوارزميةالوصف
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-3.7-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?;

// Summary statistics
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);

// Detailed failures
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
        );
    }
}

// Export to JSON for CI/CD
let json = report.to_json()?;
std::fs::write("eval_results.json", json)?;

التقييم الدفعي

التقييم المتوازي

قيّم حالات اختبار متعددة بشكل متزامن:

let results = evaluator
    .evaluate_cases_parallel(agent, &cases, 4)  // 4 concurrent evaluations
    .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());
}

أمثلة

استخدم مستوى الميزات القياسي للتقييم APIs:

cargo check -p adk-rust --no-default-features --features standard

يتم الحفاظ على معرض التقييمات القابلة للتشغيل في adk-playground.

الميزات المتقدمة

تجلب الإمكانات التالية adk-eval إلى مستوى التكافؤ مع أطر عمل مثل Braintrust وLangSmith وInspect AI. وهي إضافية إلى API الحالية، وتخضع لتفعيل الميزات عندما تُدخل تبعيات جديدة.

مُقيِّم LLM مُنظَّم

ينتج أحكامًا مكتوبة النوع (نجاح/فشل/جزئي) مع الدرجات والاستدلال عبر استدعاء الدوال أو البديل JSON:

use adk_eval::{StructuredJudge, StructuredJudgeConfig};

let judge = StructuredJudge::new(model.clone());

let verdict = judge.judge(
    "The capital of France is Paris.",
    "Paris is the capital of France.",
    "factual_accuracy"
).await?;

println!("Score: {:.2}, Verdict: {:?}", verdict.score, verdict.verdict);
println!("Reasoning: {}", verdict.reasoning);

يحاول المُقيِّم استخدام استدعاء الدوال (مخطط الاستجابة) أولًا، ثم يعود إلى طلب JSON باستخدام محلّل متساهل يتعامل مع JSON الخام، وأسوار Markdown، وJSON المضمّن في النص النثري.

تتبّع التكلفة وزمن الاستجابة

تتبّع استخدام الرموز المميّزة واحسب التكاليف المقدّرة بالدولار لكل تقييم:

use adk_eval::{CostTracker, CostMetrics};

let tracker = CostTracker::new();  // Uses default pricing tables

// Compute cost for a known model
let cost = tracker.compute_cost("gpt-4o", 2000, 800);
// → Some(0.013)

// Extract metrics from event streams
let metrics = tracker.extract_metrics(&events, duration);
println!("Tokens: {}, Latency: {}ms", metrics.total_tokens, metrics.latency_ms);

تحليل تتبّع التنفيذ

اكتشف استدعاءات الأدوات الزائدة، وحلقات التنفيذ، ودرجات كفاءة الحوسبة:

use adk_eval::{TraceAnalyzer, TraceAnalysis};

let analyzer = TraceAnalyzer::new();
let analysis = analyzer.analyze(&events);

println!("Efficiency: {:.1}%", analysis.efficiency_score * 100.0);
for diag in &analysis.diagnostics {
    println!("  [{:?}] {}", diag.pattern_type, diag.description);
}

خطوط الأساس للانحدار

احفظ مقاييس التقييم كخطوط أساس واكتشف تراجعات الجودة:

use adk_eval::BaselineStore;

let store = BaselineStore::new(".eval-baseline.json");

// Save current metrics
store.save("my_eval_set", &metrics)?;

// On next run, check for regressions
let regressions = store.check_regressions(&current_metrics, 0.05)?;
if !regressions.is_empty() {
    for reg in &regressions {
        println!("REGRESSION: {} dropped from {:.3} to {:.3}",
            reg.metric_name, reg.baseline_value, reg.current_value);
    }
}

مخرجات CI (JUnit XML)

أنشئ JUnit XML للتكامل الأصلي مع CI ‏(GitHub Actions وJenkins وGitLab CI):

use adk_eval::JunitReporter;  // requires `ci-helpers` feature

let xml = JunitReporter::generate(&report, "my_eval_suite")?;
std::fs::write("test-results.xml", xml)?;

سير عمل التعليقات التوضيحية البشرية

صدّر الحالات للمراجعة البشرية واستورد الأحكام مرة أخرى:

use adk_eval::AnnotationStore;

// Export cases for annotation
AnnotationStore::export(&cases, &results, "review.jsonl")?;

// After human review, import back
let (records, warnings) = AnnotationStore::import("review.jsonl", &valid_ids)?;

مقارنة الوكلاء بطريقة A/B

قارن بين وكيلين باستخدام اختبار الدلالة الإحصائية:

use adk_eval::{AbComparator, ab_comparator::wilcoxon_signed_rank};

// Requires `statistics` feature
let comparator = AbComparator::new(evaluator);
let report = comparator.compare(agent_a, agent_b, &eval_cases).await?;

for cmp in &report.criteria_comparisons {
    println!("{}: A={:.3} B={:.3} p={:.4} significant={}",
        cmp.criterion, cmp.agent_a_mean, cmp.agent_b_mean,
        cmp.p_value, cmp.significant);
}

حالات الاختبار المُنشأة تلقائيًا

أنشئ حالات تقييم من الأوصاف (عبر LLM) أو من سجلات أحداث الإنتاج:

use adk_eval::{TestGenerator, GeneratorConfig};

let generator = TestGenerator::with_config(model, GeneratorConfig {
    cases_per_description: 5,
    include_tool_expectations: true,
});

// From natural language
let cases = generator.generate_from_description(
    "A weather assistant that looks up forecasts by city"
).await?;

// From production events (no LLM needed)
let cases = generator.generate_from_events(&production_events)?;

مقاييس المحادثات متعددة الأدوار

قيّم المحادثات الممتدة وفق أربعة أبعاد:

use adk_eval::{ConversationScorer, ConversationScorerConfig};

let scorer = ConversationScorer::new(judge);
let metrics = scorer.score(&conversation, "Help user plan a trip").await?;

println!("Context retention: {:.2}", metrics.context_retention);
println!("Goal completion:   {:.2}", metrics.goal_completion);
println!("Coherence:         {:.2}", metrics.coherence);
println!("Topic drift:       {:.2}", metrics.topic_drift);

التشابه الدلالي المستند إلى التضمينات

قِس الحفاظ على المعنى باستخدام تضمينات المتجهات (يتطلب ميزة embedding):

use adk_eval::EmbeddingScorer;

let scorer = EmbeddingScorer::new(embedding_provider);
let score = scorer.score("expected text", "actual text").await?;
// Returns 0.0–1.0 cosine similarity

علامات الميزات

الميزةالتبعيةالإمكانية
embeddingadk-memoryالتشابه الدلالي القائم على التضمينات
ci-helpersquick-xmlإنشاء تقارير XML لـ JUnit
statisticsstatrsاختبار ويلكوكسون للرتب الموقعة لمقارنة A/B

تعمل جميع الميزات الأخرى (المحكّم المنظّم، ومتتبّع التكلفة، ومحلّل التتبّع، وخطوط الأساس، والتعليقات التوضيحية، ومولّد الاختبارات، ومقيّم المحادثات) دون أي علامات ميزات إضافية.

تكامل CLI

شغّل التقييمات من سطر الأوامر عبر cargo adk eval:

# Basic evaluation
cargo adk eval tests/my_agent.test.json

# Save baseline
cargo adk eval tests/ --save-baseline

# Check for regressions
cargo adk eval tests/ --check-regression --tolerance 0.05

# JUnit XML output for CI
cargo adk eval tests/ --format junit --output results.xml

# JSON output
cargo adk eval tests/ --format json

# Parallel execution
cargo adk eval tests/ --concurrency 4

رموز الخروج:

  • 0 — اجتازت جميع التقييمات، ولم تحدث أي تراجعات
  • 1 — اكتُشفت تراجعات (عند تعيين --check-regression)

أفضل الممارسات

  1. ابدأ ببساطة: ابدأ بالتحقق من المسار قبل إضافة الفحوصات الدلالية
  2. استخدم حالات ممثّلة: يجب أن تغطي ملفات الاختبار الحالات الحدّية والسيناريوهات الشائعة
  3. عاير الحدود: ابدأ بحدود متساهلة وشدّدها مع تحسّن الوكيل
  4. ادمج المعايير: استخدم معايير متعددة لإجراء تقييم شامل
  5. أنشئ إصدارات لملفات الاختبار: احتفظ بملفات الاختبار في نظام التحكم بالإصدارات إلى جانب كود الوكيل
  6. التكامل مع CI/CD: شغّل التقييمات في CI لاكتشاف التراجعات
  7. احفظ خطوط الأساس: استخدم --save-baseline بعد ترسيخ مستوى جودة، ثم --check-regression في CI
  8. استخدم المحكّمين المنظّمين: فضّل StructuredJudge على محكّم LLM العادي للحصول على نتائج قابلة للتحليل آليًا
  9. تتبّع التكاليف: فعّل CostTracker لمراقبة تراجعات الكفاءة إلى جانب الجودة
  10. اكتشف الحلقات: فعّل TraceAnalyzer لاكتشاف الوكلاء العالقين في أنماط تكرارية

مثال

يتوفر مثال عملي كامل يوضّح جميع الميزات:

cargo run --manifest-path examples/eval_showcase/Cargo.toml

راجع examples/eval_showcase/ للاطلاع على الكود المصدري.


السابق: ← بروتوكول A2A | التالي: التحكم في الوصول →

تقييم الوكيل - وثائق ADK-Rust | ADK-Rust