""" 影子模式执行引擎 — 数字分身生成建议但不执行,对比人类决策 """ from __future__ import annotations import logging from typing import Any, Dict, List, Optional from sqlalchemy.orm import Session from sqlalchemy import func, desc from app.core.database import SessionLocal from app.models.shadow_comparison import ShadowComparison from app.services.fingerprint_engine import fingerprint_engine logger = logging.getLogger(__name__) class ShadowExecutor: """影子模式执行器 — 观察学习阶段的核心引擎""" def __init__(self): self._unlock_thresholds = { "code_review": 0.85, "email": 0.90, "document": 0.85, "decision": 0.90, } def generate_suggestion(self, user_id: str, category: str, context: Dict[str, Any]) -> Dict[str, Any]: """基于用户指纹生成影子建议(不使用LLM,纯规则+指纹推断)。""" fp = fingerprint_engine.get_fingerprint(user_id) preference = (fp.get("preference_weights", {}).get(category) if fp else None) or {} rules = fp.get("decision_rules", []) if fp else [] # 匹配决策规则 matched_rules = [] for rule in rules: if rule.get("action", "").startswith(category): matched_rules.append(rule) suggestion = { "category": category, "based_on": "fingerprint" if fp else "default", "preference_applied": preference, "matched_rules": matched_rules[:5], "suggested_actions": self._generate_actions(category, context, preference, matched_rules), "confidence": min(0.5 + 0.05 * len(matched_rules), 0.95), } return suggestion def _generate_actions(self, category: str, context: Dict[str, Any], preference: Dict[str, Any], rules: List[Dict]) -> List[Dict[str, Any]]: """根据分类和偏好生成建议动作。""" actions = [] if category == "code_review": if preference.get("security", 0.3) > 0.3: actions.append({"priority": "high", "action": "检查安全漏洞", "detail": "SQL注入/XSS/权限校验"}) if preference.get("performance", 0.25) > 0.25: actions.append({"priority": "medium", "action": "检查性能", "detail": "N+1查询/内存泄漏/大循环"}) if preference.get("readability", 0.25) > 0.25: actions.append({"priority": "low", "action": "检查可读性", "detail": "命名/注释/函数长度"}) elif category == "document": actions.append({"priority": "medium", "action": "结构检查", "detail": "章节完整性/逻辑连贯性"}) actions.append({"priority": "low", "action": "风格统一", "detail": "术语一致性/格式规范"}) elif category == "decision": actions.append({"priority": "high", "action": "数据验证", "detail": "检查决策依据是否充分"}) actions.append({"priority": "medium", "action": "风险评估", "detail": "识别潜在风险和副作用"}) elif category == "email": actions.append({"priority": "medium", "action": "语气检查", "detail": "与收件人关系的匹配度"}) actions.append({"priority": "low", "action": "完整性检查", "detail": "回复是否涵盖所有要点"}) return actions def compare(self, user_id: str, shadow_suggestion: Dict[str, Any], user_decision: Dict[str, Any], user_action: str) -> Dict[str, Any]: """对比影子建议与用户实际决策。""" matched = 0 diverged = 0 suggested_actions = shadow_suggestion.get("suggested_actions", []) if user_action == "accept": match_score = 1.0 matched = len(suggested_actions) elif user_action == "modify": # 部分匹配 if user_decision.get("modified_actions"): user_actions = {a.get("action", "") for a in user_decision["modified_actions"]} shadow_actions = {a.get("action", "") for a in suggested_actions} matched = len(user_actions & shadow_actions) diverged = len(user_actions - shadow_actions) match_score = 0.5 if len(suggested_actions) > 0 else 0.5 elif user_action == "reject": match_score = 0.0 diverged = len(suggested_actions) else: # ignore match_score = 0.0 return { "match_score": round(match_score, 2), "matched_points": matched, "diverged_points": diverged, } def record_comparison(self, user_id: str, category: str, shadow_suggestion: Dict[str, Any], user_decision: Dict[str, Any], user_action: str, context: Optional[Dict[str, Any]] = None) -> Optional[str]: """记录一次影子对比。""" comparison = self.compare(user_id, shadow_suggestion, user_decision, user_action) db: Optional[Session] = None try: db = SessionLocal() entry = ShadowComparison( user_id=user_id, category=category, shadow_suggestion=shadow_suggestion, shadow_confidence=shadow_suggestion.get("confidence", 0.5), user_decision=user_decision, user_action=user_action, match_score=comparison["match_score"], match_detail=comparison, context=context, ) db.add(entry) db.commit() db.refresh(entry) return str(entry.id) except Exception as e: logger.error("记录影子对比失败: %s", e) if db: try: db.rollback() except Exception: pass return None finally: if db: try: db.close() except Exception: pass def get_accuracy(self, user_id: str, category: Optional[str] = None, days: int = 30) -> Dict[str, Any]: """获取影子模式准确率统计。""" db: Optional[Session] = None try: db = SessionLocal() from datetime import datetime, timedelta since = datetime.now() - timedelta(days=days) q = db.query(ShadowComparison).filter( ShadowComparison.user_id == user_id, ShadowComparison.created_at >= since, ) if category: q = q.filter(ShadowComparison.category == category) records = q.all() total = len(records) if total == 0: return {"total_comparisons": 0, "message": "暂无数据"} avg_score = sum(r.match_score or 0 for r in records) / total accepted = sum(1 for r in records if r.user_action == "accept") rejected = sum(1 for r in records if r.user_action == "reject") modified = sum(1 for r in records if r.user_action == "modify") by_category = {} for r in records: cat = r.category if cat not in by_category: by_category[cat] = {"total": 0, "sum_score": 0.0} by_category[cat]["total"] += 1 by_category[cat]["sum_score"] += (r.match_score or 0) cat_accuracy = { cat: round(d["sum_score"] / d["total"], 3) for cat, d in by_category.items() } unlocked = { cat: acc >= self._unlock_thresholds.get(cat, 0.90) for cat, acc in cat_accuracy.items() } return { "total_comparisons": total, "average_accuracy": round(avg_score, 3), "accepted": accepted, "rejected": rejected, "modified": modified, "by_category": cat_accuracy, "unlocked_categories": unlocked, "period_days": days, } except Exception as e: logger.error("获取准确率失败: %s", e) return {"error": str(e)} finally: if db: try: db.close() except Exception: pass shadow_executor = ShadowExecutor()