From 486c6063d9e04c81be796f788d9ec0eaa9a66fe7 Mon Sep 17 00:00:00 2001 From: 732642856 <732642856@qq.com> Date: Fri, 26 Jun 2026 17:17:48 +0800 Subject: [PATCH] feat(oracle): add shadbala component tolerance audit --- README.md | 2 +- scripts/oracle_boundary_audit.py | 72 +++++++++++++++++++++++++++-- tests/test_oracle_boundary_audit.py | 7 +++ 3 files changed, 75 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 8a75bdf2..aaa0d7ac 100644 --- a/README.md +++ b/README.md @@ -201,7 +201,7 @@ python3 scripts/oracle_boundary_audit.py \ --oracle-file references/oracle/dasha_shadbala_oracle_cases.json ``` -审计报告中的 `template_comparisons` 会列出 external-verified template 的 Dasha 起点差异、Shadbala 七曜分量/总分差异,并继续保持 `production_tuning_recommended: false`,防止用单个样本调生产常数。 +审计报告中的 `template_comparisons` 会列出 external-verified template 的 Dasha 起点差异、Shadbala 七曜分量/总分差异、每个分量的 Rupa 容差、单位说明和 `global_scaling_check.recommendation: reject_global_scaling`,并继续保持 `production_tuning_recommended: false`,防止用单个样本或全局倍率调生产常数。 `full-reading` 也会输出 `ai_prompt_pack`:这是给网页/app、skill 或后端 AI 代理使用的结构化 Prompt/RAG 上下文包。它不会硬编码断语,而是携带 D1/D9/Dasha/Shadbala/Ashtakavarga 的证据快照、推荐检索文档和边界提示,要求大模型基于计算证据交叉验证,避免单一配置下结论。 diff --git a/scripts/oracle_boundary_audit.py b/scripts/oracle_boundary_audit.py index a13e9422..7aea7f6f 100644 --- a/scripts/oracle_boundary_audit.py +++ b/scripts/oracle_boundary_audit.py @@ -26,6 +26,23 @@ import jyotish_engine as engine # noqa: E402 SHADBALA_PLANETS = ["Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn"] ORACLE_TEMPLATE_READY_STATUSES = {"external_verified"} +SHADBALA_COMPONENT_FIELD_MAP = { + "sthana": "sthana_bala", + "dig": "dig_bala", + "kala": "kala_bala", + "chesta": "chesta_bala", + "naisargika": "naisargika_bala", + "drik": "drik_bala", +} +SHADBALA_COMPONENT_TOLERANCES_RUPA = { + "sthana": 0.1, + "dig": 0.5, + "kala": 0.5, + "chesta": 1.0, + "naisargika": 0.05, + "drik": 1.5, +} +SHADBALA_TOTAL_TOLERANCE_RUPA = 1.0 def _resolve_path(path: str) -> str: @@ -280,24 +297,69 @@ def _template_shadbala_comparison(case: dict[str, Any]) -> dict[str, Any]: return {"status": "engine_error", "error": result["error"], "planets": {}} rows: dict[str, Any] = {} + scale_ratios: list[float] = [] for planet in SHADBALA_PLANETS: external_row = target_components.get(planet, {}) engine_row = result.get("planets", {}).get(planet, {}) engine_components = _component_totals(engine_row) if engine_row else {} external_total = external_row.get("total_rupa") if isinstance(external_row, dict) else None engine_total = round(float(engine_row.get("total_rupas", 0.0)), 4) if engine_row else None + component_deltas: dict[str, Any] = {} + if isinstance(external_row, dict): + for external_name, engine_name in SHADBALA_COMPONENT_FIELD_MAP.items(): + engine_value = engine_components.get(engine_name) + external_value = external_row.get(external_name) + tolerance = SHADBALA_COMPONENT_TOLERANCES_RUPA[external_name] + delta = ( + round(float(engine_value) - float(external_value), 4) + if isinstance(engine_value, (int, float)) and isinstance(external_value, (int, float)) + else None + ) + component_deltas[external_name] = { + "engine_rupa": engine_value, + "external_rupa": external_value, + "delta_rupa": delta, + "abs_delta_rupa": round(abs(delta), 4) if delta is not None else None, + "tolerance_rupa": tolerance, + "within_tolerance": abs(delta) <= tolerance if delta is not None else None, + } + if isinstance(engine_total, (int, float)) and isinstance(external_total, (int, float)) and external_total: + scale_ratios.append(round(float(engine_total) / float(external_total), 6)) + total_delta = ( + round(engine_total - float(external_total), 4) + if engine_total is not None and isinstance(external_total, (int, float)) + else None + ) rows[planet] = { "engine_total_rupa": engine_total, "external_total_rupa": external_total, - "total_rupa_delta": ( - round(engine_total - float(external_total), 4) - if engine_total is not None and isinstance(external_total, (int, float)) - else None + "total_rupa_delta": total_delta, + "total_abs_delta_rupa": round(abs(total_delta), 4) if total_delta is not None else None, + "total_tolerance_rupa": SHADBALA_TOTAL_TOLERANCE_RUPA, + "total_within_tolerance": ( + abs(total_delta) <= SHADBALA_TOTAL_TOLERANCE_RUPA if total_delta is not None else None ), "engine_components": engine_components, "external_components": external_row, + "component_deltas": component_deltas, } - return {"status": "compared", "planets": rows} + ratio_spread = round(max(scale_ratios) - min(scale_ratios), 6) if len(scale_ratios) >= 2 else None + return { + "status": "compared", + "unit": "rupa", + "component_tolerances": SHADBALA_COMPONENT_TOLERANCES_RUPA, + "total_tolerance_rupa": SHADBALA_TOTAL_TOLERANCE_RUPA, + "global_scaling_check": { + "allowed": False, + "recommendation": "reject_global_scaling", + "engine_to_external_total_ratios": scale_ratios, + "ratio_spread": ratio_spread, + "finding": ( + "Do not fix Shadbala with a global multiplier; inspect component-level deltas by planet." + ), + }, + "planets": rows, + } def _audit_external_verified_template_case(case: dict[str, Any]) -> dict[str, Any]: diff --git a/tests/test_oracle_boundary_audit.py b/tests/test_oracle_boundary_audit.py index 5abf06e7..b72ec4c0 100644 --- a/tests/test_oracle_boundary_audit.py +++ b/tests/test_oracle_boundary_audit.py @@ -133,4 +133,11 @@ def test_oracle_boundary_audit_compares_external_verified_template_rows(tmp_path assert comparison["dasha"]["date_delta_days"] is not None assert comparison["shadbala"]["planets"]["Sun"]["external_total_rupa"] == 21.0 assert comparison["shadbala"]["planets"]["Sun"]["total_rupa_delta"] is not None + assert comparison["shadbala"]["unit"] == "rupa" + assert comparison["shadbala"]["component_tolerances"]["sthana"] == 0.1 + assert comparison["shadbala"]["component_tolerances"]["drik"] == 1.5 + assert comparison["shadbala"]["planets"]["Sun"]["component_deltas"]["sthana"]["tolerance_rupa"] == 0.1 + assert comparison["shadbala"]["planets"]["Sun"]["component_deltas"]["drik"]["tolerance_rupa"] == 1.5 + assert comparison["shadbala"]["global_scaling_check"]["allowed"] is False + assert comparison["shadbala"]["global_scaling_check"]["recommendation"] == "reject_global_scaling" assert comparison["calibration_decision"] == "do_not_tune_single_template"