Files
Jyotisha/scripts/career_analysis.py
T
ClaudeandClaude Opus 5.5 5d64f71b81 fix: astrologer round-5 rulings 2/3/4/5/10/13 (BUG-1746..1752)
- Functional default = PVR Table 30 (functional_profile=pvr_table_30); the former
  product table is bphs_exception_profile, scored only when named (rectification
  names it, scores unchanged). profile_divergence shows both roles side by side;
  movable-lagna Moon by the table's paksha note; dual-lordship note; prior rulings
  BUG-1570/1590/#166 Q4 checked against Table 30.
- Shadbala: six open items labelled parameter_sensitive; every option combination
  computed; only planets whose pass/fail flips get minimum_check=parameter_sensitive.
- Vimshottari AD/PD rows carry parent_period_start / visible_period_start; the
  consult card cuts the balance MD from its full parent and clips at birth.
- KP DBA: full first parent, clipped at birth (no balance rescale).
- Mudda: balance fields + balance_method (angle default, time comparison apart),
  nominal_year_days, her crossing wording; Harsha combined blocked reason lists items.
- D30 _meta describes the unequal parts (as her r40).
- Golden recaptured with TTL=0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DbBXoyn7Hj3LCW3BUs5bZ8
2026-11-08 11:57:22 +08:00

269 lines
14 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Career Analysis(事业分析)引擎 v1.0
超越 vedic-astro-skills 的 AI prompt,实现结构化引擎级分析
核心分析链:
D10(Dashamsa)事业盘 → 10宫主状态 → Saturn/Jupiter角色 → Dasha事业窗口 → Yogas
"""
from typing import Dict, List, Any
import math
try:
from .functional_benefics import derive_functional_benefic_malefic, moon_paksha_from_planets, SIGN_LORDS
from .affliction_evidence import collect_affliction_evidence
except ImportError:
from functional_benefics import derive_functional_benefic_malefic, moon_paksha_from_planets, SIGN_LORDS
from affliction_evidence import collect_affliction_evidence
SIGNS = ['Aries','Taurus','Gemini','Cancer','Leo','Virgo',
'Libra','Scorpio','Sagittarius','Capricorn','Aquarius','Pisces']
CAREER_SIGN_INDICATORS = {
'Aries': ['军事/执法','工程/技术','创业/先驱','外科医学'],
'Taurus': ['金融/银行','艺术/设计','餐饮/食品','奢侈品'],
'Gemini': ['媒体/传播','教育/培训','销售/贸易','IT/编程'],
'Cancer': ['医疗/护理','房地产','酒店/旅游','心理学/咨询'],
'Leo': ['管理/领导','娱乐/表演','政府/政治','教育/学术'],
'Virgo': ['数据分析','会计/审计','编辑/校对','医疗技术'],
'Libra': ['法律/司法','艺术/美学','公共关系','人力资源'],
'Scorpio': ['投资/风控','研究/侦探','心理学/精神分析','保险/精算'],
'Sagittarius': ['国际商务','教育/哲学','法律/宗教','出版/写作'],
'Capricorn': ['管理/行政','土木工程','政府/政策','传统行业'],
'Aquarius': ['科技/创新','NGO/公益','科学/研究','网络/电商'],
'Pisces': ['艺术/影视','医疗/康复','慈善/NGO','创意/设计'],
}
PLANET_CAREER_ROLES = {
'Sun': {'domain': '领导/管理','strength': '威权、决策、独立执业'},
'Moon': {'domain': '公众/服务','strength': '人际、关怀、变动适应'},
'Mars': {'domain': '执行/技术','strength': '行动力、工程、军事'},
'Mercury': {'domain': '信息/沟通','strength': '智力、商业、IT'},
'Jupiter': {'domain': '指导/教育','strength': '教导、咨询、法律'},
'Venus': {'domain': '艺术/财富','strength': '审美、金融、奢侈'},
'Saturn': {'domain': '管理/组织','strength': '纪律、行政、工业'},
'Rahu': {'domain': '创新/涉外','strength': '科技、外企、非传统'},
'Ketu': {'domain': '研究/灵性','strength': '分析、玄学、幕后'},
}
# Candidate symbolism, not a calibrated occupation classifier. Each candidate
# carries the actual tenth-house/lord witness; scores are not probabilities.
PLANET_INDUSTRIES = {
'Sun': ['领导/管理', '权力/公职'],
'Moon': ['公众/服务', '照护/餐旅'],
'Mars': ['体育/竞技', '工程/技术', '军事/执法'],
'Mercury': ['信息/沟通', '商业/贸易', '计算/分析'],
'Jupiter': ['教育/咨询', '法律/学术'],
'Venus': ['艺术/表演', '设计/审美'],
'Saturn': ['工业/工程', '组织/行政'],
}
D10_HOUSE_MEANINGS = {
1: '事业身份/职业形象', 2: '事业财富/收入来源', 3: '事业沟通/技能',
4: '事业根基/安稳度', 5: '事业创意/子女相关', 6: '事业竞争/服务',
7: '事业合作/客户', 8: '事业变革/危机管理', 9: '事业远见/高等教育',
10: '事业成就/社会地位', 11: '事业收益/人脉', 12: '事业幕后/海外',
}
def analyze_career(planets: Dict, asc_sign: str = 'Aries',
d10_positions: Dict = None, dasha_info: Dict = None,
shadbala: Dict = None,
career_semantic_profile: Dict = None, a10: Dict = None,
user_industry: str | None = None) -> Dict:
"""
完整事业分析引擎。
Args:
planets: D1行星位置 {planet: {'sign','house','degree',...}}
asc_sign: 上升星座
d10_positions: D10分盘行星位置(可选,提升精度)
dasha_info: 当前大运/小运信息(可选)
shadbala: Shadbala结果(可选)
Returns:
结构化事业分析
"""
analysis = {'indicators': [], 'strengths': [], 'challenges': [], 'timing': [], 'fields': []}
if asc_sign not in SIGNS:
raise ValueError('asc_sign must be a valid sign')
# 占星师 2026-11-08 第五轮裁定 3:默认 PVR 表 30;月亮盈亏供动星座上升的月亮用。
functional = derive_functional_benefic_malefic(asc_sign, moon_paksha=moon_paksha_from_planets(planets))
analysis['functional_benefic_malefic'] = functional
analysis['assertion_label'] = 'single_system_inference'
analysis['career_claim_gate'] = {
'industry_claim_allowed': bool(user_industry),
'public_backstage_claim_allowed': bool(user_industry and a10),
'user_industry': user_industry,
'boundary': 'Without the user\'s actual industry, fields remain candidate symbolism; public/backstage is not inferred from 6/8/12 placement alone.',
}
# 1. 10宫分析
asc_idx = SIGNS.index(asc_sign)
h10_sign = SIGNS[(asc_idx + 9) % 12]
h10_lord = {'Aries':'Mars','Taurus':'Venus','Gemini':'Mercury','Cancer':'Moon',
'Leo':'Sun','Virgo':'Mercury','Libra':'Venus','Scorpio':'Mars',
'Sagittarius':'Jupiter','Capricorn':'Saturn','Aquarius':'Saturn','Pisces':'Jupiter'}[h10_sign]
# 10宫主状态
h10_data = planets.get(h10_lord, {})
h10_house = h10_data.get('house', 0)
h10_dignity = h10_data.get('dignity', 'neutral')
analysis['indicators'].append({
'type': '10宫主分析',
'lord': h10_lord,
'sign': h10_sign,
'house': h10_house,
'dignity': h10_dignity,
'potential_fields': CAREER_SIGN_INDICATORS.get(h10_sign, []),
})
analysis['affliction_evidence'] = collect_affliction_evidence(
target_house=10,
target_lord=h10_lord,
planets=planets,
functional_malefics=functional.get('functional_malefics', []),
varga_pressure=bool(d10_positions),
)
# 2. 行星角色分配
for planet in ['Sun','Moon','Mars','Mercury','Jupiter','Venus','Saturn']:
pd = planets.get(planet, {})
if not pd:
continue
house = pd.get('house', 0)
if house in (1, 4, 7, 10):
analysis['strengths'].append({
'planet': planet,
'role': PLANET_CAREER_ROLES.get(planet, {}).get('domain', ''),
'detail': PLANET_CAREER_ROLES.get(planet, {}).get('strength', ''),
'house': house,
'note': f'{planet}在角宫({house}) — 事业核心驱动力',
})
# 3. Saturn角色(事业核心指示星)
saturn = planets.get('Saturn', {})
saturn_house = saturn.get('house', 0)
if saturn_house in (10, 6):
analysis['strengths'].append({'planet':'Saturn','role':'事业纪律候选','detail':'土星落事业/服务宫;组织、责任或持续训练是候选主题,需结合功能角色与强度。'})
if saturn_house in (8, 12):
analysis['challenges'].append({'planet':'Saturn','issue':'需核验工作环境与压力',
'detail':f'Saturn在{saturn_house}宫;这一落位不能单独确定发展速度或后期回报。',
'assertion_label':'user_history_verification_required', 'source_path':'planets.Saturn.house'})
# 4. 事业领域推荐
scored_fields = {}
# 10宫星座
for field in CAREER_SIGN_INDICATORS.get(h10_sign, []):
scored_fields[field] = scored_fields.get(field, 0) + 3
# 10宫主
if h10_lord in PLANET_CAREER_ROLES:
scored_fields[PLANET_CAREER_ROLES[h10_lord]['domain']] = scored_fields.get(PLANET_CAREER_ROLES[h10_lord]['domain'], 0) + 2
# D10分析(如果有)
d10_positions = d10_positions or {}
d10_planets = d10_positions.get('planets', d10_positions)
d10_asc = d10_positions.get('ascendant', d10_positions.get('Ascendant'))
if isinstance(d10_asc, dict):
d10_asc = d10_asc.get('sign')
d10_h10_sign = SIGNS[(SIGNS.index(d10_asc) + 9) % 12] if d10_asc in SIGNS else None
analysis['d10_evidence'] = {'ascendant': d10_asc, 'tenth_sign': d10_h10_sign,
'status': 'executed' if d10_h10_sign else 'blocked',
'missing_inputs': [] if d10_h10_sign else ['D10 ascendant']}
if d10_h10_sign:
for field in CAREER_SIGN_INDICATORS.get(d10_h10_sign, []):
scored_fields[field] = scored_fields.get(field, 0) + 1
candidates = {}
for layer, positions, lord in (
('D1', planets, h10_lord),
('D10', d10_planets, SIGN_LORDS.get(d10_h10_sign)),
):
for planet, fields in PLANET_INDUSTRIES.items():
pd = positions.get(planet)
if not isinstance(pd, dict) or not pd:
continue
witnesses = []
if pd.get('house') == 10:
witnesses.append('tenth_house_occupant')
if planet == lord:
witnesses.append('tenth_house_lord')
for field in fields:
if not witnesses:
continue
candidate = candidates.setdefault(field, {'field': field,
'assertion_label': 'single_system_inference', 'supporting_evidence': [],
'verification_question': '实际训练、作品或职业经历是否支持这一候选方向?'})
candidate['supporting_evidence'].append({'layer': layer, 'planet': planet,
'roles': witnesses, 'source_path': f'{layer}.planets.{planet}',
'functional_role': next(k for k in ('functional_benefics', 'functional_malefics', 'functional_neutrals') if planet in functional[k])})
analysis['industry_candidates'] = sorted(candidates.values(),
key=lambda row: (-len(row['supporting_evidence']), row['field']))
top_fields = sorted(scored_fields.items(), key=lambda x: x[1], reverse=True)[:5]
ranked_clusters = (
career_semantic_profile.get('ranked_role_clusters')
if isinstance(career_semantic_profile, dict)
and career_semantic_profile.get('status') == 'used'
else None
)
if isinstance(ranked_clusters, list) and ranked_clusters:
analysis['fields'] = ranked_clusters
analysis['field_recommendation_mode'] = 'evidence_ranked_clusters'
else:
analysis['fields'] = analysis['industry_candidates'] or [
{'field': f, 'score': s, 'assertion_label': 'parameter_sensitive',
'source_path': 'D1.tenth_sign', 'boundary': 'sign-only candidate, not a career verdict'}
for f, s in top_fields]
analysis['field_recommendation_mode'] = 'legacy_sign_indicator'
# 5. 时间窗口
if dasha_info:
current = dasha_info.get('current_dasha') or {}
current_ad = current.get('antardasha') or {}
md = current.get('lord') or dasha_info.get('maha_dasha') or dasha_info.get('current_md')
ad = current_ad.get('lord') if isinstance(current_ad, dict) else None
ad = ad or dasha_info.get('antar_dasha') or dasha_info.get('current_ad')
if md:
analysis['timing'].append({'current': '-'.join(p for p in (md, ad) if p),
'mahadasha': md, 'antardasha': ad, 'start': current.get('start'), 'end': current.get('end'),
'note': '保留引擎已计算的当前周期;具体事业应期还需领域激活与交叉验证。',
'assertion_label': 'single_system_inference'})
# 6. 综合评估
sb = shadbala or {}
sb_planets = sb.get('planets', sb)
strength_row = sb_planets.get(h10_lord) or {}
h10_strength = strength_row.get('total_rupas', strength_row.get('rupas'))
strength_present = isinstance(h10_strength, (int, float)) and not isinstance(h10_strength, bool) and math.isfinite(h10_strength) and h10_strength >= 0
analysis['strength_evidence'] = {'planet': h10_lord,
'rupas': h10_strength if strength_present else None,
'min_required': strength_row.get('min_required'),
'source_path': f'shadbala.{"planets." if "planets" in sb else ""}{h10_lord}'}
analysis['assessment_status'] = 'single_system_inference' if strength_present else 'blocked'
level = (f'10宫主{h10_lord}的Shadbala为{h10_strength:g} rupas;强度不是职业成功或公众知名度的保证。'
if strength_present else '缺少10宫主强度,不能确定事业承载力;已有职业候选与盘面依据仍保留。')
visibility = []
if planets.get('Sun', {}).get('house') == 10:
visibility.append({'source_path': 'D1.planets.Sun.house', 'theme': 'public_responsibility_candidate'})
if h10_house in (1, 4, 7, 10):
visibility.append({'source_path': f'D1.planets.{h10_lord}.house', 'theme': 'angular_career_lord'})
if a10:
visibility.append({'source_path': 'special_lagnas.A10_Karma_Pada', 'value': a10,
'theme': 'public_image_point_requires_context'})
analysis['visibility'] = {'status': 'single_system_inference' if visibility else 'blocked',
'supporting_evidence': visibility,
'boundary': '缺少公众信号不等于幕后;可见度与行业、工作方式分别判断。'}
analysis['technique_audit'] = [
{'technique': name, 'status': 'executed' if present else 'blocked'} for name, present in (
('D1', bool(planets)), ('Functional Benefic/Malefic', True),
('D10', bool(d10_h10_sign)), ('A10', bool(a10)),
('Shadbala', strength_present), ('Vimshottari', bool(analysis['timing'])),
('Narayana', False), ('MEVG / Global Web Evidence', False), ('Real Case Calibration', False))]
analysis['workflow_complete'] = False
analysis['assessment'] = level
return analysis