"""Adjustable-potential assessment skill v1 (docs/05 §2.2 可调潜力辨识). For each resource: adjustable capacity = certified capacity × rolling fulfillment rate (delivered / planned over the evidence window), with confidence from the profile's own confidence, the reliability score and the amount of evidence. A resource with no execution history keeps its certified figure at the profile's confidence — the "画像时效性" risk (docs/13 §0) is made visible through `evidence_days` rather than hidden. The de-rating exponent and evidence saturation are placeholders: OPEN-QUESTION B10 (可靠性/潜力辨识口径). The target accuracy (≥ 90%) is judged by the eval harness on real execution feedback (docs/12 L2). """ from __future__ import annotations from decimal import Decimal import numpy as np from vpp_contracts.potential_assessment_request import PotentialAssessmentRequest from vpp_contracts.potential_assessment_result import PotentialAssessmentResult from . import SKILL_VERSIONS from .numeric import INTERVALS, SCALE_MW, curve, quantize SKILL = "potential-assessment" EVIDENCE_WINDOW_DAYS = 14 # OPEN-QUESTION B10 EVIDENCE_SATURATION_DAYS = 7 # OPEN-QUESTION B10: confidence saturates after this many days def assess_potential(req: PotentialAssessmentRequest) -> PotentialAssessmentResult: assessments = [] total = np.zeros(INTERVALS) for r in req.resources: profile = r.profile certified = float(Decimal(profile.certified_adjustable_mw)) recent = sorted(r.fulfillment, key=lambda f: f.market_date)[-EVIDENCE_WINDOW_DAYS:] planned = sum(Decimal(f.planned_mwh) for f in recent) delivered = sum(Decimal(f.delivered_mwh) for f in recent) if planned > 0: rate = min(Decimal(1), delivered / planned) evidence_days = len(recent) else: rate = None evidence_days = 0 derate = float(rate) if rate is not None else 1.0 adjustable = np.full(INTERVALS, certified * derate) evidence_factor = min(1.0, evidence_days / EVIDENCE_SATURATION_DAYS) base_conf = float(Decimal(profile.confidence)) reliability = float(Decimal(profile.reliability_score)) confidence = base_conf * (0.5 + 0.5 * evidence_factor) * (0.5 + 0.5 * reliability) total += adjustable assessments.append( { "resource_id": profile.resource_id, "adjustable_mw": curve(adjustable, req.market_date, SCALE_MW), "confidence": quantize(confidence, 3), "fulfillment_rate": quantize(rate, 4) if rate is not None else None, "evidence_days": evidence_days, } ) return PotentialAssessmentResult.model_validate( { "market_date": req.market_date, "assessments": assessments, "total_adjustable_mw": curve(total, req.market_date, SCALE_MW), "skill_version": SKILL_VERSIONS[SKILL], } )