vpp-ai-platform/skills-py/vpp_skills/potential.py
Thomas Bayes 23fdf49ff2
Some checks are pending
ci / typescript (push) Waiting to run
ci / python (push) Waiting to run
ci / evals (push) Blocked by required conditions
M4: resource agent, envelopes live, review loop, insight cards
- packages/domain: AwardNotice, DISPATCH_PLAN proposal payload, ExecutionReport,
  MeteringRecord, potential-assessment and dispatch-optimization contracts,
  ReviewFinding (+ typed writebacks), SemanticMemoryEntry,
  EnvelopeChangeRequest, InsightCard — exported with fixtures on both sides.
- skills-py: potential-assessment (certified × rolling fulfilment, evidence
  days) and dispatch-optimization (per-interval LP on HiGHS, shortfall
  reported) skills + routes + tests.
- packages/services: dispatch rules in the policy pack (over-allocation,
  award anchor, lineage integrity for allocations); PowerBalanceSimulator;
  SimulationGateway (permit-only, idempotent, seeded execute → ExecutionReports);
  envelope deviation-streak suspension + apply(); ReviewService (attribution,
  reliability EWMA writeback, semantic memory, envelope recommendations as
  change requests); dispatch assembler; skill client methods.
- packages/runtime: resource agent; award-decomposition, review and
  envelope-review workflows; lifecycle selects simulator/gateway by proposal
  type; trigger hooks for awards, execution reports, metering; decide()
  resumes either lifecycle or envelope-review runs; insight cards API.
- Tests: docs/07 D-1 16:00 and D+1 end to end; reliability score 0.9 → 0.880
  and the next assessment de-rates capacity; envelope suspension on a seeded
  3-day streak; WIDEN request applied only by a human. 184 TS + 80 Python.
- docs/open-questions: B10 (reliability/potential parameters). README and
  CLAUDE.md status → M4 done, M5 next.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UoYoGYzHkFyv3ALenkRPhA
2026-09-02 19:31:12 -04:00

74 lines
3.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""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],
}
)