v6.9.4: AI解读集成 — copse.top GPT 中转接入

## 架构重构
- 计算层: 纯本地 (SwissEph WASM 或 localhost:5200)
- AI解读层: copse.top GPT API 生成个性化解读
- 清晰的职责分离

## flow
1. 用户输入出生信息 → 本地计算星盘
2. 结构化数据 → copse.top → GPT 解读
3. AI 解读直接展示在星盘下方

## 修改
- api-bridge.js v4.0: 全新架构
- main.js: triggerAIReading() 自动触发
- index.html: chart-summary 展示区
This commit is contained in:
732642856
2026-06-12 07:21:00 +08:00
parent a816825126
commit 6750368bc8
3 changed files with 155 additions and 143 deletions
+116 -124
View File
@@ -1,146 +1,138 @@
/**
* API Bridge v3.0
* 自动检测环境:本地用 localhost:5200,云端用 copse.top
* 均不可达时回退到浏览器端 JS 引擎
* API Bridge v4.0
*
* 架构:
* 计算 → 纯本地 (SwissEph WASM 或 localhost:5200 Python API)
* AI解读 → copse.top (GPT API 中转)
*/
const API_BASE = (() => {
const host = window.location.hostname;
if (host === 'localhost' || host === '127.0.0.1' || host.startsWith('192.168.')) {
return 'http://localhost:5200';
}
return 'https://copse.top';
})();
const AI_BASE = 'https://copse.top';
const AI_KEY = 'sk-828a787bd2bb69d2d4707e8c05ae5cfe81b13de7be1db7f85932d49ed72e4c6a';
const API_KEY = 'sk-828a787bd2bb69d2d4707e8c05ae5cfe81b13de7be1db7f85932d49ed72e4c6a';
// ═══════════════════════════════════════════════════════════════
// 计算层 — 纯本地,不需要服务器
// ═══════════════════════════════════════════════════════════════
const API_CACHE = {};
const CACHE_TTL = 300000; // 5分钟缓存
async function apiFetch(endpoint, body = {}) {
const cacheKey = endpoint + JSON.stringify(body);
if (API_CACHE[cacheKey] && Date.now() - API_CACHE[cacheKey].ts < CACHE_TTL) {
return API_CACHE[cacheKey].data;
}
async function computeWithPython(birthData) {
try {
const resp = await fetch(`${API_BASE}${endpoint}`, {
method: body && Object.keys(body).length > 0 ? 'POST' : 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${API_KEY}`,
},
body: body && Object.keys(body).length > 0 ? JSON.stringify(body) : undefined,
const resp = await fetch('http://localhost:5200/api/chart', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(birthData),
});
const data = await resp.json();
API_CACHE[cacheKey] = { data, ts: Date.now() };
return data;
} catch (e) {
console.warn('[API] fetch failed:', e.message);
return null;
if (data.success) {
console.log('[Compute] ✅ Python API v' + data.version);
return data;
}
} catch(e) {
console.log('[Compute] Python API 不可用, 回退JS引擎');
}
return null;
}
// ═══════════════════════════════════════════════════════════════
// AI 解读层 — 通过 copse.top 调用 GPT
// ═══════════════════════════════════════════════════════════════
async function aiReading(chartData, options = {}) {
const { style = 'deep', focus = '全部' } = options;
const prompt = buildReadingPrompt(chartData, style, focus);
try {
const resp = await fetch(AI_BASE + '/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + AI_KEY,
},
body: JSON.stringify({
model: 'gpt-4',
messages: [
{ role: 'system', content: SYSTEM_PROMPT },
{ role: 'user', content: prompt },
],
temperature: 0.7,
max_tokens: 2000,
}),
});
const data = await resp.json();
const text = data?.choices?.[0]?.message?.content || '';
return { success: true, reading: text, style, focus };
} catch(e) {
console.warn('[AI] 解读失败:', e.message);
return { success: false, error: e.message };
}
}
async function checkApiAvailable() {
const r = await apiFetch('/api/health');
if (r && r.status === 'ok') {
console.log(`[API] ✅ Connected to Jyotish API v${r.version} (${r.modules})`);
return true;
const SYSTEM_PROMPT = `你是印度占星(Jyotish/Vedic Astrology)专业解盘师。
基于提供的精确星盘数据,给出个性化、严谨、有深度的解读。
规则:
- 每次判断必须引用星盘中的具体配置(行星-星座-宫位-Dasha)
- 避免刻板教条("土星落陷=不好"),必须结合多配置综合判断
- 语气专业但不冷漠,像面对面交谈
- 给出具体的时间窗口,不要说"未来几年"
- 如果某个配置有多种可能性,列出2-3种最可能的走向`;
function buildReadingPrompt(chartData, style, focus) {
const asc = chartData.ascendant?.sign || '?';
const planets = chartData.planets || {};
const yogas = (chartData.yogas || []).slice(0, 15);
const dasha = chartData.dasha || {};
let prompt = `请基于以下星盘数据给出${style === 'quick' ? '简明' : '深度'}解读。\n\n`;
prompt += `【焦点领域】${focus}\n\n`;
prompt += `【上升星座】${asc}\n\n`;
prompt += `【行星落位】\n`;
for (const [name, data] of Object.entries(planets)) {
const sign = typeof data === 'object' ? (data.sign || '?') : data;
const house = typeof data === 'object' ? (data.house || '?') : '?';
prompt += ` ${name}: ${sign} ${house}\n`;
}
return false;
}
async function apiComputeFullChart(birthData) {
const result = await apiFetch('/api/chart', birthData);
if (!result || !result.success) {
throw new Error(result?.error || '计算失败,请检查输入信息');
if (yogas.length > 0) {
prompt += `\n【主要Yoga】\n`;
for (const y of yogas) {
prompt += ` ${y.name || ''}: ${(y.combination || y.desc || '').slice(0, 60)}\n`;
}
}
return result;
if (dasha.current_md) {
prompt += `\n【当前大运】${dasha.current_md} (${dasha.current_ad || ''})\n`;
}
prompt += `\n请分析:`;
if (focus === '事业' || focus === '全部') prompt += `\n1) 事业方向与关键时间节点`;
if (focus === '感情' || focus === '全部') prompt += `\n2) 感情婚姻特征与时机`;
if (focus === '健康' || focus === '全部') prompt += `\n3) 需注意的健康周期`;
if (focus === '全部') prompt += `\n4) 当前大运的核心主题`;
return prompt;
}
async function apiGetRemedies(chartData) {
return await apiFetch('/api/remedies', {
shadbala: chartData.planets || {},
doshas: [],
dasha_lord: chartData.dasha?.current_md || '',
});
// ═══════════════════════════════════════════════════════════════
// 一键入口
// ═══════════════════════════════════════════════════════════════
async function aiFullReading(chartData) {
const reading = await aiReading(chartData, { style: 'deep', focus: '全部' });
return reading;
}
async function apiGetKP(chartData) {
return await apiFetch('/api/kp', {
planets: chartData.planets || {},
asc_sign_idx: chartData.ascendant?.sign_idx || 0,
});
async function aiQuickInsight(chartData) {
const reading = await aiReading(chartData, { style: 'quick', focus: '全部' });
return reading;
}
async function apiGetSynastry(maleMoonDeg, femaleMoonDeg) {
return await apiFetch('/api/synastry', { male_moon: maleMoonDeg, female_moon: femaleMoonDeg });
}
async function apiGetSadeSati(chartData) {
return await apiFetch('/api/sade_sati', {
moon_degree: chartData.planets?.Moon?.lon || 0,
asc_degree: chartData.ascendant?.degree || 0,
saturn_degree: chartData.planets?.Saturn?.lon || 0,
});
}
async function apiGetPMC(chartData) {
return await apiFetch('/api/pancha_mahapurusha', {
planets: chartData.planets || {},
sun_degree: chartData.planets?.Sun?.lon || 0,
});
}
async function apiGetCareer(chartData) {
return await apiFetch('/api/career', {
planets: chartData.planets || {},
asc_sign: chartData.ascendant?.sign || 'Aries',
});
}
async function apiGetRelationship(chartData) {
return await apiFetch('/api/relationship', {
planets: chartData.planets || {},
asc_sign: chartData.ascendant?.sign || 'Aries',
});
}
async function apiGetPrashna(chartData, question) {
return await apiFetch('/api/prashna', {
planets: chartData.planets || {},
question: question || 'general',
});
}
// 🔥 一键全功能计算
async function apiComputeAll(birthData) {
const chart = await apiComputeFullChart(birthData);
if (!chart || !chart.success) return chart;
// 并行请求所有分析
const [remedies, kp, sade_sati, pmc, career, relationship] = await Promise.all([
apiGetRemedies(chart),
apiGetKP(chart),
apiGetSadeSati(chart),
apiGetPMC(chart),
apiGetCareer(chart),
apiGetRelationship(chart),
]);
chart._extended = { remedies, kp, sade_sati, pmc, career, relationship };
return chart;
}
// ═══════════════════════════════════════════════════════════════
// 对外接口
// ═══════════════════════════════════════════════════════════════
window.JyotishAPI = {
checkAvailable: checkApiAvailable,
computeChart: apiComputeFullChart,
computeAll: apiComputeAll,
getRemedies: apiGetRemedies,
getKP: apiGetKP,
getSynastry: apiGetSynastry,
getSadeSati: apiGetSadeSati,
getPMC: apiGetPMC,
getCareer: apiGetCareer,
getRelationship: apiGetRelationship,
getPrashna: apiGetPrashna,
baseUrl: API_BASE,
// 计算
computeWithPython,
// AI 解读
aiReading,
aiFullReading,
aiQuickInsight,
};
+4
View File
@@ -191,6 +191,10 @@
<div class="tithi-info-section" id="tithi-info-section"></div>
<!-- Arudha 概览 -->
<div class="arudha-quick-section" id="arudha-quick-section"></div>
<!-- AI 解读 -->
<div class="ai-reading-section" id="chart-summary">
<p style="color:#999;text-align:center;padding:20px;font-size:13px">星盘计算完成后将自动生成 AI 个性化解读</p>
</div>
</div>
<!-- === Tab: Karaka === -->
+35 -19
View File
@@ -216,35 +216,29 @@ function setupForm() {
const [hour, minute] = timeVal.split(':').map(Number);
btnText.classList.add('hidden'); btnLoading.classList.remove('hidden'); btn.disabled = true;
try {
// v6.9.2: API优先, 不可用时回退JS引擎
if (window.JyotishAPI) {
chartData = await window.JyotishAPI.computeAll({ year, month, day, hour, minute, lat, lon, tz });
if (!chartData || !chartData.success) throw new Error(chartData?.error || 'API计算失败');
console.log('[Jyotish] ✅ API —', chartData.dasha_count, 'Dasha,', chartData.yogas?.length || 0, 'Yoga');
} else {
throw new Error('API桥接未加载');
}
} catch (apiErr) {
console.warn('[Jyotish] API不可用:', apiErr.message, '→ 回退JS引擎');
try {
// v6.9.4: 计算层 — 优先Python API, 回退JS引擎
chartData = await window.JyotishAPI?.computeWithPython({ year, month, day, hour, minute, lat, lon, tz });
if (!chartData || !chartData.success) {
await initEngine();
chartData = await computeChart({ year, month, day, hour, minute, lat, lon, tz });
chartData._fallback = true;
console.log('[Jyotish] ⚠️ JS引擎回退成功');
} catch (jsErr) {
console.error('[Jyotish] JS引擎也失败:', jsErr);
alert('计算失败: ' + (apiErr.message || jsErr.message) + '\n请检查网络或启动API服务器');
btnText.classList.remove('hidden'); btnLoading.classList.add('hidden'); btn.disabled = false;
return;
console.log('[Jyotish] ⚠️ JS引擎计算完成');
} else {
console.log('[Jyotish] ✅ Python API —', chartData.dasha_count, 'Dasha');
}
} catch (e) {
console.error('[Jyotish] 计算失败:', e);
alert('计算失败: ' + e.message);
btnText.classList.remove('hidden'); btnLoading.classList.add('hidden'); btn.disabled = false;
return;
}
try {
// 保存出生数据供生时校正使用
window.__jyotishBirth = { year, month, day, hour, minute, lat, lon, tz };
renderAll();
showPage('chart');
// 将当前星盘数据传给 AI 聊天
aiChatSetChartData(chartData);
// v6.9.4: 触发 AI 解读
triggerAIReading(chartData);
} catch (err) {
console.error('[Jyotish] Error:', err);
alert(t('alert.error') + err.message);
@@ -579,6 +573,28 @@ function renderTransitCompareTab(chartData) {
};
}
// ============================================================================
// AI 解读触发 (v6.9.4)
// ============================================================================
async function triggerAIReading(chartData) {
if (!window.JyotishAPI?.aiFullReading) return;
const summaryEl = document.getElementById('chart-summary');
if (summaryEl) summaryEl.innerHTML += '<p style="color:#1565c0;font-size:13px">🤖 AI 正在生成个性化解读...</p>';
try {
const result = await window.JyotishAPI.aiFullReading(chartData);
if (result.success) {
if (summaryEl) {
summaryEl.innerHTML = '<div style="background:#e3f2fd;padding:14px;border-radius:10px;margin-top:10px;font-size:14px;line-height:1.7">'
+ result.reading.replace(/\n/g, '<br>') + '</div>';
}
console.log('[AI] ✅ 解读生成成功');
}
} catch(e) {
console.warn('[AI] 解读失败:', e.message);
if (summaryEl) summaryEl.innerHTML += '<p style="color:#999;font-size:12px">AI解读不可用,请查看结构化数据</p>';
}
}
// ============================================================================
// 星盘中心摘要构建
// ============================================================================