v6.1.7: Push Yoga validation F1 above 95 percent

- Inject D9/D60/Panchanga context into yoga and full-reading flows
- Add --context-json support for explicit YogaContext payloads
- Tighten Sankha Yoga movable-sign branch to require distinct L1/L10 lords
- Regenerate validation_logic_report: FP 36, FN 67, F1 95.02%
This commit is contained in:
732642856
2026-06-08 10:27:57 +08:00
parent 57e426515d
commit 3c64e4576f
5 changed files with 269 additions and 152 deletions
+24
View File
@@ -1,5 +1,29 @@
# 印度占星 Skill 更新日志
## v6.1.72026-06-08)—— Yoga F1 首次突破 95% 与 Sankha 误报收紧
> **目标**:继续 Yoga 精度冲刺,但只发布源码语义明确、能减少误报且不牺牲召回的规则修复。
### 关键改进
- `full-reading``yoga` 子命令补齐 D9/D60/Panchanga 上下文注入:出生信息路径会从 `varga-full` 构造 YogaContext,让依赖 Navamsa / Shashtiamsa 的 Yoga 规则在主链路中实际生效。
- `yoga --context-json` 支持显式传入包含 `d9``d60``panchanga` 的上下文,便于验证脚本和外部 API 复用同一 Yoga 引擎。
- 收紧 `sankha_yoga` 第二分支:`L1 and L10 together in a movable sign` 必须是两个不同宫主实际同宫;当 `L1 == L10`(双子/处女/射手/双鱼上升常见)时,不再把共享宫主误判为“1宫主与10宫主同宫”。
- 修复效果:移除 Stephen Hawking、Max Planck、Cristiano Ronaldo 三个 Sankha Yoga false positive 中的 2 个(整体 FP 38→36),FN 保持 67 不变。
- 保持“准确率优先”:未为了短期召回扩大 Thrikaala / Dharidhra / Nishkapata 等高风险规则。
### 验证
```bash
python3 -m py_compile scripts/yoga_engine.py scripts/validate_logic_v2.py
python3 scripts/validate_logic_v2.py
python3 scripts/jyotish_engine.py full-reading --year 1990 --month 6 --day 15 --hour 10 --minute 30 --lat 39.9 --lon 116.4 --tz 8
```
- 60 张测试图,82 条可对比规则
- FP=36FN=67
- Precision=96.46%Recall=93.61%F1=95.02%
- full-reading smoke`summary.status=complete``errors=[]``modules=47``modules.yoga.context_layers=['d60','d9','panchanga']`
---
## v6.1.62026-06-07)—— full-reading 五系统推运收敛与接口修复
> **目标**:继续“合并隐藏模块→打通引擎→验证可用”的优化路线,将 Ashtottari / Yogini / Kalachakra 从独立模块进一步接入 `full-reading` 主链路。
+12 -12
View File
@@ -1,6 +1,6 @@
---
name: jyotish-vedic-astrology
version: 6.1.6
version: 6.1.7
description: 印度占星(Jyotish)专业解盘与推运系统。核心能力:PDF星盘输入→严谨解盘→精确推运应期输出。触发词:印度占星、吠陀占星、Jyotish、解盘、推运、星盘分析、Dasha、Transit、Nakshatra、Yoga、出生时间矫正、PDF星盘、读取PDF、分析PDF星盘、现代解读、误判纠错、Varga分盘、综合分析、过境分析、合盘、婚姻匹配、年运盘、Prashna、Argala、Jaimini、Shadbala、Ashtakavarga、HTML报告、深度解盘。
---
@@ -10,19 +10,19 @@ 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.1.6 | **详细变更**`CHANGELOG.md`
> **版本**v6.1.7 | **详细变更**`CHANGELOG.md`
## Yoga 逻辑验证指标
| 指标 | v6.0.45(旧基线) | v6.0.47(当前) |
|---|---|---|
| Precision | 83.26% | **92.50%** |
| Recall | 91.52% | **91.71%** |
| **F1 Score** | 87.19% | **92.10%** |
| FP | 193 | **78** |
| FN | 89 | **87** |
| 指标 | v6.0.45(旧基线) | v6.1.7(当前) |
|---|---:|---:|
| Precision | 83.26% | **96.46%** |
| Recall | 91.52% | **93.61%** |
| **F1 Score** | 87.19% | **95.02%** |
| FP | 193 | **36** |
| FN | 89 | **67** |
> v6.0.46: 修复 6 条规则FP 193→78)。v6.0.47: 修复 dharidhra bidirectional associated() → one-way aspect()FP 未进一步下降因残余根因在 lord 解析(co-lord vs 古典宫主)和 rasi/house 差异
> v6.1.7 当前真实指标以 `references/validation_logic_report.json` 为准:60张测试图、82条可对比规则FP=36、FN=67、F1=95.02%。关键提升来自 aspect 方向修正、D9/D60 上下文注入、Thrikaala Gnana 收紧、Dharidhra/Navamsa 方法恢复、五系统 Dasha Convergence 接入、full-reading YogaContext 桥接,以及 Sankha Yoga 共享宫主误报收紧
---
@@ -313,9 +313,9 @@ $PYTHON $SCRIPT <子命令> [参数]
---
**版本**v6.1.6-full-reading-five-dasha-convergence
**版本**v6.1.7-yoga-f1-95-sankha-tightening
**创建日期**2026-04-20
**最后更新**2026-06-07v6.1.6 将 Ashtottari/Yogini/Kalachakra 接入 full-readingDasa Convergence 从三系统升级为 Vimshottari + Chara + Yogini + Ashtottari + Kalachakra 五系统;修复 Pancha Pakshi/RTN 接口与替代 Dasha current 周期循环。Yoga 当前以 `references/validation_logic_report.json` 为准。)
**最后更新**2026-06-08v6.1.7 在 v6.1.6 五系统推运收敛基础上,收紧 Sankha Yoga 第二分支:L1/L10 共享宫主不再视为两宫主同宫,Yoga F1 首次达到 95.02%。Yoga 当前以 `references/validation_logic_report.json` 为准。)
---
+127 -137
View File
@@ -2,7 +2,7 @@
"summary": {
"charts_tested": 60,
"comparable_rules": 82,
"skill_total": 1020,
"skill_total": 1018,
"pyjhora_total": 1049,
"unmapped_pyjhora": 718,
"missing_mappings": {
@@ -99,27 +99,22 @@
]
},
"agreements": 982,
"false_positives": 38,
"false_positives": 36,
"false_negatives": 67,
"precision": 0.9627,
"precision": 0.9646,
"recall": 0.9361,
"f1": 0.9493
"f1": 0.9502
},
"false_positives": [
{
"chart": "Albert Einstein",
"rule_id": "bvr_dharidhra_11_precise",
"rule_name": "Dharidhra Yoga"
},
{
"chart": "Albert Einstein",
"rule_id": "bvr_bhaga_chumbana_yoga",
"rule_name": "Bhaga Chumbana Yoga"
},
{
"chart": "Nikola Tesla",
"rule_id": "bvr_bhratruvriddhi_precise",
"rule_name": "Bhratruvriddhi Yoga"
"chart": "Albert Einstein",
"rule_id": "bvr_dharidhra_11_precise",
"rule_name": "Dharidhra Yoga"
},
{
"chart": "Nikola Tesla",
@@ -127,9 +122,9 @@
"rule_name": "Krisanga Yoga"
},
{
"chart": "Stephen Hawking",
"rule_id": "sankha_yoga",
"rule_name": "Sankha Yoga"
"chart": "Nikola Tesla",
"rule_id": "bvr_bhratruvriddhi_precise",
"rule_name": "Bhratruvriddhi Yoga"
},
{
"chart": "Charles Darwin",
@@ -153,13 +148,13 @@
},
{
"chart": "Richard Feynman",
"rule_id": "bvr_018_ubhayachara_precise",
"rule_name": "Ubhayachara Yoga"
"rule_id": "bvr_nishkapata_precise",
"rule_name": "Nishkapata Yoga"
},
{
"chart": "Richard Feynman",
"rule_id": "bvr_nishkapata_precise",
"rule_name": "Nishkapata Yoga"
"rule_id": "bvr_018_ubhayachara_precise",
"rule_name": "Ubhayachara Yoga"
},
{
"chart": "Mahatma Gandhi",
@@ -188,13 +183,13 @@
},
{
"chart": "Barack Obama",
"rule_id": "bvr_yukthi_samanwithavagmi_yoga",
"rule_name": "Yukthi Samanwithavagmi Yoga"
"rule_id": "bvr_bandhubhisthyaktha_precise",
"rule_name": "Bandhubhisthyaktha Yoga"
},
{
"chart": "Barack Obama",
"rule_id": "bvr_bandhubhisthyaktha_precise",
"rule_name": "Bandhubhisthyaktha Yoga"
"rule_id": "bvr_yukthi_samanwithavagmi_yoga",
"rule_name": "Yukthi Samanwithavagmi Yoga"
},
{
"chart": "Pablo Picasso",
@@ -216,20 +211,15 @@
"rule_id": "adhi_yoga",
"rule_name": "Adhi Yoga"
},
{
"chart": "MS Dhoni",
"rule_id": "bvr_karma_malika_precise",
"rule_name": "Karma Malika Yoga"
},
{
"chart": "MS Dhoni",
"rule_id": "bvr_mathibhramana_yoga",
"rule_name": "Mathibhramana Yoga"
},
{
"chart": "Lionel Messi",
"rule_id": "brahma_yoga",
"rule_name": "Brahma Yoga"
"chart": "MS Dhoni",
"rule_id": "bvr_karma_malika_precise",
"rule_name": "Karma Malika Yoga"
},
{
"chart": "Lionel Messi",
@@ -237,14 +227,14 @@
"rule_name": "Vosi Yoga"
},
{
"chart": "Cristiano Ronaldo",
"rule_id": "bvr_dwadasa_sahodara_yoga",
"rule_name": "Dwadasa Sahodara Yoga"
"chart": "Lionel Messi",
"rule_id": "brahma_yoga",
"rule_name": "Brahma Yoga"
},
{
"chart": "Cristiano Ronaldo",
"rule_id": "sankha_yoga",
"rule_name": "Sankha Yoga"
"rule_id": "bvr_dwadasa_sahodara_yoga",
"rule_name": "Dwadasa Sahodara Yoga"
},
{
"chart": "Pele",
@@ -273,13 +263,13 @@
},
{
"chart": "Princess Diana",
"rule_id": "bvr_ayatna_griha_prapta_yoga",
"rule_name": "Ayatna Griha Prapta Yoga"
"rule_id": "bvr_bandhu_pujya_yoga",
"rule_name": "Bandhu Pujya Yoga"
},
{
"chart": "Princess Diana",
"rule_id": "bvr_bandhu_pujya_yoga",
"rule_name": "Bandhu Pujya Yoga"
"rule_id": "bvr_ayatna_griha_prapta_yoga",
"rule_name": "Ayatna Griha Prapta Yoga"
},
{
"chart": "J. Krishnamurti",
@@ -288,13 +278,13 @@
},
{
"chart": "Ramana Maharshi",
"rule_id": "bvr_matru_sneha_yoga",
"rule_name": "Matru Sneha Yoga"
"rule_id": "bvr_utthama_graha_yoga",
"rule_name": "Utthama Griha Yoga"
},
{
"chart": "Ramana Maharshi",
"rule_id": "bvr_utthama_graha_yoga",
"rule_name": "Utthama Griha Yoga"
"rule_id": "bvr_matru_sneha_yoga",
"rule_name": "Matru Sneha Yoga"
}
],
"false_negatives": [
@@ -307,14 +297,6 @@
"bahu_puthra_yoga_221"
]
},
{
"chart": "Marie Curie",
"rule_id": "parvata_yoga",
"rule_name": "Parvata Yoga",
"pyjhora_names": [
"parvata_yoga"
]
},
{
"chart": "Marie Curie",
"rule_id": "bvr_grihanasa_yoga",
@@ -332,6 +314,14 @@
"thrikaala_gnana_yoga"
]
},
{
"chart": "Marie Curie",
"rule_id": "parvata_yoga",
"rule_name": "Parvata Yoga",
"pyjhora_names": [
"parvata_yoga"
]
},
{
"chart": "Nikola Tesla",
"rule_id": "bvr_thrikaala_gnana_yoga",
@@ -370,14 +360,6 @@
"parannabhojana_yoga"
]
},
{
"chart": "Mahatma Gandhi",
"rule_id": "bvr_pisacha_grastha_yoga",
"rule_name": "Pisacha Grastha Yoga",
"pyjhora_names": [
"pisacha_grastha_yoga"
]
},
{
"chart": "Mahatma Gandhi",
"rule_id": "brahma_yoga",
@@ -395,6 +377,14 @@
"matrudeerghayur_yoga_197"
]
},
{
"chart": "Mahatma Gandhi",
"rule_id": "bvr_pisacha_grastha_yoga",
"rule_name": "Pisacha Grastha Yoga",
"pyjhora_names": [
"pisacha_grastha_yoga"
]
},
{
"chart": "Narendra Modi",
"rule_id": "bvr_nishkapata_precise",
@@ -412,14 +402,6 @@
"utthama_graha_yoga"
]
},
{
"chart": "Indira Gandhi",
"rule_id": "bvr_sumukha_precise",
"rule_name": "Sumukha Yoga",
"pyjhora_names": [
"sumukha_yoga"
]
},
{
"chart": "Indira Gandhi",
"rule_id": "bvr_nishkapata_precise",
@@ -429,6 +411,14 @@
"nishkapata_yoga_206"
]
},
{
"chart": "Indira Gandhi",
"rule_id": "bvr_sumukha_precise",
"rule_name": "Sumukha Yoga",
"pyjhora_names": [
"sumukha_yoga"
]
},
{
"chart": "Nelson Mandela",
"rule_id": "bvr_apakeerthi_yoga",
@@ -475,6 +465,15 @@
"matru_satrutwa_yoga"
]
},
{
"chart": "Margaret Thatcher",
"rule_id": "bvr_nishkapata_precise",
"rule_name": "Nishkapata Yoga",
"pyjhora_names": [
"nishkapata_yoga_205",
"nishkapata_yoga_206"
]
},
{
"chart": "Margaret Thatcher",
"rule_id": "bvr_dharidhra_11_precise",
@@ -489,15 +488,6 @@
"dharidhra_yoga_152"
]
},
{
"chart": "Margaret Thatcher",
"rule_id": "bvr_nishkapata_precise",
"rule_name": "Nishkapata Yoga",
"pyjhora_names": [
"nishkapata_yoga_205",
"nishkapata_yoga_206"
]
},
{
"chart": "Rabindranath Tagore",
"rule_id": "bvr_kapata_yoga",
@@ -534,6 +524,14 @@
"kapata_yoga_204"
]
},
{
"chart": "Wolfgang Mozart",
"rule_id": "bvr_theevrabuddhi_yoga",
"rule_name": "Theevrabuddhi Yoga",
"pyjhora_names": [
"theevrabuddhi_yoga"
]
},
{
"chart": "Wolfgang Mozart",
"rule_id": "bvr_raja_bhanga_yoga",
@@ -543,14 +541,6 @@
"raja_bhanga_yoga_299"
]
},
{
"chart": "Wolfgang Mozart",
"rule_id": "bvr_theevrabuddhi_yoga",
"rule_name": "Theevrabuddhi Yoga",
"pyjhora_names": [
"theevrabuddhi_yoga"
]
},
{
"chart": "Wolfgang Mozart",
"rule_id": "bvr_pittharoga_yoga",
@@ -645,20 +635,6 @@
"bahu_sthree_yoga"
]
},
{
"chart": "Azim Premji",
"rule_id": "bvr_dharidhra_11_precise",
"rule_name": "Dharidhra Yoga",
"pyjhora_names": [
"dharidhra_yoga_144",
"dharidhra_yoga_147",
"dharidhra_yoga_148",
"dharidhra_yoga_149",
"dharidhra_yoga_150",
"dharidhra_yoga_151",
"dharidhra_yoga_152"
]
},
{
"chart": "Azim Premji",
"rule_id": "bvr_ayatna_griha_prapta_yoga",
@@ -686,6 +662,20 @@
"nishkapata_yoga_206"
]
},
{
"chart": "Azim Premji",
"rule_id": "bvr_dharidhra_11_precise",
"rule_name": "Dharidhra Yoga",
"pyjhora_names": [
"dharidhra_yoga_144",
"dharidhra_yoga_147",
"dharidhra_yoga_148",
"dharidhra_yoga_149",
"dharidhra_yoga_150",
"dharidhra_yoga_151",
"dharidhra_yoga_152"
]
},
{
"chart": "Muhammad Ali",
"rule_id": "pushkala_yoga",
@@ -713,11 +703,11 @@
},
{
"chart": "Lionel Messi",
"rule_id": "bvr_matrunasa_precise",
"rule_name": "Matrunasa Yoga",
"rule_id": "bvr_matrudeerghayur_yoga",
"rule_name": "Matrudeerghayur Yoga",
"pyjhora_names": [
"matrunasa_yoga_198",
"matrunasa_yoga_199"
"matrudeerghayur_yoga_196",
"matrudeerghayur_yoga_197"
]
},
{
@@ -730,6 +720,15 @@
"kapata_yoga_204"
]
},
{
"chart": "Lionel Messi",
"rule_id": "bvr_matrunasa_precise",
"rule_name": "Matrunasa Yoga",
"pyjhora_names": [
"matrunasa_yoga_198",
"matrunasa_yoga_199"
]
},
{
"chart": "Lionel Messi",
"rule_id": "bvr_dharidhra_11_precise",
@@ -744,15 +743,6 @@
"dharidhra_yoga_152"
]
},
{
"chart": "Lionel Messi",
"rule_id": "bvr_matrudeerghayur_yoga",
"rule_name": "Matrudeerghayur Yoga",
"pyjhora_names": [
"matrudeerghayur_yoga_196",
"matrudeerghayur_yoga_197"
]
},
{
"chart": "Cristiano Ronaldo",
"rule_id": "bvr_matru_sneha_yoga",
@@ -810,6 +800,14 @@
"matrudeerghayur_yoga_197"
]
},
{
"chart": "Dalai Lama",
"rule_id": "bvr_utthama_graha_yoga",
"rule_name": "Utthama Griha Yoga",
"pyjhora_names": [
"utthama_graha_yoga"
]
},
{
"chart": "Dalai Lama",
"rule_id": "brahma_yoga",
@@ -819,11 +817,11 @@
]
},
{
"chart": "Dalai Lama",
"rule_id": "bvr_utthama_graha_yoga",
"rule_name": "Utthama Griha Yoga",
"chart": "Pope Francis",
"rule_id": "bvr_bhratruvriddhi_precise",
"rule_name": "Bhratruvriddhi Yoga",
"pyjhora_names": [
"utthama_graha_yoga"
"bhratruvriddhi_yoga"
]
},
{
@@ -836,11 +834,12 @@
]
},
{
"chart": "Pope Francis",
"rule_id": "bvr_bhratruvriddhi_precise",
"rule_name": "Bhratruvriddhi Yoga",
"chart": "J. Krishnamurti",
"rule_id": "bvr_grihanasa_yoga",
"rule_name": "Grihanasa Yoga",
"pyjhora_names": [
"bhratruvriddhi_yoga"
"grihanasa_yoga_191",
"grihanasa_yoga_192"
]
},
{
@@ -851,15 +850,6 @@
"vanchana_chora_bheethi_yoga"
]
},
{
"chart": "J. Krishnamurti",
"rule_id": "bvr_grihanasa_yoga",
"rule_name": "Grihanasa Yoga",
"pyjhora_names": [
"grihanasa_yoga_191",
"grihanasa_yoga_192"
]
},
{
"chart": "Ramana Maharshi",
"rule_id": "bvr_pittharoga_yoga",
@@ -868,6 +858,14 @@
"pittharoga_yoga"
]
},
{
"chart": "Paramahansa Yogananda",
"rule_id": "bvr_bandhubhisthyaktha_precise",
"rule_name": "Bandhubhisthyaktha Yoga",
"pyjhora_names": [
"bandhubhisthyaktha_yoga"
]
},
{
"chart": "Paramahansa Yogananda",
"rule_id": "bvr_matrunasa_precise",
@@ -877,14 +875,6 @@
"matrunasa_yoga_199"
]
},
{
"chart": "Paramahansa Yogananda",
"rule_id": "bvr_bandhubhisthyaktha_precise",
"rule_name": "Bandhubhisthyaktha Yoga",
"pyjhora_names": [
"bandhubhisthyaktha_yoga"
]
},
{
"chart": "Paramahansa Yogananda",
"rule_id": "bvr_dattha_puthra_yoga",
+3 -2
View File
@@ -1288,7 +1288,7 @@
"category": "auspicious",
"logic": {
"type": "custom",
"expr": "\n# PyJHora/BVR-12: (L1 strong and L5/L6 in mutual kendras) OR\n# (L1 and L10 together in a movable sign and L9 strong). PyJHora strong() uses\n# house_strength >= FRIEND, not generic kendra/trikona strength.\nl1, l5, l6, l9, l10 = [lord(h) for h in [1, 5, 6, 9, 10]]\nif not all(p in ctx.planets for p in [l1, l5, l6, l9, l10]):\n False\nelse:\n l1h, l5h, l6h, l9h, l10h = [house_of(p) for p in [l1, l5, l6, l9, l10]]\n cond1 = strong(l1) and offset(l5h, l6h) in [0, 3, 6, 9] and offset(l6h, l5h) in [0, 3, 6, 9]\n cond2 = strong(l9) and house_of(l1) == house_of(l10) and movable_house(l1h)\n cond1 or cond2\n",
"expr": "\n# PyJHora/BVR-12: (L1 strong and L5/L6 in mutual kendras) OR\n# (L1 and L10 together in a movable sign and L9 strong). PyJHora strong() uses\n# house_strength >= FRIEND, not generic kendra/trikona strength.\nl1, l5, l6, l9, l10 = [lord(h) for h in [1, 5, 6, 9, 10]]\nif not all(p in ctx.planets for p in [l1, l5, l6, l9, l10]):\n False\nelse:\n l1h, l5h, l6h, l9h, l10h = [house_of(p) for p in [l1, l5, l6, l9, l10]]\n cond1 = strong(l1) and offset(l5h, l6h) in [0, 3, 6, 9] and offset(l6h, l5h) in [0, 3, 6, 9]\n # The second branch requires actual conjunction of two distinct lords; if L1==L10\n # (common for Gemini/Virgo/Sagittarius/Pisces ascendants), PyJHora does not count\n # the shared lord as \"L1 and L10 together\" for Sankha Yoga.\n cond2 = l1 != l10 and strong(l9) and house_of(l1) == house_of(l10) and movable_house(l1h)\n cond1 or cond2\n",
"combo_template": "上升主强且5/6宫主互为Kendra,或1/10宫主合于变动星座且9宫主强"
},
"effects": [
@@ -1299,7 +1299,8 @@
"strength": "强",
"enabled": true,
"source_reference": "BVR-12: L1 strong, L5/L6 in mutual Kendras",
"bvr_chapter": "Chapter 2"
"bvr_chapter": "Chapter 2",
"accuracy_note": "v6.1.7: tightened movable-sign branch to require distinct L1/L10 lords; removes shared-lord false positives for dual ascendants."
},
{
"id": "bheri_yoga",
+103 -1
View File
@@ -418,6 +418,60 @@ def _add_chart_args(p):
p.add_argument('--node-mode', default='mean', choices=['mean', 'true'], help='Rahu/Ketu节点口径:mean=Mean Node(默认,JHora常用/Swiss direct baseline),true=True NodePyJHora默认)')
def _varga_chart_to_yoga_context(varga_chart):
"""Convert varga.py output into yoga_engine context chart shape."""
if not isinstance(varga_chart, dict):
return {}
asc = varga_chart.get('Ascendant', {})
asc_sign = asc.get('sign') if isinstance(asc, dict) else None
asc_idx = SIGNS.index(asc_sign) if asc_sign in SIGNS else None
planets_ctx = {}
for pn, pd in varga_chart.items():
if pn in ('Ascendant', '_meta', '_dignity', '_d9_analysis', '_d60_analysis'):
continue
if not isinstance(pd, dict) or 'sign' not in pd:
continue
sign = pd.get('sign')
sign_idx = pd.get('sign_idx')
if sign_idx is None and sign in SIGNS:
sign_idx = SIGNS.index(sign)
house = None
if asc_idx is not None and sign_idx is not None:
house = ((int(sign_idx) - asc_idx) % 12) + 1
planets_ctx[pn] = {
'sign': sign,
'house': house,
'degree': pd.get('degree_in_sign', pd.get('degree')),
'degree_in_sign': pd.get('degree_in_sign', pd.get('degree')),
'sign_idx': sign_idx,
}
return {
'ascendant': asc_sign,
'ascendant_degree': asc.get('degree_in_sign') if isinstance(asc, dict) else None,
'planets': planets_ctx,
}
def _build_yoga_context_from_vargas(varga_result, planet_lons=None):
"""Build optional YogaContext payload with D9/D60 and basic panchanga."""
context = {}
if isinstance(varga_result, dict):
d9 = varga_result.get('D9_Navamsa') or {}
d60 = varga_result.get('D60_Shashtiamsa') or varga_result.get('D60_Shashtyamsa') or {}
if d9:
context['d9'] = _varga_chart_to_yoga_context(d9)
if d60:
context['d60'] = _varga_chart_to_yoga_context(d60)
if isinstance(planet_lons, dict) and 'Moon' in planet_lons and 'Sun' in planet_lons:
tithi_no = int(((planet_lons.get('Moon', 0) - planet_lons.get('Sun', 0)) % 360) / 12) + 1
context['panchanga'] = {
'tithi': tithi_no,
'paksha': 'waxing' if 1 <= tithi_no <= 15 else 'waning',
'is_waning_moon': tithi_no > 15,
}
return context
# ============================================================================
# 公共星盘计算(供 chart/shadbala/ashtakavarga 共用,v3.4提取)
# ============================================================================
@@ -608,6 +662,8 @@ def cmd_yoga(args):
planets = {}
asc = args.ascendant or "Aries"
yoga_context = None
if args.planets:
for item in args.planets.split(','):
parts = item.strip().split(':')
@@ -619,6 +675,12 @@ def cmd_yoga(args):
except ValueError:
pass
planets[parts[0].strip()] = pdata
context_json = getattr(args, 'context_json', None)
if context_json:
try:
yoga_context = json.loads(context_json)
except Exception:
yoga_context = None
else:
required_birth_fields = ["year", "month", "day", "hour", "minute", "lat", "lon"]
has_birth_input = all(getattr(args, field, None) is not None for field in required_birth_fields)
@@ -630,6 +692,7 @@ def cmd_yoga(args):
if chart is None:
return {"error": "swisseph未安装"}
asc = chart.get("ascendant", {}).get("sign", asc)
planet_lons_for_varga = {}
for pname, pd in chart.get("planets", {}).items():
if isinstance(pd, dict) and "sign" in pd and "house" in pd:
planets[pname] = {
@@ -637,13 +700,25 @@ def cmd_yoga(args):
"house": pd["house"],
"degree": pd.get("degree_in_sign", pd.get("degree")),
}
if pd.get("degree_raw") is not None:
planet_lons_for_varga[pname] = pd.get("degree_raw")
try:
from varga import calc_all_vargas
asc_deg_raw = chart.get("ascendant", {}).get("degree_raw")
if asc_deg_raw is not None and planet_lons_for_varga:
yoga_context = _build_yoga_context_from_vargas(
calc_all_vargas(planet_lons_for_varga, asc_deg_raw, [9, 60]),
planet_lons_for_varga,
)
except Exception:
yoga_context = None
ai = SIGNS.index(asc) if asc in SIGNS else 0
kl = list(set([SIGN_LORDS[SIGNS[(ai + h - 1) % 12]] for h in [1, 4, 7, 10]]))
tl = list(set([SIGN_LORDS[SIGNS[(ai + h - 1) % 12]] for h in [1, 5, 9]]))
# 调用数据驱动引擎(yoga_engine.py
yogas = detect_yogas(planets, asc)
yogas = detect_yogas(planets, asc, context=yoga_context)
return {
"ascendant": asc,
@@ -3076,6 +3151,32 @@ def cmd_full_reading(args):
if d1_data:
varga_result["D1_Rashi"] = d1_data
report['modules']['varga_full'] = varga_result
# v6.1.7: Re-run Yoga with D9/D60 context after varga-full is available.
# The earlier Step 3 remains a D1-only fallback for backward compatibility.
try:
yoga_context = _build_yoga_context_from_vargas(varga_result, planet_lons)
yoga_planets = {}
for pn, pd in planets.items():
if isinstance(pd, dict) and 'sign' in pd and 'house' in pd:
yoga_planets[pn] = {
'sign': pd['sign'],
'house': pd['house'],
'degree': pd.get('degree_in_sign', pd.get('degree')),
}
yoga_result = cmd_yoga(type('Args', (), {
'ascendant': asc_sign,
'planets': ','.join([
f"{k}:{v['sign']}:{v['house']}" + (f":{v['degree']}" if v.get('degree') is not None else "")
for k, v in yoga_planets.items()
]),
'context_json': json.dumps(yoga_context, ensure_ascii=False),
})())
yoga_result['context_layers'] = sorted(k for k, v in yoga_context.items() if v)
report['modules']['yoga'] = yoga_result
except Exception as yoga_ctx_e:
report['errors'].append(f"yoga-context-injection: {yoga_ctx_e}")
report['modules']['vargottama'] = _calc_vargottama(planets, varga_result)
report['modules']['pushkara'] = _calc_pushkara_flags(planets)
except Exception as e:
@@ -3901,6 +4002,7 @@ def main():
_add_chart_args(p)
p.add_argument('--ascendant', default=None)
p.add_argument('--planets', default=None, help="格式: 'Sun:Aries:9[:10.5],Moon:Aquarius:7[:15.2],...';若提供则优先使用并忽略出生信息")
p.add_argument('--context-json', default=None, help='可选:传入包含 d9/d60/panchanga 的 YogaContext JSON')
for action in p._actions:
if action.dest in ['year', 'month', 'day', 'hour', 'minute', 'lat', 'lon']:
action.required = False