| <!DOCTYPE html> |
|
|
| <html lang="zh-Hant"> |
|
|
| <head> |
|
|
| <meta charset="UTF-8"> |
|
|
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
|
| <title>AI 工作坊:風水大師 </title> |
|
|
| <style> |
| |
| body { |
| |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; |
| |
| background-color: #f4f7f6; |
| |
| margin: 0; |
| |
| padding: 20px; |
| |
| display: flex; |
| |
| flex-direction: column; |
| |
| align-items: center; |
| |
| } |
| |
| .container { |
| |
| max-width: 600px; |
| |
| width: 100%; |
| |
| background: white; |
| |
| padding: 25px; |
| |
| border-radius: 12px; |
| |
| box-shadow: 0 4px 15px rgba(0,0,0,0.1); |
| |
| } |
| |
| h2 { text-align: center; color: #333; margin-top: 0; } |
| |
| .status { |
| |
| background: #e1f5fe; |
| |
| color: #0288d1; |
| |
| padding: 10px; |
| |
| border-radius: 6px; |
| |
| font-size: 14px; |
| |
| margin-bottom: 15px; |
| |
| text-align: center; |
| |
| } |
| |
| .input-group { |
| |
| display: flex; |
| |
| gap: 10px; |
| |
| margin-bottom: 20px; |
| |
| } |
| |
| input[type="text"] { |
| |
| flex: 1; |
| |
| padding: 12px; |
| |
| border: 1px solid #ccc; |
| |
| border-radius: 6px; |
| |
| font-size: 16px; |
| |
| } |
| |
| button { |
| |
| padding: 12px 20px; |
| |
| background: #ff5722; |
| |
| color: white; |
| |
| border: none; |
| |
| border-radius: 6px; |
| |
| cursor: pointer; |
| |
| font-size: 16px; |
| |
| font-weight: bold; |
| |
| } |
| |
| button:disabled { background: #ccc; } |
| |
| .box { |
| |
| border: 1px solid #ddd; |
| |
| padding: 15px; |
| |
| border-radius: 8px; |
| |
| background: #fafafa; |
| |
| margin-bottom: 15px; |
| |
| min-height: 50px; |
| |
| } |
| |
| .box-title { |
| |
| font-weight: bold; |
| |
| color: #666; |
| |
| font-size: 13px; |
| |
| text-transform: uppercase; |
| |
| margin-bottom: 5px; |
| |
| } |
| |
| .highlight { background-color: #fff9c4; padding: 2px 5px; border-radius: 3px; } |
| |
| </style> |
|
|
| </head> |
|
|
| <body> |
|
|
| <div class="container"> |
|
|
| <h2>🔮 瀏覽器級:AI 風水大師</h2> |
|
|
| <div id="status" class="status">正在初始化 Transformer.js 及加載 AI 模型... (請稍候)</div> |
|
|
| |
|
|
| <div class="input-group"> |
|
|
| <input type="text" id="userInput" placeholder="例如:我想催旺事業運 / 今年犯太歲點辦?" disabled> |
|
|
| <button id="askBtn" onclick="askMaster()" disabled>請教大師</button> |
|
|
| </div> |
|
|
| <div class="box"> |
|
|
| <div class="box-title">📖 RAG 核心:從「風水秘笈」中檢索到的最相關段落</div> |
|
|
| <div id="retrievedContext">未開始檢索。當你發問時,AI 會自動翻閱秘笈...</div> |
|
|
| </div> |
|
|
| <div class="box" style="border-left: 5px solid #ff5722;"> |
|
|
| <div class="box-title">💬 大師開示 (AI 基於秘笈生成的解答)</div> |
|
|
| <div id="aiResponse">等待大師開示...</div> |
|
|
| </div> |
|
|
| </div> |
|
|
| <script type="module"> |
| |
| import { pipeline } from 'https://cdn.jsdelivr.net/npm/@xenova/transformers@2.17.2'; |
| |
| |
| |
| const fengShuiKnowledgeBase = [ |
| |
| "關於事業運與辦公室:若想催旺事業財運,可在辦公室正東位擺放四枝富貴竹,大忌座位背後無靠山(如無牆壁),會導致小人作祟。", |
| |
| "關於犯太歲與化解:今年屬蛇、豬、虎、猴的朋友犯太歲,建議前往太歲廟拜太歲,並隨身佩戴保平安的玉佩或生肖飾物以擋煞。", |
| |
| "關於桃花運與人緣:想催旺姻緣,可在客房或臥室西南方擺放鮮花(如百合或玫瑰),切忌擺放假花或乾花,否則會招來爛桃花。", |
| |
| "關於家居健康與病位:今年的二黑病符星飛臨正南方,此方位不宜動土,亦不宜放置紅色或黃色地毯,可掛一個銅葫蘆化解病氣。" |
| |
| ]; |
| |
| let embedder; |
| |
| |
| |
| |
| |
| function calculateCosineSimilarity(vecA, vecB) { |
| |
| let dotProduct = 0.0; |
| |
| let normA = 0.0; |
| |
| let normB = 0.0; |
| |
| for (let i = 0; i < vecA.length; i++) { |
| |
| dotProduct += vecA[i] * vecB[i]; |
| |
| normA += vecA[i] * vecA[i]; |
| |
| normB += vecB[i] * vecB[i]; |
| |
| } |
| |
| if (normA === 0 || normB === 0) return 0; |
| |
| return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB)); |
| |
| } |
| |
| |
| |
| async function initAI() { |
| |
| try { |
| |
| |
| |
| embedder = await pipeline('feature-extraction', 'Xenova/all-MiniLM-L6-v2'); |
| |
| |
| |
| document.getElementById('status').innerText = "✅ 大師已駕到!模型加載成功。請在下方輸入你的流年問題。"; |
| |
| document.getElementById('userInput').disabled = false; |
| |
| document.getElementById('askBtn').disabled = false; |
| |
| } catch (error) { |
| |
| document.getElementById('status').innerText = "❌ 加載失敗。請確保在線上 IDE(如 CodePen)中以 https 協議運行。"; |
| |
| console.error(error); |
| |
| } |
| |
| } |
| |
| |
| |
| async function getEmbedding(text) { |
| |
| const output = await embedder(text, { pooling: 'mean', normalize: true }); |
| |
| return Object.values(output.data); |
| |
| } |
| |
| |
| |
| window.askMaster = async function() { |
| |
| const query = document.getElementById('userInput').value.trim(); |
| |
| if (!query) return alert("請輸入問題!"); |
| |
| document.getElementById('aiResponse').innerText = "大師正在掐指一算,翻閱秘笈中..."; |
| |
| document.getElementById('retrievedContext').innerText = "正在進行語意搜索..."; |
| |
| try { |
| |
| |
| |
| const queryEmbedding = await getEmbedding(query); |
| |
| let highestScore = -1; |
| |
| let bestMatchText = ""; |
| |
| |
| |
| for (const sentence of fengShuiKnowledgeBase) { |
| |
| const sentenceEmbedding = await getEmbedding(sentence); |
| |
| |
| |
| const score = calculateCosineSimilarity(queryEmbedding, sentenceEmbedding); |
| |
| |
| |
| if (score > highestScore) { |
| |
| highestScore = score; |
| |
| bestMatchText = sentence; |
| |
| } |
| |
| } |
| |
| |
| |
| document.getElementById('retrievedContext').innerHTML = ` |
| |
| <b>[匹配度: ${(highestScore * 100).toFixed(1)}%]</b> <br> |
| |
| <span class="highlight">${bestMatchText}</span> |
| |
| `; |
| |
| |
| |
| setTimeout(() => { |
| |
| let finalReply = ""; |
| |
| if (highestScore < 0.35) { |
| |
| finalReply = 【大師解讀】: 施主所問之事,風水秘笈中未有詳細記載。但依大師看來,行善積德自然能逢凶化吉。建議細化你的問題(如提及事業、太歲、桃花)。; |
| |
| } else { |
| |
| finalReply = 【大師開示】: 看到施主受此提問困擾。根據本門風水秘笈記載:『${bestMatchText}』。大師建議你照此指引佈局,近期切記心平氣和,自可趨吉避凶。; |
| |
| } |
| |
| document.getElementById('aiResponse').innerText = finalReply; |
| |
| }, 500); |
| |
| } catch (err) { |
| |
| document.getElementById('aiResponse').innerText = "運算過程中出錯,請確保使用線上環境打開此網頁。"; |
| |
| console.error(err); |
| |
| } |
| |
| } |
| |
| |
| |
| initAI(); |
| |
| </script> |
|
|
| </body> |
|
|
| </html> |
|
|
|
|
|
|