产品思维操作系统

汇聚 15 位顶级产品大师的 92 个思维模型。搜索、浏览,或直接向专家提问——让他们从不同角度质询你的决策。

思维模型库

加载中…

领域:

让专家追问你

基于上面的模型,10 位专家会从不同角度质询你——不给答案,只逼你想清楚。

document.addEventListener('DOMContentLoaded', function() { const input = document.getElementById('decision-input'); const btn = document.getElementById('ask-btn'); const btnLabel = document.getElementById('ask-btn-label'); const btnSpinner = document.getElementById('ask-btn-spinner'); const resultPanel = document.getElementById('ask-result'); const resultContent = document.getElementById('ask-content'); const loading = document.getElementById('ask-loading'); const thinkingStep = document.getElementById('thinking-step'); const statusIcon = document.getElementById('ask-status-icon'); const statusText = document.getElementById('ask-status-text'); const closeBtn = document.getElementById('ask-close'); let thinkingTimer = null; let abortController = null; let isLoading = false; let sessionId = null; // v2: 多轮对话 session let convHistory = []; // v2: 对话历史(UI 层) let firstModels = []; // v2: 缓存首轮模型,续用 let clarificationPending = false; // v2: 正在等待用户澄清 let pendingClarifyTerm = null; // v2.1: 当前等待澄清的术语名 let isClarAnswer = false; // v2.1: 当前 payload 是否是澄清回答 // textarea 自动伸高 function autoGrow() { input.style.height = 'auto'; input.style.height = Math.min(input.scrollHeight, 120) + 'px'; } input.addEventListener('input', autoGrow); // 示例问题点击 document.querySelectorAll('.example-q').forEach(function(el) { el.addEventListener('click', function() { if (isLoading) return; input.value = el.textContent; autoGrow(); triggerAsk(); }); }); // 按钮:发送 / 取消 btn.addEventListener('click', function() { if (isLoading) { cancelAsk(); } else { triggerAsk(); } }); // 键盘:Enter 发送(多轮模式下)、Cmd/Ctrl+Enter 始终发送 input.addEventListener('keydown', function(e) { if (e.key === 'Enter' && !e.shiftKey && (e.metaKey || e.ctrlKey || (sessionId && !clarificationPending))) { e.preventDefault(); if (!clarificationPending && !isLoading) triggerAsk(); } if (e.key === 'Escape' && isLoading) { e.preventDefault(); cancelAsk(); } }); closeBtn.addEventListener('click', function() { if (isLoading) cancelAsk(); resetChat(); }); // 冲突卡片点击 document.querySelectorAll('.conflict-card').forEach(function(card) { card.addEventListener('click', function() { if (isLoading) return; var title = card.getAttribute('data-title'); input.value = '我想了解 ' + title + ' 这个冲突'; autoGrow(); triggerAsk(); window.scrollTo({ top: 0, behavior: 'smooth' }); }); }); // ============== v2 工具函数 ============== function resetChat() { sessionId = null; convHistory = []; firstModels = []; clarificationPending = false; pendingClarifyTerm = null; isClarAnswer = false; resultPanel.classList.add('hidden'); resultContent.innerHTML = ''; } function appendMessage(role, content, options) { convHistory.push({ role: role, content: content }); var wrapper = document.createElement('div'); wrapper.className = 'chat-msg chat-msg-' + role; // 头像 var avatar = document.createElement('span'); if (role === 'clarify') { avatar.textContent = '🔍'; } else { avatar.textContent = role === 'user' ? '💬' : '⚔'; } avatar.className = 'chat-avatar'; // 气泡 var bubble = document.createElement('div'); bubble.className = 'chat-bubble'; bubble.style.maxWidth = role === 'user' ? '70%' : '100%'; if (role === 'clarify') { // 澄清选择:渲染为黄色提示 + 选项按钮 bubble.style.color = 'var(--color-text)'; bubble.innerHTML = '

🤔 ' + escapeHtml(content) + '

'; var optWrap = document.createElement('div'); optWrap.className = 'flex flex-wrap gap-2 mt-2'; if (options && options.length) { options.forEach(function(opt) { var optBtn = document.createElement('button'); optBtn.textContent = opt; optBtn.className = 'clarify-option text-xs px-3 py-1.5 rounded-full transition-colors'; optBtn.style.cssText = 'border:1px solid #fbbf2480; color:#fbbf24; background:transparent; cursor:pointer;'; optBtn.addEventListener('click', function() { input.value = opt; autoGrow(); // 标记为澄清回答,触发发送时 payload 会带上 isClarAnswer = true; triggerAsk(); }); optBtn.addEventListener('mouseenter', function() { optBtn.style.background = '#fbbf2420'; }); optBtn.addEventListener('mouseleave', function() { optBtn.style.background = 'transparent'; }); optWrap.appendChild(optBtn); }); } bubble.appendChild(optWrap); } else { // 普通消息:带角色标签 var label = document.createElement('div'); label.style.cssText = 'font-size:10px; margin-bottom:4px;'; if (role === 'user') { label.textContent = '你'; label.style.color = 'var(--color-text-dim)'; } else { label.textContent = '专家追问'; label.style.color = 'var(--color-accent)'; } bubble.appendChild(label); var body = document.createElement('div'); body.style.cssText = 'color: var(--color-text); line-height:1.7;'; body.innerHTML = role === 'user' ? escapeHtml(content) : formatAnswer(content); bubble.appendChild(body); } wrapper.appendChild(avatar); wrapper.appendChild(bubble); resultContent.appendChild(wrapper); // 滚动到底部 resultContent.scrollTop = resultContent.scrollHeight; } // ============== v2 增强 triggerAsk ============== function triggerAsk() { var question = input.value.trim(); if (!question) { input.focus(); return; } // 检查每日限流 var today = new Date().toISOString().slice(0, 10); var usageKey = 'pm_usage_' + today; var usage = parseInt(localStorage.getItem(usageKey) || '0'); if (usage >= 30) { resultPanel.classList.remove('hidden'); loading.classList.add('hidden'); resultContent.classList.remove('hidden'); statusIcon.textContent = '⚠️'; statusText.textContent = '今日额度已用完'; resultContent.innerHTML = '

今日追问次数已达上限(30次),明天再来吧!

'; return; } // 显示结果面板 resultPanel.classList.remove('hidden'); // v2 多轮:如果已有 session,直接追加而不是清空 if (sessionId) { // 追加模式:不清空已有内容 appendMessage('user', question); // 加一个加载指示器 var loadMsg = document.createElement('div'); loadMsg.className = 'chat-msg chat-msg-assistant'; loadMsg.id = 'chat-loading-msg'; loadMsg.innerHTML = '
专家思考中… ...
'; resultContent.appendChild(loadMsg); resultContent.scrollTop = resultContent.scrollHeight; resultContent.classList.remove('hidden'); loading.classList.add('hidden'); } else { // 首轮:显示加载骨架 resultContent.innerHTML = ''; resultContent.classList.add('hidden'); loading.classList.remove('hidden'); statusIcon.textContent = '💭'; statusText.textContent = '专家思考中…'; } setLoadingState(true); // 平滑滚动 setTimeout(function() { resultPanel.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); }, 50); // 本地匹配模型(仅在首轮需要) var matchedModels = firstModels.length > 0 ? firstModels : getTopModels(question, 4); if (!sessionId) firstModels = matchedModels; if (!sessionId) { startThinkingAnimation(matchedModels.map(function(m){ return m.model; })); } // 构建 payload(v2.1 加上澄清标记) var payload = { question: question, session_id: sessionId || '' }; if (isClarAnswer) { payload.is_clarification_answer = true; if (pendingClarifyTerm) payload.clarify_term = pendingClarifyTerm; isClarAnswer = false; // 重置标记 } // 只有首轮传 models if (!sessionId) { payload.models = matchedModels.map(function(m) { return { expert: m.model.expert, name: m.model.name, core_principle: m.model.core_principle || '', decision_logic: m.model.decision_logic || '', triggers: m.model.triggers || [] }; }); } abortController = new AbortController(); var fetchTimeoutId = setTimeout(function(){ if (abortController) { abortController._isTimeout = true; abortController.abort(); } }, 75000); var slowHintTimerId = setTimeout(function(){ if (thinkingStep && isLoading) { thinkingStep.style.opacity = '0'; setTimeout(function(){ thinkingStep.textContent = 'GLM 思考中(平均 20-30 秒)… 耐心等等。'; thinkingStep.style.opacity = '1'; }, 200); } }, 25000); fetch('/api/ask', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), signal: abortController.signal }) .then(function(resp) { var status = resp.status; return resp.json().then(function(data){ return { data: data, status: status, ok: resp.ok }; }).catch(function(){ return { data: { error: '服务返回了非 JSON 响应' }, status: status, ok: false }; }); }) .then(function(result) { clearTimeout(fetchTimeoutId); clearTimeout(slowHintTimerId); var data = result.data; var status = result.status; abortController = null; stopThinkingAnimation(); loading.classList.add('hidden'); setLoadingState(false); // 移除加载指示器 var loadMsg = document.getElementById('chat-loading-msg'); if (loadMsg) loadMsg.remove(); if (!result.ok) { var errMsg = (data && data.error) || '服务异常'; showError(status, errMsg, question, matchedModels); return; } if (data.error) { statusIcon.textContent = '⚠️'; statusText.textContent = '出了点问题'; resultContent.innerHTML = '

' + escapeHtml(data.error) + '

'; return; } // v2: 保存 session_id if (data.session_id) { sessionId = data.session_id; } // v2.1: 处理澄清请求 if (data.needs_clarification) { clarificationPending = true; pendingClarifyTerm = data.term; appendMessage('clarify', data.message, data.options); input.value = ''; input.focus(); } else { clarificationPending = false; pendingClarifyTerm = null; resultContent.classList.remove('hidden'); if (resultContent.querySelector('.chat-msg')) { // 多轮模式:追加回答到聊天区 appendMessage('assistant', data.answer); statusIcon.textContent = '⚔'; statusText.textContent = '专家追问'; } else { // 首轮:显示完整回答 + 匹配模型链接 statusIcon.textContent = '⚔'; statusText.textContent = '专家追问'; appendMessage('user', question); appendMessage('assistant', data.answer); } // 匹配模型标签(始终显示) if (firstModels.length > 0 && !document.getElementById('model-tags')) { var tagDiv = document.createElement('div'); tagDiv.id = 'model-tags'; tagDiv.className = 'mt-3 pt-3 flex flex-wrap gap-2'; tagDiv.style.borderTop = '1px solid var(--color-border)'; for (var i = 0; i < firstModels.length; i++) { var m = firstModels[i].model; tagDiv.innerHTML += '' + m.expert + '·' + m.name + ''; } resultContent.appendChild(tagDiv); } } // 清空输入框,准备下一轮 input.value = ''; input.style.height = 'auto'; // 记录使用 localStorage.setItem(usageKey, String(usage + 1)); saveToLog(question); }) .catch(function(err) { clearTimeout(fetchTimeoutId); clearTimeout(slowHintTimerId); var loadMsg = document.getElementById('chat-loading-msg'); if (loadMsg) loadMsg.remove(); var isTimeout = abortController && abortController._isTimeout; if (err && err.name === 'AbortError' && !isTimeout) return; abortController = null; stopThinkingAnimation(); loading.classList.add('hidden'); setLoadingState(false); resultContent.classList.remove('hidden'); if (isTimeout) { showError(504, 'GLM 响应超时(75秒)', question, matchedModels); } else { showError(0, (err && err.message) || '网络请求失败', question, matchedModels); } saveToLog(question); }); } function showError(status, errMsg, question, matchedModels) { statusIcon.textContent = status === 429 ? '⏳' : '⚠️'; statusText.textContent = status === 429 ? '请求过于频繁' : status >= 500 ? 'GLM 服务暂时异常' : '出了点问题'; if (status === 429) { resultContent.innerHTML = '

' + escapeHtml(errMsg) + '

'; } else if (status >= 500 || status === 0 || (status === 200 && errMsg)) { resultContent.innerHTML = '

' + escapeHtml(errMsg) + '

' + renderLocalResult(question, matchedModels); } } function setLoadingState(loading) { isLoading = loading; input.disabled = false; if (loading) { btnLabel.classList.add('hidden'); btnSpinner.classList.remove('hidden'); btnSpinner.classList.add('inline-flex'); } else { btnLabel.classList.remove('hidden'); btnSpinner.classList.add('hidden'); btnSpinner.classList.remove('inline-flex'); } } function cancelAsk() { if (abortController) { abortController.abort(); abortController = null; } stopThinkingAnimation(); loading.classList.add('hidden'); resultContent.classList.remove('hidden'); setLoadingState(false); var loadMsg = document.getElementById('chat-loading-msg'); if (loadMsg) loadMsg.remove(); statusIcon.textContent = '⏸'; statusText.textContent = '已取消'; } function startThinkingAnimation(matchedExperts) { var steps = ['正在匹配相关专家…']; if (matchedExperts && matchedExperts.length > 0) { var names = matchedExperts.slice(0, 3).map(function(m){ return m.expert; }).join('、'); steps.push('召唤 ' + names + ' 进入思考…'); steps.push(names.split('、')[0] + '正在调取心智模型…'); steps.push('多视角对撞中,寻找冲突核心…'); steps.push('组织追问语言中…'); } else { steps.push('召唤专家进入思考…'); steps.push('多视角对撞中…'); steps.push('组织追问语言中…'); } var i = 0; thinkingStep.textContent = steps[0]; if (thinkingTimer) clearInterval(thinkingTimer); thinkingTimer = setInterval(function() { i = (i + 1) % steps.length; thinkingStep.style.opacity = '0'; setTimeout(function(){ thinkingStep.textContent = steps[i]; thinkingStep.style.opacity = '1'; }, 200); }, 1800); } function stopThinkingAnimation() { if (thinkingTimer) { clearInterval(thinkingTimer); thinkingTimer = null; } } // ============== 渲染工具函数 ============== function escapeHtml(s) { return String(s).replace(/[&<>"']/g, function(c){ return { '&':'&','<':'<','>':'>','"':'"',"'":''' }[c]; }); } function formatAnswer(text) { return text .replace(/\*\*(.+?)\*\*/g, '$1') .replace(/⚔/g, '') .replace(/💬/g, '💬') .replace(/```markdown\n?/g, '') .replace(/\n```/g, '') .replace(/```/g, '') .replace(/\n\n/g, '

') .replace(/\n- /g, '
• ') .replace(/\n/g, '
'); } function getTopModels(q, count) { var modelData = JSON.parse(document.getElementById('model-triggers-data').textContent); var matches = []; for (var i = 0; i < modelData.length; i++) { var m = modelData[i]; var score = 0; var triggers = m.triggers || []; for (var j = 0; j < triggers.length; j++) { if (q.includes(triggers[j].substring(0, 4)) || triggers[j].includes(q.substring(0, 4))) { score += 2; } } var domains = m.domains || []; for (var k = 0; k < domains.length; k++) { if (q.includes(domains[k])) score += 3; } if (q.includes(m.name) || m.name.includes(q.substring(0, 3))) score += 1; var keywords = ['增长', '克制', '砍', '减法', '招人', '人才', '成本', '速度', '决策', '定价', '付费', '功能', '产品', '用户', '创业', '融资', '竞争', '迭代', '简化', '聚焦', '垂直', '整合', '社区', '社交', '设计', '体验']; for (var w = 0; w < keywords.length; w++) { if (q.includes(keywords[w])) { for (var t = 0; t < triggers.length; t++) { if (triggers[t].includes(keywords[w])) score += 2; } for (var d = 0; d < domains.length; d++) { if (domains[d].includes(keywords[w])) score += 1; } } } if (score > 0) matches.push({ model: m, score: score }); } matches.sort(function(a, b) { return b.score - a.score; }); if (matches.length === 0) { var shuffled = modelData.sort(function() { return 0.5 - Math.random(); }); matches = shuffled.slice(0, count).map(function(m) { return { model: m, score: 1 }; }); } return matches.slice(0, count); } function renderLocalResult(q, matchedModels) { var html = '

(AI 暂时不可用,以下为相关模型推荐)

'; for (var idx = 0; idx < matchedModels.length; idx++) { var item = matchedModels[idx].model; var bgColor = item.color || '#7a7a90'; html += '
'; html += '' + item.expert + ' '; html += '' + item.name + ''; html += '查看 →'; html += '
'; } return html; } // ============== 投票(保存到 localStorage) ============== document.addEventListener('click', function(e) { if (e.target.classList.contains('vote-btn')) { var expert = e.target.textContent; var votes = JSON.parse(localStorage.getItem('pm_votes') || '{}'); votes[expert] = (votes[expert] || 0) + 1; localStorage.setItem('pm_votes', JSON.stringify(votes)); e.target.style.background = e.target.style.borderColor; e.target.style.color = '#09090f'; e.target.textContent = '✓ ' + expert; } }); // ============== 决策日志 ============== function saveToLog(question) { var log = JSON.parse(localStorage.getItem('pm_decision_log') || '[]'); log.unshift({ q: question, time: new Date().toISOString() }); if (log.length > 50) log = log.slice(0, 50); localStorage.setItem('pm_decision_log', JSON.stringify(log)); } // ======== 冲突浏览器:筛选 + 加载更多 ======== var allConflictsEl = document.getElementById('all-conflicts-data'); if (allConflictsEl) { var allConflicts = JSON.parse(allConflictsEl.textContent); var conflictListEl = document.getElementById('conflict-list'); var loadMoreBtn = document.getElementById('conflict-load-more'); var remainingEl = document.getElementById('conflict-remaining'); var filterBtns = document.querySelectorAll('.conflict-filter-btn'); var currentDomain = 'all'; var currentLimit = 12; function renderConflicts() { var filtered = currentDomain === 'all' ? allConflicts : allConflicts.filter(function(c) { return (c.domains || []).indexOf(currentDomain) !== -1; }); var visible = filtered.slice(0, currentLimit); var html = visible.map(function(c) { return '
' + '' + '' + '' + '' + '' + '
' + '
冲突场景:' + escapeHtml(c.scenario) + '
' + '
化解思路:' + escapeHtml(c.resolution) + '
' + '
' + '
'; }).join(''); conflictListEl.innerHTML = html || '
这个领域暂无冲突记录
'; if (loadMoreBtn) { var remaining = filtered.length - currentLimit; if (remaining > 0) { loadMoreBtn.style.display = ''; if (remainingEl) remainingEl.textContent = remaining; } else { loadMoreBtn.style.display = 'none'; } } } filterBtns.forEach(function(b) { b.addEventListener('click', function() { currentDomain = b.getAttribute('data-domain'); currentLimit = 12; filterBtns.forEach(function(other) { if (other === b) { other.style.background = 'var(--color-accent)'; other.style.color = '#09090f'; other.style.borderColor = 'var(--color-accent)'; } else { other.style.background = 'transparent'; other.style.color = 'var(--color-text-muted)'; other.style.borderColor = 'var(--color-border)'; } }); renderConflicts(); }); }); if (loadMoreBtn) { loadMoreBtn.addEventListener('click', function() { currentLimit += 12; renderConflicts(); }); } } });