maref 0.30.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- drift_guard/README.md +108 -0
- drift_guard/__init__.py +0 -0
- drift_guard/ab_testing.py +283 -0
- drift_guard/adaptive_threshold.py +245 -0
- drift_guard/drift_benchmark.py +346 -0
- drift_guard/metrics.py +242 -0
- drift_guard/pipeline.py +312 -0
- drift_guard/policy_sandbox.py +455 -0
- drift_guard/py.typed +0 -0
- drift_guard/types.py +155 -0
- formal/MAREFDeskJoint.tla +168 -0
- formal/MAREF_Consensus.tla +208 -0
- formal/MAREF_ConsensusMC.cfg +26 -0
- formal/MAREF_TestIntegration.tla +677 -0
- formal/MAREF_TestIntegrationMC.cfg +28 -0
- formal/MarefLite.tla +104 -0
- formal/MarefLiteMC.cfg +25 -0
- formal/MarefLiteModel.tla +159 -0
- formal/README.md +53 -0
- formal/__init__.py +0 -0
- formal/gray_code_validator.py +187 -0
- formal/py.typed +0 -0
- maref/__init__.py +63 -0
- maref/__main__.py +180 -0
- maref/agent_card_config.py +202 -0
- maref/certification.py +593 -0
- maref/compliance/__init__.py +95 -0
- maref/compliance/compliance_monitor.py +468 -0
- maref/compliance/data_sovereignty.py +684 -0
- maref/compliance/eu_ai_act.py +137 -0
- maref/compliance/five_eyes.py +173 -0
- maref/compliance/hipaa/__init__.py +436 -0
- maref/compliance/pci_dss/__init__.py +453 -0
- maref/compliance/registry.py +479 -0
- maref/compliance/report_generator.py +552 -0
- maref/config.py +45 -0
- maref/consensus/__init__.py +0 -0
- maref/consensus/consistency_dsl.py +149 -0
- maref/consensus/nack_protocol.py +254 -0
- maref/consensus/vector_clock.py +162 -0
- maref/cross_validator/__init__.py +39 -0
- maref/cross_validator/ast_normalizer.py +515 -0
- maref/cross_validator/consensus_algorithm.py +591 -0
- maref/crypto/__init__.py +26 -0
- maref/crypto/aia_adapter.py +163 -0
- maref/crypto/benchmark.py +150 -0
- maref/crypto/sm2.py +186 -0
- maref/crypto/sm3.py +51 -0
- maref/crypto/sm4.py +44 -0
- maref/crypto/sm4_gcm.py +192 -0
- maref/desktop/__init__.py +41 -0
- maref/desktop/action_recorder.py +239 -0
- maref/desktop/agent.py +614 -0
- maref/desktop/api_router.py +275 -0
- maref/desktop/browser_auth.py +136 -0
- maref/desktop/browser_controller.py +202 -0
- maref/desktop/clipboard.py +190 -0
- maref/desktop/context_isolation.py +203 -0
- maref/desktop/controller.py +702 -0
- maref/desktop/desktop_governance.py +182 -0
- maref/desktop/file_ops.py +284 -0
- maref/desktop/file_watcher.py +177 -0
- maref/desktop/input_controller.py +505 -0
- maref/desktop/mobile_bridge.py +566 -0
- maref/desktop/opencua_bench.py +455 -0
- maref/desktop/opencua_loader.py +137 -0
- maref/desktop/platform_layer.py +134 -0
- maref/desktop/policy_decision_tree.py +326 -0
- maref/desktop/safety_gate_desktop.py +262 -0
- maref/desktop/screen_capture.py +316 -0
- maref/desktop/screen_parser.py +600 -0
- maref/desktop/task_executor.py +190 -0
- maref/desktop/verification.py +334 -0
- maref/desktop/window_manager.py +361 -0
- maref/desktop/workflow_templates.py +181 -0
- maref/eivl/__init__.py +51 -0
- maref/eivl/merkle_auditor.py +535 -0
- maref/eivl/wasm_sandbox.py +552 -0
- maref/evolution/__init__.py +23 -0
- maref/evolution/__main__.py +102 -0
- maref/evolution/engine.py +408 -0
- maref/evolution/metrics.py +222 -0
- maref/evolution/real_metrics.py +151 -0
- maref/evolution/reporter.py +108 -0
- maref/executor/__init__.py +27 -0
- maref/executor/api.py +177 -0
- maref/executor/checkpointer.py +196 -0
- maref/executor/notifications.py +122 -0
- maref/executor/persistent_store.py +188 -0
- maref/executor/queue.py +452 -0
- maref/executor/scheduler.py +253 -0
- maref/executor/session.py +214 -0
- maref/executor/types.py +105 -0
- maref/executor/worker.py +179 -0
- maref/features/__init__.py +15 -0
- maref/features/feature_flags.py +167 -0
- maref/gaas/__init__.py +17 -0
- maref/gaas/api.py +276 -0
- maref/gaas/audit_service.py +141 -0
- maref/gaas/billing.py +120 -0
- maref/gaas/cb_pool.py +137 -0
- maref/gaas/governance_router.py +195 -0
- maref/gaas/hitl_service.py +173 -0
- maref/gaas/models.py +125 -0
- maref/gaas/tenant.py +80 -0
- maref/gaas/trust_service.py +70 -0
- maref/governance/__init__.py +43 -0
- maref/governance/audit.py +388 -0
- maref/governance/circuit_breaker.py +149 -0
- maref/governance/constants.py +77 -0
- maref/governance/db.py +199 -0
- maref/governance/oscillation.py +191 -0
- maref/governance/percv_hooks.py +149 -0
- maref/governance/state_machine.py +240 -0
- maref/governance/threat_bridge.py +136 -0
- maref/governance/trust_bridge.py +185 -0
- maref/governance/types.py +77 -0
- maref/human/__init__.py +44 -0
- maref/human/decision_api.py +280 -0
- maref/human/interrupt_protocol.py +164 -0
- maref/human/rule_engine.py +244 -0
- maref/identity/__init__.py +13 -0
- maref/identity/credential.py +121 -0
- maref/identity/did_registry.py +70 -0
- maref/identity/trust_engine.py +131 -0
- maref/inference/__init__.py +19 -0
- maref/inference/memory_trust.py +148 -0
- maref/integration/__init__.py +254 -0
- maref/integration/a2a_bridge.py +257 -0
- maref/integration/a2a_secure_transport.py +212 -0
- maref/integration/a2a_types.py +142 -0
- maref/integration/deerflow_bridge.py +209 -0
- maref/integration/feature_dev/__init__.py +35 -0
- maref/integration/feature_dev/content_producer.py +305 -0
- maref/integration/feature_dev/content_scorer.py +179 -0
- maref/integration/feature_dev/doc_ingestor.py +463 -0
- maref/integration/feature_dev/feature_cycle.py +314 -0
- maref/integration/feature_dev/llm_client.py +142 -0
- maref/integration/feature_dev/progress_tracker.py +165 -0
- maref/integration/feature_dev/task_generator.py +167 -0
- maref/integration/feature_dev/verification_engine.py +291 -0
- maref/integration/flag_bridge.py +228 -0
- maref/integration/gateway.py +241 -0
- maref/integration/hitl.py +182 -0
- maref/integration/hitl_api.py +253 -0
- maref/integration/mcp_bridge.py +142 -0
- maref/integration/mcp_client.py +230 -0
- maref/integration/mcp_governance.py +833 -0
- maref/integration/mcp_security.py +321 -0
- maref/integration/mcp_security_middleware.py +212 -0
- maref/integration/mcp_server.py +242 -0
- maref/integration/mcp_transport.py +283 -0
- maref/integration/memory_bridge.py +253 -0
- maref/integration/percv/__init__.py +45 -0
- maref/integration/percv/card_bridge.py +202 -0
- maref/integration/percv/config.py +76 -0
- maref/integration/percv/cost_monitor.py +122 -0
- maref/integration/percv/feedback_loop.py +194 -0
- maref/integration/percv/gateway_adapter.py +391 -0
- maref/integration/percv/orchestrator.py +349 -0
- maref/integration/percv/pipeline_adapter.py +235 -0
- maref/integration/percv/ratchet_bridge.py +227 -0
- maref/integration/percv/verification_bridge.py +336 -0
- maref/integration/protocol_bridge.py +127 -0
- maref/integration/remote_bridge.py +187 -0
- maref/integration/symphony.py +239 -0
- maref/integration/test_platform/__init__.py +97 -0
- maref/integration/test_platform/card_adapter.py +243 -0
- maref/integration/test_platform/eval_observer.py +241 -0
- maref/integration/test_platform/quality_gate.py +265 -0
- maref/integration/test_platform/schema.py +219 -0
- maref/integration/test_platform/score_mapper.py +213 -0
- maref/integration/test_platform/state_trigger.py +311 -0
- maref/integration/test_platform/tla_verifier.py +254 -0
- maref/knowledge/__init__.py +24 -0
- maref/knowledge/graph.py +478 -0
- maref/knowledge/hypothesis_cycle.py +148 -0
- maref/knowledge/relations.py +224 -0
- maref/learning/__init__.py +24 -0
- maref/learning/ab_test.py +208 -0
- maref/learning/replay.py +300 -0
- maref/learning/scheduler.py +133 -0
- maref/life_state/__init__.py +18 -0
- maref/life_state/federation.py +150 -0
- maref/life_state/health.py +187 -0
- maref/life_state/lifecycle.py +155 -0
- maref/life_state/messaging.py +147 -0
- maref/life_state/metadata.py +124 -0
- maref/life_state/registry.py +148 -0
- maref/life_state/resources.py +129 -0
- maref/life_state/sandbox.py +149 -0
- maref/life_state/state_machine.py +151 -0
- maref/marketplace/__init__.py +40 -0
- maref/marketplace/registry.py +250 -0
- maref/marketplace/reputation.py +146 -0
- maref/marketplace/semantic_matcher.py +97 -0
- maref/marketplace/version_negotiator.py +142 -0
- maref/memory/__init__.py +37 -0
- maref/memory/memory_manager.py +457 -0
- maref/monitoring/__init__.py +49 -0
- maref/monitoring/safety_dashboard.py +122 -0
- maref/monitoring/security_orchestrator.py +590 -0
- maref/monitoring/threat_intelligence.py +490 -0
- maref/obs/__init__.py +34 -0
- maref/obs/aggregator.py +119 -0
- maref/obs/client.py +242 -0
- maref/obs/hasher.py +36 -0
- maref/obs/levels.py +42 -0
- maref/obs/oscillator_params.py +122 -0
- maref/obs/pipeline.py +198 -0
- maref/obs/py.typed +0 -0
- maref/obs/schema.py +44 -0
- maref/observability/__init__.py +48 -0
- maref/observability/error_budget.py +164 -0
- maref/observability/logging.py +115 -0
- maref/observability/otel_middleware.py +222 -0
- maref/observability/red_metrics.py +200 -0
- maref/observability/trace_context.py +93 -0
- maref/observation/__init__.py +31 -0
- maref/observation/detector.py +175 -0
- maref/observation/otel_bridge.py +356 -0
- maref/observation/probes.py +212 -0
- maref/observation/registry.py +70 -0
- maref/observation/store.py +183 -0
- maref/orchestration/__init__.py +33 -0
- maref/orchestration/decomposer.py +102 -0
- maref/orchestration/dispatcher.py +175 -0
- maref/orchestration/joint_machine.py +126 -0
- maref/orchestration/operational_validator.py +237 -0
- maref/orchestration/plan_executor.py +368 -0
- maref/orchestration/task_graph.py +292 -0
- maref/performance.py +568 -0
- maref/production/__init__.py +19 -0
- maref/production/_constants.py +7 -0
- maref/production/asset_scaffolder.py +95 -0
- maref/production/character_factory.py +166 -0
- maref/production/content_assembler.py +85 -0
- maref/production/content_engine.py +97 -0
- maref/production/hypothesis_validator.py +106 -0
- maref/production/ip_cli.py +210 -0
- maref/production/script_writer.py +219 -0
- maref/production/templates/character_profile.md +38 -0
- maref/production/templates/episode_script.md +44 -0
- maref/protocols/__init__.py +33 -0
- maref/protocols/protocol_bridge.py +491 -0
- maref/py.typed +0 -0
- maref/recursive/__init__.py +716 -0
- maref/recursive/admission_testing.py +288 -0
- maref/recursive/agent_24_state_machine.py +299 -0
- maref/recursive/agent_credit_rating.py +334 -0
- maref/recursive/agent_discovery_negotiation.py +238 -0
- maref/recursive/agent_dispatcher.py +100 -0
- maref/recursive/agent_economy.py +407 -0
- maref/recursive/agent_handoff.py +329 -0
- maref/recursive/agent_health.py +133 -0
- maref/recursive/agent_marketplace.py +278 -0
- maref/recursive/audit_schema.py +188 -0
- maref/recursive/blast_radius.py +136 -0
- maref/recursive/builtin_skills/code_security_audit.yaml +49 -0
- maref/recursive/capability_contracts.py +667 -0
- maref/recursive/carbon_silicon_symbiosis.py +363 -0
- maref/recursive/chaos_injector.py +45 -0
- maref/recursive/chaos_resilience.py +91 -0
- maref/recursive/code_parser.py +80 -0
- maref/recursive/complexity_budget.py +282 -0
- maref/recursive/continuous_optimizer.py +327 -0
- maref/recursive/convergence_dashboard.py +259 -0
- maref/recursive/correlation_engine.py +362 -0
- maref/recursive/cost_tracker.py +359 -0
- maref/recursive/coverage_tracker.py +97 -0
- maref/recursive/creative_generator.py +263 -0
- maref/recursive/cross_system_adapter.py +308 -0
- maref/recursive/dashboard_v3.py +142 -0
- maref/recursive/decision_market.py +578 -0
- maref/recursive/distributed_bft.py +368 -0
- maref/recursive/distributed_crdt.py +244 -0
- maref/recursive/eight_trigrams_governance.py +307 -0
- maref/recursive/evolution_dsl.py +266 -0
- maref/recursive/experience_pool.py +175 -0
- maref/recursive/federation.py +117 -0
- maref/recursive/formal_planner.py +418 -0
- maref/recursive/four_phase_governance.py +383 -0
- maref/recursive/hitl_v2.py +275 -0
- maref/recursive/hook_chain.py +103 -0
- maref/recursive/hook_registry.py +75 -0
- maref/recursive/hook_templates.py +156 -0
- maref/recursive/hook_topics.py +33 -0
- maref/recursive/hybrid_decomposer.py +254 -0
- maref/recursive/instance_cloner.py +362 -0
- maref/recursive/integrity_baseline.py +122 -0
- maref/recursive/internal_agents.py +117 -0
- maref/recursive/joint_state_machine.py +124 -0
- maref/recursive/live_migration.py +237 -0
- maref/recursive/memory_three_temperature.py +374 -0
- maref/recursive/meta_agent_closure.py +325 -0
- maref/recursive/meta_governance.py +195 -0
- maref/recursive/metacognition.py +186 -0
- maref/recursive/observability.py +230 -0
- maref/recursive/ontology_drift.py +328 -0
- maref/recursive/orchestration_perf.py +194 -0
- maref/recursive/otel_dashboard.py +45 -0
- maref/recursive/permission_matrix.py +103 -0
- maref/recursive/reliability_matrix.py +178 -0
- maref/recursive/resilience_v2.py +219 -0
- maref/recursive/role_composer.py +121 -0
- maref/recursive/role_lifecycle.py +138 -0
- maref/recursive/role_registry.py +138 -0
- maref/recursive/rule_freeze_zone.py +232 -0
- maref/recursive/runtime_kg.py +153 -0
- maref/recursive/safety_gate_v2.py +302 -0
- maref/recursive/saga_orchestrator.py +409 -0
- maref/recursive/schema_aligner.py +261 -0
- maref/recursive/self_architect.py +339 -0
- maref/recursive/self_diagnostician.py +153 -0
- maref/recursive/self_executor.py +714 -0
- maref/recursive/self_healer.py +289 -0
- maref/recursive/self_knowledge.py +173 -0
- maref/recursive/self_observer.py +153 -0
- maref/recursive/self_optimizer.py +196 -0
- maref/recursive/self_orchestrator.py +179 -0
- maref/recursive/self_version.py +182 -0
- maref/recursive/signed_agent_cards.py +194 -0
- maref/recursive/skill_executor.py +161 -0
- maref/recursive/skill_loader.py +120 -0
- maref/recursive/skill_schema.py +299 -0
- maref/recursive/skill_trigger.py +59 -0
- maref/recursive/stigmergy_swarm.py +262 -0
- maref/recursive/task_decomposer.py +126 -0
- maref/recursive/time_awareness.py +178 -0
- maref/recursive/tla_replay.py +261 -0
- maref/recursive/trust_engine_v2.py +415 -0
- maref/recursive/trust_v2.py +154 -0
- maref/recursive/unified_audit.py +131 -0
- maref/recursive/zero_trust.py +262 -0
- maref/redblue/__init__.py +20 -0
- maref/redblue/attack_executor.py +137 -0
- maref/redblue/attack_vector.py +225 -0
- maref/redblue/red_blue_engine.py +262 -0
- maref/security/__init__.py +10 -0
- maref/security/agent_identity/__init__.py +349 -0
- maref/security/behavior_monitor.py +215 -0
- maref/security/byzantine_enhancer.py +174 -0
- maref/security/decorators.py +51 -0
- maref/security/keyring_store.py +117 -0
- maref/security/message_security.py +131 -0
- maref/security/sanitizer.py +129 -0
- maref/security/security_proofs.py +459 -0
- maref/security/state_monitor.py +157 -0
- maref/security/trust_api.py +89 -0
- maref/security/trust_boundary/__init__.py +240 -0
- maref/security/trust_chain/__init__.py +183 -0
- maref/security/trust_graph.py +191 -0
- maref/security/trust_integration/__init__.py +392 -0
- maref/security/trust_visualization.py +96 -0
- maref/security/weighted_consensus.py +93 -0
- maref/serverless_handler.py +60 -0
- maref/stress/__init__.py +16 -0
- maref/stress/chaos_engine.py +398 -0
- maref/stress/distributed_harness.py +86 -0
- maref/stress/emergence_harness.py +156 -0
- maref/stress/real_faults.py +186 -0
- maref/stress/real_latency.py +92 -0
- maref/stress/resilience_tracker.py +80 -0
- maref/stress/stability_test.py +233 -0
- maref/stress/stress_harness.py +188 -0
- maref/stress/stress_level.py +84 -0
- maref/stress/stress_result.py +63 -0
- maref/supply_chain/__init__.py +42 -0
- maref/supply_chain/sbom_generator.py +1037 -0
- maref/supply_chain/vulnerability_scanner.py +1073 -0
- maref/testing/__init__.py +5 -0
- maref/testing/mock_validator.py +43 -0
- maref/tools/__init__.py +67 -0
- maref/tools/browser_server.py +225 -0
- maref/tools/email_server.py +318 -0
- maref/tools/file_server.py +289 -0
- maref/tools/git_server.py +281 -0
- maref/tools/registry.py +154 -0
- maref/tools/shell_server.py +186 -0
- maref/tools/tool_schema.py +217 -0
- maref/tools/web_search_server.py +265 -0
- maref-0.30.0.dist-info/METADATA +212 -0
- maref-0.30.0.dist-info/RECORD +415 -0
- maref-0.30.0.dist-info/WHEEL +4 -0
- maref-0.30.0.dist-info/entry_points.txt +2 -0
- maref-0.30.0.dist-info/licenses/LICENSE +190 -0
- maref-0.30.0.dist-info/licenses/NOTICE +39 -0
- maref_lite/README.md +51 -0
- maref_lite/__init__.py +1 -0
- maref_lite/_constants.py +26 -0
- maref_lite/cli.py +672 -0
- maref_lite/governance.py +401 -0
- maref_lite/keys_cli.py +130 -0
- maref_lite/meta_learning.py +284 -0
- maref_lite/obs_cli.py +129 -0
- maref_lite/percv_cli.py +491 -0
- maref_lite/policy.py +162 -0
- maref_lite/py.typed +0 -0
- maref_lite/recursive_governance.py +391 -0
- maref_lite/state_machine.py +57 -0
- research/autoresearch_loop.py +648 -0
- research/autoresearch_phase10.py +462 -0
- research/autoresearch_phase9.py +474 -0
- research/chaos_engineering.py +366 -0
- research/config.py +95 -0
- research/continuous_engine.py +483 -0
- research/dashscope_client.py +394 -0
- research/discovery_engine.py +273 -0
- research/experiment_registry.py +311 -0
- research/fault_recovery.py +147 -0
- research/finding_models.py +90 -0
- research/knowledge_graph.py +10 -0
- research/orchestrator.py +194 -0
- research/py.typed +0 -0
- research/vector_store.py +252 -0
drift_guard/README.md
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# MAREF DriftGuard
|
|
2
|
+
|
|
3
|
+
## 概述
|
|
4
|
+
|
|
5
|
+
DriftGuard 是 MAREF 治理框架的安全层,负责检测和缓解 LoRA(Low-Rank Adaptation)微调过程中的人格漂移。核心功能包括:
|
|
6
|
+
|
|
7
|
+
1. **统计散度计算** — KL 散度、JS 散度、Hellinger 距离
|
|
8
|
+
2. **阈值监控** — 基于配置的严重性分级(LOW/MEDIUM/HIGH/CRITICAL)
|
|
9
|
+
3. **人工仲裁门控** — HIGH/CRITICAL 级别需要人工审批
|
|
10
|
+
4. **基座模型重置** — 自动回滚到已知良好的基线
|
|
11
|
+
5. **自动化流水线** — 周期性检测、事件记录、趋势分析
|
|
12
|
+
|
|
13
|
+
## 架构
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
Base Model Weights
|
|
17
|
+
|
|
|
18
|
+
v
|
|
19
|
+
LoRA Adapter (A, B matrices) ---> Delta = scaling * B * A
|
|
20
|
+
| |
|
|
21
|
+
| v
|
|
22
|
+
| Drift Metrics (KL/JS/Hellinger)
|
|
23
|
+
| |
|
|
24
|
+
v v
|
|
25
|
+
Current Weights <--- Base Reset <--- Severity Classification
|
|
26
|
+
|
|
|
27
|
+
v
|
|
28
|
+
Human Gate (HIGH/CRITICAL)
|
|
29
|
+
|
|
|
30
|
+
v
|
|
31
|
+
Action Execution
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## 核心组件
|
|
35
|
+
|
|
36
|
+
| 模块 | 说明 |
|
|
37
|
+
|------|------|
|
|
38
|
+
| `types.py` | 数据类型:DriftSeverity, DriftAction, DriftReading, DriftEvent, PipelineConfig |
|
|
39
|
+
| `metrics.py` | 散度计算:kl_divergence, js_divergence, hellinger_distance, compute_drift_metrics |
|
|
40
|
+
| `pipeline.py` | 流水线:HumanArbitrationGate, BaseModelReset, DriftDetectionPipeline |
|
|
41
|
+
|
|
42
|
+
## 严重性分级
|
|
43
|
+
|
|
44
|
+
| 级别 | KL 阈值 | Hellinger 阈值 | 自动动作 |
|
|
45
|
+
|------|---------|----------------|----------|
|
|
46
|
+
| NONE | < 0.1 | < 0.2 | 无 |
|
|
47
|
+
| LOW | >= 0.1 | >= 0.2 | ALERT |
|
|
48
|
+
| MEDIUM | >= 0.5 | >= 0.5 | QUARANTINE |
|
|
49
|
+
| HIGH | >= 0.5 | >= 0.5 | BASE_RESET(需人工审批) |
|
|
50
|
+
| CRITICAL | >= 1.0 | >= 0.5 | EMERGENCY_HALT |
|
|
51
|
+
|
|
52
|
+
## 人工仲裁门控
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
LOW/MEDIUM severity ──→ AUTO(自动执行)
|
|
56
|
+
HIGH/CRITICAL severity ──→ PENDING_REVIEW ──→ 人工审批 ──→ APPROVED/REJECTED
|
|
57
|
+
└──→ 超时 ──→ TIMEOUT
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## 使用示例
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
import asyncio
|
|
64
|
+
import numpy as np
|
|
65
|
+
from src.drift_guard.pipeline import DriftDetectionPipeline
|
|
66
|
+
from src.drift_guard.types import ModelSignature, PipelineConfig
|
|
67
|
+
|
|
68
|
+
async def main():
|
|
69
|
+
# 配置流水线
|
|
70
|
+
config = PipelineConfig(
|
|
71
|
+
kl_warning=0.1,
|
|
72
|
+
kl_critical=0.5,
|
|
73
|
+
kl_max=1.0,
|
|
74
|
+
)
|
|
75
|
+
pipeline = DriftDetectionPipeline(config)
|
|
76
|
+
|
|
77
|
+
# 模拟权重
|
|
78
|
+
baseline = np.random.randn(1000)
|
|
79
|
+
current = baseline + np.random.randn(1000) * 0.5
|
|
80
|
+
|
|
81
|
+
# 检测漂移
|
|
82
|
+
event = await pipeline.check_drift(
|
|
83
|
+
baseline_weights=baseline,
|
|
84
|
+
current_weights=current,
|
|
85
|
+
model=ModelSignature("lora-adapter", "v1"),
|
|
86
|
+
baseline=ModelSignature("base-model", "v1"),
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
if event:
|
|
90
|
+
print(f"Drift detected: {event.reading.severity.name}")
|
|
91
|
+
print(f"Action: {event.action_taken.name}")
|
|
92
|
+
print(f"KL divergence: {event.reading.kl_divergence:.4f}")
|
|
93
|
+
|
|
94
|
+
asyncio.run(main())
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## 设计决策
|
|
98
|
+
|
|
99
|
+
### 为什么使用三种散度指标?
|
|
100
|
+
- **KL 散度**:最常用,但对分布尾部敏感
|
|
101
|
+
- **JS 散度**:对称且有界,适合比较
|
|
102
|
+
- **Hellinger 距离**:有界 [0,1],便于设置阈值
|
|
103
|
+
|
|
104
|
+
### 为什么需要人工门控?
|
|
105
|
+
根据可行性报告,递归训练导致的人格漂移是最高风险。HIGH/CRITICAL 级别的自动重置可能导致服务中断,需要人类确认。
|
|
106
|
+
|
|
107
|
+
### 基座重置的冷却期?
|
|
108
|
+
防止频繁重置导致的震荡。默认 60 秒冷却期,确保系统有时间稳定。
|
drift_guard/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MAREF A/B Testing Framework
|
|
3
|
+
|
|
4
|
+
Phase 9: Provides controlled experimentation for policy changes.
|
|
5
|
+
Allows comparing two policy configurations side-by-side before
|
|
6
|
+
committing to a full deployment.
|
|
7
|
+
|
|
8
|
+
Key features:
|
|
9
|
+
- Parallel policy execution
|
|
10
|
+
- Statistical significance testing
|
|
11
|
+
- Automatic winner selection
|
|
12
|
+
- Safe fallback to baseline
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import copy
|
|
18
|
+
import statistics
|
|
19
|
+
import time
|
|
20
|
+
from dataclasses import dataclass, field
|
|
21
|
+
from typing import Any
|
|
22
|
+
|
|
23
|
+
from drift_guard.policy_sandbox import PolicySandbox
|
|
24
|
+
from drift_guard.types import PipelineConfig
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class ABTestMetrics:
|
|
29
|
+
"""Metrics collected during A/B test."""
|
|
30
|
+
|
|
31
|
+
true_positives: int = 0
|
|
32
|
+
false_positives: int = 0
|
|
33
|
+
true_negatives: int = 0
|
|
34
|
+
false_negatives: int = 0
|
|
35
|
+
latency_ms: list[float] = field(default_factory=list)
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def precision(self) -> float:
|
|
39
|
+
if self.true_positives + self.false_positives == 0:
|
|
40
|
+
return 0.0
|
|
41
|
+
return self.true_positives / (self.true_positives + self.false_positives)
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def recall(self) -> float:
|
|
45
|
+
if self.true_positives + self.false_negatives == 0:
|
|
46
|
+
return 0.0
|
|
47
|
+
return self.true_positives / (self.true_positives + self.false_negatives)
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def f1_score(self) -> float:
|
|
51
|
+
if self.precision + self.recall == 0:
|
|
52
|
+
return 0.0
|
|
53
|
+
return 2 * (self.precision * self.recall) / (self.precision + self.recall)
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def mean_latency(self) -> float:
|
|
57
|
+
if not self.latency_ms:
|
|
58
|
+
return 0.0
|
|
59
|
+
return statistics.mean(self.latency_ms)
|
|
60
|
+
|
|
61
|
+
def to_dict(self) -> dict[str, Any]:
|
|
62
|
+
return {
|
|
63
|
+
"true_positives": self.true_positives,
|
|
64
|
+
"false_positives": self.false_positives,
|
|
65
|
+
"true_negatives": self.true_negatives,
|
|
66
|
+
"false_negatives": self.false_negatives,
|
|
67
|
+
"precision": self.precision,
|
|
68
|
+
"recall": self.recall,
|
|
69
|
+
"f1_score": self.f1_score,
|
|
70
|
+
"mean_latency_ms": self.mean_latency,
|
|
71
|
+
"sample_size": len(self.latency_ms),
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@dataclass
|
|
76
|
+
class ABTestResult:
|
|
77
|
+
"""Result of an A/B test comparison."""
|
|
78
|
+
|
|
79
|
+
test_id: str
|
|
80
|
+
baseline_metrics: ABTestMetrics
|
|
81
|
+
variant_metrics: ABTestMetrics
|
|
82
|
+
winner: str # "baseline", "variant", or "tie"
|
|
83
|
+
confidence: float # 0.0 - 1.0
|
|
84
|
+
improvement_pct: dict[str, float] = field(default_factory=dict)
|
|
85
|
+
recommendation: str = ""
|
|
86
|
+
|
|
87
|
+
def to_dict(self) -> dict[str, Any]:
|
|
88
|
+
return {
|
|
89
|
+
"test_id": self.test_id,
|
|
90
|
+
"baseline_metrics": self.baseline_metrics.to_dict(),
|
|
91
|
+
"variant_metrics": self.variant_metrics.to_dict(),
|
|
92
|
+
"winner": self.winner,
|
|
93
|
+
"confidence": self.confidence,
|
|
94
|
+
"improvement_pct": self.improvement_pct,
|
|
95
|
+
"recommendation": self.recommendation,
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class ABTestFramework:
|
|
100
|
+
"""
|
|
101
|
+
A/B testing framework for policy changes.
|
|
102
|
+
|
|
103
|
+
Runs two policy configurations in parallel and compares
|
|
104
|
+
their performance on identical inputs.
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
def __init__(self, sandbox: PolicySandbox | None = None) -> None:
|
|
108
|
+
self._sandbox = sandbox or PolicySandbox()
|
|
109
|
+
self._active_tests: dict[str, dict[str, Any]] = {}
|
|
110
|
+
|
|
111
|
+
def create_test(
|
|
112
|
+
self,
|
|
113
|
+
test_id: str,
|
|
114
|
+
baseline_config: PipelineConfig,
|
|
115
|
+
variant_config: PipelineConfig,
|
|
116
|
+
min_samples: int = 100,
|
|
117
|
+
) -> bool:
|
|
118
|
+
"""
|
|
119
|
+
Create a new A/B test.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
test_id: Unique identifier for this test
|
|
123
|
+
baseline_config: The current/baseline policy
|
|
124
|
+
variant_config: The new policy to test
|
|
125
|
+
min_samples: Minimum samples before declaring winner
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
True if test was created successfully
|
|
129
|
+
"""
|
|
130
|
+
if test_id in self._active_tests:
|
|
131
|
+
return False
|
|
132
|
+
|
|
133
|
+
self._active_tests[test_id] = {
|
|
134
|
+
"baseline_config": copy.deepcopy(baseline_config),
|
|
135
|
+
"variant_config": copy.deepcopy(variant_config),
|
|
136
|
+
"baseline_metrics": ABTestMetrics(),
|
|
137
|
+
"variant_metrics": ABTestMetrics(),
|
|
138
|
+
"min_samples": min_samples,
|
|
139
|
+
"started_at": time.time(),
|
|
140
|
+
"completed": False,
|
|
141
|
+
}
|
|
142
|
+
return True
|
|
143
|
+
|
|
144
|
+
def record_sample(
|
|
145
|
+
self,
|
|
146
|
+
test_id: str,
|
|
147
|
+
variant: str, # "baseline" or "variant"
|
|
148
|
+
predicted_drift: bool,
|
|
149
|
+
actual_drift: bool,
|
|
150
|
+
latency_ms: float,
|
|
151
|
+
) -> bool:
|
|
152
|
+
"""
|
|
153
|
+
Record a sample for an active A/B test.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
test_id: Test identifier
|
|
157
|
+
variant: Which variant this sample belongs to
|
|
158
|
+
predicted_drift: Whether drift was predicted
|
|
159
|
+
actual_drift: Whether drift actually occurred
|
|
160
|
+
latency_ms: Detection latency in milliseconds
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
True if sample was recorded
|
|
164
|
+
"""
|
|
165
|
+
if test_id not in self._active_tests:
|
|
166
|
+
return False
|
|
167
|
+
|
|
168
|
+
test = self._active_tests[test_id]
|
|
169
|
+
if test["completed"]:
|
|
170
|
+
return False
|
|
171
|
+
|
|
172
|
+
metrics = (
|
|
173
|
+
test["baseline_metrics"]
|
|
174
|
+
if variant == "baseline"
|
|
175
|
+
else test["variant_metrics"]
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
# Update confusion matrix
|
|
179
|
+
if predicted_drift and actual_drift:
|
|
180
|
+
metrics.true_positives += 1
|
|
181
|
+
elif predicted_drift and not actual_drift:
|
|
182
|
+
metrics.false_positives += 1
|
|
183
|
+
elif not predicted_drift and not actual_drift:
|
|
184
|
+
metrics.true_negatives += 1
|
|
185
|
+
else:
|
|
186
|
+
metrics.false_negatives += 1
|
|
187
|
+
|
|
188
|
+
metrics.latency_ms.append(latency_ms)
|
|
189
|
+
return True
|
|
190
|
+
|
|
191
|
+
def evaluate_test(self, test_id: str) -> ABTestResult | None:
|
|
192
|
+
"""
|
|
193
|
+
Evaluate an A/B test and determine winner.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
test_id: Test identifier
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
ABTestResult if test is complete, None otherwise
|
|
200
|
+
"""
|
|
201
|
+
if test_id not in self._active_tests:
|
|
202
|
+
return None
|
|
203
|
+
|
|
204
|
+
test = self._active_tests[test_id]
|
|
205
|
+
baseline = test["baseline_metrics"]
|
|
206
|
+
variant = test["variant_metrics"]
|
|
207
|
+
|
|
208
|
+
# Check if we have enough samples
|
|
209
|
+
total_samples = (
|
|
210
|
+
len(baseline.latency_ms) + len(variant.latency_ms)
|
|
211
|
+
)
|
|
212
|
+
if total_samples < test["min_samples"]:
|
|
213
|
+
return None
|
|
214
|
+
|
|
215
|
+
# Mark as completed
|
|
216
|
+
test["completed"] = True
|
|
217
|
+
|
|
218
|
+
# Calculate improvements
|
|
219
|
+
improvements = {}
|
|
220
|
+
if baseline.f1_score > 0:
|
|
221
|
+
improvements["f1_score"] = (
|
|
222
|
+
(variant.f1_score - baseline.f1_score) / baseline.f1_score * 100
|
|
223
|
+
)
|
|
224
|
+
if baseline.mean_latency > 0:
|
|
225
|
+
improvements["latency"] = (
|
|
226
|
+
(baseline.mean_latency - variant.mean_latency)
|
|
227
|
+
/ baseline.mean_latency
|
|
228
|
+
* 100
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
# Determine winner based on F1 score (primary) and latency (secondary)
|
|
232
|
+
winner = "tie"
|
|
233
|
+
confidence = 0.5
|
|
234
|
+
|
|
235
|
+
f1_diff = variant.f1_score - baseline.f1_score
|
|
236
|
+
if abs(f1_diff) > 0.05: # At least 5% difference in F1
|
|
237
|
+
if f1_diff > 0:
|
|
238
|
+
winner = "variant"
|
|
239
|
+
confidence = min(0.5 + abs(f1_diff), 0.95)
|
|
240
|
+
else:
|
|
241
|
+
winner = "baseline"
|
|
242
|
+
confidence = min(0.5 + abs(f1_diff), 0.95)
|
|
243
|
+
|
|
244
|
+
# Generate recommendation
|
|
245
|
+
if winner == "variant" and confidence > 0.7:
|
|
246
|
+
recommendation = (
|
|
247
|
+
f"Variant shows significant improvement "
|
|
248
|
+
f"({improvements.get('f1_score', 0):.1f}% F1, "
|
|
249
|
+
f"{improvements.get('latency', 0):.1f}% latency). Recommend deployment."
|
|
250
|
+
)
|
|
251
|
+
elif winner == "baseline":
|
|
252
|
+
recommendation = (
|
|
253
|
+
"Baseline performs better. Reject variant and investigate."
|
|
254
|
+
)
|
|
255
|
+
else:
|
|
256
|
+
recommendation = (
|
|
257
|
+
"No clear winner. Consider extending test or revising variant."
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
return ABTestResult(
|
|
261
|
+
test_id=test_id,
|
|
262
|
+
baseline_metrics=baseline,
|
|
263
|
+
variant_metrics=variant,
|
|
264
|
+
winner=winner,
|
|
265
|
+
confidence=confidence,
|
|
266
|
+
improvement_pct=improvements,
|
|
267
|
+
recommendation=recommendation,
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
def get_test_status(self, test_id: str) -> dict[str, Any] | None:
|
|
271
|
+
"""Get current status of an A/B test."""
|
|
272
|
+
if test_id not in self._active_tests:
|
|
273
|
+
return None
|
|
274
|
+
|
|
275
|
+
test = self._active_tests[test_id]
|
|
276
|
+
return {
|
|
277
|
+
"test_id": test_id,
|
|
278
|
+
"completed": test["completed"],
|
|
279
|
+
"baseline_samples": len(test["baseline_metrics"].latency_ms),
|
|
280
|
+
"variant_samples": len(test["variant_metrics"].latency_ms),
|
|
281
|
+
"min_samples": test["min_samples"],
|
|
282
|
+
"elapsed_seconds": time.time() - test["started_at"],
|
|
283
|
+
}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MAREF DriftGuard Adaptive Threshold Module
|
|
3
|
+
|
|
4
|
+
Phase 8: Implements self-adjusting thresholds based on historical
|
|
5
|
+
drift detection performance. This enables the system to learn
|
|
6
|
+
from its own detection history and optimize sensitivity.
|
|
7
|
+
|
|
8
|
+
Key features:
|
|
9
|
+
- False positive/negative tracking
|
|
10
|
+
- Threshold auto-tuning with bounds
|
|
11
|
+
- Performance metric collection
|
|
12
|
+
- Safe rollback to baseline
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from dataclasses import dataclass
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class ThresholdPerformance:
|
|
23
|
+
"""Performance metrics for a threshold configuration."""
|
|
24
|
+
|
|
25
|
+
true_positives: int = 0
|
|
26
|
+
false_positives: int = 0
|
|
27
|
+
true_negatives: int = 0
|
|
28
|
+
false_negatives: int = 0
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def precision(self) -> float:
|
|
32
|
+
"""Precision = TP / (TP + FP)."""
|
|
33
|
+
if self.true_positives + self.false_positives == 0:
|
|
34
|
+
return 0.0
|
|
35
|
+
return self.true_positives / (self.true_positives + self.false_positives)
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def recall(self) -> float:
|
|
39
|
+
"""Recall = TP / (TP + FN)."""
|
|
40
|
+
if self.true_positives + self.false_negatives == 0:
|
|
41
|
+
return 0.0
|
|
42
|
+
return self.true_positives / (self.true_positives + self.false_negatives)
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def f1_score(self) -> float:
|
|
46
|
+
"""F1 = 2 * (precision * recall) / (precision + recall)."""
|
|
47
|
+
if self.precision + self.recall == 0:
|
|
48
|
+
return 0.0
|
|
49
|
+
return 2 * (self.precision * self.recall) / (self.precision + self.recall)
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def false_positive_rate(self) -> float:
|
|
53
|
+
"""FPR = FP / (FP + TN)."""
|
|
54
|
+
if self.false_positives + self.true_negatives == 0:
|
|
55
|
+
return 0.0
|
|
56
|
+
return self.false_positives / (self.false_positives + self.true_negatives)
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def false_negative_rate(self) -> float:
|
|
60
|
+
"""FNR = FN / (FN + TP)."""
|
|
61
|
+
if self.false_negatives + self.true_positives == 0:
|
|
62
|
+
return 0.0
|
|
63
|
+
return self.false_negatives / (self.false_negatives + self.true_positives)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@dataclass
|
|
67
|
+
class AdaptiveThresholdConfig:
|
|
68
|
+
"""Configuration for adaptive threshold behavior."""
|
|
69
|
+
|
|
70
|
+
# Learning rate for threshold adjustments (0.0 - 1.0)
|
|
71
|
+
learning_rate: float = 0.1
|
|
72
|
+
|
|
73
|
+
# Target false positive rate (0.0 - 1.0)
|
|
74
|
+
target_fpr: float = 0.05
|
|
75
|
+
|
|
76
|
+
# Target false negative rate (0.0 - 1.0)
|
|
77
|
+
target_fnr: float = 0.02
|
|
78
|
+
|
|
79
|
+
# Minimum allowed threshold values
|
|
80
|
+
min_kl_warning: float = 0.01
|
|
81
|
+
min_kl_critical: float = 0.1
|
|
82
|
+
min_kl_max: float = 0.5
|
|
83
|
+
|
|
84
|
+
# Maximum allowed threshold values
|
|
85
|
+
max_kl_warning: float = 0.5
|
|
86
|
+
max_kl_critical: float = 2.0
|
|
87
|
+
max_kl_max: float = 5.0
|
|
88
|
+
|
|
89
|
+
# Window size for performance evaluation
|
|
90
|
+
evaluation_window: int = 100
|
|
91
|
+
|
|
92
|
+
# Enable/disable adaptive behavior
|
|
93
|
+
enabled: bool = True
|
|
94
|
+
|
|
95
|
+
def to_dict(self) -> dict[str, Any]:
|
|
96
|
+
return {
|
|
97
|
+
"learning_rate": self.learning_rate,
|
|
98
|
+
"target_fpr": self.target_fpr,
|
|
99
|
+
"target_fnr": self.target_fnr,
|
|
100
|
+
"min_kl_warning": self.min_kl_warning,
|
|
101
|
+
"min_kl_critical": self.min_kl_critical,
|
|
102
|
+
"min_kl_max": self.min_kl_max,
|
|
103
|
+
"max_kl_warning": self.max_kl_warning,
|
|
104
|
+
"max_kl_critical": self.max_kl_critical,
|
|
105
|
+
"max_kl_max": self.max_kl_max,
|
|
106
|
+
"evaluation_window": self.evaluation_window,
|
|
107
|
+
"enabled": self.enabled,
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class AdaptiveThresholdManager:
|
|
112
|
+
"""
|
|
113
|
+
Manages adaptive threshold adjustment for drift detection.
|
|
114
|
+
|
|
115
|
+
Tracks detection outcomes and adjusts thresholds to optimize
|
|
116
|
+
the balance between sensitivity and specificity.
|
|
117
|
+
"""
|
|
118
|
+
|
|
119
|
+
def __init__(self, config: AdaptiveThresholdConfig | None = None) -> None:
|
|
120
|
+
self._config = config or AdaptiveThresholdConfig()
|
|
121
|
+
self._performance = ThresholdPerformance()
|
|
122
|
+
self._history: list[tuple[float, bool, bool]] = []
|
|
123
|
+
# (threshold_used, was_alert, was_true_drift)
|
|
124
|
+
|
|
125
|
+
def record_outcome(
|
|
126
|
+
self,
|
|
127
|
+
threshold_used: float,
|
|
128
|
+
predicted_drift: bool,
|
|
129
|
+
actual_drift: bool,
|
|
130
|
+
) -> None:
|
|
131
|
+
"""
|
|
132
|
+
Record the outcome of a drift detection decision.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
threshold_used: The threshold value that was applied
|
|
136
|
+
predicted_drift: Whether the system predicted drift
|
|
137
|
+
actual_drift: Whether there was actual drift (ground truth)
|
|
138
|
+
"""
|
|
139
|
+
self._history.append((threshold_used, predicted_drift, actual_drift))
|
|
140
|
+
|
|
141
|
+
# Update performance metrics
|
|
142
|
+
if predicted_drift and actual_drift:
|
|
143
|
+
self._performance.true_positives += 1
|
|
144
|
+
elif predicted_drift and not actual_drift:
|
|
145
|
+
self._performance.false_positives += 1
|
|
146
|
+
elif not predicted_drift and not actual_drift:
|
|
147
|
+
self._performance.true_negatives += 1
|
|
148
|
+
else: # not predicted_drift and actual_drift
|
|
149
|
+
self._performance.false_negatives += 1
|
|
150
|
+
|
|
151
|
+
# Trim history to evaluation window
|
|
152
|
+
if len(self._history) > self._config.evaluation_window:
|
|
153
|
+
self._history.pop(0)
|
|
154
|
+
|
|
155
|
+
def should_adjust(self) -> bool:
|
|
156
|
+
"""Check if enough data exists to make an adjustment."""
|
|
157
|
+
return len(self._history) >= self._config.evaluation_window
|
|
158
|
+
|
|
159
|
+
def compute_adjustment(self, current_threshold: float) -> float:
|
|
160
|
+
"""
|
|
161
|
+
Compute the recommended threshold adjustment.
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
The delta to apply to the current threshold.
|
|
165
|
+
Positive = increase threshold (less sensitive)
|
|
166
|
+
Negative = decrease threshold (more sensitive)
|
|
167
|
+
"""
|
|
168
|
+
if not self._config.enabled or not self.should_adjust():
|
|
169
|
+
return 0.0
|
|
170
|
+
|
|
171
|
+
fpr = self._performance.false_positive_rate
|
|
172
|
+
fnr = self._performance.false_negative_rate
|
|
173
|
+
|
|
174
|
+
adjustment = 0.0
|
|
175
|
+
|
|
176
|
+
# If FPR is too high, increase threshold (make it harder to trigger)
|
|
177
|
+
if fpr > self._config.target_fpr:
|
|
178
|
+
adjustment += self._config.learning_rate * (fpr - self._config.target_fpr)
|
|
179
|
+
|
|
180
|
+
# If FNR is too high, decrease threshold (make it easier to trigger)
|
|
181
|
+
if fnr > self._config.target_fnr:
|
|
182
|
+
adjustment -= self._config.learning_rate * (fnr - self._config.target_fnr)
|
|
183
|
+
|
|
184
|
+
return adjustment
|
|
185
|
+
|
|
186
|
+
def adjust_threshold(self, current_threshold: float, threshold_type: str) -> float:
|
|
187
|
+
"""
|
|
188
|
+
Apply adaptive adjustment to a threshold with bounds checking.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
current_threshold: Current threshold value
|
|
192
|
+
threshold_type: One of "warning", "critical", "max"
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
The adjusted threshold value
|
|
196
|
+
"""
|
|
197
|
+
adjustment = self.compute_adjustment(current_threshold)
|
|
198
|
+
new_threshold = current_threshold + adjustment
|
|
199
|
+
|
|
200
|
+
# Apply bounds based on threshold type
|
|
201
|
+
if threshold_type == "warning":
|
|
202
|
+
new_threshold = max(
|
|
203
|
+
self._config.min_kl_warning,
|
|
204
|
+
min(self._config.max_kl_warning, new_threshold),
|
|
205
|
+
)
|
|
206
|
+
elif threshold_type == "critical":
|
|
207
|
+
new_threshold = max(
|
|
208
|
+
self._config.min_kl_critical,
|
|
209
|
+
min(self._config.max_kl_critical, new_threshold),
|
|
210
|
+
)
|
|
211
|
+
elif threshold_type == "max":
|
|
212
|
+
new_threshold = max(
|
|
213
|
+
self._config.min_kl_max,
|
|
214
|
+
min(self._config.max_kl_max, new_threshold),
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
return new_threshold
|
|
218
|
+
|
|
219
|
+
def get_performance(self) -> ThresholdPerformance:
|
|
220
|
+
"""Get current performance metrics."""
|
|
221
|
+
return self._performance
|
|
222
|
+
|
|
223
|
+
def get_stats(self) -> dict[str, Any]:
|
|
224
|
+
"""Get comprehensive statistics."""
|
|
225
|
+
return {
|
|
226
|
+
"config": self._config.to_dict(),
|
|
227
|
+
"performance": {
|
|
228
|
+
"true_positives": self._performance.true_positives,
|
|
229
|
+
"false_positives": self._performance.false_positives,
|
|
230
|
+
"true_negatives": self._performance.true_negatives,
|
|
231
|
+
"false_negatives": self._performance.false_negatives,
|
|
232
|
+
"precision": self._performance.precision,
|
|
233
|
+
"recall": self._performance.recall,
|
|
234
|
+
"f1_score": self._performance.f1_score,
|
|
235
|
+
"false_positive_rate": self._performance.false_positive_rate,
|
|
236
|
+
"false_negative_rate": self._performance.false_negative_rate,
|
|
237
|
+
},
|
|
238
|
+
"history_size": len(self._history),
|
|
239
|
+
"ready_to_adjust": self.should_adjust(),
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
def reset(self) -> None:
|
|
243
|
+
"""Reset all performance tracking."""
|
|
244
|
+
self._performance = ThresholdPerformance()
|
|
245
|
+
self._history.clear()
|