"""风控 Agent 的只读工具处理器。""" from __future__ import annotations from collections import Counter from typing import Any from app.core.contracts import RequestContext from app.core.risk_contracts import RiskAlertEvidenceQuery, RiskAlertQuery from app.infrastructure.db import SessionFactory from app.repository.fund_query_repository import PageRequest from app.repository.risk_repository import RiskRepository from app.service.risk_judgement_service import ( assess_alert_detail, assess_alert_list_item, ) from app.service.risk_query_service import scope_from_context async def search_risk_alerts_tool( arguments: RiskAlertQuery, context: RequestContext, ) -> dict[str, Any]: async with SessionFactory() as session: repository = RiskRepository(session, scope=scope_from_context(context)) items: list[dict[str, Any]] = [] offset = 0 while True: page = await repository.list_alerts( customer_no=arguments.customer_no, product_code=arguments.product_code, product_name=arguments.product_name, risk_level=arguments.risk_level, rule_code=arguments.rule_code, start_time=arguments.start_time, end_time=arguments.end_time, page=PageRequest(limit=10, offset=offset), ) for item in page.items: record = item.to_dict() record["disposition_hint"] = assess_alert_list_item(record) items.append(record) if page.next_offset is None: return build_alert_search_result( items, arguments.model_dump(mode="json", exclude_none=True), ) offset = page.next_offset def build_alert_search_result( items: list[dict[str, Any]], filters: dict[str, Any] | None = None, ) -> dict[str, Any]: """构建完整分组汇总和精简预警明细。""" compact_items = [_compact_alert_item(item) for item in items] rule_counts: Counter[str] = Counter() disposition_counts: Counter[str] = Counter() for item in compact_items: rule_counts.update(item.get("rule_codes") or ()) hint = item.get("disposition_hint") or {} if isinstance(hint, dict) and hint.get("verdict"): disposition_counts[str(hint["verdict"])] += 1 return { "total": len(compact_items), "filters": filters or {}, "summary": { "customer_groups": _customer_groups(compact_items), "product_groups": _product_groups(compact_items), "risk_level_counts": dict(sorted(Counter( str(item.get("risk_level") or "未知") for item in compact_items ).items())), "rule_counts": dict(sorted(rule_counts.items())), "disposition_counts": dict(sorted(disposition_counts.items())), "complete": True, }, "items": compact_items, } def _compact_alert_item(item: dict[str, Any]) -> dict[str, Any]: fields = ( "alert_no", "customer_no", "customer_name", "product_code", "product_name", "alert_type", "risk_level", "rule_codes", "evidence_summary", "status", "event_status", "created_at", "disposition_hint", ) return {field: item.get(field) for field in fields if field in item} def _customer_groups(items: list[dict[str, Any]]) -> list[dict[str, Any]]: groups: dict[str, dict[str, Any]] = {} for item in items: customer_no = item.get("customer_no") if not customer_no: continue key = str(customer_no) group = groups.setdefault( key, { "customer_no": key, "customer_name": item.get("customer_name"), "alert_count": 0, "risk_levels": set(), }, ) group["alert_count"] += 1 if item.get("risk_level"): group["risk_levels"].add(str(item["risk_level"])) return [ { **group, "risk_levels": sorted(group["risk_levels"]), } for group in sorted( groups.values(), key=lambda value: (-value["alert_count"], value["customer_no"]), ) ] def _product_groups(items: list[dict[str, Any]]) -> list[dict[str, Any]]: groups: dict[str, dict[str, Any]] = {} for item in items: product_code = item.get("product_code") product_name = item.get("product_name") key = str(product_code or product_name or "") if not key: continue group = groups.setdefault( key, { "product_code": product_code, "product_name": product_name, "alert_count": 0, "risk_levels": set(), }, ) group["alert_count"] += 1 if item.get("risk_level"): group["risk_levels"].add(str(item["risk_level"])) return [ { **group, "risk_levels": sorted(group["risk_levels"]), } for group in sorted( groups.values(), key=lambda value: ( -value["alert_count"], str(value["product_code"] or value["product_name"]), ), ) ] async def get_risk_overview_tool( arguments: RiskAlertQuery, context: RequestContext, ) -> dict[str, Any]: del arguments async with SessionFactory() as session: return await RiskRepository(session, scope=scope_from_context(context)).overview() async def get_alert_evidence_tool( arguments: RiskAlertEvidenceQuery, context: RequestContext, ) -> dict[str, Any] | None: async with SessionFactory() as session: record = await RiskRepository( session, scope=scope_from_context(context), ).get_alert_detail(arguments.alert_no) if record is None: return None detail = record.to_dict() detail["disposition_assessment"] = assess_alert_detail(detail) return detail