feat: add oracle evidence safeguards
This commit is contained in:
@@ -1817,6 +1817,17 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
'不要仅凭单一配置下结论;核心判断至少交叉 D1、D9、Dasha、Shadbala/Ashtakavarga 或 Transit 中的两个证据层。',
|
||||
'必须显式标注置信度和边界:Dasha/PDF 起点差异、Shadbala 外部绝对值 oracle 尚未完成时,不得声称已经完全校准。',
|
||||
]
|
||||
oracle_progress = {
|
||||
'scope': 'external_oracle_evidence_validation',
|
||||
'collection_queue': 'external_oracle_collection_queue',
|
||||
'total_packets': 5,
|
||||
'valid_packets': 0,
|
||||
'ready_for_calibration': 0,
|
||||
'production_tuning_allowed': False,
|
||||
'artifact_policy': 'references/oracle/artifacts/',
|
||||
'promotion_rule': 'external_verified requires source_artifact, filled target values, and non-local-engine external evidence.',
|
||||
'boundary': 'Dasha/Shadbala absolute values are not externally calibrated until enough packets pass validation.',
|
||||
}
|
||||
return {
|
||||
'schema_version': 1,
|
||||
'mode': 'jyotish_structured_prompt_pack',
|
||||
@@ -1844,6 +1855,7 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
'quality_boundary': {
|
||||
'external_oracle_status': 'D1/D9/VedAstro longitude boundary covered; Dasha/Shadbala external absolute calibration still requires multi-source oracle expansion.',
|
||||
},
|
||||
'oracle_progress': oracle_progress,
|
||||
},
|
||||
'retrieval_plan': {
|
||||
'local_reference_docs': [
|
||||
@@ -1858,6 +1870,7 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
'no_single_factor_conclusion',
|
||||
'd1_d9_dasha_cross_validation',
|
||||
'oracle_boundary_visible',
|
||||
'external_oracle_evidence_validation',
|
||||
'confidence_labeled_reading',
|
||||
],
|
||||
},
|
||||
|
||||
@@ -567,6 +567,20 @@ def _planet_snapshot(planets, planet_name):
|
||||
}
|
||||
|
||||
|
||||
def _oracle_progress_snapshot():
|
||||
return {
|
||||
'scope': 'external_oracle_evidence_validation',
|
||||
'collection_queue': 'external_oracle_collection_queue',
|
||||
'total_packets': 5,
|
||||
'valid_packets': 0,
|
||||
'ready_for_calibration': 0,
|
||||
'production_tuning_allowed': False,
|
||||
'artifact_policy': 'references/oracle/artifacts/',
|
||||
'promotion_rule': 'external_verified requires source_artifact, filled target values, and non-local-engine external evidence.',
|
||||
'boundary': 'Dasha/Shadbala absolute values are not externally calibrated until enough packets pass validation.',
|
||||
}
|
||||
|
||||
|
||||
def _build_ai_prompt_pack(report):
|
||||
"""Build a compact, evidence-first prompt pack for downstream AI/RAG reading."""
|
||||
modules = report.get('modules', {}) if isinstance(report, dict) else {}
|
||||
@@ -640,6 +654,7 @@ def _build_ai_prompt_pack(report):
|
||||
'warnings': report.get('warnings', []),
|
||||
'external_oracle_status': 'D1/D9/VedAstro longitude boundary covered; Dasha/Shadbala external absolute calibration still requires multi-source oracle expansion.',
|
||||
},
|
||||
'oracle_progress': _oracle_progress_snapshot(),
|
||||
}
|
||||
|
||||
prompt_lines = [
|
||||
@@ -670,6 +685,7 @@ def _build_ai_prompt_pack(report):
|
||||
'no_single_factor_conclusion',
|
||||
'd1_d9_dasha_cross_validation',
|
||||
'oracle_boundary_visible',
|
||||
'external_oracle_evidence_validation',
|
||||
'confidence_labeled_reading',
|
||||
],
|
||||
},
|
||||
|
||||
@@ -58,6 +58,19 @@ SOURCE_GUIDANCE = {
|
||||
"The row is not derived from this repository's local Shadbala output.",
|
||||
],
|
||||
},
|
||||
"ashtakoot": {
|
||||
"preferred_sources": ["JHora compatibility screenshot", "VedAstro HTTP/API output", "AstroSage public compatibility screen"],
|
||||
"steps": [
|
||||
"Set both birth records or both Moon longitudes in the external compatibility tool.",
|
||||
"Record the 36-point total score and all eight Kuta component scores.",
|
||||
"Capture Kuja/Manglik status and any visible ayanamsa, node mode or matching settings.",
|
||||
],
|
||||
"promotion_criteria": [
|
||||
"All eight Ashtakoot component targets are filled from an external compatibility source.",
|
||||
"The 36-point total is traceable to component scores.",
|
||||
"The row is not derived from this repository's local synastry/ashtakoot output.",
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -67,6 +80,16 @@ FIELD_TO_MODULE = {
|
||||
"ascendant_longitude_deg": "longitude",
|
||||
"vimshottari_start_date": "dasha",
|
||||
"shadbala_components": "shadbala",
|
||||
"total_score": "ashtakoot",
|
||||
"varna": "ashtakoot",
|
||||
"vashya": "ashtakoot",
|
||||
"tara": "ashtakoot",
|
||||
"yoni": "ashtakoot",
|
||||
"graha_maitri": "ashtakoot",
|
||||
"gana": "ashtakoot",
|
||||
"bhakoot": "ashtakoot",
|
||||
"nadi": "ashtakoot",
|
||||
"kuja_status": "ashtakoot",
|
||||
}
|
||||
|
||||
REQUIRED_EVIDENCE_METADATA_FIELDS = [
|
||||
|
||||
@@ -24,6 +24,29 @@ LOCAL_ENGINE_MARKERS = [
|
||||
"oracle_collection_queue.py",
|
||||
"oracle_boundary_audit.py",
|
||||
]
|
||||
SHADBALA_REQUIRED_PLANETS = ["Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn"]
|
||||
SHADBALA_REQUIRED_COMPONENTS = ["sthana", "dig", "kala", "chesta", "naisargika", "drik"]
|
||||
ASHTAKOOT_SCORE_RANGES = {
|
||||
"target.total_score": (0.0, 36.0),
|
||||
"target.varna": (0.0, 1.0),
|
||||
"target.vashya": (0.0, 2.0),
|
||||
"target.tara": (0.0, 3.0),
|
||||
"target.yoni": (0.0, 4.0),
|
||||
"target.graha_maitri": (0.0, 5.0),
|
||||
"target.gana": (0.0, 6.0),
|
||||
"target.bhakoot": (0.0, 7.0),
|
||||
"target.nadi": (0.0, 8.0),
|
||||
}
|
||||
ASHTAKOOT_COMPONENT_FIELDS = [
|
||||
"target.varna",
|
||||
"target.vashya",
|
||||
"target.tara",
|
||||
"target.yoni",
|
||||
"target.graha_maitri",
|
||||
"target.gana",
|
||||
"target.bhakoot",
|
||||
"target.nadi",
|
||||
]
|
||||
|
||||
|
||||
def _resolve_path(path: str) -> str:
|
||||
@@ -72,6 +95,13 @@ def _validate_packet(task: dict[str, Any]) -> dict[str, Any]:
|
||||
if _is_blank(value):
|
||||
problems.append(f"placeholder_unfilled:{field}")
|
||||
|
||||
shadbala_components = target_placeholders.get("target.shadbala_components")
|
||||
if "target.shadbala_components" in expected_fields:
|
||||
problems.extend(_validate_shadbala_components(shadbala_components))
|
||||
|
||||
if "target.total_score" in expected_fields:
|
||||
problems.extend(_validate_ashtakoot_scores(target_placeholders))
|
||||
|
||||
integrity = packet.get("integrity_checks", {})
|
||||
if integrity.get("must_not_come_from_local_engine") and _is_local_engine_artifact(packet):
|
||||
problems.append("local_engine_artifact_rejected")
|
||||
@@ -95,6 +125,58 @@ def _validate_packet(task: dict[str, Any]) -> dict[str, Any]:
|
||||
}
|
||||
|
||||
|
||||
def _validate_shadbala_components(value: Any) -> list[str]:
|
||||
problems: list[str] = []
|
||||
if _is_blank(value) or not isinstance(value, dict):
|
||||
return ["missing_shadbala_component:all_planets"]
|
||||
for planet in SHADBALA_REQUIRED_PLANETS:
|
||||
row = value.get(planet)
|
||||
if not isinstance(row, dict) or _is_blank(row):
|
||||
problems.append(f"missing_shadbala_component:{planet}")
|
||||
continue
|
||||
for component in SHADBALA_REQUIRED_COMPONENTS:
|
||||
component_value = row.get(component)
|
||||
if _is_blank(component_value):
|
||||
problems.append(f"missing_shadbala_component:{planet}.{component}")
|
||||
continue
|
||||
if not isinstance(component_value, (int, float)) or isinstance(component_value, bool):
|
||||
problems.append(f"invalid_shadbala_component_type:{planet}.{component}")
|
||||
continue
|
||||
if component_value < 0:
|
||||
problems.append(f"invalid_shadbala_component_negative:{planet}.{component}")
|
||||
return problems
|
||||
|
||||
|
||||
def _is_number(value: Any) -> bool:
|
||||
return isinstance(value, (int, float)) and not isinstance(value, bool)
|
||||
|
||||
|
||||
def _validate_ashtakoot_scores(target_placeholders: dict[str, Any]) -> list[str]:
|
||||
problems: list[str] = []
|
||||
numeric_values: dict[str, float] = {}
|
||||
for field, (minimum, maximum) in ASHTAKOOT_SCORE_RANGES.items():
|
||||
if field not in target_placeholders:
|
||||
continue
|
||||
value = target_placeholders.get(field)
|
||||
if _is_blank(value):
|
||||
continue
|
||||
if not _is_number(value):
|
||||
problems.append(f"invalid_ashtakoot_score_type:{field}")
|
||||
continue
|
||||
numeric_value = float(value)
|
||||
numeric_values[field] = numeric_value
|
||||
if numeric_value < minimum or numeric_value > maximum:
|
||||
problems.append(f"invalid_ashtakoot_score_range:{field}")
|
||||
|
||||
if "target.total_score" in numeric_values and all(
|
||||
field in numeric_values for field in ASHTAKOOT_COMPONENT_FIELDS
|
||||
):
|
||||
component_sum = sum(numeric_values[field] for field in ASHTAKOOT_COMPONENT_FIELDS)
|
||||
if abs(component_sum - numeric_values["target.total_score"]) > 0.01:
|
||||
problems.append("ashtakoot_score_sum_mismatch")
|
||||
return problems
|
||||
|
||||
|
||||
def build_report(queue: dict[str, Any]) -> dict[str, Any]:
|
||||
packets = [_validate_packet(task) for task in queue.get("tasks", [])]
|
||||
ready_for_calibration = sum(1 for packet in packets if packet["ready_for_calibration"])
|
||||
|
||||
Reference in New Issue
Block a user