guanwei 1.2.7 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -87,6 +87,43 @@ AI 层:多 LLM 适配(OpenAI 兼容 / Google 格式,DeepSeek / Gemini / Gr
87
87
  ③ 历史:GET /api/divine?username= → 档案管理页列表/详情/删除
88
88
  ```
89
89
 
90
+ ## 🤖 开放分发(v1.3.0 · 程序化调用排盘)
91
+
92
+ 排盘能力已封装为可被程序调用的服务(与 Web 端共用 `shared/core` 单一算法副本):
93
+
94
+ **MCP Server**:支持三种传输,本地与国内客户端通吃——
95
+
96
+ ① stdio(Claude Code / Cursor 等本地 agent):
97
+
98
+ ```jsonc
99
+ // claude_desktop_config.json
100
+ { "mcpServers": { "guanwei": { "command": "npx", "args": ["tsx", "packages/guanwei-api/src/mcp.ts"], "cwd": "/你的/guanwei路径" } } }
101
+ ```
102
+
103
+ ② HTTP / SSE(WorkBuddy / ima / Trae 等国内客户端,填 URL 即可):
104
+
105
+ ```jsonc
106
+ // WorkBuddy MCP 配置(type 选 sse 或 http 均可)
107
+ { "mcpServers": { "guanwei": { "type": "http", "url": "http://127.0.0.1:3020/mcp" } } }
108
+ // 先启动服务:cd packages/guanwei-api && npm start (/mcp 为 Streamable HTTP,/mcp/sse 为旧版 SSE)
109
+ ```
110
+
111
+ ```text
112
+ agent:用观微排一个 1993-01-23 寅时的八字
113
+ → 工具 guanwei_chart(art: "bazi", inputs: {...}) → 完整盘面
114
+ ```
115
+
116
+ **REST API**(`packages/guanwei-api`,免费无 Key):
117
+
118
+ ```bash
119
+ cd packages/guanwei-api && npm start # http://127.0.0.1:3020/v1
120
+ curl -X POST http://127.0.0.1:3020/v1/chart -H "Content-Type: application/json" \
121
+ -d '{"art":"liuren","inputs":{"datetime":"2026-08-29T12:00:00"}}'
122
+ curl http://127.0.0.1:3020/v1/arts # 九术能力清单 + 参数 schema
123
+ ```
124
+
125
+ > 排盘免费(纯计算零 token);协议风格与未来托管 API 一致(/v1、统一错误码),本地自部署与第三方集成同源。
126
+
90
127
  ## 🚀 快速开始(一行命令)
91
128
 
92
129
  ### ⚡ 方式一:npm 一行安装(推荐,国内几秒装完)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "guanwei",
3
3
  "private": false,
4
- "version": "1.2.7",
4
+ "version": "1.3.1",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "dev": "vite",
@@ -6486,6 +6486,531 @@
6486
6486
  "records": [],
6487
6487
  "token": "b949c6fcac9b821cc74e1afb5a50acfacebafa21c79d13c61ae892998f33bbe9",
6488
6488
  "tokenExpires": 1791206668608
6489
+ },
6490
+ {
6491
+ "username": "联调5050",
6492
+ "passHash": "scrypt$bf40f5daa6a9a53952861bbd3273ba26$4aa265c117b3a102c07214fa27a66b6b66503c372c9ce1b96cf3eaaa54f0c78f48007a5499132ef03a04cd85033d3bbef0e919cf9aab2526fd87511aa41e22fe",
6493
+ "createdAt": 1788656535121,
6494
+ "profile": {
6495
+ "birthDate": "1996-09-09",
6496
+ "birthHourIndex": 5,
6497
+ "gender": "男",
6498
+ "location": null
6499
+ },
6500
+ "samples": [],
6501
+ "records": [
6502
+ {
6503
+ "artId": "bazi",
6504
+ "createdAt": 1,
6505
+ "result": {
6506
+ "dayGZ": "壬申"
6507
+ }
6508
+ }
6509
+ ],
6510
+ "token": "c3844cd28357240447326062dc92a29b4cbf7bf05cc4f63c4f16d320fb951d32",
6511
+ "tokenExpires": 1791248535194
6512
+ },
6513
+ {
6514
+ "username": "鉴权5280",
6515
+ "passHash": "scrypt$dbe5830db7570713fc95920032f943b5$9ca907f2ba3dc583a46e7d3c56c5e1f71705cee897d87eacd4c391e365d301ea4f083644acdaa2b003919635d5402a3972a7718e94266428d4a7b0bc4cbeb1d2",
6516
+ "createdAt": 1788656535313,
6517
+ "profile": {
6518
+ "birthDate": "1999-01-01"
6519
+ },
6520
+ "samples": [],
6521
+ "records": [],
6522
+ "token": "6db524357e54767784b4d4ad932ea0f45f71ebf96af44467baa6b1e49cf15754",
6523
+ "tokenExpires": 1791248535313
6524
+ },
6525
+ {
6526
+ "username": "他人5332",
6527
+ "passHash": "scrypt$1e363a1711a69d5920a5ed049279b602$82b72356633397200f974327c4aef9b03b561fd6824d90190d1ef83331235bb6b6883ce718d79cd830c128f2fa08814f62f25f0a9cc67e991db9f5ae4816ec5e",
6528
+ "createdAt": 1788656535361,
6529
+ "profile": {},
6530
+ "samples": [],
6531
+ "records": [],
6532
+ "token": "c5355a2b7376d9e1b99411f6232631244c53bacdba76ea43b746380947a8cfaf",
6533
+ "tokenExpires": 1791248535361
6534
+ },
6535
+ {
6536
+ "username": "轮换5368",
6537
+ "passHash": "scrypt$4400cc24ab2b9eedaa69f40ae1449826$0a2f9b327a17522775cda6d4e53e5d18545825bc8602a468d5a977a53ac0e49eb9875c4675b1462413244637431d7658976a05f576e62db0b1ef4f276999c9d3",
6538
+ "createdAt": 1788656535400,
6539
+ "profile": {},
6540
+ "samples": [],
6541
+ "records": [],
6542
+ "token": "dfee2d9a2d5366361e5fe7b8eacb2cd42c784d286e344bd8150ba4fa6eeaa55d",
6543
+ "tokenExpires": 1791248535437
6544
+ },
6545
+ {
6546
+ "username": "占位5446",
6547
+ "passHash": "scrypt$2d3199a92acffc2830b4cc672f3eb7b2$f3423d89fefe892488a9ab5e04b77724251d7d787446b9242c88566ce70e647e2c0615d44faf300e117df1f774ed7dee6eab78ece955446ac8b7ab8c67f883fa",
6548
+ "createdAt": 1788656535450,
6549
+ "profile": {},
6550
+ "samples": [],
6551
+ "records": [],
6552
+ "token": "e8709cd87cde3047be5507d5b2889fa26f40ab140519436ca8d362fd50335e44",
6553
+ "tokenExpires": 1791248535544
6554
+ },
6555
+ {
6556
+ "username": "甲5546",
6557
+ "passHash": "scrypt$2f4f33fd5e453036459ce9cf6e995e49$8c6c0969034ec6a1003643de622f62aa335a0a3974e6e7362cda82a4b5795c0192eb714ba4e7e5917d6aa8a1c1f0d43e9286372270301ac7bed702b4ba0458a3",
6558
+ "createdAt": 1788656535582,
6559
+ "profile": {},
6560
+ "samples": [],
6561
+ "records": [],
6562
+ "token": "5aca19f53c1be5d88a38df04bc0e6244c58ac980691c859c123ef3cd91ec7fbb",
6563
+ "tokenExpires": 1791248535582
6564
+ },
6565
+ {
6566
+ "username": "乙5546",
6567
+ "passHash": "scrypt$38ca20b58b781db8c17e77ac14df4a57$35d9423d48bbad4ab75f4677ae12786f574dfa678b9f32bc1cc4c0c20db11f0e67e2608f6b0b71c5a09cf1c97f9851c48466a668431e6b373aad4c180598325d",
6568
+ "createdAt": 1788656535621,
6569
+ "profile": {},
6570
+ "samples": [],
6571
+ "records": [],
6572
+ "token": "17e392a444b48f658ca24356da281b2fd31dd1e7eb16fc7978becd58aa238adc",
6573
+ "tokenExpires": 1791248535621
6574
+ },
6575
+ {
6576
+ "username": "列删5668",
6577
+ "passHash": "scrypt$bc34567711d77c2a736756b8cb6a0c3b$1fd68b5244cde889d4351794c24de4352c7f0125ace41680bc6dfe97c743221c8651838ee6437957542b0e85413a1483e73cd0794ab34c20bc5029710c273eac",
6578
+ "createdAt": 1788656535700,
6579
+ "profile": {},
6580
+ "samples": [],
6581
+ "records": [],
6582
+ "token": "1d59b0ddc8e830d6521c2985050a2b1d8543983453429f8b00e4f8cdfd945631",
6583
+ "tokenExpires": 1791248535700
6584
+ },
6585
+ {
6586
+ "username": "删乙5668",
6587
+ "passHash": "scrypt$f887a7e8f5a9533ccd07cfd7c63bbc7e$4bb2f63af55c704fbd801b39b3d3c79b9c889076057673b18c85a343bd8350dc5e7160d566a49e031f31ba1c028b8729687325970ceef66cefe0f9e1c51464b9",
6588
+ "createdAt": 1788656535745,
6589
+ "profile": {},
6590
+ "samples": [],
6591
+ "records": [],
6592
+ "token": "2b1e8e6fee6b55fa6ec7b34698d5417e0ffdbc558a5dbfbbfaf6a817e089ed77",
6593
+ "tokenExpires": 1791248535745
6594
+ },
6595
+ {
6596
+ "username": "联调7905",
6597
+ "passHash": "scrypt$49cdc5afb9eb173d5455c48d8f31983a$b7b02ad7add1aaf4904a5f797d4a3554d48aed88228b5030d6ce712821c23e0501cfbc5edda8a8b1368294c16e41e7bd9571f74cfa5c670d97703b2e23ad4ae4",
6598
+ "createdAt": 1788656727972,
6599
+ "profile": {
6600
+ "birthDate": "1996-09-09",
6601
+ "birthHourIndex": 5,
6602
+ "gender": "男",
6603
+ "location": null
6604
+ },
6605
+ "samples": [],
6606
+ "records": [
6607
+ {
6608
+ "artId": "bazi",
6609
+ "createdAt": 1,
6610
+ "result": {
6611
+ "dayGZ": "壬申"
6612
+ }
6613
+ }
6614
+ ],
6615
+ "token": "119ed7b0aa8a1e6e123578a26f36f7b931b5ada3a892ddf22eb51f89f2b35604",
6616
+ "tokenExpires": 1791248728039
6617
+ },
6618
+ {
6619
+ "username": "鉴权8122",
6620
+ "passHash": "scrypt$e265380493f0dedfafd8d7f4f90a8cef$9f8397ec3b242ae775fc1004cd5b1f6e98b37a61aa9cd4baf18597e41e5912e38c9b78a09b0d99b07ab671850f433c46c7132d734cc85d41e219e810f2bd050d",
6621
+ "createdAt": 1788656728159,
6622
+ "profile": {
6623
+ "birthDate": "1999-01-01"
6624
+ },
6625
+ "samples": [],
6626
+ "records": [],
6627
+ "token": "8ab64b73eac240b2a0b186f7b2fddc03d8a7061ccf33d6dbbee39cb5dfae1054",
6628
+ "tokenExpires": 1791248728159
6629
+ },
6630
+ {
6631
+ "username": "他人8184",
6632
+ "passHash": "scrypt$176de87bd3ade7866cb88e2f3ff18b4f$241cd9c463ee1c785b916972936c8b8fed9b0355cea0e319712cb05afb19b95a57a7b2d8738a1d19640e9085196a95e868bc5d66fde3675e1f33994e296bf360",
6633
+ "createdAt": 1788656728227,
6634
+ "profile": {},
6635
+ "samples": [],
6636
+ "records": [],
6637
+ "token": "b9d5e19626a4b1287c3b99b5b205237c9ef59c76ad5d992e77fe3e4489fe4f27",
6638
+ "tokenExpires": 1791248728227
6639
+ },
6640
+ {
6641
+ "username": "轮换8236",
6642
+ "passHash": "scrypt$c559ee39ef9941f21daca916c9522831$3e1a47d4bfd1e7a9f0a515dda6aa65610fa1f328894a11b6f3fa7da2c41ec037e56c1fff48f04d854351e012dadf91be8493263fceee464bddf1d2e7f791c102",
6643
+ "createdAt": 1788656728270,
6644
+ "profile": {},
6645
+ "samples": [],
6646
+ "records": [],
6647
+ "token": "c621eb2629313b1e6aab90a1db26970a620821639c8b2ea03ccfc13c6b047395",
6648
+ "tokenExpires": 1791248728314
6649
+ },
6650
+ {
6651
+ "username": "占位8326",
6652
+ "passHash": "scrypt$6890b74d8a4a8c337986d93fe785417c$eea609b0149683ffdd3734a8949fbbc6b0398cd27813f3dba938d70bd42504007f66ddda73cf591a6620544c7e1f6e9e23e5c2fd2e4f69b60d55c13ffb4276cf",
6653
+ "createdAt": 1788656728335,
6654
+ "profile": {},
6655
+ "samples": [],
6656
+ "records": [],
6657
+ "token": "8052720e19f2e6d8e0c1c3a58547faa976302f826dfbf36592859b904ce46595",
6658
+ "tokenExpires": 1791248728428
6659
+ },
6660
+ {
6661
+ "username": "甲8431",
6662
+ "passHash": "scrypt$24331e9122f4668629e7e3f47a66d4cc$96ad98c72e92f3aa20da504657005b2b53a5ce0e4097b15682d922df9d142e5970429e32af4e759220445b803f14237540bf4653c954be3cb1a80d1721a33fdc",
6663
+ "createdAt": 1788656728467,
6664
+ "profile": {},
6665
+ "samples": [],
6666
+ "records": [],
6667
+ "token": "f1cb56f7c7a4a5ecc5f22b0c6cf5e4cc211be4bd2757f702e698eaaacf8a4e43",
6668
+ "tokenExpires": 1791248728467
6669
+ },
6670
+ {
6671
+ "username": "乙8431",
6672
+ "passHash": "scrypt$77935be141f0a4f4db438e922a762304$eaed3aaf76450dc406b3edf82327a7b6de108c1162ecf91b4ae0ad136918ee7e9a517ea2c3c00262859319b6b0568bf27f217ca91178c43bfcabaea0d4b4254b",
6673
+ "createdAt": 1788656728511,
6674
+ "profile": {},
6675
+ "samples": [],
6676
+ "records": [],
6677
+ "token": "a42c05c0c350a70b63fa9b8ad7d06e44b48b396b1494c4be9838bcc1305907be",
6678
+ "tokenExpires": 1791248728511
6679
+ },
6680
+ {
6681
+ "username": "列删8549",
6682
+ "passHash": "scrypt$b59bb912b8ec3b772d6780f54e633b3c$467f6c4c7dc95d4509cab0e2c5956649b2df650f2fe2a6517bb7e6b9be3233fc649ec339785ad77054aeaec528f1c17a8f2d905c5aab337ca62ffcbbb9e3611b",
6683
+ "createdAt": 1788656728586,
6684
+ "profile": {},
6685
+ "samples": [],
6686
+ "records": [],
6687
+ "token": "39d7929d8ae538dc1ab2453f49a0239aa83922f9ca947023648ce25d10bef248",
6688
+ "tokenExpires": 1791248728586
6689
+ },
6690
+ {
6691
+ "username": "删乙8549",
6692
+ "passHash": "scrypt$9370c356aae368426704ad5f63d534c8$1596e1eab509523f1e62899c47bd135a7bc770c7583231d23128e452661a11437bfa10e8c867f174ca621a66213344fc962d4962d8acafef2f4292ee5a6f9768",
6693
+ "createdAt": 1788656728629,
6694
+ "profile": {},
6695
+ "samples": [],
6696
+ "records": [],
6697
+ "token": "a195fcc6854001dd3b94aad7aa386ce0a0aeda65189e48029b5e0c7e537e80ef",
6698
+ "tokenExpires": 1791248728629
6699
+ },
6700
+ {
6701
+ "username": "联调0077",
6702
+ "passHash": "scrypt$50edd92f69ee72f3e0a68201f1bc5d20$ffcf31876a36ea9f4b068ff1c65946446015e044d257ed94a19b7b696c0a3b8bfd71344509175632d11309869c7945e2ca2c231890aeec0af55edd5963cb4bcb",
6703
+ "createdAt": 1788656750131,
6704
+ "profile": {
6705
+ "birthDate": "1996-09-09",
6706
+ "birthHourIndex": 5,
6707
+ "gender": "男",
6708
+ "location": null
6709
+ },
6710
+ "samples": [],
6711
+ "records": [
6712
+ {
6713
+ "artId": "bazi",
6714
+ "createdAt": 1,
6715
+ "result": {
6716
+ "dayGZ": "壬申"
6717
+ }
6718
+ }
6719
+ ],
6720
+ "token": "dc26a50c9d65a0f58d1a9b2fb2fd3de2b761370363db2eaa3041544b3dca5397",
6721
+ "tokenExpires": 1791248750189
6722
+ },
6723
+ {
6724
+ "username": "鉴权0281",
6725
+ "passHash": "scrypt$65d1e113491e9f536756c140f8379375$08ac8f2710e866b22b6061062085ef23a0ee8b8898f69f20b59503339481f4374ced21eed1271a187b5c6f084993431ce12c5ff958ad8434aa5d1ccf8655071d",
6726
+ "createdAt": 1788656750313,
6727
+ "profile": {
6728
+ "birthDate": "1999-01-01"
6729
+ },
6730
+ "samples": [],
6731
+ "records": [],
6732
+ "token": "1e068fb703b577ab8bfb6ce010112d39490958c1f0de002ce917e938bb266ba4",
6733
+ "tokenExpires": 1791248750313
6734
+ },
6735
+ {
6736
+ "username": "他人0335",
6737
+ "passHash": "scrypt$2e4bd91e7c5e7eb5ed1624f4ad37f14c$7348e352613dab4da05a5a20ddcf8aa315588c1c3f1a883f9854fc15fe3d59a755b6b9beb48f0d3a464396460bbd7ddc07e8f5d90f8d5fbf2a1c88934e06fc65",
6738
+ "createdAt": 1788656750378,
6739
+ "profile": {},
6740
+ "samples": [],
6741
+ "records": [],
6742
+ "token": "cf0c100819459c4f6bcb2b2213cdf55ca478ff7de2610dc3fe297fcde0115d94",
6743
+ "tokenExpires": 1791248750378
6744
+ },
6745
+ {
6746
+ "username": "轮换0384",
6747
+ "passHash": "scrypt$3e6b39a19490d89c279575a1487f7ef6$f9116d02e9c83b349138ca308eb621f7c8206a68adef459c325537ec1ff76497cbc20d83e530313ae69f4ddd0de2aa4c7f97a30341b013173776c5e83b2cad57",
6748
+ "createdAt": 1788656750427,
6749
+ "profile": {},
6750
+ "samples": [],
6751
+ "records": [],
6752
+ "token": "d2e0fa44d0787c3dadae450bcfdcc2da8c21828e6cc20431b6781c61fb98a114",
6753
+ "tokenExpires": 1791248750470
6754
+ },
6755
+ {
6756
+ "username": "占位0481",
6757
+ "passHash": "scrypt$70e48545fcf9b358db4ea5d62c5c5e02$23678b8e4e3dc36e6da8600478c681e2f9b10c90abfbbd9fdd45326e223edf038cf0b475947ddafe9d2740498f3db104c08b78d6cc188e5e5c60ffb46c971872",
6758
+ "createdAt": 1788656750485,
6759
+ "profile": {},
6760
+ "samples": [],
6761
+ "records": [],
6762
+ "token": "32a48906aba1a838f03d1d53e5e1423212b39f275d8566d1b80c9f05ed6ad561",
6763
+ "tokenExpires": 1791248750571
6764
+ },
6765
+ {
6766
+ "username": "甲0573",
6767
+ "passHash": "scrypt$e7d97388393be7ff74627db02c86ab85$c00f3fbbc7ba849664db2887ee250dd69e34d234588f4c6ae0ff4bdceb256f24fa9b5f0789f81744b7fdde2c346365c5ad4d8bb5ea4c4f655afe2faff99231db",
6768
+ "createdAt": 1788656750606,
6769
+ "profile": {},
6770
+ "samples": [],
6771
+ "records": [],
6772
+ "token": "244f43af5d6ac85187ceb43fab5ee96b4e9e4fc6be1dd757e05728f5396b103c",
6773
+ "tokenExpires": 1791248750606
6774
+ },
6775
+ {
6776
+ "username": "乙0573",
6777
+ "passHash": "scrypt$ba780cc69712a61fc6298cc5f66cf4cd$621a99751fee71c830135212bd0a5dadb57dccc0bcc24de321324ae5856bc1f51035ec7efeb4679367e5a5438c0d9b93c982f6173bd62081cd55b6c74ff81a61",
6778
+ "createdAt": 1788656750646,
6779
+ "profile": {},
6780
+ "samples": [],
6781
+ "records": [],
6782
+ "token": "6ba780a775f3d206f96edf69bd146d5c3531c52ef3e08d87b56c5230b60accf9",
6783
+ "tokenExpires": 1791248750646
6784
+ },
6785
+ {
6786
+ "username": "列删0677",
6787
+ "passHash": "scrypt$60eebf3efab799e0966cf94d92017e14$340cb6dbe30f22d46e45b82b981849a957f17e35c6fbb0cf6a1cc00261456f6cf4968b49efe43ebbb1c18b043199f56c63591b39990c4afe6135f4748a8606df",
6788
+ "createdAt": 1788656750717,
6789
+ "profile": {},
6790
+ "samples": [],
6791
+ "records": [],
6792
+ "token": "403ad44e3d82f0cffb45efb7b32033e548f4cd4ab3d3f6296f48233d5ca4b9be",
6793
+ "tokenExpires": 1791248750717
6794
+ },
6795
+ {
6796
+ "username": "删乙0677",
6797
+ "passHash": "scrypt$a387cfd0aed6e0639c1a77371231856e$ae510248082fdc35243c8d64b71c424f9f98c410574a6eba1ff8de4d55765014740ca3895f0d06bea7b3c0b2ef39cc403622a62379a7be6ec5921f3e735446d7",
6798
+ "createdAt": 1788656750766,
6799
+ "profile": {},
6800
+ "samples": [],
6801
+ "records": [],
6802
+ "token": "c9f8abdca63dd948b07289e84586b6d73f47ed0bab242426a380dbc0a699ba28",
6803
+ "tokenExpires": 1791248750766
6804
+ },
6805
+ {
6806
+ "username": "联调0920",
6807
+ "passHash": "scrypt$b3e44e91990f43009245b75a26bc5668$5b7d46076ba87cdfd4431b4984a62a1815961a43944ac5d899d1c8c9d21e895e39a9e10fd67316dbf3247709a4749de350c566a9e721e22cfc42198e3f93b1d6",
6808
+ "createdAt": 1788692021155,
6809
+ "profile": {
6810
+ "birthDate": "1996-09-09",
6811
+ "birthHourIndex": 5,
6812
+ "gender": "男",
6813
+ "location": null
6814
+ },
6815
+ "samples": [],
6816
+ "records": [
6817
+ {
6818
+ "artId": "bazi",
6819
+ "createdAt": 1,
6820
+ "result": {
6821
+ "dayGZ": "壬申"
6822
+ }
6823
+ }
6824
+ ],
6825
+ "token": "7baadea6b1ec7c37594f9e52540764e3d4b49b5f3644f1284b69ec512d4f6f6d",
6826
+ "tokenExpires": 1791284021240
6827
+ },
6828
+ {
6829
+ "username": "鉴权1351",
6830
+ "passHash": "scrypt$17c68ceaef1053763a8ce408e79fb4ea$568506b83b12b2feb295fad6f9c86d9044a57fe4332afcd80a482a5794f7765b434072f54d1d5ccafc34dcb23db5753ef2fad0f0d6e532a18b156b2c225cd7e2",
6831
+ "createdAt": 1788692021404,
6832
+ "profile": {
6833
+ "birthDate": "1999-01-01"
6834
+ },
6835
+ "samples": [],
6836
+ "records": [],
6837
+ "token": "f5c91eb4a741bba362be18a02132c737e0c2ccb3f9a66daf2fc2dddca5d24af7",
6838
+ "tokenExpires": 1791284021404
6839
+ },
6840
+ {
6841
+ "username": "他人1431",
6842
+ "passHash": "scrypt$0121cbb769e65c8028224d58076e98fd$e352709575b831b54df4c479b1396911361ece31c70b839255ffc60207e9d868627f72188e756233f8a833bbc2b0e8ba24d9ecae101db80777e62a44912b8e33",
6843
+ "createdAt": 1788692021476,
6844
+ "profile": {},
6845
+ "samples": [],
6846
+ "records": [],
6847
+ "token": "6371725afe5a009162d9f5c994692b8e795a22b42831b300db262dab395df6f9",
6848
+ "tokenExpires": 1791284021476
6849
+ },
6850
+ {
6851
+ "username": "轮换1483",
6852
+ "passHash": "scrypt$8255e545c833fb6419a28e1a9cf53e88$e3479f165d360d20116f31eb75e2ee2cbe0bb18bb37990435de2cc24ad41d529b0348dbddf169006adf7f6538890ea68515eca23dc2296a99e4ce7a3bf685d19",
6853
+ "createdAt": 1788692021532,
6854
+ "profile": {},
6855
+ "samples": [],
6856
+ "records": [],
6857
+ "token": "04795b1ed52fc3bd1b44621b2128b016d585986616fddcf8988ed7221786fd5b",
6858
+ "tokenExpires": 1791284021581
6859
+ },
6860
+ {
6861
+ "username": "占位1599",
6862
+ "passHash": "scrypt$b5e1f0efa5a1c303a00152459ce7b730$30e55da9c26444dfaad83fe9f4a0e969d5dd43c0191c377c045440164813d4b618213ff20005c48ee24b6506dd9d8fc567dff6b18295acf0d76f5e9f4e20a4ca",
6863
+ "createdAt": 1788692021607,
6864
+ "profile": {},
6865
+ "samples": [],
6866
+ "records": [],
6867
+ "token": "a064961555076b251c344853ca3e21686685aad8d232b865245201dc6da000ab",
6868
+ "tokenExpires": 1791284021723
6869
+ },
6870
+ {
6871
+ "username": "甲1725",
6872
+ "passHash": "scrypt$5c671cee1ead0df6bd279823b1de0db4$55b040754e3d50180963cc374f2806ac7261198bb843d47f3274c5ec1b32eb19176a5af31c12db2053201a0e0c63e3214e534dd2a56b0b4b88873480ce16ba5a",
6873
+ "createdAt": 1788692021773,
6874
+ "profile": {},
6875
+ "samples": [],
6876
+ "records": [],
6877
+ "token": "aeafd9be9260fa23168b020c58ea2098f2f9666010e87c0f3d87b1b373833b8a",
6878
+ "tokenExpires": 1791284021773
6879
+ },
6880
+ {
6881
+ "username": "乙1725",
6882
+ "passHash": "scrypt$fbe8cd4a21204ba6087424b10231fa46$0e281fe8051f1b32f5146aabb3d905a9910c8cbd4e06c5b73260a850e3eb2de548040df747a29632c58bf00eadf9dc2afd3669950992eec0adf08733f1114e1f",
6883
+ "createdAt": 1788692021836,
6884
+ "profile": {},
6885
+ "samples": [],
6886
+ "records": [],
6887
+ "token": "a05b9198f87e59b3c20d6aa80eabfa7d3089355255c7c3d4e7a691eba3eeb207",
6888
+ "tokenExpires": 1791284021836
6889
+ },
6890
+ {
6891
+ "username": "列删1876",
6892
+ "passHash": "scrypt$7d421308bd2b8975c59718958ed68d79$ae6cc92bccb534c8e415d19bdbcd22971756713a08a0214bf53bfcaa16d742c4dd38879ab9cdde255467fcb67b7fe4e5840eb13e27d021b63785ecd6e1fa150b",
6893
+ "createdAt": 1788692021926,
6894
+ "profile": {},
6895
+ "samples": [],
6896
+ "records": [],
6897
+ "token": "3b6d939c0471028737694c2592609a66ecade056ed6f2cf47396956b0019ef74",
6898
+ "tokenExpires": 1791284021926
6899
+ },
6900
+ {
6901
+ "username": "删乙1876",
6902
+ "passHash": "scrypt$34ebafd6be46114ce059ad7793be3419$57d8fa3b82ea11bd0f0429ec996d17b649d4ad8749fba48cf0010489ae4ba1bb349436a6f45a867cdb1fdbea09f514b2a3b3d6efa0efdd97662a469f428a4b07",
6903
+ "createdAt": 1788692021985,
6904
+ "profile": {},
6905
+ "samples": [],
6906
+ "records": [],
6907
+ "token": "f30476cd4f4afdf46c58d532b5a08951438f203e6deb771cd142a4184901f93d",
6908
+ "tokenExpires": 1791284021985
6909
+ },
6910
+ {
6911
+ "username": "联调5919",
6912
+ "passHash": "scrypt$61617ac7655aacf7e85411d201abb354$010c6205adfa1f6a06a117e65e1419568c28dd40d167efb04439595116d192b6e73f4534b053407858cbc4e3a9a5b2e26c32220b67eea8fe9599f87721027f84",
6913
+ "createdAt": 1788692036015,
6914
+ "profile": {
6915
+ "birthDate": "1996-09-09",
6916
+ "birthHourIndex": 5,
6917
+ "gender": "男",
6918
+ "location": null
6919
+ },
6920
+ "samples": [],
6921
+ "records": [
6922
+ {
6923
+ "artId": "bazi",
6924
+ "createdAt": 1,
6925
+ "result": {
6926
+ "dayGZ": "壬申"
6927
+ }
6928
+ }
6929
+ ],
6930
+ "token": "436cfd937f13766e21b571637463981666d9b91ea89731a0fa543ea1dccd328d",
6931
+ "tokenExpires": 1791284036080
6932
+ },
6933
+ {
6934
+ "username": "鉴权6197",
6935
+ "passHash": "scrypt$394a1414484e881b248562f7f1cafa1f$852b116f573ceca771bd0516eae28dc7d70846102c863f3335cc1721a5a3060fcc42dce4487dc2c61f8295def4d800307df29e99d70dbae101723af5747bb1f5",
6936
+ "createdAt": 1788692036235,
6937
+ "profile": {
6938
+ "birthDate": "1999-01-01"
6939
+ },
6940
+ "samples": [],
6941
+ "records": [],
6942
+ "token": "c75cabdfaca45b2d80a15f37c2206c85476dd6bb2ebb768c6a86555a208697be",
6943
+ "tokenExpires": 1791284036235
6944
+ },
6945
+ {
6946
+ "username": "他人6257",
6947
+ "passHash": "scrypt$52ae4f2d6607fa810615fd7111b61581$464dd9a90e29744a6e7d5137aceab33387d4ff88441664da9f9edcc9f17a92f30a1401d376568a54a7f63db2be6e1e4b3077142f6797238727947c5347c0a102",
6948
+ "createdAt": 1788692036291,
6949
+ "profile": {},
6950
+ "samples": [],
6951
+ "records": [],
6952
+ "token": "bbd9260fd82eb4e3bfba7c032c696dfb413fa09bd033494ba925018023185a15",
6953
+ "tokenExpires": 1791284036291
6954
+ },
6955
+ {
6956
+ "username": "轮换6296",
6957
+ "passHash": "scrypt$14f754e03aa26a5ce519b58ff93670d2$a084faa6a518fccc99404b1902ee97ff5061a8abfc131896c5ab8cad461207ba2d4ca72ee5f18a2fc83ba07fc38ed5156810ec3bdb0fd45cb354834d7c35ada5",
6958
+ "createdAt": 1788692036351,
6959
+ "profile": {},
6960
+ "samples": [],
6961
+ "records": [],
6962
+ "token": "6e1c01a41071ccd0e9353ac0d64e48abac3770cbbeb9f27ae880a21670b612b7",
6963
+ "tokenExpires": 1791284036412
6964
+ },
6965
+ {
6966
+ "username": "占位6423",
6967
+ "passHash": "scrypt$03bf137f279940b8b585b69b063b1866$de6543be25426cbccc9e78bdc422289cddb12601f571a0c828105e43924b4b83fa2faaf8b5c2f521ae9e8eb302ff03e65ddc8f2af014db0f2215cf449afeaccf",
6968
+ "createdAt": 1788692036428,
6969
+ "profile": {},
6970
+ "samples": [],
6971
+ "records": [],
6972
+ "token": "5b83f27fa338894fcd8fdf75c02717e6d08023755284206f770ea68af7d66d4f",
6973
+ "tokenExpires": 1791284036535
6974
+ },
6975
+ {
6976
+ "username": "甲6539",
6977
+ "passHash": "scrypt$a94f90f6b3566526a193162c0bf54821$33af76e18aa41592f6e2530362a0f02cbc4b8c6556dd9df6cfff73d4f76e1a66feabc6ab70a157616857903a287a71719307ed32472c98e6dcbb1dfbd97161b0",
6978
+ "createdAt": 1788692036587,
6979
+ "profile": {},
6980
+ "samples": [],
6981
+ "records": [],
6982
+ "token": "a24f8c1d3f137d47956e3a97f4acc43e94b040d0b6a0314854b07e04b00667e7",
6983
+ "tokenExpires": 1791284036587
6984
+ },
6985
+ {
6986
+ "username": "乙6539",
6987
+ "passHash": "scrypt$f7c6960d9bf33b0796b59ea09580c114$92b1b8ac38b2c0f8d2e49d620fd220f577495f42d9f00f69825537a0e0b8a24f38e179b01c93c6692e0688199c4a8831a1b1a2006b132fbd28acc9593c9d2802",
6988
+ "createdAt": 1788692036642,
6989
+ "profile": {},
6990
+ "samples": [],
6991
+ "records": [],
6992
+ "token": "ccaff7ec65a9e580ca22a6e382942e30dc3e925f552167d31caa8cf3a9064182",
6993
+ "tokenExpires": 1791284036642
6994
+ },
6995
+ {
6996
+ "username": "列删6690",
6997
+ "passHash": "scrypt$a77656ae195c6e0070ed70c9e2becf9d$ffcf0b222081213532a537289305c63eda9666225f64b0ed70f6a416338a258d17108de0bf05a46c903109701204726bf48d6ebb4cc131b2258c351f549c4003",
6998
+ "createdAt": 1788692036750,
6999
+ "profile": {},
7000
+ "samples": [],
7001
+ "records": [],
7002
+ "token": "d442a4a6f620575bb9a68477443501f74ab4cd6d5dc9d8ab26f67a7dc8bac3b8",
7003
+ "tokenExpires": 1791284036750
7004
+ },
7005
+ {
7006
+ "username": "删乙6690",
7007
+ "passHash": "scrypt$3ac73506186bbba8287cca53f374ca97$98fcf13fc8b5ca54c3b1dd4eb6a1bec3d3e6557aa734afcecaa3a3227b3dedcc69ece17372fc1205a58972003fda2c42f70d368dbfca2df9333764c02f1fd261",
7008
+ "createdAt": 1788692036799,
7009
+ "profile": {},
7010
+ "samples": [],
7011
+ "records": [],
7012
+ "token": "2b11d5c2b970d9bc3008817430f29f91ec5fa9626800751ea2a2975f5fea862c",
7013
+ "tokenExpires": 1791284036799
6489
7014
  }
6490
7015
  ]
6491
7016
  }
Binary file
@@ -8,6 +8,8 @@ import { buildMessages, buildReportMessages, buildStep1Messages, buildStep2Messa
8
8
  import { chatOnce, chatStream, activeProvider, providerStatus, lastFinishReason } from '../services/llmProvider.js';
9
9
  import { getDivination, attachReport, markAiFailed } from '../services/divineStore.js';
10
10
  import { verifyRelatives, fixFamilyRelatives } from '../services/relativesCheck.js';
11
+ import { orchestrateZiweiDeep } from '../services/orchestrator.js';
12
+ import { MINGPAN_TEMPLATE } from '../services/promptBuilder.js';
11
13
 
12
14
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
13
15
 
@@ -46,7 +48,7 @@ router.get('/providers', (_req, res) => {
46
48
 
47
49
  // 非流式解读(保底通道)
48
50
  router.post('/interpret', async (req, res) => {
49
- const { artId, question, divineId, semantic, profile, report, fit, username } = req.body;
51
+ const { artId, question, divineId, semantic, profile, report, fit, username, orchestrate } = req.body;
50
52
  if (!artId) return res.status(400).json({ error: '缺少必要参数' });
51
53
  try {
52
54
  // 从 SQLite 读排盘数据(v6:不再信任前端直传 resultRaw)
@@ -61,6 +63,23 @@ router.post('/interpret', async (req, res) => {
61
63
  } else {
62
64
  return res.status(400).json({ error: 'DIVINE_REQUIRED', message: '请先起占(divineId 缺失)' });
63
65
  }
66
+ // 真·多 Agent 编排模式(v1:紫微深度三步链式)
67
+ if (orchestrate === 'ziwei-deep' && artId === 'ziwei') {
68
+ const { full } = await orchestrateZiweiDeep(resultRaw, question, MINGPAN_TEMPLATE, (recProfile as any)?.gender || (profile as any)?.gender);
69
+ const parsed2 = parseReport(full, 'mingpan');
70
+ const relErrs2 = verifyRelatives('ziwei', resultRaw, parsed2);
71
+ if (relErrs2.length && relErrs2.length <= 6) {
72
+ console.warn('[ai] 六亲表述矛盾,定向修正:', relErrs2.join(';'));
73
+ const fixed2 = await fixFamilyRelatives('ziwei', resultRaw, parsed2, relErrs2);
74
+ if (fixed2) parsed2.family = fixed2 as any;
75
+ }
76
+ if (parsed2.quality === 'ok') {
77
+ attachReport(divineId, parsed2, 'ok');
78
+ return res.json({ provider: activeProvider()?.id, report: parsed2, sections: parseSections(full), orchestrated: true });
79
+ }
80
+ markAiFailed(divineId, 'ziwei', 'mingpan', '编排质量未达标', full.slice(0, 4000));
81
+ return res.status(502).json({ error: 'AI_REPORT_INVALID', message: 'AI 输出不符合报告结构,请重试' });
82
+ }
64
83
  // 统一报告模式:两步管线(Step1 盘面解析 → Step2 深度报告),Step1 失败降级单步
65
84
  const kind = ['bazi', 'ziwei', 'astrology'].includes(artId) ? 'mingpan' : 'zhanwen';
66
85
  let step1Text = '';
@@ -1,15 +1,6 @@
1
1
  // 排盘路由:登录用户起占 → 后端引擎计算 → SQLite 入库 → 返回 resultRaw + display
2
2
  import { Router } from 'express';
3
- import { baziCalc } from '../../../shared/core/engine/bazi.js';
4
- import { ziweiCalc } from '../../../shared/core/engine/ziwei.js';
5
- import { astrologyCalc } from '../../../shared/core/engine/astrology.js';
6
- import { qimenCalc } from '../../../shared/core/engine/qimen.js';
7
- import { meihuaCalc } from '../../../shared/core/engine/meihua.js';
8
- import { liuyaoCalc } from '../../../shared/core/engine/liuyao.js';
9
- import { liurenCalc } from '../../../shared/core/engine/liuren.js';
10
- import { xiaoliurenCalc } from '../../../shared/core/engine/xiaoliuren.js';
11
- import { tarotDraw } from '../../../shared/core/engine/tarot.js';
12
- import { allTarotSpreads } from '../../../shared/core/data/tarotSpreads.js';
3
+ import { chartCalc } from '../../../shared/core/engine/chart.js';
13
4
  import { createDivination, listDivinations, getDivination, deleteDivination } from '../services/divineStore.js';
14
5
  import fs from 'fs';
15
6
  import path from 'path';
@@ -91,56 +82,9 @@ router.post('/', (req, res) => {
91
82
 
92
83
  let resultRaw: unknown;
93
84
  try {
94
- switch (artId) {
95
- case 'bazi': {
96
- const i = inputs as any;
97
- resultRaw = baziCalc({ y: i.y, m: i.m, d: i.d, hourIndex: i.hourIndex, time: i.time, gender: i.gender, location: i.location });
98
- break;
99
- }
100
- case 'ziwei': {
101
- const i = inputs as any;
102
- resultRaw = ziweiCalc({ ganzhi: i.ganzhi, month: i.month, day: i.day, hour: i.hour, time: i.time, location: i.location, gender: i.gender, birthYear: i.birthYear });
103
- break;
104
- }
105
- case 'astrology': {
106
- const i = inputs as any;
107
- resultRaw = astrologyCalc(i.y, i.m, i.d, i.hour || 0, i.min || 0, i.lng, i.lat);
108
- break;
109
- }
110
- case 'qimen': {
111
- resultRaw = qimenCalc({ datetime: (inputs as any).datetime || new Date() });
112
- break;
113
- }
114
- case 'meihua': {
115
- const i = inputs as any;
116
- resultRaw = meihuaCalc({ mode: i.mode || 'time', n1: i.n1, n2: i.n2, n3: i.n3, now: i.now ? new Date(i.now) : undefined });
117
- break;
118
- }
119
- case 'liuyao': {
120
- const now = new Date();
121
- resultRaw = liuyaoCalc(undefined, { y: now.getFullYear(), m: now.getMonth() + 1, d: now.getDate() });
122
- break;
123
- }
124
- case 'liuren': {
125
- resultRaw = liurenCalc((inputs as any).datetime || new Date());
126
- break;
127
- }
128
- case 'xiaoliuren': {
129
- const i = inputs as any;
130
- resultRaw = xiaoliurenCalc(i.mode || 'time', i.m, i.d, i.h, i.n1, i.n2, i.n3);
131
- break;
132
- }
133
- case 'tarot': {
134
- const i = inputs as any;
135
- const cards = tarotDraw(Math.max(1, i.n || 3));
136
- const spread = allTarotSpreads().find((s: any) => s.id === i.spread) || allTarotSpreads()[0];
137
- resultRaw = { spread: spread || { id: 'three', name: '圣三角', description: '', positions: [] }, cards };
138
- break;
139
- }
140
- default:
141
- return res.status(400).json({ error: '术无此名' });
142
- }
85
+ resultRaw = chartCalc(artId, inputs);
143
86
  } catch (e: any) {
87
+ if (e?.message?.includes('术无此名')) return res.status(400).json({ error: '术无此名' });
144
88
  console.error('[divine] 推演异常:', e);
145
89
  return res.status(500).json({ error: 'DIVINE_FAILED', message: '推演未应机' });
146
90
  }
@@ -0,0 +1,106 @@
1
+ // 真·多 Agent 编排 v1(guanwei-pro 雏形):
2
+ // 命盘类分步链式推理——Step A 结构解构 → Step B 宫位/星曜专题 → Step C 汇总成报告
3
+ // 与单轮/两步管线区别:每一步独立 LLM 调用、上一步输出作为下一步唯一事实源,步间事实约束
4
+ // 设计:每步 prompt 短而聚焦(降低单次输出幻觉面),关键事实链(chartBrief 十二宫清单)全程逐字引用
5
+ import { chartBrief } from './chartBrief.js';
6
+ import { duanyuPromptBlock } from './duanyu.js';
7
+ import { chatOnce } from './llmProvider.js';
8
+ import { nowFact } from './promptBuilder.js';
9
+
10
+ export type OrchestrateMode = 'off' | 'ziwei-deep';
11
+
12
+ // 每步输出 ≤ 400 字的事实性专题,不做人生论断(论断集中在最后汇总步)
13
+ function stepMessages(agentName: string, step: string, brief: string, prior: string[], question?: string): { role: string; content: string }[] {
14
+ const priorBlock = prior.length
15
+ ? prior.map((p, i) => `【上一步结论 ${i + 1}】${p}`).join('\n')
16
+ : '(本步为首步)';
17
+ const system = [
18
+ '你是一位' + agentName + ',负责紫微斗数分步推理中的一个环节。',
19
+ '【最高约束】所有盘面事实(宫位/主星/辅星/四化/亮度)必须逐字引用下方【盘面事实】,不得推算、不得编造。',
20
+ '【本步任务】' + step,
21
+ '【输出】只输出一个 JSON 对象 {"points": ["要点1", "要点2", ...]}(3-6 条,每条 ≤80 字,只陈述盘面结构与通义,不做人生论断)。',
22
+ ].join('\n');
23
+ const user = [
24
+ question ? '【所问】' + question + '(仅作背景,本步不回答)' : '',
25
+ '【盘面事实(不可更改)】',
26
+ brief,
27
+ '',
28
+ nowFact(),
29
+ '',
30
+ priorBlock,
31
+ '',
32
+ '请输出本步 JSON。',
33
+ ].filter(Boolean).join('\n');
34
+ return [
35
+ { role: 'system', content: system },
36
+ { role: 'user', content: user },
37
+ ];
38
+ }
39
+
40
+ function parsePoints(text: string): string[] {
41
+ try {
42
+ const obj = JSON.parse(text);
43
+ if (Array.isArray(obj.points)) return obj.points.map(String);
44
+ } catch { /* 非 JSON 时按行切 */ }
45
+ return text.split('\n').map(l => l.trim()).filter(l => l && !l.startsWith('{') && !l.startsWith('}')).slice(0, 8);
46
+ }
47
+
48
+ // 汇总步:把前面各步要点 + 盘面事实汇总成完整解读
49
+ function finalMessages(agentName: string, artName: string, schemaTemplate: string, brief: string, steps: string[], question?: string, gender?: string): { role: string; content: string }[] {
50
+ const stepBlock = steps.map((s, i) => `【分步推演 ${i + 1}】${s}`).join('\n\n');
51
+ const system = [
52
+ '你是一位' + agentName + '。',
53
+ '【任务】基于下面给出的【盘面事实】与【分步推演结论】,生成完整解读报告(JSON)。',
54
+ '【盘面事实一致性 · 最高约束】所有盘面事实必须与【盘面事实】逐字一致,不得编造或自行推算;六亲/宫位论断须注明盘面依据。',
55
+ '【质量】内容充实、具体到场景与行为;不作绝对化断言;结尾附免责声明。',
56
+ '【输出 Schema(字段名不可更改)】' + schemaTemplate,
57
+ ].join('\n');
58
+ const user = [
59
+ '【性别语境】' + (gender || '未录'),
60
+ question ? '【所问之事】' + question : '',
61
+ '【盘面事实(不可更改)】',
62
+ brief,
63
+ '',
64
+ stepBlock,
65
+ '',
66
+ '请输出完整解读报告 JSON。',
67
+ ].filter(Boolean).join('\n');
68
+ return [
69
+ { role: 'system', content: system },
70
+ { role: 'user', content: user },
71
+ ];
72
+ }
73
+
74
+ /**
75
+ * 紫微深度编排:三步独立推理后汇总。
76
+ * 返回 { full, steps }(full 为最终报告文本;steps 为各步要点便于调试/审计)
77
+ */
78
+ export async function orchestrateZiweiDeep(
79
+ resultRaw: unknown,
80
+ question: string | undefined,
81
+ schemaTemplate: string,
82
+ gender?: string,
83
+ ): Promise<{ full: string; steps: string[] }> {
84
+ const brief = chartBrief('ziwei', resultRaw);
85
+ const duanyu = duanyuPromptBlock('ziwei', resultRaw);
86
+ const steps: string[] = [];
87
+
88
+ const STEPS = [
89
+ '解构命盘骨架:命宫地支/五行局/紫微落宫/身宫/生年四化/主星布局格局,提炼盘的总体气质。',
90
+ '专题拆解:依十二宫主题(命/兄弟/夫妻/子女/财帛/疾厄/迁移/交友/官禄/田宅/福德/父母)逐宫提炼其星曜组合的侧重,命宫重点。',
91
+ '行运推演:结合当前大限(年龄段/宫位/主星)与流年宫位,提炼行运主题与关键节点。',
92
+ ];
93
+ const prior: string[] = [];
94
+ for (const [i, step] of STEPS.entries()) {
95
+ const msgs = stepMessages('紫微斗数专家', step, brief, prior, question);
96
+ const text = await chatOnce(msgs as any);
97
+ const pts = parsePoints(text);
98
+ prior.push(pts.join(';'));
99
+ steps.push(pts.join(';'));
100
+ console.log(`[orchestrator] Step ${i + 1} 完成(${pts.length} 要点)`);
101
+ }
102
+ // 汇总步:含断语引证
103
+ const finalSys = finalMessages('紫微斗数专家', '紫微斗数', schemaTemplate, brief + (duanyu ? '\n' + duanyu : ''), prior, question, gender);
104
+ const full = await chatOnce(finalSys as any);
105
+ return { full, steps };
106
+ }
@@ -53,7 +53,7 @@ const ZHANWEN_SAMPLE = {
53
53
  disclaimer: '免责声明',
54
54
  suitability: { suitable: true, note: '适问性说明 2-3 句', suggestion: '换问建议或无' },
55
55
  };
56
- const MINGPAN_TEMPLATE = JSON.stringify(MINGPAN_SAMPLE, null, 2);
56
+ export const MINGPAN_TEMPLATE = JSON.stringify(MINGPAN_SAMPLE, null, 2);
57
57
  const ZHANWEN_TEMPLATE = JSON.stringify(ZHANWEN_SAMPLE, null, 2);
58
58
  const MINGPAN_ARTS = ['bazi', 'ziwei', 'astrology'];
59
59
 
@@ -80,7 +80,7 @@ function hourUnknownNote(profile?: unknown): string {
80
80
 
81
81
  // 命主已知人生经历 → 解读校准注入(报告须呼应该年份事件、不得与其矛盾)
82
82
  // 当前公历时间事实:注入给 AI,防止其自行推算年份(占问类无出生档案,AI 不知道"现在是哪年")
83
- function nowFact(): string {
83
+ export function nowFact(): string {
84
84
  const d = new Date();
85
85
  const p = (n: number) => String(n).padStart(2, '0');
86
86
  const week = '日一二三四五六'[d.getDay()];
@@ -0,0 +1,76 @@
1
+ // 九术排盘统一调度:artId + inputs → resultRaw
2
+ // 唯一算法副本(divine 路由 / packages/guanwei-api / MCP / 未来托管共用,禁止各层另写 switch)
3
+ import { baziCalc } from './bazi.js';
4
+ import { ziweiCalc } from './ziwei.js';
5
+ import { astrologyCalc } from './astrology.js';
6
+ import { qimenCalc } from './qimen.js';
7
+ import { meihuaCalc } from './meihua.js';
8
+ import { liuyaoCalc } from './liuyao.js';
9
+ import { liurenCalc } from './liuren.js';
10
+ import { xiaoliurenCalc } from './xiaoliuren.js';
11
+ import { tarotDraw } from './tarot.js';
12
+ import { allTarotSpreads } from '../data/tarotSpreads.js';
13
+
14
+ export const CHART_ARTS = ['bazi', 'ziwei', 'astrology', 'qimen', 'meihua', 'liuyao', 'liuren', 'xiaoliuren', 'tarot'] as const;
15
+ export type ChartArtId = typeof CHART_ARTS[number];
16
+
17
+ /**
18
+ * 九术排盘(纯函数):artId + inputs → resultRaw
19
+ * 抛错:未知术名 / 输入不合法 → Error(调用方转 400)
20
+ */
21
+ export function chartCalc(artId: string, inputs: any): unknown {
22
+ switch (artId) {
23
+ case 'bazi': {
24
+ const i = inputs || {};
25
+ if (i.y == null || i.m == null || i.d == null) throw new Error('BaziInput 需 y/m/d');
26
+ return baziCalc({ y: i.y, m: i.m, d: i.d, hourIndex: i.hourIndex, time: i.time, gender: i.gender, location: i.location });
27
+ }
28
+ case 'ziwei': {
29
+ const i = inputs || {};
30
+ return ziweiCalc({ ganzhi: i.ganzhi, month: i.month, day: i.day, hour: i.hour, time: i.time, location: i.location, gender: i.gender, birthYear: i.birthYear });
31
+ }
32
+ case 'astrology': {
33
+ const i = inputs || {};
34
+ return astrologyCalc(i.y, i.m, i.d, i.hour || 0, i.min || 0, i.lng, i.lat);
35
+ }
36
+ case 'qimen': {
37
+ return qimenCalc({ datetime: inputs?.datetime ? new Date(inputs.datetime) : new Date() });
38
+ }
39
+ case 'meihua': {
40
+ const i = inputs || {};
41
+ return meihuaCalc({ mode: i.mode || 'time', n1: i.n1, n2: i.n2, n3: i.n3, now: i.now ? new Date(i.now) : undefined });
42
+ }
43
+ case 'liuyao': {
44
+ const now = new Date();
45
+ return liuyaoCalc(undefined, { y: now.getFullYear(), m: now.getMonth() + 1, d: now.getDate() });
46
+ }
47
+ case 'liuren': {
48
+ return liurenCalc(inputs?.datetime ? new Date(inputs.datetime) : new Date());
49
+ }
50
+ case 'xiaoliuren': {
51
+ const i = inputs || {};
52
+ return xiaoliurenCalc(i.mode || 'time', i.m, i.d, i.h, i.n1, i.n2, i.n3);
53
+ }
54
+ case 'tarot': {
55
+ const i = inputs || {};
56
+ const cards = tarotDraw(Math.max(1, i.n || 3));
57
+ const spread = allTarotSpreads().find((s: any) => s.id === i.spread) || allTarotSpreads()[0];
58
+ return { spread: spread || { id: 'three', name: '圣三角', description: '', positions: [] }, cards };
59
+ }
60
+ default:
61
+ throw new Error('术无此名: ' + artId);
62
+ }
63
+ }
64
+
65
+ /** 每术输入参数说明(/v1/arts 能力清单用) */
66
+ export const CHART_INPUT_SCHEMA: Record<string, Record<string, string>> = {
67
+ bazi: { y: '公历年', m: '公历月', d: '公历日', hourIndex: '时辰 0-11(0=子)', gender: '男/女', location: '{lng,lat,province,city,district}' },
68
+ ziwei: { ganzhi: '年干支', month: '农历月', day: '农历日', hour: '时辰 0-11', gender: '男/女', location: '{lng,lat}' },
69
+ astrology: { y: '公历年', m: '月', d: '日', hour: '时 0-23', min: '分', lng: '经度', lat: '纬度' },
70
+ qimen: { datetime: '起局时刻 ISO 字符串' },
71
+ meihua: { mode: 'time|number', n1: '报数1', n2: '报数2', n3: '报数3' },
72
+ liuyao: {},
73
+ liuren: { datetime: '起课时刻 ISO 字符串' },
74
+ xiaoliuren: { mode: 'time', m: '农历月', d: '农历日', h: '时辰 0-11' },
75
+ tarot: { n: '抽牌数(默认3)', spread: '牌阵 id' },
76
+ };