"""Exploration summary aggregation (v0.9 票据 05). 体验记录聚合成探索摘要:会话数、目标达成率、问题清单(blockers/misled 按出现次数降序)、judge 复核结论(已就位才纳入;findings 全量收各档发现, poor 档排前)。缺则无痕:无会话返回 None。纯函数,不落库。 """ from agenteval.exploration.models import ExplorationSession, ExplorationSessionStatus from agenteval.exploration.summary import summarize_exploration def _session(experience=None, judge_review=None, status=ExplorationSessionStatus.COMPLETED) -> ExplorationSession: return ExplorationSession( campaign_id="c-1", target_id="t-1", persona={"name": "x"}, goal="查询账单", status=status, experience=experience, judge_review=judge_review, ) def _experience(goal_achieved: bool, blockers=None, misled=None) -> dict: return { "goal_achieved": goal_achieved, "blockers": blockers or [], "misled": misled or [], "emotion": "neutral", "notes": "", } def test_no_sessions_returns_none(): assert summarize_exploration([]) is None def test_aggregates_counts_rate_and_issue_lists(): sessions = [ _session(_experience(True, blockers=["缴费入口难找"])), _session(_experience(False, blockers=["缴费入口难找", "验证码收不到"], misled=["被误导选了错误套餐"])), _session(_experience(False, blockers=["验证码收不到"])), ] summary = summarize_exploration(sessions) assert summary["session_count"] == 3 assert summary["sessions_with_experience"] == 3 assert summary["goal_achieved_count"] == 1 assert summary["goal_achievement_rate"] == round(1 / 3, 4) # 问题清单按出现次数降序(同频按首次出现顺序) assert summary["issues"] == [ {"issue": "缴费入口难找", "count": 2}, {"issue": "验证码收不到", "count": 2}, ] assert summary["misled"] == [{"issue": "被误导选了错误套餐", "count": 1}] def test_sessions_without_experience_excluded_from_rate_denominator(): sessions = [ _session(_experience(True)), _session(), # 未关闭/无体验记录 ] summary = summarize_exploration(sessions) assert summary["session_count"] == 2 assert summary["sessions_with_experience"] == 1 assert summary["goal_achievement_rate"] == 1.0 assert summary["issues"] == [] def test_no_experience_at_all_rate_is_none(): summary = summarize_exploration([_session(), _session()]) assert summary["goal_achievement_rate"] is None assert summary["issues"] == [] and summary["misled"] == [] def test_judge_review_included_only_when_completed(): good_review = { "status": "completed", "dimensions": [ {"dimension": "attitude", "rating": "good", "comment": "友好"}, {"dimension": "professionalism", "rating": "acceptable", "comment": "基本准确"}, {"dimension": "hallucination", "rating": "poor", "comment": "编造了政策"}, ], "summary": "存在幻觉", } failed_review = {"status": "failed", "error": "解析失败"} sessions = [ _session(_experience(True), judge_review=good_review), _session(_experience(False), judge_review=failed_review), _session(_experience(True)), # 未复核 ] summary = summarize_exploration(sessions) judge = summary["judge_review"] assert judge["reviewed_sessions"] == 1 # 复核发现全量纳入,poor 档排前(票据 05:复核结论一并纳入) assert judge["findings"] == [ {"dimension": "hallucination", "rating": "poor", "comment": "编造了政策"}, {"dimension": "professionalism", "rating": "acceptable", "comment": "基本准确"}, {"dimension": "attitude", "rating": "good", "comment": "友好"}, ] # 复核总体结论一并纳入 assert judge["summaries"] == ["存在幻觉"] def test_judge_review_absent_when_none_reviewed(): summary = summarize_exploration([_session(_experience(True))]) assert summary["judge_review"] is None