fix: improve context-aware yoga precision

This commit is contained in:
732642856
2026-06-05 18:00:00 +08:00
parent 93610326fe
commit 975f00f5f2
5 changed files with 124 additions and 22 deletions
+49
View File
@@ -1,5 +1,54 @@
# 印度占星 Skill 更新日志
## v6.0.372026-06-05)—— Context-aware Yoga 精度提升
> **目标**:利用 v6.0.36 已接入的 D9/Navamsa、Panchanga、Gulika/Maandi 上下文,继续修复 PyJHora/B.V. Raman 高影响 Yoga 误差,而不是回退到 D1-only 近似。
### 规则修复
- 拆分 PyJHora 中两个不同函数:
- `kaahala_yoga` → 新增 `bvr_kaahala_yoga`:4宫主与木星互为 Kendra,且上升主有力。
- `kahala_yoga` 保留原 BVR 逻辑:4宫主与9宫主互为 Kendra,且上升主有力。
- 新增 `bvr_kapata_yoga`,实现 PyJHora/B.V. Raman 202-204 三个 Kapata 条件,避免继续把 `kapata_yoga_*` 错映射到 `nishkapata`
- 修复 `bvr_mathibhramana_yoga`:补入 293 waning Moon/tithi 条件、BVR variation 1-4,包括 D9 中 Mercury/Rahu/6宫主/Mars 的组合。
- 修复 `bvr_matrunasa_precise`:在 Moon 受凶星夹制/合相/相位基础上,补入 199 的双层 Navamsa dispositor 链落 6/8/12。
- 修复 `bvr_nishkapata_precise`:补入第4宫为吉性星座、天然吉星、强星、以及上升主入4宫受吉星影响的 205/206 条件。
- 修复 `bvr_bahu_puthra_precise`:补入 Rahu 在5宫且非 Saturn Navamsa,以及7宫主关联行星的 Navamsa 主落 1/2/5。
- 调整 `bvr_rogagrastha_precise` 的弱上升主判定,贴近 PyJHora 的 sign-strength 口径。
- 修正 `vanchana_chora_bheethi` 语义条件:Gulika 在三方不再单独触发,必须符合 PyJHora 条件组合,减少过宽 FP。
### 映射修复
- `validate_logic_v2.py` 中:
- `kaahala_yoga` 改映射到新增 `bvr_kaahala_yoga`
- `kahala_yoga` 显式映射到 `kahala_yoga`
- `kapata_yoga_202/203/204` 改映射到新增 `bvr_kapata_yoga`
### 验证结果
- `scripts/validate_logic_v2.py`
- Precision **85.60%**v6.0.36 为 83.84%
- Recall **72.38%**v6.0.36 为 69.64%
- F1 **78.44%**v6.0.36 为 76.08%
- FP130FN295
- `scripts/run_quality_gate.py --skip-yoga-logic`:通过。
- pytest **35 passed**
- BPHS/Ashtakavarga 不变量 **18/18**
- golden case 通过
### 规则库状态
- JSON 规则总数:**475**
- 启用规则:**381**
- 当前验证报告:`references/validation_logic_report.json`
### 后续优化点
- 下一轮优先处理仍在 Top 队列中的:`vanchana_chora_bheethi``rogagrastha``swaveeryaddhana``kaalanirdesat_puthranaasa``sankha``dehasthoulya`
- 建议继续把 PyJHora 的 sign-strength、natural benefics/malefics 与 aspect helper 进一步对齐,减少“同名但判定口径略不同”的残余误差。
---
## v6.0.362026-06-05)—— Yoga 验证数据结构升级与 D9/Panchanga/Upagraha 上下文接入
> **目标**:为继续提升 Yoga 逻辑准确率补齐验证数据层,不再把所有 B.V. Raman / PyJHora 条件硬塞进 D1-only 近似,优先让标准测试集携带 D9/Navamsa、tithi/waning Moon、Gulika/Maandi 等上下文。
+4 -4
View File
@@ -1,6 +1,6 @@
---
name: jyotish-vedic-astrology
version: 6.0.36
version: 6.0.37
description: 印度占星(Jyotish)专业解盘与推运系统。核心能力:PDF星盘输入→严谨解盘→精确推运应期输出。触发词:印度占星、吠陀占星、Jyotish、解盘、推运、星盘分析、Dasha、Transit、Nakshatra、Yoga、出生时间矫正、PDF星盘、读取PDF、分析PDF星盘、现代解读、误判纠错、Varga分盘、综合分析、过境分析、合盘、婚姻匹配、年运盘、Prashna、Argala、Jaimini、Shadbala、Ashtakavarga、HTML报告、深度解盘。
---
@@ -10,7 +10,7 @@ description: 印度占星(Jyotish)专业解盘与推运系统。核心能力
> **严格路由**`references/strict-workflow-router.md`(⭐涉及事业/婚恋/财务/应期/技法验证时必须优先读取)
> **覆盖矩阵**`references/technique-capability-matrix.md`(⭐判断技法 covered/partial/missing 时必须参考)
> **机器注册表**`references/technique_registry.json` + `scripts/audit_capabilities.py`(⭐用于自动审计与CI门禁)
> **版本**v6.0.36-yoga-context-fixtures | **详细变更**`CHANGELOG.md`
> **版本**v6.0.37-context-aware-yoga-precision | **详细变更**`CHANGELOG.md`
---
@@ -301,9 +301,9 @@ $PYTHON $SCRIPT <子命令> [参数]
---
**版本**v6.0.36-yoga-context-fixtures
**版本**v6.0.37-context-aware-yoga-precision
**创建日期**2026-04-20
**最后更新**2026-06-05v6.0.36 将标准 Yoga 验证数据升级为 schema 2.0,加入 D1/D9/Panchanga/Gulika/Maandi 扩展上下文,并让 YogaEngine/validate_logic_v2 接收 context;当前 473 条 JSON 规则、379 条启用规则,D1/Rasi 逻辑验证 F1=76.08%,报告以 `references/validation_logic_report.json` 为准)
**最后更新**2026-06-05v6.0.37 基于 D9/Panchanga/Gulika/Maandi context 修复高影响 Yoga,拆分 PyJHora `kaahala_yoga``kahala_yoga`,新增 Kapata 高置信规则并修正多个 B.V. Raman 条件;当前 475 条 JSON 规则、381 条启用规则,D1/Rasi 逻辑验证 F1=78.44%,报告以 `references/validation_logic_report.json` 为准)
---
+51 -11
View File
@@ -1,7 +1,7 @@
{
"schema_version": "1.0",
"description": "Yoga 规则库 - 数据驱动架构 v1.0",
"total_rules": 473,
"total_rules": 475,
"rules": [
{
"id": "raja_yoga",
@@ -9081,7 +9081,7 @@
"category": "health",
"logic": {
"type": "custom",
"expr": "\n# PyJHora BVR-11 exact D1-safe logic: (a) Lagna lord in Lagna joined by\n# dusthana lord; (b) weak Lagna lord joined by any kendra/trine lord.\nasc_lord = lord(1)\ndusthana_lords = [lord(h) for h in [6, 8, 12]]\nquad_trine_lords = [lord(h) for h in [1, 4, 5, 7, 9, 10]]\nif asc_lord not in ctx.planets:\n False\nelse:\n condition_a = house_of(asc_lord) == 1 and any(dl in ctx.planets and same_house(asc_lord, dl) for dl in dusthana_lords)\n condition_b = (not strong(asc_lord)) and any(qtl in ctx.planets and same_house(asc_lord, qtl) for qtl in quad_trine_lords)\n condition_a or condition_b\n",
"expr": "\n# PyJHora BVR-11: (a) Lagna lord in Lagna joined by dusthana lord;\n# (b) weak Lagna lord joined by any kendra/trine lord.\nasc_lord = lord(1)\ndusthana_lords = [lord(h) for h in [6, 8, 12]]\nquad_trine_lords = [lord(h) for h in [1, 4, 5, 7, 9, 10]]\nif asc_lord not in ctx.planets:\n False\nelse:\n condition_a = house_of(asc_lord) == 1 and any(dl in ctx.planets and same_house(asc_lord, dl) for dl in dusthana_lords)\n weak_by_sign = not (exalted(asc_lord) or own(asc_lord) or moola(asc_lord))\n condition_b = weak_by_sign and any(qtl in ctx.planets and same_house(asc_lord, qtl) for qtl in quad_trine_lords)\n condition_a or condition_b\n",
"combo_template": "上升主在Lagna合凶宫主,或弱上升主合角/三方主"
},
"effects": [
@@ -9301,8 +9301,8 @@
"category": "durbhaga",
"logic": {
"type": "custom",
"expr": "\n# Moon hemmed, associated, or aspected by evil planets\nif 'Moon' not in ctx.planets:\n False\nelse:\n moon_h = ctx.planets['Moon'].get('house')\n evil = {'Mars', 'Saturn', 'Rahu', 'Ketu', 'Sun'}\n h_prev = ((moon_h - 1 + 10) % 12) + 1\n h_next = ((moon_h - 1 + 1) % 12) + 1\n prev_evil = any(ctx.planets[p].get('house') == h_prev for p in evil if p in ctx.planets)\n next_evil = any(ctx.planets[p].get('house') == h_next for p in evil if p in ctx.planets)\n hemmed = prev_evil and next_evil\n associated = any(same_house('Moon', p) for p in evil if p in ctx.planets)\n aspected = any(aspect(p, 'Moon') for p in evil if p in ctx.planets)\n hemmed or associated or aspected\n",
"combo_template": "月亮受凶星影响"
"expr": "\n# PyJHora/BVR 198-199.\nif 'Moon' not in ctx.planets:\n False\nelse:\n evil = set(MALEFICS)\n moon_h = house_of('Moon')\n prev_h = ((moon_h - 2) % 12) + 1 if moon_h else None\n next_h = (moon_h % 12) + 1 if moon_h else None\n hemmed = bool(prev_h and next_h and house_has_malefic(prev_h) and house_has_malefic(next_h))\n associated = any(same_house('Moon', p) for p in evil if p in ctx.planets)\n aspected = any(aspect(p, 'Moon') for p in evil if p in ctx.planets)\n y198 = hemmed or associated or aspected\n l4 = lord(4)\n nav_lord = navamsa_dispositor(l4) if l4 else None\n final_planet = navamsa_dispositor(nav_lord) if nav_lord else None\n y199 = final_planet in ctx.planets and house_of(final_planet) in [6, 8, 12]\n y198 or y199\n",
"combo_template": "月亮受凶星夹制/合相/相位,或4宫主Navamsa链最终落6/8/12"
},
"effects": [
"母亲健康不佳",
@@ -9322,8 +9322,8 @@
"category": "auspicious",
"logic": {
"type": "custom",
"expr": "\n# 4th house occupied by benefic or exalted planet\nbenefics = {'Jupiter', 'Venus', 'Mercury', 'Moon'}\nplanets_in_4 = [p for p, info in ctx.planets.items() if info.get('house') == 4]\nhas_benefic = any(p in benefics for p in planets_in_4)\nhas_exalted = any(exal(p) for p in planets_in_4)\nhas_benefic or has_exalted\n",
"combo_template": "第4宫有吉星或旺星"
"expr": "\n# PyJHora/BVR 205-206 Nishkapata Yoga.\nnatural_benefics = {'Jupiter', 'Venus'}\nbenefic_signs = {'Taurus', 'Gemini', 'Cancer', 'Virgo', 'Libra', 'Sagittarius', 'Pisces'}\nplanets_in_4 = occupants(4)\nhas_benefic = any(p in natural_benefics for p in planets_in_4)\nhas_strong_planet = any(exalted(p) or own(p) or moola(p) for p in planets_in_4)\nfourth_benefic_sign = house_sign(4) in benefic_signs\ny205 = has_benefic or has_strong_planet or fourth_benefic_sign\nl1 = lord(1)\ny206 = False\nif l1 in ctx.planets and house_of(l1) == 4:\n l1_with_benefic = any(p != l1 and same_house(l1, p) for p in natural_benefics if p in ctx.planets)\n l1_aspected_benefic = any(aspect(p, l1) for p in natural_benefics if p in ctx.planets and p != l1)\n y206 = l1_with_benefic or l1_aspected_benefic\ny205 or y206\n",
"combo_template": "第4宫有天然吉星/强星/吉性星座,或上升主入4宫且受吉星影响"
},
"effects": [
"心地善良",
@@ -9343,8 +9343,8 @@
"category": "putra",
"logic": {
"type": "custom",
"expr": "\n# 5th lord strong and associated with benefics\nlord_5 = lord(5)\nif lord_5 not in ctx.planets:\n False\nelse:\n benefics = {'Jupiter', 'Venus', 'Mercury', 'Moon'}\n in_kendra_trikona = ctx.planets[lord_5].get('house') in [1, 4, 5, 7, 9, 10]\n associated = any(same_house(lord_5, b) for b in benefics if b in ctx.planets)\n in_kendra_trikona and associated\n",
"combo_template": "5宫主强且与吉星有关联"
"expr": "\n# PyJHora/BVR 220-221 Bahu Puthra Yoga.\n# 220: Rahu in 5th and Rahu is not in Saturn navamsa.\ny220 = house_of('Rahu') == 5 and d9_sign_of('Rahu') not in {'Capricorn', 'Aquarius'}\n# 221: lord of Navamsa occupied by a planet associated with 7th lord is in 1st/2nd/5th in Rasi.\nl7 = lord(7)\ny221 = False\nif l7 in ctx.planets:\n associated_planets = [p for p in ctx.planets if p != l7 and associated(p, l7)]\n for p in associated_planets:\n disp = navamsa_dispositor(p)\n if disp in ctx.planets and house_of(disp) in [1, 2, 5]:\n y221 = True\n break\ny220 or y221\n",
"combo_template": "Rahu在5且非土星Navamsa,或7宫主关联行星的Navamsa主落1/2/5"
},
"effects": [
"子女众多",
@@ -11627,8 +11627,8 @@
"category": "special",
"logic": {
"type": "custom",
"expr": "\n# PyJHora 291/292/294 D1-safe variants. Variant 293 needs tithi/waning Moon\n# and remains unavailable from static planet_positions_60 data.\njm = 'Jupiter' in ctx.planets and 'Mars' in ctx.planets and house_of('Jupiter') == 1 and house_of('Mars') == 7\nsm = 'Saturn' in ctx.planets and 'Mars' in ctx.planets and house_of('Saturn') == 1 and house_of('Mars') in [5, 7, 9]\nmoon_mercury = False\nif 'Moon' in ctx.planets and 'Mercury' in ctx.planets:\n moon_mercury = kendra(house_of('Moon')) and kendra(house_of('Mercury')) and any(p != 'Moon' and aspect(p, 'Moon') for p in ctx.planets) and any(p != 'Mercury' and aspect(p, 'Mercury') for p in ctx.planets)\njm or sm or moon_mercury\n",
"combo_template": "木火1/7、土升火5/7/9,或月水角宫并受相位"
"expr": "\n# PyJHora/BVR 291-294 plus BVR variations.\njm = 'Jupiter' in ctx.planets and 'Mars' in ctx.planets and house_of('Jupiter') == 1 and house_of('Mars') == 7\nsm = 'Saturn' in ctx.planets and 'Mars' in ctx.planets and house_of('Saturn') == 1 and house_of('Mars') in [5, 7, 9]\nsaturn_waning = 'Saturn' in ctx.planets and house_of('Saturn') == 12 and is_waning_moon()\nmoon_mercury = False\nif 'Moon' in ctx.planets and 'Mercury' in ctx.planets:\n moon_mercury = kendra(house_of('Moon')) and kendra(house_of('Mercury')) and any(p != 'Moon' and aspect(p, 'Moon') for p in ctx.planets) and any(p != 'Mercury' and aspect(p, 'Mercury') for p in ctx.planets)\nl6 = lord(6)\nvariation_1 = house_of('Rahu') == 6 and aspect('Ketu', 'Rahu')\nvariation_2 = l6 in ctx.planets and same_house(l6, 'Mars')\nvariation_3 = 'Mercury' in ctx.planets and same_house('Mercury', 'Mars') and same_house('Mercury', 'Sun')\nd9_l6 = d9_lord_of_house(6)\nvariation_4 = d9_house_of('Mercury') == 6 and d9_house_of('Rahu') == 6 and d9_l6 in ctx.planets and same_house(d9_l6, 'Mars')\njm or sm or saturn_waning or moon_mercury or variation_1 or variation_2 or variation_3 or variation_4\n",
"combo_template": "木火1/7、土升火5/7/9、土12宫逢亏月,或BVR精神扰动变体"
},
"effects": [
"Mental disturbance, confusion, erratic behavior"
@@ -13765,7 +13765,47 @@
"strength": "中",
"enabled": true,
"source_reference": "BVR-36: Malika Yoga starting from 5th house"
},
{
"id": "bvr_kaahala_yoga",
"name": "Kaahala Yoga",
"name_cn": "勇气格局(Kaahala",
"source": "B.V. Raman / PyJHora",
"bvr_id": "BVR-15-alt",
"category": "auspicious",
"logic": {
"type": "custom",
"expr": "\n# PyJHora kaahala_yoga: 4th lord and Jupiter in mutual quadrants; Lagna lord strong.\nl1 = lord(1)\nl4 = lord(4)\nif l1 not in ctx.planets or l4 not in ctx.planets or 'Jupiter' not in ctx.planets:\n False\nelse:\n l1_strong = strong(l1)\n mutual_kendra = offset(house_of(l4), house_of('Jupiter')) in [0, 3, 6, 9]\n l1_strong and mutual_kendra\n",
"combo_template": "4宫主与木星互为Kendra,且上升主有力"
},
"effects": [
"Courage, initiative, status and practical drive"
],
"strength": "moderate",
"enabled": true,
"draft": false,
"evidence_note": "v6.0.37: split from kahala_yoga; aligned to PyJHora kaahala_yoga() definition."
},
{
"id": "bvr_kapata_yoga",
"name": "Kapata Yoga",
"name_cn": "欺诈瑜伽",
"source": "B.V. Raman / PyJHora",
"bvr_id": "BVR-202-204",
"category": "durbhaga",
"logic": {
"type": "custom",
"expr": "\n# PyJHora/BVR 202-204 Kapata Yoga.\nevil = set(MALEFICS)\nl4 = lord(4)\nl10 = lord(10)\nmaandi_h = maandi_house()\nif l4 not in ctx.planets or l10 not in ctx.planets:\n False\nelse:\n # 202: 4th joined by a malefic; 4th lord joined/aspected/hemmed by malefics.\n malefic_in_4 = any(house_of(m) == 4 for m in evil if m in ctx.planets)\n l4_h = house_of(l4)\n l4_joined_malefic = any(m != l4 and same_house(l4, m) for m in evil if m in ctx.planets)\n l4_aspected_malefic = any(aspect(m, l4) for m in evil if m in ctx.planets)\n prev_h = ((l4_h - 2) % 12) + 1 if l4_h else None\n next_h = (l4_h % 12) + 1 if l4_h else None\n l4_hemmed = bool(prev_h and next_h and house_has_malefic(prev_h) and house_has_malefic(next_h))\n yoga202 = malefic_in_4 and (l4_joined_malefic or l4_aspected_malefic or l4_hemmed)\n # 203: Saturn, Mars, Rahu in 4th; malefic 10th lord aspected by malefics.\n yoga203 = all(house_of(p) == 4 for p in ['Saturn', 'Mars', 'Rahu']) and l10 in evil and any(aspect(m, l10) for m in evil if m in ctx.planets and m != l10)\n # 204: 4th lord joins Saturn, Mandi, Rahu and is aspected by malefics.\n yoga204 = bool(maandi_h and house_of(l4) == house_of('Saturn') == house_of('Rahu') == maandi_h and any(aspect(m, l4) for m in evil if m in ctx.planets and m != l4))\n yoga202 or yoga203 or yoga204\n",
"combo_template": "4宫/4宫主受凶星、Rahu、Maandi/Mandi 等严重影响"
},
"effects": [
"Deception, distrust, household/family complications"
],
"strength": "moderate",
"enabled": true,
"draft": false,
"evidence_note": "v6.0.37: high-confidence PyJHora Kapata 202-204 implementation."
}
],
"total_enabled_rules": 379
"total_enabled_rules": 381
}
+9 -5
View File
@@ -78,9 +78,10 @@ PYJHORA_VARIANTS = {
# Vahana variants
'vahana_yoga_209': 'bvr_vahana_yoga',
'vahana_yoga_210': 'bvr_vahana_yoga',
# Kapata -> Nishkapata
'kapata_yoga_202': 'bvr_nishkapata_precise',
'kapata_yoga_204': 'bvr_nishkapata_precise',
# Kapata family
'kapata_yoga_202': 'bvr_kapata_yoga',
'kapata_yoga_203': 'bvr_kapata_yoga',
'kapata_yoga_204': 'bvr_kapata_yoga',
'pisacha_grastha_yoga': 'bvr_pisacha_grastha_yoga',
'kaalanirdesat_puthranaasa_yoga_229': 'bvr_kaalanirdesat_puthranaasa_yoga',
'dwadasa_sahodara_yoga': 'bvr_dwadasa_sahodara_yoga',
@@ -95,8 +96,11 @@ PYJHORA_VARIANTS = {
'sula_yoga': 'bvr_soola_yoga',
# Kedara
'kedara_yoga': 'bvr_kedaara_yoga',
# Kaahala/Kahala
'kaahala_yoga': 'kahala_yoga',
# Kaahala/Kahala are distinct PyJHora functions:
# - kaahala_yoga: L4 and Jupiter in mutual quadrants + strong L1
# - kahala_yoga: L4 and L9 in mutual kendras + strong L1
'kaahala_yoga': 'bvr_kaahala_yoga',
'kahala_yoga': 'kahala_yoga',
# Direct mappings for common yogas
'vosi_yoga': 'bvr_vosi_precise',
'sunaphaa_yoga': 'bvr_sunaphaa_precise',
+11 -2
View File
@@ -1327,12 +1327,21 @@ class YogaEngine:
if ctype == "malefic_in_lagna_or_gulika_in_trine_OR_gulika_with_kendra_trine_lord_OR_l1_with_rahu_sat_ket":
l1 = house_lord(1)
gh = ctx.upagraha_house("gulika")
# PyJHora: condition 3 stands alone; condition 1 requires BOTH malefic in Lagna AND Gulika in trine.
l1_with_rahu_sat_ket = l1 and any(ctx.house_of(l1) == ctx.house_of(p) for p in ["Rahu", "Saturn", "Ketu"] if p in ctx.planets and p != l1)
if l1_with_rahu_sat_ket:
return success()
if gh is None:
return []
malefic_in_lagna = any(ctx.house_of(p) == 1 for p in MALEFICS if p in ctx.planets)
gulika_in_trine = gh in (1, 5, 9)
if malefic_in_lagna and gulika_in_trine:
return success()
kendra_trine_lords = {house_lord(h) for h in (1, 4, 5, 7, 9, 10)}
gulika_with_kendra_trine_lord = gh is not None and any(
gulika_with_kendra_trine_lord = any(
lord and lord in ctx.planets and ctx.house_of(lord) == gh for lord in kendra_trine_lords
)
return success() if any(ctx.house_of(p) == 1 for p in MALEFICS if p in ctx.planets) or gulika_in_trine or gulika_with_kendra_trine_lord or (l1 and any(related(l1, p) for p in ["Rahu", "Saturn", "Ketu"] if p in ctx.planets)) else []
return success() if gulika_with_kendra_trine_lord else []
if ctype == "same_lord_for_1st_and_4th":
return success() if house_lord(1) == house_lord(4) else []
if ctype == "1st_and_4th_lords_are_natural_or_temporal_friends":