File size: 8,800 Bytes
b3fbea6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
"""Run Agent 3 (Content/Exercise Generation) evaluations with 3B and 7B models."""

import json
import logging
import sys
from datetime import datetime
from pathlib import Path

# Add project root to path
PROJECT_ROOT = Path(__file__).parent.parent
sys.path.insert(0, str(PROJECT_ROOT))

from src.evaluation.baseline import BaselineEvaluator  # noqa: E402

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)

logger = logging.getLogger(__name__)

# Output directory
OUTPUT_DIR = PROJECT_ROOT / "data" / "evaluation" / "content_agent"


def save_results(output_dir: Path, results: dict, model_size: str, model_responses: dict):
    """Save evaluation results to files."""
    output_dir.mkdir(parents=True, exist_ok=True)

    # Save model responses (for manual inspection)
    responses_file = output_dir / f"evaluation_outputs_{model_size.lower()}.json"
    with open(responses_file, "w", encoding="utf-8") as f:
        json.dump(model_responses, f, indent=2, ensure_ascii=False)
    logger.info(f"βœ“ Responses saved to {responses_file}")

    # Save detailed results as JSON
    results_json = {
        "metadata": {
            "model_size": model_size,
            "evaluation_date": datetime.now().isoformat(),
            "agent": "Agent 3 (Content/Exercise Generation)",
        },
        "exercise_generation": results,
    }

    results_file = output_dir / f"results_{model_size.lower()}.json"
    with open(results_file, "w", encoding="utf-8") as f:
        json.dump(results_json, f, indent=2, ensure_ascii=False)
    logger.info(f"βœ“ Results saved to {results_file}")

    # Generate markdown report
    report = generate_markdown_report(results, model_size)
    report_file = output_dir / f"evaluation_report_{model_size.lower()}.md"
    with open(report_file, "w", encoding="utf-8") as f:
        f.write(report)
    logger.info(f"βœ“ Report saved to {report_file}")


def generate_markdown_report(results: dict, model_size: str) -> str:
    """Generate markdown evaluation report."""
    # Calculate pass rates
    total = results.get("total", 0)
    passed = results.get("passed", 0)
    failed = results.get("failed", 0)
    pass_rate = passed / total if total > 0 else 0

    report = [
        f"# Agent 3 (Content Generation) - {model_size} Evaluation",
        "",
        f"**Model:** Qwen/Qwen2.5-{model_size}-Instruct",
        f"**Evaluation Date:** {datetime.now().strftime('%Y-%m-%d')}",
        "",
        "## Summary",
        "",
        f"- **Total Test Cases:** {total}",
        f"- **Passed:** {passed}",
        f"- **Failed:** {failed}",
        f"- **Pass Rate:** {pass_rate:.1%}",
        "",
        "## Metrics Summary",
        "",
    ]

    # Add per-metric breakdown
    metrics = results.get("metrics", {})
    for metric_name, metric_results in sorted(metrics.items()):
        if not metric_results:
            continue

        metric_passed = sum(1 for r in metric_results if r.get("passed", False))
        metric_total = len(metric_results)
        metric_pass_rate = metric_passed / metric_total if metric_total > 0 else 0
        avg_score = (
            sum(r.get("score", 0) for r in metric_results) / metric_total if metric_total > 0 else 0
        )

        report.append(f"### {metric_name.replace('_', ' ').title()}")
        report.append(f"- **Pass Rate:** {metric_passed}/{metric_total} ({metric_pass_rate:.1%})")
        report.append(f"- **Average Score:** {avg_score:.3f}")
        report.append("")

    # Add detailed test case results
    report.append("## Detailed Results")
    report.append("")
    report.append(format_test_results(results))

    return "\n".join(report)


def format_test_results(results: dict) -> str:
    """Format test results for markdown."""
    output = []
    metrics = results.get("metrics", {})

    if not metrics:
        return "No test results available"

    # Group test results by test_id
    test_status = {}  # test_id -> {metric_name: (passed, reason)}

    # Process each metric
    for metric_name, metric_results in metrics.items():
        if not metric_results:
            continue

        for metric_result in metric_results:
            test_id = metric_result.get("test_id", "unknown")
            passed = metric_result.get("passed", False)
            score = metric_result.get("score", 0)
            reason = metric_result.get("reason", "")

            if test_id not in test_status:
                test_status[test_id] = {}
            test_status[test_id][metric_name] = (passed, score, reason)

    # Format by test case
    for test_id in sorted(test_status.keys()):
        metrics_data = test_status[test_id]
        # Test passes if all metrics pass
        all_passed = all(passed for passed, _, _ in metrics_data.values())
        status = "βœ“" if all_passed else "βœ—"

        output.append(f"### {status} {test_id}")
        output.append("")

        for metric_name, (passed, score, reason) in metrics_data.items():
            metric_status = "βœ“" if passed else "βœ—"
            output.append(
                f"- {metric_status} **{metric_name.replace('_', ' ').title()}:** {score:.2f}"
            )
            if reason:
                # Truncate long reasons
                display_reason = reason if len(reason) <= 200 else reason[:197] + "..."
                output.append(f"  - {display_reason}")
        output.append("")

    return "\n".join(output)


def main():
    """Run content agent evaluation with 3B and 7B models."""
    logger.info("Initializing BaselineEvaluator...")

    # Use content_agent_test_cases.json in content_agent subdirectory
    test_cases_path = (
        PROJECT_ROOT / "data" / "evaluation" / "content_agent" / "content_agent_test_cases.json"
    )
    evaluator = BaselineEvaluator(test_cases_path=test_cases_path)

    # Full evaluation (all test cases in exercise_gen)
    sample_size = None  # None = all test cases

    # ========================================================================
    # 3B Evaluation
    # ========================================================================
    logger.info("\n" + "=" * 80)
    logger.info("AGENT 3 (CONTENT GENERATION) - 3B EVALUATION")
    logger.info("=" * 80 + "\n")

    logger.info("Running exercise_generation with 3B model...")
    responses_3b, results_3b = evaluator.run_exercise_generation_baseline(
        sample_size=sample_size, use_7b=False
    )
    logger.info("βœ“ Exercise generation (3B) complete")

    # Save 3B results
    save_results(OUTPUT_DIR, results_3b, "3B", responses_3b)

    # ========================================================================
    # 7B Evaluation
    # ========================================================================
    logger.info("\n" + "=" * 80)
    logger.info("AGENT 3 (CONTENT GENERATION) - 7B EVALUATION")
    logger.info("=" * 80 + "\n")

    logger.info("Running exercise_generation with 7B model...")
    responses_7b, results_7b = evaluator.run_exercise_generation_baseline(
        sample_size=sample_size, use_7b=True
    )
    logger.info("βœ“ Exercise generation (7B) complete")

    # Save 7B results
    save_results(OUTPUT_DIR, results_7b, "7B", responses_7b)

    # ========================================================================
    # Summary
    # ========================================================================
    logger.info("\n" + "=" * 80)
    logger.info("SUMMARY")
    logger.info("=" * 80 + "\n")

    def format_summary(results: dict) -> str:
        total = results.get("total", 0)
        passed = results.get("passed", 0)
        pass_rate = passed / total if total > 0 else 0
        return f"{passed}/{total} passed ({pass_rate:.1%})"

    logger.info("Exercise Generation:")
    logger.info(f"  3B: {format_summary(results_3b)}")
    logger.info(f"  7B: {format_summary(results_7b)}")

    # Metric-level comparison
    logger.info("\nMetric-Level Comparison:")
    for metric_name in results_3b.get("metrics", {}).keys():
        metric_3b = results_3b["metrics"][metric_name]
        metric_7b = results_7b["metrics"][metric_name]

        passed_3b = sum(1 for r in metric_3b if r.get("passed", False))
        total_3b = len(metric_3b)
        rate_3b = passed_3b / total_3b if total_3b > 0 else 0

        passed_7b = sum(1 for r in metric_7b if r.get("passed", False))
        total_7b = len(metric_7b)
        rate_7b = passed_7b / total_7b if total_7b > 0 else 0

        logger.info(f"  {metric_name}:")
        logger.info(f"    3B: {passed_3b}/{total_3b} ({rate_3b:.1%})")
        logger.info(f"    7B: {passed_7b}/{total_7b} ({rate_7b:.1%})")

    logger.info("\nβœ“ Evaluation complete!")


if __name__ == "__main__":
    main()