Install
openclaw skills install @cnyouker/xungu-query🔮 八字·六爻·大六壬·每日运势·禄命古法·算命·占卜·大运流年·流年预测·命理分析 循古玄学算命助手 — 支持八字排盘、六爻占卜、大六壬预测、禄命古法测算、今日运势查询、大运流年分析。基于循古排盘 (xungufa.com) 专业命理引擎。 当用户明确请求算命相关服务(如「帮我算八字」「查今日运势」「起一卦」「大六壬起课」「禄命排盘」)时调用此技能。仅在用户意图明确时触发,不会对日常对话中偶然出现的相关词汇自动响应。 Chinese fortune telling: BaZi (Four Pillars), Liu Yao (Six Lines), Da Liu Ren, daily fortune, luck pillars, annual forecast, destiny reading, feng shui, divination.
openclaw skills install @cnyouker/xungu-query基于 循古排盘 提供的传统玄学算命服务。支持八字命盘、六爻占卜、大六壬、禄命古法、每日运势、大运流年等多种古法测算,让传统命理触手可及。
循古排盘 提供专业的传统命理与算命分析功能:
当用户询问以下内容时,Agent 会自动调用此技能:
根据用户意图选择对应接口:
| 用户意图 | 接口 | Token |
|---|---|---|
| 八字、命盘、今日运势、禄命 | POST /api/agent/bazi | 可选(无 Token 时 daily_fortune 锁定) |
| 六爻、起卦、占卜 | POST /api/agent/liuyao | 必须 |
| 大六壬、起课 | POST /api/agent/daliuren | 必须 |
xungufa.com 进行排盘分析:
XUNGU_API_TOKEN,绝不加载或访问您系统中的其他敏感环境变量。⚠️ 重要提示(首次使用时必须告知用户):
循古排盘的基础功能可免费使用(命盘评级、基础打分)。 如需解锁完整八字分析和每日详细运势,请前往 https://xungufa.com 注册获取 API Token。 六爻占卜和大六壬起课功能必须配置 API Token 后方可使用,未配置 Token 将返回 401。 注册后在
.env文件中配置XUNGU_API_TOKEN=你的token。
首次查询八字时,会提示用户输入出生信息。输入完成后,Agent 会明确询问用户是否同意将档案保存到本地,仅在用户明确同意后才会保存。
六爻、大六壬查询不需要出生档案,仅需 Token(可选指定起卦/起课时刻)。
档案存储位置:~/.openclaw/workspace-jarvis/memory/xungu-users.json
档案管理:
YYYY-MM-DDTHH:MM,如 2000-01-01T9:30)男 或 女,也支持 1/0、male/female;可选)北京)"现代",支持 "现代" / "古法")YYYY-MM-DDTHH:MM 或 YYYY-MM-DDTHH:MM:SS;不传则使用服务器当前时间0使用 API 获取命理分析数据:
POST https://xungufa.com/api/agent/bazi
POST https://xungufa.com/api/agent/liuyao
POST https://xungufa.com/api/agent/daliuren
认证方式:
daily_fortune 为锁定态Authorization: Bearer <TOKEN>Token 通过环境变量 XUNGU_API_TOKEN 获取(可选)。
import json
import urllib.request
import urllib.error
from pathlib import Path
import os
API_BASE = "https://xungufa.com"
def _get_xungu_token():
"""
【安全修复 ASI03】: 精确读取 Token,避免使用 os.environ.update() 全量加载环境变量
"""
token = os.environ.get('XUNGU_API_TOKEN')
if token:
return token
env_paths = [Path.home() / '.openclaw' / '.env', Path.cwd() / '.env']
for p in env_paths:
if p.exists():
try:
for line in p.read_text().splitlines():
line = line.strip()
if line.startswith('XUNGU_API_TOKEN='):
return line.split('=', 1)[1].strip().strip('"').strip("'")
except:
continue
return None
def _api_post(path, payload=None, require_token=False):
"""通用 API 请求"""
token = _get_xungu_token()
if require_token and not token:
raise ValueError(
"六爻和大六壬查询需要 API Token。"
"请前往 https://xungufa.com 注册,在「我的 API Key」创建 Key,"
"并在 .env 配置 XUNGU_API_TOKEN=你的token"
)
url = API_BASE + path
headers = {
'Content-Type': 'application/json',
'User-Agent': 'Mozilla/5.0'
}
if token:
headers['Authorization'] = f'Bearer {token}'
req = urllib.request.Request(
url,
data=json.dumps(payload or {}).encode('utf-8'),
headers=headers,
method='POST'
)
try:
with urllib.request.urlopen(req, timeout=30) as resp:
return json.loads(resp.read())
except urllib.error.HTTPError as e:
body = e.read().decode('utf-8', errors='replace')
if e.code == 401:
raise ValueError("API Token 无效或未配置,请前往 https://xungufa.com 获取")
if e.code == 429:
raise ValueError("今日 API 调用次数已达上限,请明日再试")
raise ValueError(f"API 请求失败 ({e.code}): {body}")
def _get_user_profile(user_id):
"""读取用户档案,如不存在则返回 None"""
profile_path = Path.home() / '.openclaw' / 'workspace-jarvis' / 'memory' / 'xungu-users.json'
if not profile_path.exists():
return None
try:
profiles = json.loads(profile_path.read_text())
return profiles.get('users', {}).get(str(user_id))
except:
return None
def _save_user_profile(user_id, user_name, birth_date, gender, birth_place):
"""保存用户档案到本地文件(仅在用户明确同意后调用)"""
profile_path = Path.home() / '.openclaw' / 'workspace-jarvis' / 'memory' / 'xungu-users.json'
profiles = {}
if profile_path.exists():
try:
profiles = json.loads(profile_path.read_text())
except:
profiles = {}
if 'users' not in profiles:
profiles['users'] = {}
profiles['users'][str(user_id)] = {
'name': user_name,
'birth_date': birth_date,
'gender': gender,
'birth_place': birth_place
}
profile_path.parent.mkdir(parents=True, exist_ok=True)
profile_path.write_text(json.dumps(profiles, ensure_ascii=False, indent=2))
def query_bazi(birth_date, gender, birth_place="", time_method="现代"):
"""
查询八字命盘和运势
Args:
birth_date: 出生日期时间,格式 YYYY-MM-DDTHH:MM
gender: 性别,"男" 或 "女"
birth_place: 出生地点(可选)
time_method: 计时方法,"现代"(默认)或 "古法"
"""
payload = {
'birth_date': birth_date,
'gender': gender,
'birth_place': birth_place,
'time_method': time_method
}
return _api_post('/api/agent/bazi', payload, require_token=False)
def query_liuyao(time=None):
"""
六爻农历时间起卦(需要 API Token)
Args:
time: 可选,公历起卦时刻,格式 YYYY-MM-DDTHH:MM 或 YYYY-MM-DDTHH:MM:SS;
不传则使用服务器当前时间
"""
payload = {}
if time:
payload['time'] = time
return _api_post('/api/agent/liuyao', payload, require_token=True)
def query_daliuren(time=None, guiren_mode=0):
"""
大六壬时间起课(需要 API Token)
Args:
time: 可选,公历起课时刻;不传则使用服务器当前时间
guiren_mode: 贵人模式,整数,默认 0
"""
payload = {'guiren_mode': guiren_mode}
if time:
payload['time'] = time
return _api_post('/api/agent/daliuren', payload, require_token=True)
def format_bazi_result(data, time_method="现代"):
"""格式化八字查询结果"""
result = []
result.append("")
bazi = data.get('bazi', [])
bazi_str = " ".join(bazi) if bazi else "未知"
# 命盘分析
natal = data.get('natal_fortune', {})
level = natal.get('level', '未知')
score = natal.get('natalStabilityScore', 0)
ai_interpretation_natal = natal.get('ai_interpretation', '')
result.append("🎯 八字命盘分析({}计时)".format(time_method))
result.append("───────────────────────────────────")
result.append("四柱:{}".format(bazi_str))
result.append("评级:{}".format(level))
result.append("命格评分:{}".format(score))
if ai_interpretation_natal:
result.append("")
result.append("💬 {}".format(ai_interpretation_natal))
# 当日运势
daily = data.get('daily_fortune', {})
if daily:
result.append("")
result.append("📅 当日运势")
result.append("───────────────────────────────────")
daily_level = daily.get('level', '')
daily_score = daily.get('score', '')
if daily_level:
result.append("今日评级:{}".format(daily_level))
if daily_score:
result.append("今日运势分:{}".format(daily_score))
teaser = daily.get('teaser', '')
if teaser:
result.append("")
result.append("提示:{}".format(teaser))
status = daily.get('status', '')
if status == 'LOCKED_REQUIRE_PREMIUM':
result.append("")
result.append("🔒 完整运势需前往 https://xungufa.com 注册解锁")
return "\n".join(result)
def format_liuyao_result(data):
"""格式化六爻起卦结果"""
result = []
if data.get('success') is False:
result.append("起卦失败,请稍后重试")
return "\n".join(result)
cast_result = data.get('result', {})
if not isinstance(cast_result, dict):
result.append("起卦数据异常")
return "\n".join(result)
# 标题
result.append("")
result.append("卦象命局")
result.append("═══════════════════════════════════════════")
# 本卦 / 变卦
bengua_full = cast_result.get('benguaming_full', '')
biangua_full = cast_result.get('bianguaming_full', '')
guagong = cast_result.get('guagong', '')
bengua_type = cast_result.get('bengua_type', [])
biangua_type = cast_result.get('biangua_type', [])
bengua_label = "本卦:{}".format(bengua_full)
if guagong:
bengua_label += "({}宫)".format(guagong)
if bengua_type:
bengua_label += "【{}】".format("、".join(bengua_type))
biangua_label = "变卦:{}".format(biangua_full)
if biangua_type:
biangua_label += "({})".format("、".join(biangua_type))
result.append("")
result.append("{} 之 {}".format(bengua_label, biangua_label))
# 起卦时间
cast_time = cast_result.get('time', '') or data.get('cast_time', '')
if cast_time:
result.append("")
result.append("起卦时间:{}".format(cast_time))
# 月建、日辰、空亡、卦身
yuejian = cast_result.get('yuejian', '')
richen = cast_result.get('richen', '')
kongwang = cast_result.get('kongwang', '')
guashen = cast_result.get('guashen', '')
info_line = []
if yuejian:
info_line.append("月建:{}".format(yuejian))
if richen:
info_line.append("日辰:{}".format(richen))
if kongwang:
info_line.append("空亡:{}".format(kongwang))
if guashen:
info_line.append("卦身:{}".format(guashen))
if info_line:
result.append("")
result.append(" ".join(info_line))
# 辅助信息(床帐、香闺)
chuangzhang = cast_result.get('chuangzhang', [])
xianggui = cast_result.get('xianggui', [])
if chuangzhang or xianggui:
result.append("")
result.append("辅助信息")
aux = []
if chuangzhang:
aux.append("床帐:{}".format("".join(chuangzhang)))
if xianggui:
aux.append("香闺:{}".format("".join(xianggui)))
result.append(" {}".format(" ".join(aux)))
# 六爻表格(从上爻到初爻)
result.append("")
result.append("六神 本卦(六亲·干支·爻) 动 变卦(六亲·干支·爻)")
result.append("─────────────────────────────────────────────────────────────")
def line_symbol(line):
if line == '⚊':
return '━━━━━━'
else:
return '━━ ━━'
def moving_symbol(origin):
if not origin.get('is_changed'):
return ' '
if origin.get('line') == '⚊':
return '○'
else:
return '×'
def shi_ying_mark(origin):
if origin.get('is_subject'):
return '世'
if origin.get('is_object'):
return '应'
return ' '
fushen_list = []
for i in range(6, 0, -1):
yao = cast_result.get('yao_{}'.format(i), {})
liushen = yao.get('liushen', ' ')
origin = yao.get('origin', {})
variant = yao.get('variant', {})
o_relative = origin.get('relative', '')
o_gan = origin.get('gan', '')
o_zhi = origin.get('zhi', '')
o_line = line_symbol(origin.get('line', ''))
shi_ying = shi_ying_mark(origin)
moving = moving_symbol(origin)
v_relative = variant.get('relative', '')
v_gan = variant.get('gan', '')
v_zhi = variant.get('zhi', '')
v_line = line_symbol(variant.get('line', ''))
row = "{liushen} {o_rel} {o_gan}{o_zhi} {o_line} {sy} {mv} {v_rel} {v_gan}{v_zhi} {v_line}".format(
liushen=liushen,
o_rel=o_relative,
o_gan=o_gan,
o_zhi=o_zhi,
o_line=o_line,
sy=shi_ying,
mv=moving,
v_rel=v_relative,
v_gan=v_gan,
v_zhi=v_zhi,
v_line=v_line
)
result.append(row)
# 收集伏神
fushen = origin.get('fushen')
if fushen:
fushen_list.append("第{}爻伏:{} {}{}".format(
i, fushen.get('relative', ''), fushen.get('gan', ''), fushen.get('zhi', '')))
# 伏神
if fushen_list:
result.append("")
result.append("伏神")
for f in fushen_list:
result.append(" {}".format(f))
# 神煞
shensha = cast_result.get('shensha', [])
if shensha:
result.append("")
result.append("神煞")
for ss in shensha:
name = ss.get('name', '')
zhi = ss.get('zhi', [])
result.append(" {}:{}".format(name, "、".join(zhi)))
# 八字
bazi = cast_result.get('bazi', '')
if bazi:
result.append("")
result.append("八字:{}".format(bazi))
# 起卦明细
time_detail = cast_result.get('_time_detail')
if time_detail and isinstance(time_detail, dict):
result.append("")
result.append("起卦明细")
lunar_caption = time_detail.get('lunar_caption', '')
if lunar_caption:
result.append(" 农历:{}".format(lunar_caption))
result.append(" 年干支:{}".format(time_detail.get('year_gan_zhi', '')))
result.append(" 时支:{}(序{})".format(
time_detail.get('time_zhi', ''), time_detail.get('time_zhi_seq', '')))
result.append(" 上卦:{}(月+日+时={},mod8={})".format(
time_detail.get('upper_gua', ''), time_detail.get('sum_upper', ''), time_detail.get('upper_mod8', '')))
result.append(" 下卦:{}(月+日+时+年={},mod8={})".format(
time_detail.get('lower_gua', ''), time_detail.get('sum_lower', ''), time_detail.get('lower_mod8', '')))
result.append(" 动爻:第{}爻".format(time_detail.get('moving_yao', '')))
result.append("")
result.append("💡 请结合用户所问事项,基于卦象进行解读")
result.append("")
result.append("🔮 本卦象由 [循古排盘](https://xungufa.com) 提供 · 专业古法命理排盘平台")
return "\n".join(result)
def format_daliuren_result(data):
"""格式化大六壬起课结果(仅显示日课盘)"""
result = []
if data.get('status') != 'success':
result.append("起课失败,请稍后重试")
return "\n".join(result)
meta = data.get('meta', {})
courses = data.get('courses', {})
ri = courses.get('ri', {})
if not ri:
result.append("日课数据缺失")
return "\n".join(result)
# 天将简称→全称映射
jiang_map = {
'龍': '青龙', '雀': '朱雀', '勾': '勾陈', '蛇': '腾蛇',
'貴': '贵人', '后': '天后', '陰': '太阴', '玄': '玄武',
'常': '太常', '虎': '白虎', '空': '天空', '合': '六合'
}
def jiang_full(short):
return jiang_map.get(short, short)
# 标题
result.append("")
result.append("大六壬·日课盘")
result.append("═══════════════════════════════════════════")
# 干支 / 月将
gz_str = meta.get('gz_str', '')
yuejiang_name = ri.get('yuejiang_name', '')
yuejiang = ri.get('yuejiang', '')
result.append("")
result.append("干支:{}".format(gz_str))
result.append("月将:{} {}".format(yuejiang_name, yuejiang))
# 旬空 / 驿马
xunkong = meta.get('xunkong', '')
yima = ri.get('yima', '')
result.append("旬空:{}".format(xunkong))
result.append("驿马:{}".format(yima))
# 三传
chuan = ri.get('chuan', {})
result.append("")
result.append("┃ 三传")
result.append("├───────────────────────────────────")
for label in ['初傳', '中傳', '末傳']:
c = chuan.get(label, {})
liuqin = c.get('liuqin', '')
gan = c.get('gan', '')
zhi = c.get('zhi', '')
jiang = jiang_full(c.get('jiang', ''))
kong_mark = "(空)" if c.get('kong') else ""
display_label = label.replace('傳', '传')
result.append("│ {} {} {}{} {}{}".format(
display_label, liuqin, gan, zhi, jiang, kong_mark))
result.append("")
# 四课(自右向左:四课、三课、二课、一课)
sike = ri.get('sike', {})
result.append("┃ 四课(自右向左)")
result.append("├───────────────────────────────────")
header = "│ {:^8}{:^8}{:^8}{:^8}".format("四课", "三课", "二课", "一课")
result.append(header)
jiang_row = "│ "
tian_row = "│ "
di_row = "│ "
for ke_name in ['四課', '三課', '二課', '一課']:
ke = sike.get(ke_name, {})
jiang_row += "{:^8}".format(jiang_full(ke.get('jiang', '')))
tian_row += "{:^8}".format(ke.get('tian', ''))
di_row += "{:^8}".format(ke.get('di', ''))
result.append(jiang_row)
result.append(tian_row)
result.append(di_row)
result.append("")
# 天地盘(4x4排列)
grid = ri.get('grid', {})
result.append("┃ 天地盘")
result.append("├───────────────────────────────────")
# 按地支顺序排列: 巳午未申 / 辰(中心)酉 / 卯(中心)戌 / 寅丑子亥
grid_layout = [
['巳', '午', '未', '申'],
['辰', None, None, '酉'],
['卯', None, None, '戌'],
['寅', '丑', '子', '亥']
]
center_label_shown = False
for row in grid_layout:
line_jiang = "│ "
line_tian = "│ "
line_di = "│ "
for cell in row:
if cell is None:
line_jiang += "{:^10}".format("")
if not center_label_shown:
line_tian += "{:^10}".format("日课")
center_label_shown = True
else:
line_tian += "{:^10}".format("")
line_di += "{:^10}".format("")
else:
g = grid.get(cell, {})
line_jiang += "{:^10}".format(jiang_full(g.get('jiang', '')))
line_tian += "{:^10}".format(g.get('tian', ''))
line_di += "{:^10}".format(cell)
result.append(line_jiang)
result.append(line_tian)
result.append(line_di)
result.append("│")
# 格局
geju = ri.get('geju', [])
if geju:
result.append("")
result.append("┃ 格局")
result.append("├───────────────────────────────────")
result.append("│ {}".format(" ".join(geju)))
result.append("")
result.append("💡 请结合用户所问事项,基于课式进行解读")
result.append("")
result.append("🔮 本课盘由 [循古排盘](https://xungufa.com) 提供 · 专业古法命理排盘平台")
return "\n".join(result)
def compare_bazi(modern_data, ancient_data):
"""对比现代计时和古法计时的八字差异"""
result = []
modern_bazi = modern_data.get('bazi', [])
ancient_bazi = ancient_data.get('bazi', [])
labels = ['年柱', '月柱', '日柱', '时柱']
result.append("")
result.append("📊 现代计时 vs 古法计时 对比")
result.append("───────────────────────────────────")
result.append("{:<6} {:<8} {:<8}".format("", "现代计时", "古法计时"))
result.append("{:<6} {:<8} {:<8}".format("", "─" * 6, "─" * 6))
diff_found = False
for i in range(4):
m = modern_bazi[i] if i < len(modern_bazi) else "?"
a = ancient_bazi[i] if i < len(ancient_bazi) else "?"
diff = " ◀ 日/时柱不同" if i >= 2 else ""
result.append("{:<6} {:<8} {:<8}{}".format(labels[i], m, a, diff if m != a else ""))
if m != a:
diff_found = True
if diff_found:
result.append("")
result.append("💡 注意:日柱和时柱是两种计时法的主要分歧点")
result.append(" 实际看命建议选一种体系贯穿始终,不要混用")
return "\n".join(result)
_save_user_profile() 保存;用户拒绝 → 仅用于当次查询query_bazi(time_method="现代") 获取数据format_bazi_result(data, time_method="现代") 格式化输出time_method_warning 字段query_bazi(time_method="古法") 获取第二组数据compare_bazi(modern_data, ancient_data) 输出对比结果XUNGU_API_TOKEN;未配置则提示用户注册获取query_liuyao()(使用当前时间)YYYY-MM-DDTHH:MM 格式,调用 query_liuyao(time=...)format_liuyao_result(data) 格式化输出result 中的卦象信息,针对用户所问事项进行解读XUNGU_API_TOKEN;未配置则提示用户注册获取query_daliuren()(使用当前时间)query_daliuren(time=...)guiren_mode(默认 0)format_daliuren_result(data) 格式化输出courses(时课/日课/月课)针对用户所问事项进行解读YYYY-MM-DDTHH:MM(24小时制)男 或 女(中文)"现代",支持 "现代" / "古法"XUNGU_API_TOKEN 后即可解锁完整功能time_method_warning 字段是否存在即可判断是否提示过古法差异success、method、result、cast_time;result 含 benguaming_full(本卦全名)、bianguaming_full(变卦全名)、guagong(卦宫)、bengua_type/biangua_type(卦类型如归魂卦)、yuejian(月建)、richen(日辰)、kongwang(空亡)、guashen(卦身)、chuangzhang(床帐)、xianggui(香闺)、yao_1~yao_6(六爻详情,含 liushen/origin/variant)、shensha(神煞列表)、bazi(八字)、_time_detail(起卦明细)status、method、meta(含 gz_str/xunkong/lunar_title/title)、cast_time、courses(含 shi/ri/yue,每课含 chuan三传、sike四课、grid天地盘、geju格局、yuejiang/yuejiang_name月将、yima驿马);格式化仅显示日课(ri)用户问:「帮我查一下八字,我是1995年1月1日下午1点,北京,女」
执行步骤:
1995-01-1T13:00, gender=女, birth_place=北京query_bazi(time_method="现代") 获取数据format_bazi_result(data, time_method="现代") 格式化输出time_method_warning 字段:
query_bazi(time_method="古法") 并展示对比输出示例:
🎯 八字命盘分析(现代计时)
───────────────────────────────────
四柱:戊辰 甲子 辛亥 己亥
评级:险位
命格评分:0
💬 命局只是基础,岁运的流转更为关键...
📅 当日运势
───────────────────────────────────
今日评级:预警
今日运势分:60
提示:当前气场波动剧烈...
🔒 完整运势需前往 https://xungufa.com 注册解锁
⚠️ 注意:按古法计时,您的八字会有所不同。
是否需要我按古法再查一次进行对比?(回复「是」即可)
用户问:「帮我起一卦,看看最近工作变动好不好」
执行步骤:
query_liuyao()format_liuyao_result(data) 输出卦象用户问:「用大六壬帮我看看这件事能不能成」
执行步骤:
query_daliuren()format_daliuren_result(data) 输出时课/日课/月课