Add Rahu Ketu node mode option
This commit is contained in:
@@ -1,5 +1,18 @@
|
||||
# 印度占星 Skill 更新日志
|
||||
|
||||
## v6.0.7-node-mode-arbitration(2026-06-03)— Rahu/Ketu 节点口径仲裁与参数冻结
|
||||
|
||||
> **触发原因**:可信度 benchmark 第四轮确认第三轮 PyJHora 中 Rahu/Ketu 的剩余差异主要来自 Mean Node / True Node 口径差异,而非 D9/D10 计算 bug。
|
||||
|
||||
### 修复内容
|
||||
|
||||
- `scripts/jyotish_engine.py` 的出生数据类子命令新增 `--node-mode mean|true` 参数,默认 `mean`,可选 `true` 用于对齐 PyJHora 默认口径。
|
||||
- `compute_chart_data()` 输出 `birth_info.node_mode` 与 `node_mode_note`,把 Rahu/Ketu 节点模式纳入参数冻结证据。
|
||||
- `SKILL.md` 新增“Rahu/Ketu 节点口径冻结”强制规则,要求 benchmark 与解盘输出显式声明 Mean/True Node。
|
||||
- 第四轮报告确认:当前 skill vs Swiss Mean Node 80/80 匹配;vs Swiss True Node 与 PyJHora 默认均为 54/80,差异模式完全一致。
|
||||
|
||||
---
|
||||
|
||||
## v6.0.6-d10-benchmark-fix(2026-06-03)— PyJHora benchmark 暴露的 D10 口径修复
|
||||
|
||||
> **触发原因**:可信度 benchmark 第三轮接入 PyJHora 后,D10 出现系统性错配;交叉查证 D10 规则为“奇数星座从本宫起算,偶数星座从第九宫起算(含本宫计数)”。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: jyotish-vedic-astrology
|
||||
version: 6.0.6
|
||||
version: 6.0.7
|
||||
description: 印度占星(Jyotish)专业解盘与推运系统。核心能力:PDF星盘输入→严谨解盘→精确推运应期输出。触发词:印度占星、吠陀占星、Jyotish、解盘、推运、星盘分析、Dasha、Transit、Nakshatra、Yoga、出生时间矫正、PDF星盘、读取PDF、分析PDF星盘、现代解读、误判纠错、Varga分盘、综合分析、过境分析、合盘、婚姻匹配、年运盘、Prashna、Argala、Jaimini、KP系统、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.6-d10-benchmark-fix | **详细变更**:`CHANGELOG.md`
|
||||
> **版本**:v6.0.7-node-mode-arbitration | **详细变更**:`CHANGELOG.md`
|
||||
|
||||
---
|
||||
|
||||
@@ -88,6 +88,15 @@ description: 印度占星(Jyotish)专业解盘与推运系统。核心能力
|
||||
|
||||
→ 完整规范:`references/transit-actionable-output-guide.md`
|
||||
|
||||
### Rahu/Ketu 节点口径冻结(v6.0.7-node-mode)
|
||||
|
||||
**所有 benchmark 与解盘输出必须显式声明 Rahu/Ketu 使用 Mean Node 还是 True Node。**
|
||||
|
||||
- 当前 skill 默认:`--node-mode mean`(Swiss Ephemeris Mean Node)。
|
||||
- 可选:`--node-mode true`(Swiss Ephemeris True Node,用于对齐 PyJHora 默认口径)。
|
||||
- PyJHora 4.8.6 的 `rasi_chart()` 默认使用 True Node;第三轮 benchmark 的 Rahu/Ketu 差异已由第四轮仲裁确认为 Mean/True Node 口径差异,不应再误判为 D9/D10 计算 bug。
|
||||
- 输出 `birth_info.node_mode` 与 `node_mode_note` 必须保留,作为参数冻结证据。
|
||||
|
||||
---
|
||||
|
||||
## 核心能力速查
|
||||
@@ -255,9 +264,9 @@ $PYTHON $SCRIPT <子命令> [参数]
|
||||
|
||||
---
|
||||
|
||||
**版本**:v6.0.6-d10-benchmark-fix
|
||||
**版本**:v6.0.7-node-mode-arbitration
|
||||
**创建日期**:2026-04-20
|
||||
**最后更新**:2026-06-03(v6.0.6 修正 D10 偶数星座起算与非D9分盘度数缩放口径)
|
||||
**最后更新**:2026-06-03(v6.0.7 新增 Rahu/Ketu Mean/True Node 口径冻结与 `--node-mode` 参数)
|
||||
|
||||
---
|
||||
|
||||
|
||||
+30
-22
@@ -242,7 +242,8 @@ def _get_dignity_level(planet, sign, deg_in_sign=None):
|
||||
return 'ENEMY'
|
||||
return 'NEUTRAL'
|
||||
PLANET_CN = {"Ketu": "南交点Ketu", "Venus": "金星Venus", "Sun": "太阳Sun", "Moon": "月亮Moon", "Mars": "火星Mars", "Rahu": "北交点Rahu", "Jupiter": "木星Jupiter", "Saturn": "土星Saturn", "Mercury": "水星Mercury"}
|
||||
PLANETS_SWE = {'Sun': swe.SUN, 'Moon': swe.MOON, 'Mars': swe.MARS, 'Mercury': swe.MERCURY, 'Jupiter': swe.JUPITER, 'Venus': swe.VENUS, 'Saturn': swe.SATURN, 'Rahu': swe.MEAN_NODE} if HAS_SWE else {}
|
||||
BASE_PLANETS_SWE = {'Sun': swe.SUN, 'Moon': swe.MOON, 'Mars': swe.MARS, 'Mercury': swe.MERCURY, 'Jupiter': swe.JUPITER, 'Venus': swe.VENUS, 'Saturn': swe.SATURN} if HAS_SWE else {}
|
||||
PLANETS_SWE = {**BASE_PLANETS_SWE, 'Rahu': swe.MEAN_NODE} if HAS_SWE else {}
|
||||
|
||||
|
||||
def output_json(data):
|
||||
@@ -260,13 +261,14 @@ def _add_chart_args(p):
|
||||
p.add_argument('--lat', type=float, required=True)
|
||||
p.add_argument('--lon', type=float, required=True)
|
||||
p.add_argument('--tz', type=float, default=0)
|
||||
p.add_argument('--node-mode', default='mean', choices=['mean', 'true'], help='Rahu/Ketu节点口径:mean=Mean Node(默认,JHora常用/Swiss direct baseline),true=True Node(PyJHora默认)')
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# 公共星盘计算(供 chart/shadbala/ashtakavarga 共用,v3.4提取)
|
||||
# ============================================================================
|
||||
def compute_chart_data(year, month, day, hour, minute, lat, lon, tz):
|
||||
"""计算星盘核心数据,返回 (result_dict, asc_idx, jd, ayanamsa)"""
|
||||
def compute_chart_data(year, month, day, hour, minute, lat, lon, tz, node_mode='mean'):
|
||||
"""计算星盘核心数据,返回 (result_dict, asc_idx, jd, ayanamsa)。node_mode: mean|true。"""
|
||||
if not HAS_SWE:
|
||||
return None, None, None, None
|
||||
swe.set_ephe_path('')
|
||||
@@ -274,10 +276,15 @@ def compute_chart_data(year, month, day, hour, minute, lat, lon, tz):
|
||||
jd = swe.julday(year, month, day, hour_decimal)
|
||||
ayanamsa = swe.get_ayanamsa(jd)
|
||||
|
||||
node_mode = (node_mode or 'mean').lower()
|
||||
if node_mode not in ('mean', 'true'):
|
||||
node_mode = 'mean'
|
||||
node_pid = swe.TRUE_NODE if node_mode == 'true' else swe.MEAN_NODE
|
||||
result = {"birth_info": {
|
||||
"date": f"{year}-{month:02d}-{day:02d}", "time": f"{hour:02d}:{minute:02d}",
|
||||
"tz": f"UTC{'+' if tz >= 0 else ''}{tz}", "lat": lat, "lon": lon,
|
||||
"julian_day": round(jd, 6), "ayanamsa": round(ayanamsa, 4)
|
||||
"julian_day": round(jd, 6), "ayanamsa": round(ayanamsa, 4),
|
||||
"node_mode": node_mode, "node_mode_note": "mean=Mean Node; true=True Node. PyJHora默认true,本skill默认mean。"
|
||||
}, "ascendant": None, "planets": {}, "houses": {}}
|
||||
|
||||
asc_lon, _ = swe.houses(jd, lat, lon, b'A')
|
||||
@@ -296,7 +303,8 @@ def compute_chart_data(year, month, day, hour, minute, lat, lon, tz):
|
||||
"lord": SIGN_LORDS[SIGNS[si]]}
|
||||
|
||||
nak_span = 360.0 / 27
|
||||
for pname, pid in PLANETS_SWE.items():
|
||||
planets_swe = {**BASE_PLANETS_SWE, 'Rahu': node_pid}
|
||||
for pname, pid in planets_swe.items():
|
||||
try:
|
||||
pos, _ = swe.calc_ut(jd, pid)
|
||||
lon_p = (pos[0] - ayanamsa) % 360; lat_p = pos[1]; spd = pos[3]
|
||||
@@ -336,7 +344,7 @@ def compute_chart_data(year, month, day, hour, minute, lat, lon, tz):
|
||||
# 1. 星盘计算
|
||||
# ============================================================================
|
||||
def cmd_chart(args):
|
||||
result, asc_idx, jd, ayanamsa = compute_chart_data(args.year, args.month, args.day, args.hour, args.minute, args.lat, args.lon, args.tz)
|
||||
result, asc_idx, jd, ayanamsa = compute_chart_data(args.year, args.month, args.day, args.hour, args.minute, args.lat, args.lon, args.tz, getattr(args, 'node_mode', 'mean'))
|
||||
if result is None:
|
||||
return {"error": "swisseph未安装"}
|
||||
# v3.5: --validate 触发 R1-R10 校验
|
||||
@@ -872,7 +880,7 @@ def cmd_predict(args):
|
||||
if getattr(args, 'past_verify', False) and args.year:
|
||||
chart, asc_idx, jd, ayanamsa = compute_chart_data(
|
||||
args.year, args.month, args.day, args.hour, args.minute,
|
||||
args.lat, args.lon, args.tz)
|
||||
args.lat, args.lon, args.tz, getattr(args, 'node_mode', 'mean'))
|
||||
if chart is None:
|
||||
return {"error": "swisseph未安装"}
|
||||
return _past_event_verify(chart, asc_idx, args)
|
||||
@@ -1435,7 +1443,7 @@ def cmd_double_transit_pac(args):
|
||||
# 1. 计算本命星盘
|
||||
chart, asc_idx, jd, ayanamsa = compute_chart_data(
|
||||
args.year, args.month, args.day, args.hour, args.minute,
|
||||
args.lat, args.lon, args.tz)
|
||||
args.lat, args.lon, args.tz, getattr(args, 'node_mode', 'mean'))
|
||||
if chart is None:
|
||||
return {"error": "swisseph未安装"}
|
||||
|
||||
@@ -1679,7 +1687,7 @@ def cmd_transit_ll7l(args):
|
||||
"""Transit LL/7L 连接 + 互换检测"""
|
||||
chart, asc_idx, jd, ayanamsa = compute_chart_data(
|
||||
args.year, args.month, args.day, args.hour, args.minute,
|
||||
args.lat, args.lon, args.tz)
|
||||
args.lat, args.lon, args.tz, getattr(args, 'node_mode', 'mean'))
|
||||
if chart is None:
|
||||
return {"error": "swisseph未安装"}
|
||||
|
||||
@@ -1764,7 +1772,7 @@ def cmd_planetary_congregation(args):
|
||||
"""行星聚集检测"""
|
||||
chart, asc_idx, jd, ayanamsa = compute_chart_data(
|
||||
args.year, args.month, args.day, args.hour, args.minute,
|
||||
args.lat, args.lon, args.tz)
|
||||
args.lat, args.lon, args.tz, getattr(args, 'node_mode', 'mean'))
|
||||
if chart is None:
|
||||
return {"error": "swisseph未安装"}
|
||||
|
||||
@@ -1844,7 +1852,7 @@ def cmd_vivah_saham(args):
|
||||
"""Vivah Saham 计算 + Transit 激活"""
|
||||
chart, asc_idx, jd, ayanamsa = compute_chart_data(
|
||||
args.year, args.month, args.day, args.hour, args.minute,
|
||||
args.lat, args.lon, args.tz)
|
||||
args.lat, args.lon, args.tz, getattr(args, 'node_mode', 'mean'))
|
||||
if chart is None:
|
||||
return {"error": "swisseph未安装"}
|
||||
|
||||
@@ -1908,7 +1916,7 @@ def cmd_vivah_saham(args):
|
||||
def cmd_shadbala(args):
|
||||
chart, asc_idx, jd, ayanamsa = compute_chart_data(
|
||||
args.year, args.month, args.day, args.hour, args.minute,
|
||||
args.lat, args.lon, args.tz)
|
||||
args.lat, args.lon, args.tz, getattr(args, 'node_mode', 'mean'))
|
||||
if chart is None:
|
||||
return {"error": "swisseph未安装"}
|
||||
try:
|
||||
@@ -1930,7 +1938,7 @@ def cmd_shadbala(args):
|
||||
def cmd_ashtakavarga(args):
|
||||
chart, asc_idx, jd, ayanamsa = compute_chart_data(
|
||||
args.year, args.month, args.day, args.hour, args.minute,
|
||||
args.lat, args.lon, args.tz)
|
||||
args.lat, args.lon, args.tz, getattr(args, 'node_mode', 'mean'))
|
||||
if chart is None:
|
||||
return {"error": "swisseph未安装"}
|
||||
try:
|
||||
@@ -1990,7 +1998,7 @@ def cmd_memory(args):
|
||||
def cmd_validate(args):
|
||||
chart, asc_idx, jd, ayanamsa = compute_chart_data(
|
||||
args.year, args.month, args.day, args.hour, args.minute,
|
||||
args.lat, args.lon, args.tz)
|
||||
args.lat, args.lon, args.tz, getattr(args, 'node_mode', 'mean'))
|
||||
if chart is None:
|
||||
return {"error": "swisseph未安装"}
|
||||
try:
|
||||
@@ -2119,7 +2127,7 @@ def cmd_audit(args):
|
||||
"""P1-P12 行星审计:调用 chart→shadbala→ashtakavarga→yoga,输出统一审计报告"""
|
||||
chart, asc_idx, jd, ayanamsa = compute_chart_data(
|
||||
args.year, args.month, args.day, args.hour, args.minute,
|
||||
args.lat, args.lon, args.tz)
|
||||
args.lat, args.lon, args.tz, getattr(args, 'node_mode', 'mean'))
|
||||
if chart is None:
|
||||
return {"error": "swisseph未安装"}
|
||||
|
||||
@@ -2384,7 +2392,7 @@ def cmd_report(args):
|
||||
def cmd_varga_full(args):
|
||||
chart, asc_idx, jd, ayanamsa = compute_chart_data(
|
||||
args.year, args.month, args.day, args.hour, args.minute,
|
||||
args.lat, args.lon, args.tz)
|
||||
args.lat, args.lon, args.tz, getattr(args, 'node_mode', 'mean'))
|
||||
if chart is None:
|
||||
return {"error": "swisseph未安装"}
|
||||
try:
|
||||
@@ -2405,7 +2413,7 @@ def cmd_varga_full(args):
|
||||
def cmd_aspects(args):
|
||||
chart, asc_idx, jd, ayanamsa = compute_chart_data(
|
||||
args.year, args.month, args.day, args.hour, args.minute,
|
||||
args.lat, args.lon, args.tz)
|
||||
args.lat, args.lon, args.tz, getattr(args, 'node_mode', 'mean'))
|
||||
if chart is None:
|
||||
return {"error": "swisseph未安装"}
|
||||
try:
|
||||
@@ -2428,7 +2436,7 @@ def cmd_aspects(args):
|
||||
def cmd_jaimini(args):
|
||||
chart, asc_idx, jd, ayanamsa = compute_chart_data(
|
||||
args.year, args.month, args.day, args.hour, args.minute,
|
||||
args.lat, args.lon, args.tz)
|
||||
args.lat, args.lon, args.tz, getattr(args, 'node_mode', 'mean'))
|
||||
if chart is None:
|
||||
return {"error": "swisseph未安装"}
|
||||
try:
|
||||
@@ -2475,7 +2483,7 @@ def cmd_jaimini(args):
|
||||
def cmd_nakshatra_adv(args):
|
||||
chart, asc_idx, jd, ayanamsa = compute_chart_data(
|
||||
args.year, args.month, args.day, args.hour, args.minute,
|
||||
args.lat, args.lon, args.tz)
|
||||
args.lat, args.lon, args.tz, getattr(args, 'node_mode', 'mean'))
|
||||
if chart is None:
|
||||
return {"error": "swisseph未安装"}
|
||||
try:
|
||||
@@ -2513,7 +2521,7 @@ def cmd_nakshatra_adv(args):
|
||||
def cmd_argala(args):
|
||||
chart, asc_idx, jd, ayanamsa = compute_chart_data(
|
||||
args.year, args.month, args.day, args.hour, args.minute,
|
||||
args.lat, args.lon, args.tz)
|
||||
args.lat, args.lon, args.tz, getattr(args, 'node_mode', 'mean'))
|
||||
if chart is None:
|
||||
return {"error": "swisseph未安装"}
|
||||
try:
|
||||
@@ -2537,7 +2545,7 @@ def cmd_argala(args):
|
||||
def cmd_tajika(args):
|
||||
chart, asc_idx, jd, ayanamsa = compute_chart_data(
|
||||
args.year, args.month, args.day, args.hour, args.minute,
|
||||
args.lat, args.lon, args.tz)
|
||||
args.lat, args.lon, args.tz, getattr(args, 'node_mode', 'mean'))
|
||||
if chart is None:
|
||||
return {"error": "swisseph未安装"}
|
||||
try:
|
||||
@@ -3065,7 +3073,7 @@ def cmd_full_reading(args):
|
||||
# ── Step 1: 核心星盘 ──
|
||||
chart, asc_idx, jd, ayanamsa = compute_chart_data(
|
||||
args.year, args.month, args.day, args.hour, args.minute,
|
||||
args.lat, args.lon, args.tz)
|
||||
args.lat, args.lon, args.tz, getattr(args, 'node_mode', 'mean'))
|
||||
if chart is None:
|
||||
return {"error": "swisseph未安装,无法计算星盘"}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user