diff --git a/backend/agenteval/web/app.py b/backend/agenteval/web/app.py index 8ae5edb..eb58d1b 100644 --- a/backend/agenteval/web/app.py +++ b/backend/agenteval/web/app.py @@ -42,6 +42,60 @@ def _has_pending_task() -> bool: session.close() +def _supplement_decision_logs(session) -> int: + """Platform audit backfill for decision logs. + + 方案③的决策日志由 OpenClaw agent 上报(LLM 自主,尽力而为)——异常路径 + (如卡死恢复后重试)agent 可能跳过上报,导致决策过程页面为空。这里按评估 + 状态推导决策并补录:欠账时补 execute_session,所有会话完成后补 + start_analysis。只补"该类型缺失"的,不重复;且只记录状态,不改变 agent + 的实际执行。 + + Returns: + 补录的决策日志条数。 + """ + from agenteval.intelligent_eval.decision_logs import create_decision_log + from agenteval.intelligent_eval.models import IntelligentEvalStatus + from agenteval.storage.db import IntelligentEvalDB, IntelligentEvalDecisionLogDB, IntelligentEvalSessionDB + from sqlmodel import select + + evals = session.exec( + select(IntelligentEvalDB).where(IntelligentEvalDB.status == IntelligentEvalStatus.EXECUTING.value) + ).all() + + added = 0 + for ev in evals: + plan = ev.get_plan() if ev.plan else {} + estimated = plan.get("estimated_sessions", 0) + sessions = session.exec( + select(IntelligentEvalSessionDB).where(IntelligentEvalSessionDB.eval_id == ev.id) + ).all() + completed = sum(1 for s in sessions if s.status == "completed") + types = { + l.decision_type + for l in session.exec( + select(IntelligentEvalDecisionLogDB).where( + IntelligentEvalDecisionLogDB.eval_id == ev.id + ) + ).all() + } + if "execute_session" not in types and completed < estimated: + create_decision_log( + ev.id, "execute_session", "平台兜底:时段欠账需执行会话", "platform", + {"platform_supplemented": True, "completed": completed, "estimated": estimated}, + session, + ) + added += 1 + elif "start_analysis" not in types and sessions and completed >= estimated: + create_decision_log( + ev.id, "start_analysis", "平台兜底:所有会话已完成开始分析", "platform", + {"platform_supplemented": True, "completed": completed, "estimated": estimated}, + session, + ) + added += 1 + return added + + async def _trigger_intelligent_worker() -> bool: """Trigger OpenClaw's headless agent to run the intelligent-eval worker skill. @@ -111,6 +165,10 @@ async def _intelligent_eval_scan_loop() -> None: n = scan_and_enqueue_tasks(session) if n: _logger.info("智能评估扫描:入队 %d 个 Worker 任务", n) + # 审计兜底:agent 未上报决策日志时,平台按评估状态补录 + added = _supplement_decision_logs(session) + if added: + _logger.info("决策日志兜底:补录 %d 条", added) finally: session.close() except Exception as exc: diff --git a/tests/integration/test_intelligent_eval_scan_scheduler.py b/tests/integration/test_intelligent_eval_scan_scheduler.py index b30d224..359f385 100644 --- a/tests/integration/test_intelligent_eval_scan_scheduler.py +++ b/tests/integration/test_intelligent_eval_scan_scheduler.py @@ -108,3 +108,42 @@ def test_trigger_worker_msg_has_execute_semantics(monkeypatch): assert "立即完成当前任务" in joined assert "agenteval-intelligent-worker" in joined assert "agenteval-intelligent-analyst" in joined + + +def test_supplement_execute_session_log(monkeypatch, db_session): + """Executing eval with deficit and no execute_session log → platform backfills.""" + from agenteval.intelligent_eval.models import IntelligentEvalStatus + from agenteval.storage.db import IntelligentEvalDB + import agenteval.web.app as app_mod + + ev = IntelligentEvalDB( + name="supp-eval", target_id="t1", + status=IntelligentEvalStatus.EXECUTING.value, started_at=__import__("agenteval.storage.db", fromlist=["utc_now"]).utc_now(), + ) + ev.set_plan({"time_distribution": [{"time_slot": "0-1h", "sessions": 1}], "estimated_sessions": 1}) + db_session.add(ev) + db_session.commit() + + assert app_mod._supplement_decision_logs(db_session) == 1 + # second call: already backfilled → 0 + assert app_mod._supplement_decision_logs(db_session) == 0 + + +def test_supplement_start_analysis_log(monkeypatch, db_session): + """Executing eval with all sessions completed and no start_analysis → backfill.""" + from agenteval.intelligent_eval.models import IntelligentEvalStatus + from agenteval.storage.db import IntelligentEvalDB, IntelligentEvalSessionDB, utc_now + import agenteval.web.app as app_mod + + ev = IntelligentEvalDB( + name="supp-eval2", target_id="t1", + status=IntelligentEvalStatus.EXECUTING.value, started_at=utc_now(), + ) + ev.set_plan({"time_distribution": [{"time_slot": "0-1h", "sessions": 1}], "estimated_sessions": 1}) + db_session.add(ev) + db_session.commit() + s = IntelligentEvalSessionDB(eval_id=ev.id, target_id=ev.target_id, status="completed", goal="g") + db_session.add(s) + db_session.commit() + + assert app_mod._supplement_decision_logs(db_session) == 1