可验证的命理推理系统 · Verifiable Chinese Astrology Reasoning System
不是「会算命的聊天机器人」。排盘由确定性引擎算,结论必须能追到命盘要素与规则出处, 每一句典籍断语都要能在固化底本里逐字找到。
- 确定性排盘 —— 排盘交给
sxtwl(寿星天文历),不让模型算命盘 - 规则引擎 —— 81 条规则:68 条结构层(由可执行定义表唯一确定)+ 13 条典籍层(带逐字引文)
- 证据链追踪 —— 每条结论的每个条件都能追到具体命盘要素,断环会被 Critic 拦下
- 可验证的引证体系 —— 典籍断语挂在固化底本上,改一个字就装配失败
- 流派透明 —— 分歧并列披露,不投票选一个「真答案」
- 不做预测 —— 信号是结构描述,强度是证据等级而非事件概率
- 持续评估 —— 30 案例 × 6 条行为不变量,可当回归门禁
| 项 | 实测值 |
|---|---|
| 全量测试 | 1022 passed |
| 规则总数 | 81(结构层 68 + 典籍层 13) |
| API 接口 | 12 个 |
| 评估门禁 | 30/30 passed |
ruff |
干净 |
mypy |
3 errors,全在未改动的 engines/bazi/sxtwl_provider.py(sxtwl 的 C 扩展无类型存根) |
已可运行:python -m api.server 起服务与界面,输入生辰即得命盘、证据链、
审查结果与流年时间轴。
用户 / API / Web UI
↓
MingLiService(应用服务层)
↓
┌───────────────────────┼───────────────────────┐
↓ ↓ ↓
Bazi Engine Knowledge Time Engine
engines/bazi knowledge/ engines/bazi
sxtwl 确定性排盘 规则库 + 引证体系 time_engine
↓ ↓ ↓
四柱 / 藏干 / 十神 81 条规则 + 底本 流年 / 大运 / 流月
纳音 / 地支关系 逐字引文 + URN ↓
└───────────────────────┼───────────────────────┘
↓
Evidence Layer ★核心创新
事实 → 条件追踪 → 证据链 → 强度算式
↓
AnalystAgent(模板 或 provider 出稿)
↓
CriticAgent(独立复核)
↓
结论 + 证据强度 + 冲突 + 审查结果
证据链的每一跳:结论 → 规则 → 条件的期望值与实际值 → 命盘要素出处。
最后那一跳(source,如 chart.pillars.month)是「可追溯」落地的位置 ——
没有它,用户只能看到「规则要求正官」,看不到「正官来自月支藏干」。
命盘 → LLM → 「你今年财运不错」
命盘
↓ 确定性排盘(sxtwl)
四柱 己巳 丁丑 丁酉 丙午,日主丁
↓ 事实提取(不带吉凶)
事实:日主有根 / 月支本气为食神 / 四支六害丑午 ...
↓ 规则匹配(81 条,条件逐条追踪)
命中 13 条:STRUCT_ROOT_001、STRUCT_PATTERN_001 ...
↓ 证据链(事实 + 规则 + 条件取值 + 命盘出处)
↓ 强度算式(支持量凹饱和 − 冲突扣减)
结论(证据强度 0.79 —— 这是证据等级,不是事件概率)
用户能清楚看到:这个结论基于哪些规则?每条规则的哪个条件被什么命盘事实满足? 证据有多厚?不同流派怎么看?哪些依据还没校核过?
强度为什么不用「置信度均值」:均值完全不看命中条数 —— 命中 3 条和 20 条会得到
同一个分数。这里用 raw / (raw + K) 凹饱和:对条数单调递增、边际递减,
且永远到不了 1.0 —— 结构层证据再厚也只是「命盘里有什么」,不该被读成「结论几乎确定」。
典籍断语最大的风险不是写错,是无从核对 —— 一句「《滴天髓》云……」可能是转抄的 二手话术。所以引文不是自由文本,而是一条能在固化底本里逐字定位的引用。
urn:divination:bazi:sanmingtonghui:ws761697:j8.liujia_ri.jiachen_jiazi
└─ 系统 ─┘ └书┘ └── 版本 ──┘ └卷.篇.条┘
version 段是刻意加的:同一部书有多个本子,共用 URN 会让反查回来的是错的元数据。
text_sha256—— 我们归一化后的正文,用来发现本地篡改source_revision_sha1—— 上游 MediaWiki 的修订哈希,用来发现上游漂移
| 角色 | 含义 | 可引用 |
|---|---|---|
base |
底本,引文逐字回对的对象 | ✅ |
collation |
同一部书的另一传本,用于核异文 | ❌ |
reserve |
已固化、尚无消费方 | ❌ |
Corpus.citation() 拒绝任何非 base 的 URN —— 「两份都不可引、只有底本可引」的
门禁立得住,异文核对的结论才可信。
一条原子写错不会抛异常,只会静默地永远匹配不上,或者更糟 —— 匹配上了但断错了。 所以装配期一次性挡住:
- 逐字定位 ——
exact_quote必须在底本里逐字找得到(繁体转简体也算失败) - 键存在 —— 条件键必须在事实词表
FACT_SCHEMA内 - 元数据一致 —— 引用元数据与底本记录相符
- 角色门禁 —— 非
base不可引 - 引文限制必须落进条件 —— 引文说了「申月」,条件里就必须有
month_branch
第五条来自一次真实事故:录甲辰日时「申月,煞星㑹印,俱貴」漏写了 month_branch,
引文逐字对得上、键都在词表、元数据也一致 —— 前四条一条都没拦,而 predicate
对任何月份都命中。前四条守的是「引文可核、键存在、元数据一致」,
没有一条问「引文说过的限制有没有落进条件」。
Corpus.problems() 校验两份正文的 sha256 与篇章表标签。哈希不符时不再做派生检查 ——
拿一份「不知道被改成了什么」的文本去核篇名,报出来的「篇名找不到」只是哈希不符的副产品,
却会把诊断指向别的地方。
四柱干支、藏干、十神、纳音、地支关系(六合/六冲/六害/相破/自刑/三刑)、五行计数。 年月柱按节气精确时刻分界(不是日期零点);支持历史夏令时;晚子时口径显式可选。
81 条规则,每条带来源、流派、类别、主题、置信度、校核状态。
- 结构层 68 条 —— 十神有根/虚透、两种格局命名法、月令、日主强弱、五行缺失与偏旺、
地支关系。由
engines/bazi的定义表唯一确定,跑一遍代码就能证伪,不需要文献背书。 - 典籍层 13 条 —— 《三命通會》卷八「六甲日甲子時斷」,每条带 URN、逐字引文、 底本与版本、可打开的出处地址。
两种格局命名法并列收录(本气定格 / 透干定格),它们只在月支本气不透而中气余气透干时 给出不同格局名 —— 那是真实分歧,系统并列输出,不投票。
- 流年 —— 干支对日主的十神、该十神在四支有没有根、流年支与命盘四支的结构关系(冲太岁等)
- 大运 —— 顺逆按阳年男/阴年女顺行;起运数到相邻的节(不是下一个立春); 三日折一年折算到日
- 流月 —— 由「节」划界,一个干支年恰好十二个月
输出形如 2028年,信号:财星坐流年、六合巳、相破巳,证据强度0.44 ——
不是「2028 年你会发财」。区别不在措辞,在产出物本身:这一层没有任何一处
给出必然性判断,因为信号的定义就是「命盘里出现了什么结构」。
大运需要性别:顺逆规则不同流派有分歧,缺性别时只算流年、不猜默认方向。
POST /api/analyze/multi 一次分析多个主题,服务端并发执行。
- 命盘只算一次,扇出点在它之后,各主题共享同一张不可变命盘
- 主题之间无跨主题依赖,依赖图只有一层 —— 不需要 DAG 调度器
- 故障隔离:单个主题失败不影响其余,失败项以
ok=false如实带回原因 - 实测 4 主题 12 次 provider 调用:0.84s vs 串行估算 3.00s(3.6x)
诚实口径:确定性路径是纯 CPU 的,Python 的 GIL 让线程拿不到 CPU 并行度, 所以收益主要是故障隔离与故障定位;注入真 LLM 后每个主题是一次网络调用, 线程池这时才真正并行。
- CriticAgent 独立复算结论引用的规则是否都在证据链内,越界引用会披露
- 契约层 只给模型证据链(不含原始命盘与全量规则库),只接受
rule_ids与text两个键 - 契约本身可通过
GET /api/llm/contract审计 —— 只写在源码注释里的契约等于没有契约
static/index.html 单文件、无框架、无构建步骤。输入生辰 → 看命盘、证据链、
审查结果、流年时间轴。古典规则额外展示书名、底本、逐字原文与可点开的出处地址。
# 创建虚拟环境并安装依赖
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
# 跑测试
pytest tests/
# 启动服务与 Web 界面(在仓库根目录执行)
python -m api.server
# 然后打开 http://127.0.0.1:8000界面:输入生辰 → 看命盘、证据链、审查结果、流年时间轴。接口文档在 /docs。
python evaluation/runner.py # 人读报告
python evaluation/runner.py --json # 机器可读跑 10 个金标准案例 × 3 个主题,检查六条系统行为不变量:
- 证据链不断环
- 审查通过
- 结论不越界引用
- 不下确定性判断
- 强度在 [0, 1) 内
- 结果可复现(同一输入两次逐字段相同)
第 6 条尤其值得单独评:它单点测不出来,而一旦哪一层悄悄引入了顺序依赖, 症状是「偶尔不一样」,靠人工跑几次发现不了。
退出码非 0 表示有未过项,可直接当门禁用。
评的不是「排盘准不准」 —— 那已由 tests/test_bazi_golden.py 用 sxtwl 与
lunar-python 两套独立实现交叉确认,比任何自建评估都严格。
python scripts/atom_checklist.py # 四处同步状态一览
python scripts/atom_checklist.py --suggest-witness ID # 为规则找见证命盘加一条原子要同步动四处:原子 JSON、事实提取、独立复核的 audit_fact_map、
见证命盘 RULE_WITNESS。后三处都在测试文件里,而改原子的人盯着的是第一个 ——
这个脚本把那四处的状态一次摆出来。
12 个接口,完整描述在 /docs(Swagger UI)与 /openapi.json。
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/health |
健康检查,返回当前排盘 provider |
| POST | /api/chart |
排盘:四柱、藏干、十神、纳音、地支关系 |
| POST | /api/analyze |
按问题分析:事实、证据链、强度算式、审查结果 |
| POST | /api/analyze/multi |
多主题并发分析,故障隔离 |
| POST | /api/timeline |
流年时间轴(带性别则含大运) |
| GET | /api/topics |
已登记的分析主题及其别名 |
| GET | /api/rules |
规则库,可按流派/类别/层级/校核状态过滤 |
| GET | /api/rules/stats |
规则库统计 |
| GET | /api/rules/{rule_id} |
单条规则详情 |
| GET | /api/schools |
流派清单与已知分歧点 |
| GET | /api/classics/backlog |
典籍层待录入清单及阻塞原因 |
| GET | /api/llm/contract |
推理层与模型之间的提示契约 |
错误约定:输入不合法、超出历法库支持范围、provider 不可用时一律返回 422,
detail 为可直接展示给用户的中文说明;不存在的资源返回 404。不返回 500 堆栈。
- 大运需要性别。顺逆规则不同流派有分歧,缺性别时只算流年、不猜默认方向。
启用后结果带
approximate: true(三日折一年)。 - LLM provider 未接线。默认走确定性模板出稿,离线可运行;契约见
GET /api/llm/contract。要接真 provider 就注入一个,接口只有complete(request) -> str。 - 典籍层只覆盖《三命通會》卷八「六甲日甲子時斷」(13 条)。其余条目
被阻塞在「拿到可核验底本」这一步,见
GET /api/classics/backlog。 - 紫微斗数排盘引擎未做。
engines/下只有bazi/;没有引擎,那批规则的 条件无处落地。底本已固化挂role=reserve,等引擎到位。 - MingLi-Bench / fate-bench 未集成。当前评估是自建的行为不变量检查, 不是命理知识题准确率,因此不给出准确率数字。
- 主题层的断语覆盖不均。事业/婚姻/健康/财运四个主题目前只有公共结构层证据
(
status=base_only),结论不构成对该主题的判断,coverage_note里写了原因。
- 新增
knowledge/flow_facts.py:流年/大运对日主的十神、有根判定、与四支的结构关系。 两条轴共用一份实现,只靠轴名前缀区分。 - 新增
knowledge/time_windows.py:TimeWindow/Signal/analyze_period。 信号由事实直接命名并带source可回溯;主题过滤用显式声明的表。 - 强度公式内核提为
score_support,本命局与时间轴共用同一算式 —— 「本命局 0.62,2028 年 0.48」要有意义,两边必须是同一种东西。 - 接入
POST /api/timeline,Phase 6 的代码从死代码变成可用端点。
MingLiService.analyze_multi():命盘只算一次,扇出点在它之后;analyze拆出「吃已算好命盘」的内核,两条路径共用,不另写一份。- 标准库
ThreadPoolExecutor,不建队列、不建消息总线 —— 进程内传不可变 dataclass 就够。 - 故障隔离 +
POST /api/analyze/multi。
- 固化公版底本(《三命通會》卷八,欽定四庫全書本),两级哈希 + 永久链接。
knowledge/urn.py:Canonical Citation URN。- 五条加载期守卫(含新增的第五条「引文说过的限制必须落进条件」)。
- 甲辰日甲子时七条断语解除暂缓,规则 68 → 81,
destiny主题 6 → 13 条。 Corpus.problems()完整性自查补厚到各 5 条测试。
static/index.html单文件,无框架无构建;python -m api.server即起。- 全程
textContent不用innerHTML—— 结论、规则原文、书名来自语料, 生辰来自用户输入,拼 HTML 字符串等于自己开一个注入口。
evaluation/runner.py:10 金标准案例 × 3 主题 × 6 条行为不变量。- 评估器自身可被证伪:每条不变量都喂一份故意违规的 payload 断言报错。
scripts/atom_checklist.py:新增原子的收口清单,四处同步从靠人记得变成可查。- 见证命盘独占性检查:一张见证盘只该命中它见证的那条规则,多命中的要显式登记。
| 依赖 | 用途 |
|---|---|
sxtwl==2.0.7 |
排盘历法底座(寿星天文历,C 扩展) |
lunar_python==1.4.8 |
仅用于差分测试交叉校验,不参与生产排盘 |
fastapi / uvicorn |
API 服务 |
pydantic |
请求与响应模型 |
pytest |
测试 |
rich |
终端输出 |
requests / python-dotenv |
语料抓取与配置 |
- 前端 —— 原生 HTML + CSS + JS 单文件,无框架、无构建步骤
- 存储 —— 语料用 JSON + 纯文本固化在仓库内(
corpus/canonical/); 运行日志用 JSONL。没有数据库:规则库是代码,语料是固化文件, 两者都不需要运行时存储
规划早期列过这些,实际做下来不需要:规则库与语料都是版本化的静态资产
(要的是可 diff、可复核,不是可查询),界面只需一个页面(引入构建步骤
就多一类构建故障),而模型调用被刻意收窄成一个 complete(request) -> str
—— 接口越窄,能绕过证据链的地方就越少。
完整计划见 MINGLI_PLAN.md。各 Phase 验收报告见
PHASE0_REPORT.md … PHASE6_REPORT.md。
- 调研 GitHub 优质项目、制定执行计划
- 排盘引擎(sxtwl,10 个双实现交叉确认的 golden case)
- 规则库(81 条:结构层 68 + 典籍层 13)
- Evidence Layer(条件追踪、主题覆盖、可审计强度算式)
- Reasoning Agent(确定性模板 / provider 出稿双路径)
- Critic Agent(独立复核 + 多轮修正闭环)
- Time Engine(流年、大运、流月、事件窗口)
- 自建评估(30 案例 × 6 条行为不变量,可当门禁)
- MingLi-Bench 集成(160 道命理知识题)
- fate-bench 集成(295 个历史人物事件)
- 用户记忆系统(历史提问、命盘档案)
- Web UI
- RESTful API(12 个接口)
- 紫微斗数(底本已固化挂
reserve,缺排盘引擎) - 流月接入事件窗口(卡在「月柱十神怎么取」这个流派分歧)
- 合婚分析 / 择吉系统 / MCP Server
- 性能优化、安全加固、多语言、移动端
- 计算与推理分离 —— 模型不负责计算,只负责理解问题与解释证据
- 证据前置 —— 先有事实和规则,再有结论
- 可验证高于可读 —— 引文必须逐字可回溯,宁可留白也不写「大概是这样」
- 流派透明 —— 不同流派的判断差异必须明确告知用户
- 不做预测 —— 输出结构信号与证据等级,不输出必然性判断
- 本地优先 —— 核心计算可离线运行,不依赖外部服务
- 接口越窄越安全 —— 能绕过证据链的地方越少越好
- 诚实留白 —— 没有可核验底本时拒绝录入,并说明卡在哪一步
每个 Phase 完成后输出十节报告:
- 修改了什么 2. 为什么修改 3. 文件清单 4. 数据结构 5. API
- 测试结果 7. 构建结果 8. 已知问题 9. 未实现功能 10. 下一阶段建议
变异探针纪律:关键守卫撤掉后必须有测试变红。探针脚本开跑前先验基线干净 —— 基线红着时「抓住」没有意义,分不清红是变异造成的还是本来就在红。
- HeiGe-SuanMing —— 多引擎系统(八字+紫微+梅花+六爻+奇门)。
本项目取其
tests/test_paipan.py的排盘预期做外部口径对照 (见tests/test_bazi_heige_acceptance.py),未集成为引擎 —— 生产排盘走SxtwlBaziProvider。 - lunar_python —— 第二实现,仅用于差分测试交叉校验
- DestinyLinker/MingLi-Bench —— 命理 LLM 评估基准(待集成)
- shunshi-ai/fate-bench —— 历史人物事件验证(待集成)
- SylarLong/iztro —— 紫微斗数排盘库(待评估)
欢迎贡献:
- 典籍底本 —— 提供可核验的公有领域文本与版本信息
- 规则原子 —— 按
scripts/extract_atoms.py产出待审队列,人工写转述 - 边界用例 —— 历法边界(节气时刻、夏令时、晚子时、闰月)
- 流派依据 —— 有出处支撑的分歧点
- Bug 修复
新增规则前请先跑 python scripts/atom_checklist.py,它会告诉你四处里还缺哪几处。
MIT License
- GitHub Issues:问题反馈
- Discussions:技术讨论
感谢以下项目的启发与参考:
- HeiGe-SuanMing —— 排盘口径的外部对照来源
- sxtwl / 寿星天文历 —— 确定性历法底座
- lunar_python —— 差分测试的第二实现
- MingLi-Bench / fate-bench —— 评估基准的思路
- iztro —— 紫微斗数排盘的可选实现
当前状态:Phase 0-6 完成,Phase 7/8/10 部分完成;系统可完整运行 —— 排盘、规则、证据链、审查、流年时间轴、并发多主题分析、Web 界面与评估门禁全部就绪。
下一步:接入真 LLM provider;集成 MingLi-Bench / fate-bench; 流月接入事件窗口(需先定「月柱十神怎么取」的流派口径)。