Files
hack/tools/scripts/llm-key-hunter/results/_cache/content/abiscuitttt_fdty-auto.json
T

1 line
359 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"repo": "abiscuitttt/fdty-auto", "files": {"fdty_top.js": {"c31f67ff234e6311d40534fa3c376b6183f6e917": {"t": 1785746006.1875212, "n": 10116, "h": "066ec9da1810e041", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '<div id=\"fdty-msg\"></div><div id=\"fdty-stream\" style=\"color:#999;font-size:11px;white-space:pre-wrap;word-break:break-all;max-height:120px;overflow:hidden;margin-top:2px;padding-top:2px;border-top:1px dashed #ddd\"></div><div id=\"fdty-progress\" style=\"color:#999;font-size:11px\"></div>';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n var stream_el = doc.getElementById('fdty-stream');\n var progress_el = doc.getElementById('fdty-progress');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n function progress(msg) {\n console.log('[fdty] ' + msg);\n progress_el.textContent = msg;\n }\n\n function clearProgress() {\n progress_el.textContent = '';\n }\n\n function streamShow(text) {\n stream_el.textContent = text;\n }\n\n function streamAppend(text) {\n stream_el.textContent += text;\n stream_el.scrollTop = stream_el.scrollHeight;\n }\n\n function streamClear() {\n stream_el.textContent = '';\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/chat/completions';\n var MAX_CONCURRENCY = 5;\n\n // 并发限制器:同时对最多 limit 个 item 执行 fn,其余排队\n function concurrentMap(items, limit, fn) {\n return new Promise(function(resolve) {\n var results = new Array(items.length);\n var running = 0, idx = 0, done = 0, total = items.length;\n if (total === 0) { resolve(results); return; }\n\n function next() {\n while (running < limit && idx < total) {\n var i = idx++;\n running++;\n fn(items[i], i).then(function(r) {\n results[i] = r;\n running--;\n done++;\n if (done === total) resolve(results);\n else next();\n });\n }\n }\n next();\n });\n }\n\n var SYSTEM_PROMPT = [\n '# 角色',\n '你是复旦大学体育理论考试判题员。运用运动科学常识与体育规则作答。',\n '',\n '# 知识领域',\n '题目涵盖十个领域:体育概论、健康体能、体质评价、运动保健、田径、球类、游泳与救生、形体运动、民族传统体育与武术、击剑。',\n '',\n '# 答题原则',\n '- 注意题干中的否定词(不/非/无/没有)和双重否定——切勿答反',\n '- 含\"完全\"\"绝不\"\"所有\"\"从不\"等绝对化用词的判断题通常为错,但公认事实和规则条文除外',\n '- 必须为每道题输出答案,不确定时基于常识推理给出最佳猜测,不得空题',\n '',\n '# 输出',\n '输出纯JSON对象,键为题号,值为答案。',\n '判断:\"对\"/\"错\" 单选:\"A\"/\"B\"/\"C\"/\"D\"'].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n thinking: { type: 'enabled' },\n reasoning_effort: 'max',\n max_tokens: 4096,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1 & 2 ============\n function startVoting() {\n log(questions.length + ' 题 每题3票 | 并发窗口=' + MAX_CONCURRENCY, '#1a73e8');\n\n var totalOk = 0;\n var done1 = 0;\n var temps = [0.1, 0.6, 1.2];\n var unresolved = [];\n var phase2Ok = 0;\n\n concurrentMap(questions, MAX_CONCURRENCY, function(q) {\n var batch = [];\n for (var i = 0; i < 3; i++) batch.push(callAPI([q], temps[i]));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done1++;\n progress('投票 ' + done1 + '/' + questions.length + ' 完成');\n if (best && max === 3) {\n if (selectAnswer(q, best)) totalOk++;\n }\n return { q: q, best: best, count: max, counts: counts };\n });\n }).then(function(results) {\n clearProgress();\n streamClear();\n\n var disputed = [];\n results.forEach(function(r) {\n if (!r.best || r.count < 3) disputed.push(r.q);\n });\n log('完成 ' + totalOk + '/' + questions.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) return;\n\n // ============ Phase 2: 分歧题仲裁 ============\n var done2 = 0;\n log('Phase 2 仲裁 ' + disputed.length + ' 题 (每题10次)...', '#e37400');\n\n return concurrentMap(disputed, MAX_CONCURRENCY, function(q) {\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.3));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done2++;\n progress('仲裁 ' + done2 + '/' + disputed.length + ' 完成');\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n }).then(function() {\n clearProgress();\n streamClear();\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n })\n .catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n }\n\n // ============ 启动 ============\n startVoting();\n})();\n// test\n"}, "9f3f3ad61955beb63c69e9ab80ccb61da57b6058": {"t": 1785758719.147477, "n": 10116, "h": "066ec9da1810e041", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '<div id=\"fdty-msg\"></div><div id=\"fdty-stream\" style=\"color:#999;font-size:11px;white-space:pre-wrap;word-break:break-all;max-height:120px;overflow:hidden;margin-top:2px;padding-top:2px;border-top:1px dashed #ddd\"></div><div id=\"fdty-progress\" style=\"color:#999;font-size:11px\"></div>';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n var stream_el = doc.getElementById('fdty-stream');\n var progress_el = doc.getElementById('fdty-progress');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n function progress(msg) {\n console.log('[fdty] ' + msg);\n progress_el.textContent = msg;\n }\n\n function clearProgress() {\n progress_el.textContent = '';\n }\n\n function streamShow(text) {\n stream_el.textContent = text;\n }\n\n function streamAppend(text) {\n stream_el.textContent += text;\n stream_el.scrollTop = stream_el.scrollHeight;\n }\n\n function streamClear() {\n stream_el.textContent = '';\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/chat/completions';\n var MAX_CONCURRENCY = 5;\n\n // 并发限制器:同时对最多 limit 个 item 执行 fn,其余排队\n function concurrentMap(items, limit, fn) {\n return new Promise(function(resolve) {\n var results = new Array(items.length);\n var running = 0, idx = 0, done = 0, total = items.length;\n if (total === 0) { resolve(results); return; }\n\n function next() {\n while (running < limit && idx < total) {\n var i = idx++;\n running++;\n fn(items[i], i).then(function(r) {\n results[i] = r;\n running--;\n done++;\n if (done === total) resolve(results);\n else next();\n });\n }\n }\n next();\n });\n }\n\n var SYSTEM_PROMPT = [\n '# 角色',\n '你是复旦大学体育理论考试判题员。运用运动科学常识与体育规则作答。',\n '',\n '# 知识领域',\n '题目涵盖十个领域:体育概论、健康体能、体质评价、运动保健、田径、球类、游泳与救生、形体运动、民族传统体育与武术、击剑。',\n '',\n '# 答题原则',\n '- 注意题干中的否定词(不/非/无/没有)和双重否定——切勿答反',\n '- 含\"完全\"\"绝不\"\"所有\"\"从不\"等绝对化用词的判断题通常为错,但公认事实和规则条文除外',\n '- 必须为每道题输出答案,不确定时基于常识推理给出最佳猜测,不得空题',\n '',\n '# 输出',\n '输出纯JSON对象,键为题号,值为答案。',\n '判断:\"对\"/\"错\" 单选:\"A\"/\"B\"/\"C\"/\"D\"'].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n thinking: { type: 'enabled' },\n reasoning_effort: 'max',\n max_tokens: 4096,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1 & 2 ============\n function startVoting() {\n log(questions.length + ' 题 每题3票 | 并发窗口=' + MAX_CONCURRENCY, '#1a73e8');\n\n var totalOk = 0;\n var done1 = 0;\n var temps = [0.1, 0.6, 1.2];\n var unresolved = [];\n var phase2Ok = 0;\n\n concurrentMap(questions, MAX_CONCURRENCY, function(q) {\n var batch = [];\n for (var i = 0; i < 3; i++) batch.push(callAPI([q], temps[i]));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done1++;\n progress('投票 ' + done1 + '/' + questions.length + ' 完成');\n if (best && max === 3) {\n if (selectAnswer(q, best)) totalOk++;\n }\n return { q: q, best: best, count: max, counts: counts };\n });\n }).then(function(results) {\n clearProgress();\n streamClear();\n\n var disputed = [];\n results.forEach(function(r) {\n if (!r.best || r.count < 3) disputed.push(r.q);\n });\n log('完成 ' + totalOk + '/' + questions.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) return;\n\n // ============ Phase 2: 分歧题仲裁 ============\n var done2 = 0;\n log('Phase 2 仲裁 ' + disputed.length + ' 题 (每题10次)...', '#e37400');\n\n return concurrentMap(disputed, MAX_CONCURRENCY, function(q) {\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.3));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done2++;\n progress('仲裁 ' + done2 + '/' + disputed.length + ' 完成');\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n }).then(function() {\n clearProgress();\n streamClear();\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n })\n .catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n }\n\n // ============ 启动 ============\n startVoting();\n})();\n// test\n"}, "7005851742fd4a7fa46252a384ddc95035508da0": {"t": 1785758720.5512323, "n": 10108, "h": "d4bbf9196fc3eae0", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '<div id=\"fdty-msg\"></div><div id=\"fdty-stream\" style=\"color:#999;font-size:11px;white-space:pre-wrap;word-break:break-all;max-height:120px;overflow:hidden;margin-top:2px;padding-top:2px;border-top:1px dashed #ddd\"></div><div id=\"fdty-progress\" style=\"color:#999;font-size:11px\"></div>';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n var stream_el = doc.getElementById('fdty-stream');\n var progress_el = doc.getElementById('fdty-progress');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n function progress(msg) {\n console.log('[fdty] ' + msg);\n progress_el.textContent = msg;\n }\n\n function clearProgress() {\n progress_el.textContent = '';\n }\n\n function streamShow(text) {\n stream_el.textContent = text;\n }\n\n function streamAppend(text) {\n stream_el.textContent += text;\n stream_el.scrollTop = stream_el.scrollHeight;\n }\n\n function streamClear() {\n stream_el.textContent = '';\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/chat/completions';\n var MAX_CONCURRENCY = 5;\n\n // 并发限制器:同时对最多 limit 个 item 执行 fn,其余排队\n function concurrentMap(items, limit, fn) {\n return new Promise(function(resolve) {\n var results = new Array(items.length);\n var running = 0, idx = 0, done = 0, total = items.length;\n if (total === 0) { resolve(results); return; }\n\n function next() {\n while (running < limit && idx < total) {\n var i = idx++;\n running++;\n fn(items[i], i).then(function(r) {\n results[i] = r;\n running--;\n done++;\n if (done === total) resolve(results);\n else next();\n });\n }\n }\n next();\n });\n }\n\n var SYSTEM_PROMPT = [\n '# 角色',\n '你是复旦大学体育理论考试判题员。运用运动科学常识与体育规则作答。',\n '',\n '# 知识领域',\n '题目涵盖十个领域:体育概论、健康体能、体质评价、运动保健、田径、球类、游泳与救生、形体运动、民族传统体育与武术、击剑。',\n '',\n '# 答题原则',\n '- 注意题干中的否定词(不/非/无/没有)和双重否定——切勿答反',\n '- 含\"完全\"\"绝不\"\"所有\"\"从不\"等绝对化用词的判断题通常为错,但公认事实和规则条文除外',\n '- 必须为每道题输出答案,不确定时基于常识推理给出最佳猜测,不得空题',\n '',\n '# 输出',\n '输出纯JSON对象,键为题号,值为答案。',\n '判断:\"对\"/\"错\" 单选:\"A\"/\"B\"/\"C\"/\"D\"'].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n thinking: { type: 'enabled' },\n reasoning_effort: 'max',\n max_tokens: 4096,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1 & 2 ============\n function startVoting() {\n log(questions.length + ' 题 每题3票 | 并发窗口=' + MAX_CONCURRENCY, '#1a73e8');\n\n var totalOk = 0;\n var done1 = 0;\n var temps = [0.1, 0.6, 1.2];\n var unresolved = [];\n var phase2Ok = 0;\n\n concurrentMap(questions, MAX_CONCURRENCY, function(q) {\n var batch = [];\n for (var i = 0; i < 3; i++) batch.push(callAPI([q], temps[i]));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done1++;\n progress('投票 ' + done1 + '/' + questions.length + ' 完成');\n if (best && max === 3) {\n if (selectAnswer(q, best)) totalOk++;\n }\n return { q: q, best: best, count: max, counts: counts };\n });\n }).then(function(results) {\n clearProgress();\n streamClear();\n\n var disputed = [];\n results.forEach(function(r) {\n if (!r.best || r.count < 3) disputed.push(r.q);\n });\n log('完成 ' + totalOk + '/' + questions.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) return;\n\n // ============ Phase 2: 分歧题仲裁 ============\n var done2 = 0;\n log('Phase 2 仲裁 ' + disputed.length + ' 题 (每题10次)...', '#e37400');\n\n return concurrentMap(disputed, MAX_CONCURRENCY, function(q) {\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.3));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done2++;\n progress('仲裁 ' + done2 + '/' + disputed.length + ' 完成');\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n }).then(function() {\n clearProgress();\n streamClear();\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n })\n .catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n }\n\n // ============ 启动 ============\n startVoting();\n})();\n"}, "b73733d47dbd73de18116ca44b04ba9197b57f22": {"t": 1785758722.0091326, "n": 10112, "h": "efcc20ad9049a851", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '<div id=\"fdty-msg\"></div><div id=\"fdty-stream\" style=\"color:#999;font-size:11px;white-space:pre-wrap;word-break:break-all;max-height:120px;overflow:hidden;margin-top:2px;padding-top:2px;border-top:1px dashed #ddd\"></div><div id=\"fdty-progress\" style=\"color:#999;font-size:11px\"></div>';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n var stream_el = doc.getElementById('fdty-stream');\n var progress_el = doc.getElementById('fdty-progress');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n function progress(msg) {\n console.log('[fdty] ' + msg);\n progress_el.textContent = msg;\n }\n\n function clearProgress() {\n progress_el.textContent = '';\n }\n\n function streamShow(text) {\n stream_el.textContent = text;\n }\n\n function streamAppend(text) {\n stream_el.textContent += text;\n stream_el.scrollTop = stream_el.scrollHeight;\n }\n\n function streamClear() {\n stream_el.textContent = '';\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/chat/completions';\n var MAX_CONCURRENCY = 5;\n\n // 并发限制器:同时对最多 limit 个 item 执行 fn,其余排队\n function concurrentMap(items, limit, fn) {\n return new Promise(function(resolve) {\n var results = new Array(items.length);\n var running = 0, idx = 0, done = 0, total = items.length;\n if (total === 0) { resolve(results); return; }\n\n function next() {\n while (running < limit && idx < total) {\n var i = idx++;\n running++;\n fn(items[i], i).then(function(r) {\n results[i] = r;\n running--;\n done++;\n if (done === total) resolve(results);\n else next();\n });\n }\n }\n next();\n });\n }\n\n var SYSTEM_PROMPT = [\n '# 角色',\n '你是复旦大学体育理论考试判题员。运用运动科学常识与体育规则作答。',\n '',\n '# 知识领域',\n '题目涵盖十个领域:体育概论、健康体能、体质评价、运动保健、田径、球类、游泳与救生、形体运动、民族传统体育与武术、击剑。',\n '',\n '# 答题原则',\n '- 注意题干中的否定词(不/非/无/没有)和双重否定——切勿答反',\n '- 含\"完全\"\"绝不\"\"所有\"\"从不\"等绝对化用词的判断题通常为错,但公认事实和规则条文除外',\n '- 必须为每道题输出答案,不确定时基于常识推理给出最佳猜测,不得空题',\n '',\n '# 输出',\n '输出纯JSON对象,键为题号,值为答案。',\n '判断:\"对\"/\"错\" 单选:\"A\"/\"B\"/\"C\"/\"D\"'].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n thinking: { type: 'enabled' },\n reasoning_effort: 'max',\n max_tokens: 4096,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1 & 2 ============\n function startVoting() {\n log(questions.length + ' 题 每题3票 | 并发窗口=' + MAX_CONCURRENCY, '#1a73e8');\n\n var totalOk = 0;\n var done1 = 0;\n var temps = [0.1, 0.6, 1.2];\n\n concurrentMap(questions, MAX_CONCURRENCY, function(q) {\n var batch = [];\n for (var i = 0; i < 3; i++) batch.push(callAPI([q], temps[i]));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done1++;\n progress('投票 ' + done1 + '/' + questions.length + ' 完成');\n if (best && max === 3) {\n if (selectAnswer(q, best)) totalOk++;\n }\n return { q: q, best: best, count: max, counts: counts };\n });\n }).then(function(results) {\n clearProgress();\n streamClear();\n\n var disputed = [];\n results.forEach(function(r) {\n if (!r.best || r.count < 3) disputed.push(r.q);\n });\n log('完成 ' + totalOk + '/' + questions.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) return;\n\n // ============ Phase 2: 分歧题仲裁 ============\n var done2 = 0;\n log('Phase 2 仲裁 ' + disputed.length + ' 题 (每题10次)...', '#e37400');\n\n var unresolved = [];\n var phase2Ok = 0;\n return concurrentMap(disputed, MAX_CONCURRENCY, function(q) {\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.3));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done2++;\n progress('仲裁 ' + done2 + '/' + disputed.length + ' 完成');\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n }).then(function() {\n clearProgress();\n streamClear();\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n })\n .catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n }\n\n // ============ 启动 ============\n startVoting();\n})();\n"}, "1f12866ca65f55c111a5281fa1019867f6d3b352": {"t": 1785758723.2814238, "n": 10941, "h": "03a66299c220852a", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '<div id=\"fdty-msg\"></div><div id=\"fdty-stream\" style=\"color:#999;font-size:11px;white-space:pre-wrap;word-break:break-all;max-height:120px;overflow:hidden;margin-top:2px;padding-top:2px;border-top:1px dashed #ddd\"></div><div id=\"fdty-progress\" style=\"color:#999;font-size:11px\"></div>';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n var stream_el = doc.getElementById('fdty-stream');\n var progress_el = doc.getElementById('fdty-progress');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n function progress(msg) {\n console.log('[fdty] ' + msg);\n progress_el.textContent = msg;\n }\n\n function clearProgress() {\n progress_el.textContent = '';\n }\n\n function streamShow(text) {\n stream_el.textContent = text;\n }\n\n function streamAppend(text) {\n stream_el.textContent += text;\n stream_el.scrollTop = stream_el.scrollHeight;\n }\n\n function streamClear() {\n stream_el.textContent = '';\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/chat/completions';\n var MAX_CONCURRENCY = 5;\n\n // 并发限制器:同时对最多 limit 个 item 执行 fn,其余排队\n function concurrentMap(items, limit, fn) {\n return new Promise(function(resolve) {\n var results = new Array(items.length);\n var running = 0, idx = 0, done = 0, total = items.length;\n if (total === 0) { resolve(results); return; }\n\n function next() {\n while (running < limit && idx < total) {\n var i = idx++;\n running++;\n fn(items[i], i).then(function(r) {\n results[i] = r;\n running--;\n done++;\n if (done === total) resolve(results);\n else next();\n });\n }\n }\n next();\n });\n }\n\n var SYSTEM_PROMPT = [\n '# 角色',\n '复旦大学体育理论考试专业判题员。根据运动科学常识与体育规则,为每道题目给出最准确的答案。',\n '',\n '# 知识覆盖领域',\n '- 体育概论:体育概念与分类、体育功能、奥林匹克精神、高等学校体育、体育锻炼与心理健康/社会适应',\n '- 健康体能:体能要素(心肺耐力/肌肉力量与耐力/柔韧性/身体成分)、健康体能锻炼原则',\n '- 体质健康:健康与体质概念、亚健康状态、体质评价方法(BMI/体脂率/最大吸氧量/12分钟跑)、营养膳食、运动性疲劳与恢复',\n '- 运动保健:运动生理反应(肌肉酸痛/极点与第二次呼吸/心血管适应)、运动损伤预防与处理(扭挫伤/肌肉拉伤/痉挛/低血糖/晕厥)、运动处方',\n '- 田径:走跑跳投项目群、短跑与中长跑技术、立定跳远、体质健康测试',\n '- 球类:篮球/排球/足球/羽毛球/网球/乒乓球/棒垒球/手球/气排球/高尔夫——各项目规则、基本技术、基本战术',\n '- 游泳与救生:游泳技术(蛙泳/自由泳/仰泳/蝶泳)、专项素质训练、水中救生方法',\n '- 形体运动:健美操/艺术体操/体育舞蹈/健美——基本技术、裁判规则、形体评价标准',\n '- 民族传统体育:长拳/太极拳/咏春拳/剑术/木兰拳(扇)/龙舞/八段锦/防身术/射艺——技术特点与功法',\n '- 击剑:击剑起源与发展、基本技术(花剑/重剑/佩剑)、战术类型、比赛规则与场地器材',\n '',\n '# 判题原则',\n '1. 常识推理:运用运动科学常识与体育规则进行推理判断;涉及具体数字、年份、人名的题目基于最合理的常识推理作答',\n '2. 绝对化判断:题干中出现\"完全\"\"绝不\"\"一定\"\"所有\"\"从不\"等绝对化用词时,通常为错误表述。但公认事实类绝对陈述(如\"奥林匹克格言是更快更高更强\")属于正确——需结合语境判断',\n '3. 否定识别:识别题干中的否定词(\"不\"\"非\"\"无\"\"没有\")和双重否定结构,避免因误读否定而答反',\n '4. 规则标准:运动规则类题目以最新国际通用比赛规则和中国现行规则为准',\n '5. 必出答案:遇到不确定的题目,基于最合理推理给出答案,不得输出空值、\"不确定\"或跳过任何题目',\n '',\n '# 输出格式',\n '仅输出JSON对象,键为题号(数字字符串),值为答案字符串。',\n '判断题:\"对\"或\"错\";单选题:大写字母A/B/C/D。',\n '示例: {\"1\":\"对\",\"2\":\"错\",\"3\":\"C\"}',\n '错误示例(禁止):{\"1\":\"正确\"} ← 必须用\"对\"而非\"正确\";{\"1\":\"True\"} ← 必须用中文\"对\";{\"1\":\"c\"} ← 必须用大写字母'].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n thinking: { type: 'enabled' },\n reasoning_effort: 'max',\n max_tokens: 4096,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1 & 2 ============\n function startVoting() {\n log(questions.length + ' 题 每题3票 | 并发窗口=' + MAX_CONCURRENCY, '#1a73e8');\n\n var totalOk = 0;\n var done1 = 0;\n var temps = [0.1, 0.6, 1.2];\n\n concurrentMap(questions, MAX_CONCURRENCY, function(q) {\n var batch = [];\n for (var i = 0; i < 3; i++) batch.push(callAPI([q], temps[i]));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done1++;\n progress('投票 ' + done1 + '/' + questions.length + ' 完成');\n if (best && max === 3) {\n if (selectAnswer(q, best)) totalOk++;\n }\n return { q: q, best: best, count: max, counts: counts };\n });\n }).then(function(results) {\n clearProgress();\n streamClear();\n\n var disputed = [];\n results.forEach(function(r) {\n if (!r.best || r.count < 3) disputed.push(r.q);\n });\n log('完成 ' + totalOk + '/' + questions.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) return;\n\n // ============ Phase 2: 分歧题仲裁 ============\n var done2 = 0;\n log('Phase 2 仲裁 ' + disputed.length + ' 题 (每题10次)...', '#e37400');\n\n var unresolved = [];\n var phase2Ok = 0;\n return concurrentMap(disputed, MAX_CONCURRENCY, function(q) {\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.3));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done2++;\n progress('仲裁 ' + done2 + '/' + disputed.length + ' 完成');\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n }).then(function() {\n clearProgress();\n streamClear();\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n })\n .catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n }\n\n // ============ 启动 ============\n startVoting();\n})();\n"}, "4c47a868d5e7691bb62622884182c7fdcfc043b9": {"t": 1785758724.6890626, "n": 14628, "h": "30434213d09e9355", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '<div id=\"fdty-msg\"></div><div id=\"fdty-stream\" style=\"color:#999;font-size:11px;white-space:pre-wrap;word-break:break-all;max-height:120px;overflow:hidden;margin-top:2px;padding-top:2px;border-top:1px dashed #ddd\"></div><div id=\"fdty-progress\" style=\"color:#999;font-size:11px\"></div>';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n var stream_el = doc.getElementById('fdty-stream');\n var progress_el = doc.getElementById('fdty-progress');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n function progress(msg) {\n console.log('[fdty] ' + msg);\n progress_el.textContent = msg;\n }\n\n function clearProgress() {\n progress_el.textContent = '';\n }\n\n function streamShow(text) {\n stream_el.textContent = text;\n }\n\n function streamAppend(text) {\n stream_el.textContent += text;\n stream_el.scrollTop = stream_el.scrollHeight;\n }\n\n function streamClear() {\n stream_el.textContent = '';\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/chat/completions';\n var MAX_CONCURRENCY = 5;\n\n // 并发限制器:同时对最多 limit 个 item 执行 fn,其余排队\n function concurrentMap(items, limit, fn) {\n return new Promise(function(resolve) {\n var results = new Array(items.length);\n var running = 0, idx = 0, done = 0, total = items.length;\n if (total === 0) { resolve(results); return; }\n\n function next() {\n while (running < limit && idx < total) {\n var i = idx++;\n running++;\n fn(items[i], i).then(function(r) {\n results[i] = r;\n running--;\n done++;\n if (done === total) resolve(results);\n else next();\n });\n }\n }\n next();\n });\n }\n\n var SYSTEM_PROMPT = [\n '你是一位复旦大学体育理论考试专业答题助手。',\n '',\n '## 知识范围',\n '运动生理学、体能训练理论、运动规则(篮球/排球/足球/田径/游泳/击剑等)、健康体能与体质评价、运动损伤预防、民族传统体育。',\n '',\n '## 答题步骤',\n '1. 仔细阅读题目,识别考点属于哪个知识领域',\n '2. 结合运动科学常识和体育规则进行推理判断',\n '3. 检查题干中是否有绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),该类表述通常为错误',\n '4. 涉及具体年份、数据、百分比的事实题,严格对照已知知识点',\n '5. 注意否定词和双重否定,避免被题干迷惑',\n '6. 给出最终答案',\n '',\n '## 输出格式',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,不要markdown包裹,不要解释。',\n '示例: {\"1\":\"对\",\"2\":\"错\",\"3\":\"C\"}'].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n\n // ============ 加载章节摘要 ============\n var CHAPTERS_URL = 'https://fdty.oss-cn-beijing.aliyuncs.com/chapters_summary.md';\n var chaptersData = null;\n var relevanceMap = null;\n\n function fetchChapters() {\n return fetch(CHAPTERS_URL).then(function(r) {\n if (!r.ok) throw new Error('HTTP ' + r.status);\n return r.text();\n }).then(function(text) {\n chaptersData = parseSummary(text);\n log('加载 ' + chaptersData.length + ' 章知识库', '#1a73e8');\n }).catch(function() {\n log('知识库加载失败,使用无上下文模式');\n });\n }\n\n function parseSummary(text) {\n var blocks = text.split(/\\n---\\n/);\n var chapters = [];\n blocks.forEach(function(block) {\n var h2 = block.match(/^## (.+\\.md)/m);\n var bold = block.match(/^\\*\\*(.+?)\\*\\*/m);\n var quote = block.match(/^> (.+)/m);\n var secs = [];\n var secMatch;\n var secRe = /^ - (.+)$/gm;\n while ((secMatch = secRe.exec(block)) !== null) {\n secs.push(secMatch[1]);\n }\n if (bold) {\n chapters.push({\n id: h2 ? h2[1] : '',\n title: bold[1],\n summary: quote ? quote[1] : '',\n sections: secs\n });\n }\n });\n return chapters;\n }\n\n function buildChapterIndex() {\n return chaptersData.map(function(ch) {\n return '## ' + ch.id + '\\n摘要: ' + ch.summary + '\\n章节: ' + ch.sections.join(', ');\n }).join('\\n\\n');\n }\n\n function phase0Mapping() {\n if (!chaptersData) return Promise.resolve(null);\n log('Phase 0: 分析章节相关性...', '#1a73e8');\n progress('Phase 0 分析中...');\n\n var userContent = '\"\"\"章节大纲\"\"\"\\n' + buildChapterIndex() +\n '\\n\\n\"\"\"题目列表\"\"\"\\n' + questions.map(function(q) {\n return '[' + q.num + '] ' + q.text;\n }).join('\\n');\n\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: 0.1,\n thinking: { type: 'disabled' },\n max_tokens: 2048,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: [\n '你是大学体育理论考试出题分析师。任务:根据题目内容,判断每道题出自教材的哪个章节。',\n '',\n '## 步骤',\n '1. 阅读题目,识别其考查的知识点',\n '2. 在章节大纲中匹配最相关的1-2个章节',\n '3. 输出关联结果',\n '',\n '## 规则',\n '- 每道题标记1-2个最相关的章节文件名',\n '- 只输出JSON,不要任何解释或markdown',\n '- 格式: {\"题号\":[\"文件名1.md\",\"文件名2.md\"]}',\n '- 示例: {\"1\":[\"第一章_体育概论与体育锻炼.md\"],\"2\":[\"第六章_球类运动.md\",\"第五章_田径.md\"]}'\n ].join('\\n') },\n { role: 'user', content: userContent }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) { return r.json(); })\n .then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n try { return JSON.parse(content); } catch(e) { return null; }\n }).catch(function() { return null; });\n }\n\n var fullChapters = {};\n\n function getKnowledge(q) {\n if (!relevanceMap) return '';\n var ids = relevanceMap[String(q.num)];\n if (!ids || !ids.length) return '';\n var parts = [];\n ids.forEach(function(id) {\n var text = fullChapters[id];\n if (text) parts.push(text.substring(0, 4000));\n });\n return parts.length ? '\"\"\"教材参考\"\"\"\\n' + parts.join('\\n\\n---\\n\\n') + '\\n\"\"\"教材参考结束\"\"\"\\n\\n' : '';\n }\n\n // ============ API 调用 ============\n function buildUserPrompt(qs, context) {\n context = context || '';\n return context + qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp, context) {\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n thinking: { type: 'enabled' },\n reasoning_effort: 'max',\n max_tokens: 4096,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs, context) }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1 & 2 ============\n function startVoting() {\n log(questions.length + ' 题 每题3票 | 并发窗口=' + MAX_CONCURRENCY +\n (relevanceMap ? ' (RAG)' : ''), '#1a73e8');\n\n var totalOk = 0;\n var done1 = 0;\n var temps = [0.1, 0.6, 1.2];\n\n concurrentMap(questions, MAX_CONCURRENCY, function(q) {\n var ctx = getKnowledge(q);\n var batch = [];\n for (var i = 0; i < 3; i++) batch.push(callAPI([q], temps[i], ctx));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done1++;\n progress('投票 ' + done1 + '/' + questions.length + ' 完成');\n if (best && max === 3) {\n if (selectAnswer(q, best)) totalOk++;\n }\n return { q: q, best: best, count: max, counts: counts };\n });\n }).then(function(results) {\n clearProgress();\n streamClear();\n\n var disputed = [];\n results.forEach(function(r) {\n if (!r.best || r.count < 3) disputed.push(r.q);\n });\n log('完成 ' + totalOk + '/' + questions.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) return;\n\n // ============ Phase 2: 分歧题仲裁 ============\n var done2 = 0;\n log('Phase 2 仲裁 ' + disputed.length + ' 题 (每题10次)...', '#e37400');\n\n var unresolved = [];\n var phase2Ok = 0;\n return concurrentMap(disputed, MAX_CONCURRENCY, function(q) {\n var ctx = getKnowledge(q);\n\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.3, ctx));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done2++;\n progress('仲裁 ' + done2 + '/' + disputed.length + ' 完成');\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n }).then(function() {\n clearProgress();\n streamClear();\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n })\n .catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n }\n\n // ============ 启动 ============\n function loadFullChapters(map) {\n if (!map) return Promise.resolve();\n var ids = {};\n Object.keys(map).forEach(function(k) {\n (map[k] || []).forEach(function(id) { ids[id] = true; });\n });\n var needed = Object.keys(ids);\n if (!needed.length) return Promise.resolve();\n progress('加载教材章节...');\n return Promise.all(needed.map(function(id) {\n var url = 'https://fdty.oss-cn-beijing.aliyuncs.com/chapters/' + id;\n return fetch(url).then(function(r) { return r.text(); }).then(function(t) {\n fullChapters[id] = t;\n }).catch(function() {});\n })).then(function() {\n log('加载 ' + Object.keys(fullChapters).length + ' 章教材内容');\n });\n }\n\n fetchChapters().then(function() {\n return phase0Mapping();\n }).then(function(map) {\n relevanceMap = map;\n return loadFullChapters(map);\n }).then(function() {\n clearProgress();\n startVoting();\n });\n})();\n"}, "6bc35071159a1700bb29fa7ca77659d55db67a05": {"t": 1785758726.0289295, "n": 14627, "h": "e7f9ac9df82e96e3", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '<div id=\"fdty-msg\"></div><div id=\"fdty-stream\" style=\"color:#999;font-size:11px;white-space:pre-wrap;word-break:break-all;max-height:120px;overflow:hidden;margin-top:2px;padding-top:2px;border-top:1px dashed #ddd\"></div><div id=\"fdty-progress\" style=\"color:#999;font-size:11px\"></div>';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n var stream_el = doc.getElementById('fdty-stream');\n var progress_el = doc.getElementById('fdty-progress');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n function progress(msg) {\n console.log('[fdty] ' + msg);\n progress_el.textContent = msg;\n }\n\n function clearProgress() {\n progress_el.textContent = '';\n }\n\n function streamShow(text) {\n stream_el.textContent = text;\n }\n\n function streamAppend(text) {\n stream_el.textContent += text;\n stream_el.scrollTop = stream_el.scrollHeight;\n }\n\n function streamClear() {\n stream_el.textContent = '';\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/chat/completions';\n var MAX_CONCURRENCY = 5;\n\n // 并发限制器:同时对最多 limit 个 item 执行 fn,其余排队\n function concurrentMap(items, limit, fn) {\n return new Promise(function(resolve) {\n var results = new Array(items.length);\n var running = 0, idx = 0, done = 0, total = items.length;\n if (total === 0) { resolve(results); return; }\n\n function next() {\n while (running < limit && idx < total) {\n var i = idx++;\n running++;\n fn(items[i], i).then(function(r) {\n results[i] = r;\n running--;\n done++;\n if (done === total) resolve(results);\n else next();\n });\n }\n }\n next();\n });\n }\n\n var SYSTEM_PROMPT = [\n '你是一位复旦大学体育理论考试专业答题助手。',\n '',\n '## 知识范围',\n '运动生理学、体能训练理论、运动规则(篮球/排球/足球/田径/游泳/击剑等)、健康体能与体质评价、运动损伤预防、民族传统体育。',\n '',\n '## 答题步骤',\n '1. 仔细阅读题目,识别考点属于哪个知识领域',\n '2. 结合运动科学常识和体育规则进行推理判断',\n '3. 检查题干中是否有绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),该类表述通常为错误',\n '4. 涉及具体年份、数据、百分比的事实题,严格对照已知知识点',\n '5. 注意否定词和双重否定,避免被题干迷惑',\n '6. 给出最终答案',\n '',\n '## 输出格式',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,不要markdown包裹,不要解释。',\n '示例: {\"1\":\"对\",\"2\":\"错\",\"3\":\"C\"}'].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n\n // ============ 加载章节摘要 ============\n var CHAPTERS_URL = 'https://fdty.oss-cn-beijing.aliyuncs.com/chapters_summary.md';\n var chaptersData = null;\n var relevanceMap = null;\n\n function fetchChapters() {\n return fetch(CHAPTERS_URL).then(function(r) {\n if (!r.ok) throw new Error('HTTP ' + r.status);\n return r.text();\n }).then(function(text) {\n chaptersData = parseSummary(text);\n log('加载 ' + chaptersData.length + ' 章知识库', '#1a73e8');\n }).catch(function() {\n log('知识库加载失败,使用无上下文模式');\n });\n }\n\n function parseSummary(text) {\n var blocks = text.split(/\\n---\\n/);\n var chapters = [];\n blocks.forEach(function(block) {\n var h2 = block.match(/^## (.+\\.md)/m);\n var bold = block.match(/^\\*\\*(.+?)\\*\\*/m);\n var quote = block.match(/^> (.+)/m);\n var secs = [];\n var secMatch;\n var secRe = /^ - (.+)$/gm;\n while ((secMatch = secRe.exec(block)) !== null) {\n secs.push(secMatch[1]);\n }\n if (bold) {\n chapters.push({\n id: h2 ? h2[1] : '',\n title: bold[1],\n summary: quote ? quote[1] : '',\n sections: secs\n });\n }\n });\n return chapters;\n }\n\n function buildChapterIndex() {\n return chaptersData.map(function(ch) {\n return '## ' + ch.id + '\\n摘要: ' + ch.summary + '\\n章节: ' + ch.sections.join(', ');\n }).join('\\n\\n');\n }\n\n function phase0Mapping() {\n if (!chaptersData) return Promise.resolve(null);\n log('Phase 0: 分析章节相关性...', '#1a73e8');\n progress('Phase 0 分析中...');\n\n var userContent = '\"\"\"章节大纲\"\"\"\\n' + buildChapterIndex() +\n '\\n\\n\"\"\"题目列表\"\"\"\\n' + questions.map(function(q) {\n return '[' + q.num + '] ' + q.text;\n }).join('\\n');\n\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: 0.1,\n thinking: { type: 'disabled' },\n max_tokens: 2048,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: [\n '你是大学体育理论考试出题分析师。任务:根据题目内容,判断每道题出自教材的哪个章节。',\n '',\n '## 步骤',\n '1. 阅读题目,识别其考查的知识点',\n '2. 在章节大纲中匹配最相关的1-2个章节',\n '3. 输出关联结果',\n '',\n '## 规则',\n '- 每道题标记1-2个最相关的章节文件名',\n '- 只输出JSON,不要任何解释或markdown',\n '- 格式: {\"题号\":[\"文件名1.md\",\"文件名2.md\"]}',\n '- 示例: {\"1\":[\"第一章_体育概论与体育锻炼.md\"],\"2\":[\"第六章_球类运动.md\",\"第五章_田径.md\"]}'\n ].join('\\n') },\n { role: 'user', content: userContent }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) { return r.json(); })\n .then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n try { return JSON.parse(content); } catch(e) { return null; }\n }).catch(function() { return null; });\n }\n\n var fullChapters = {};\n\n function getKnowledge(q) {\n if (!relevanceMap) return '';\n var ids = relevanceMap[String(q.num)];\n if (!ids || !ids.length) return '';\n var parts = [];\n ids.forEach(function(id) {\n var text = fullChapters[id];\n if (text) parts.push(text.substring(0, 4000));\n });\n return parts.length ? '\"\"\"教材参考\"\"\"\\n' + parts.join('\\n\\n---\\n\\n') + '\\n\"\"\"教材参考结束\"\"\"\\n\\n' : '';\n }\n\n // ============ API 调用 ============\n function buildUserPrompt(qs, context) {\n context = context || '';\n return context + qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp, context) {\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n thinking: { type: 'enabled' },\n reasoning_effort: 'max',\n max_tokens: 4096,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs, context) }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1 & 2 ============\n function startVoting() {\n log(questions.length + ' 题 每题3票 | 并发窗口=' + MAX_CONCURRENCY +\n (relevanceMap ? ' (RAG)' : ''), '#1a73e8');\n\n var totalOk = 0;\n var done1 = 0;\n var temps = [0.1, 0.6, 1.2];\n\n concurrentMap(questions, MAX_CONCURRENCY, function(q) {\n var ctx = getKnowledge(q);\n var batch = [];\n for (var i = 0; i < 3; i++) batch.push(callAPI([q], temps[i], ctx));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done1++;\n progress('投票 ' + done1 + '/' + questions.length + ' 完成');\n if (best && max === 3) {\n if (selectAnswer(q, best)) totalOk++;\n }\n return { q: q, best: best, count: max, counts: counts };\n });\n }).then(function(results) {\n clearProgress();\n streamClear();\n\n var disputed = [];\n results.forEach(function(r) {\n if (!r.best || r.count < 3) disputed.push(r.q);\n });\n log('完成 ' + totalOk + '/' + questions.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) return;\n\n // ============ Phase 2: 分歧题仲裁 ============\n var done2 = 0;\n log('Phase 2 仲裁 ' + disputed.length + ' 题 (每题10次)...', '#e37400');\n\n var unresolved = [];\n var phase2Ok = 0;\n concurrentMap(disputed, MAX_CONCURRENCY, function(q) {\n var ctx = getKnowledge(q);\n\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.3, ctx));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done2++;\n progress('仲裁 ' + done2 + '/' + disputed.length + ' 完成');\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n\n }).then(function() {\n clearProgress();\n streamClear();\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n });\n }).catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n }\n\n // ============ 启动 ============\n function loadFullChapters(map) {\n if (!map) return Promise.resolve();\n var ids = {};\n Object.keys(map).forEach(function(k) {\n (map[k] || []).forEach(function(id) { ids[id] = true; });\n });\n var needed = Object.keys(ids);\n if (!needed.length) return Promise.resolve();\n progress('加载教材章节...');\n return Promise.all(needed.map(function(id) {\n var url = 'https://fdty.oss-cn-beijing.aliyuncs.com/chapters/' + id;\n return fetch(url).then(function(r) { return r.text(); }).then(function(t) {\n fullChapters[id] = t;\n }).catch(function() {});\n })).then(function() {\n log('加载 ' + Object.keys(fullChapters).length + ' 章教材内容');\n });\n }\n\n fetchChapters().then(function() {\n return phase0Mapping();\n }).then(function(map) {\n relevanceMap = map;\n return loadFullChapters(map);\n }).then(function() {\n clearProgress();\n startVoting();\n });\n})();\n"}, "cac66caefde6cd89666ff25d01a3950301e316f3": {"t": 1785758727.331913, "n": 15677, "h": "d72dea3013cd31d7", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '<div id=\"fdty-msg\"></div><div id=\"fdty-stream\" style=\"color:#999;font-size:11px;white-space:pre-wrap;word-break:break-all;max-height:120px;overflow:hidden;margin-top:2px;padding-top:2px;border-top:1px dashed #ddd\"></div><div id=\"fdty-progress\" style=\"color:#999;font-size:11px\"></div>';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n var stream_el = doc.getElementById('fdty-stream');\n var progress_el = doc.getElementById('fdty-progress');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n function progress(msg) {\n console.log('[fdty] ' + msg);\n progress_el.textContent = msg;\n }\n\n function clearProgress() {\n progress_el.textContent = '';\n }\n\n function streamShow(text) {\n stream_el.textContent = text;\n }\n\n function streamAppend(text) {\n stream_el.textContent += text;\n stream_el.scrollTop = stream_el.scrollHeight;\n }\n\n function streamClear() {\n stream_el.textContent = '';\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/chat/completions';\n var ZHIPU_KEY = window.__ZHIPU_KEY || '';\n var MAX_CONCURRENCY = 5;\n\n // 并发限制器:同时对最多 limit 个 item 执行 fn,其余排队\n function concurrentMap(items, limit, fn) {\n return new Promise(function(resolve) {\n var results = new Array(items.length);\n var running = 0, idx = 0, done = 0, total = items.length;\n if (total === 0) { resolve(results); return; }\n\n function next() {\n while (running < limit && idx < total) {\n var i = idx++;\n running++;\n fn(items[i], i).then(function(r) {\n results[i] = r;\n running--;\n done++;\n if (done === total) resolve(results);\n else next();\n });\n }\n }\n next();\n });\n }\n\n function webSearch(query) {\n if (!ZHIPU_KEY) return Promise.resolve([]);\n return fetch('https://open.bigmodel.cn/api/paas/v4/web_search', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + ZHIPU_KEY },\n body: JSON.stringify({ search_query: query, search_engine: 'search_pro_quark', search_intent: false, count: 3, content_size: 'medium' })\n }).then(function(r) { return r.json(); }).then(function(d) {\n return (d.search_result || []).map(function(r) { return r.title + '\\n ' + r.content; });\n }).catch(function() { return []; });\n }\n\n var SYSTEM_PROMPT = [\n '你是一位复旦大学体育理论考试专业答题助手。',\n '',\n '## 知识范围',\n '运动生理学、体能训练理论、运动规则(篮球/排球/足球/田径/游泳/击剑等)、健康体能与体质评价、运动损伤预防、民族传统体育。',\n '',\n '## 答题步骤',\n '1. 仔细阅读题目,识别考点属于哪个知识领域',\n '2. 结合运动科学常识和体育规则进行推理判断',\n '3. 检查题干中是否有绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),该类表述通常为错误',\n '4. 涉及具体年份、数据、百分比的事实题,严格对照已知知识点',\n '5. 注意否定词和双重否定,避免被题干迷惑',\n '6. 给出最终答案',\n '',\n '## 输出格式',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,不要markdown包裹,不要解释。',\n '示例: {\"1\":\"对\",\"2\":\"错\",\"3\":\"C\"}'].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n\n // ============ 加载章节摘要 ============\n var CHAPTERS_URL = 'https://fdty.oss-cn-beijing.aliyuncs.com/chapters_summary.md';\n var chaptersData = null;\n var relevanceMap = null;\n\n function fetchChapters() {\n return fetch(CHAPTERS_URL).then(function(r) {\n if (!r.ok) throw new Error('HTTP ' + r.status);\n return r.text();\n }).then(function(text) {\n chaptersData = parseSummary(text);\n log('加载 ' + chaptersData.length + ' 章知识库', '#1a73e8');\n }).catch(function() {\n log('知识库加载失败,使用无上下文模式');\n });\n }\n\n function parseSummary(text) {\n var blocks = text.split(/\\n---\\n/);\n var chapters = [];\n blocks.forEach(function(block) {\n var h2 = block.match(/^## (.+\\.md)/m);\n var bold = block.match(/^\\*\\*(.+?)\\*\\*/m);\n var quote = block.match(/^> (.+)/m);\n var secs = [];\n var secMatch;\n var secRe = /^ - (.+)$/gm;\n while ((secMatch = secRe.exec(block)) !== null) {\n secs.push(secMatch[1]);\n }\n if (bold) {\n chapters.push({\n id: h2 ? h2[1] : '',\n title: bold[1],\n summary: quote ? quote[1] : '',\n sections: secs\n });\n }\n });\n return chapters;\n }\n\n function buildChapterIndex() {\n return chaptersData.map(function(ch) {\n return '## ' + ch.id + '\\n摘要: ' + ch.summary + '\\n章节: ' + ch.sections.join(', ');\n }).join('\\n\\n');\n }\n\n function phase0Mapping() {\n if (!chaptersData) return Promise.resolve(null);\n log('Phase 0: 分析章节相关性...', '#1a73e8');\n progress('Phase 0 分析中...');\n\n var userContent = '\"\"\"章节大纲\"\"\"\\n' + buildChapterIndex() +\n '\\n\\n\"\"\"题目列表\"\"\"\\n' + questions.map(function(q) {\n return '[' + q.num + '] ' + q.text;\n }).join('\\n');\n\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: 0.1,\n thinking: { type: 'disabled' },\n max_tokens: 2048,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: [\n '你是大学体育理论考试出题分析师。任务:根据题目内容,判断每道题出自教材的哪个章节。',\n '',\n '## 步骤',\n '1. 阅读题目,识别其考查的知识点',\n '2. 在章节大纲中匹配最相关的1-2个章节',\n '3. 输出关联结果',\n '',\n '## 规则',\n '- 每道题标记1-2个最相关的章节文件名',\n '- 只输出JSON,不要任何解释或markdown',\n '- 格式: {\"题号\":[\"文件名1.md\",\"文件名2.md\"]}',\n '- 示例: {\"1\":[\"第一章_体育概论与体育锻炼.md\"],\"2\":[\"第六章_球类运动.md\",\"第五章_田径.md\"]}'\n ].join('\\n') },\n { role: 'user', content: userContent }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) { return r.json(); })\n .then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n try { return JSON.parse(content); } catch(e) { return null; }\n }).catch(function() { return null; });\n }\n\n var fullChapters = {};\n\n function getKnowledge(q) {\n if (!relevanceMap) return '';\n var ids = relevanceMap[String(q.num)];\n if (!ids || !ids.length) return '';\n var parts = [];\n ids.forEach(function(id) {\n var text = fullChapters[id];\n if (text) parts.push(text.substring(0, 4000));\n });\n return parts.length ? '\"\"\"教材参考\"\"\"\\n' + parts.join('\\n\\n---\\n\\n') + '\\n\"\"\"教材参考结束\"\"\"\\n\\n' : '';\n }\n\n // ============ API 调用 ============\n function buildUserPrompt(qs, context) {\n context = context || '';\n return context + qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp, context) {\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n thinking: { type: 'enabled' },\n reasoning_effort: 'max',\n max_tokens: 4096,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs, context) }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1 & 2 ============\n function startVoting() {\n log(questions.length + ' 题 每题3票 | 并发窗口=' + MAX_CONCURRENCY +\n (relevanceMap ? ' (RAG)' : ''), '#1a73e8');\n\n var totalOk = 0;\n var done1 = 0;\n var temps = [0.1, 0.6, 1.2];\n\n concurrentMap(questions, MAX_CONCURRENCY, function(q) {\n var ctx = getKnowledge(q);\n var batch = [];\n for (var i = 0; i < 3; i++) batch.push(callAPI([q], temps[i], ctx));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done1++;\n progress('投票 ' + done1 + '/' + questions.length + ' 完成');\n if (best && max === 3) {\n if (selectAnswer(q, best)) totalOk++;\n }\n return { q: q, best: best, count: max, counts: counts };\n });\n }).then(function(results) {\n clearProgress();\n streamClear();\n\n var disputed = [];\n results.forEach(function(r) {\n if (!r.best || r.count < 3) disputed.push(r.q);\n });\n log('完成 ' + totalOk + '/' + questions.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) return;\n\n // ============ Phase 2: 分歧题搜索+仲裁 ============\n var done2 = 0;\n log('Phase 2 仲裁 ' + disputed.length + ' 题 (搜索+每题10次)...', '#e37400');\n\n var unresolved = [];\n var phase2Ok = 0;\n concurrentMap(disputed, MAX_CONCURRENCY, function(q) {\n // 先搜索,拿到结果后再投票\n var searchQuery = q.text.replace(/[^一-龥a-zA-Z0-9]/g, ' ').replace(/\\s+/g, ' ').trim().slice(0, 60);\n return webSearch(searchQuery).then(function(searchResults) {\n var searchCtx = searchResults.length ? '\"\"\"网络搜索参考\"\"\"\\n' + searchResults.join('\\n\\n') + '\\n\"\"\"搜索结束\"\"\"\\n\\n' : '';\n var ctx = getKnowledge(q) + searchCtx;\n\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.3, ctx));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done2++;\n progress('仲裁 ' + done2 + '/' + disputed.length + ' 完成');\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n });\n\n }).then(function() {\n clearProgress();\n streamClear();\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n });\n }).catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n }\n\n // ============ 启动 ============\n function loadFullChapters(map) {\n if (!map) return Promise.resolve();\n var ids = {};\n Object.keys(map).forEach(function(k) {\n (map[k] || []).forEach(function(id) { ids[id] = true; });\n });\n var needed = Object.keys(ids);\n if (!needed.length) return Promise.resolve();\n progress('加载教材章节...');\n return Promise.all(needed.map(function(id) {\n var url = 'https://fdty.oss-cn-beijing.aliyuncs.com/chapters/' + id;\n return fetch(url).then(function(r) { return r.text(); }).then(function(t) {\n fullChapters[id] = t;\n }).catch(function() {});\n })).then(function() {\n log('加载 ' + Object.keys(fullChapters).length + ' 章教材内容');\n });\n }\n\n fetchChapters().then(function() {\n return phase0Mapping();\n }).then(function(map) {\n relevanceMap = map;\n return loadFullChapters(map);\n }).then(function() {\n clearProgress();\n startVoting();\n });\n})();\n"}, "931dd2759bfeac56b630f7be01d1b54869d8721e": {"t": 1785758728.6646147, "n": 15032, "h": "1ede4c2151c95d97", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '<div id=\"fdty-msg\"></div><div id=\"fdty-stream\" style=\"color:#999;font-size:11px;white-space:pre-wrap;word-break:break-all;max-height:120px;overflow:hidden;margin-top:2px;padding-top:2px;border-top:1px dashed #ddd\"></div><div id=\"fdty-progress\" style=\"color:#999;font-size:11px\"></div>';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n var stream_el = doc.getElementById('fdty-stream');\n var progress_el = doc.getElementById('fdty-progress');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n function progress(msg) {\n console.log('[fdty] ' + msg);\n progress_el.textContent = msg;\n }\n\n function clearProgress() {\n progress_el.textContent = '';\n }\n\n function streamShow(text) {\n stream_el.textContent = text;\n }\n\n function streamAppend(text) {\n stream_el.textContent += text;\n stream_el.scrollTop = stream_el.scrollHeight;\n }\n\n function streamClear() {\n stream_el.textContent = '';\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/chat/completions';\n var ZHIPU_KEY = window.__ZHIPU_KEY || '';\n\n function webSearch(query) {\n if (!ZHIPU_KEY) return Promise.resolve([]);\n return fetch('https://open.bigmodel.cn/api/paas/v4/web_search', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + ZHIPU_KEY },\n body: JSON.stringify({ search_query: query, search_engine: 'search_pro_quark', search_intent: false, count: 3, content_size: 'medium' })\n }).then(function(r) { return r.json(); }).then(function(d) {\n return (d.search_result || []).map(function(r) { return r.title + '\\n ' + r.content; });\n }).catch(function() { return []; });\n }\n\n var SYSTEM_PROMPT = [\n '你是一位复旦大学体育理论考试专业答题助手。',\n '',\n '## 知识范围',\n '运动生理学、体能训练理论、运动规则(篮球/排球/足球/田径/游泳/击剑等)、健康体能与体质评价、运动损伤预防、民族传统体育。',\n '',\n '## 答题步骤',\n '1. 仔细阅读题目,识别考点属于哪个知识领域',\n '2. 结合运动科学常识和体育规则进行推理判断',\n '3. 检查题干中是否有绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),该类表述通常为错误',\n '4. 涉及具体年份、数据、百分比的事实题,严格对照已知知识点',\n '5. 注意否定词和双重否定,避免被题干迷惑',\n '6. 给出最终答案',\n '',\n '## 输出格式',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,不要markdown包裹,不要解释。',\n '示例: {\"1\":\"对\",\"2\":\"错\",\"3\":\"C\"}'].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n\n // ============ 加载章节摘要 ============\n var CHAPTERS_URL = 'https://fdty.oss-cn-beijing.aliyuncs.com/chapters_summary.md';\n var chaptersData = null;\n var relevanceMap = null;\n\n function fetchChapters() {\n return fetch(CHAPTERS_URL).then(function(r) {\n if (!r.ok) throw new Error('HTTP ' + r.status);\n return r.text();\n }).then(function(text) {\n chaptersData = parseSummary(text);\n log('加载 ' + chaptersData.length + ' 章知识库', '#1a73e8');\n }).catch(function() {\n log('知识库加载失败,使用无上下文模式');\n });\n }\n\n function parseSummary(text) {\n var blocks = text.split(/\\n---\\n/);\n var chapters = [];\n blocks.forEach(function(block) {\n var h2 = block.match(/^## (.+\\.md)/m);\n var bold = block.match(/^\\*\\*(.+?)\\*\\*/m);\n var quote = block.match(/^> (.+)/m);\n var secs = [];\n var secMatch;\n var secRe = /^ - (.+)$/gm;\n while ((secMatch = secRe.exec(block)) !== null) {\n secs.push(secMatch[1]);\n }\n if (bold) {\n chapters.push({\n id: h2 ? h2[1] : '',\n title: bold[1],\n summary: quote ? quote[1] : '',\n sections: secs\n });\n }\n });\n return chapters;\n }\n\n function buildChapterIndex() {\n return chaptersData.map(function(ch) {\n return '## ' + ch.id + '\\n摘要: ' + ch.summary + '\\n章节: ' + ch.sections.join(', ');\n }).join('\\n\\n');\n }\n\n function phase0Mapping() {\n if (!chaptersData) return Promise.resolve(null);\n log('Phase 0: 分析章节相关性...', '#1a73e8');\n progress('Phase 0 分析中...');\n\n var userContent = '\"\"\"章节大纲\"\"\"\\n' + buildChapterIndex() +\n '\\n\\n\"\"\"题目列表\"\"\"\\n' + questions.map(function(q) {\n return '[' + q.num + '] ' + q.text;\n }).join('\\n');\n\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: 0.1,\n thinking: { type: 'disabled' },\n max_tokens: 2048,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: [\n '你是大学体育理论考试出题分析师。任务:根据题目内容,判断每道题出自教材的哪个章节。',\n '',\n '## 步骤',\n '1. 阅读题目,识别其考查的知识点',\n '2. 在章节大纲中匹配最相关的1-2个章节',\n '3. 输出关联结果',\n '',\n '## 规则',\n '- 每道题标记1-2个最相关的章节文件名',\n '- 只输出JSON,不要任何解释或markdown',\n '- 格式: {\"题号\":[\"文件名1.md\",\"文件名2.md\"]}',\n '- 示例: {\"1\":[\"第一章_体育概论与体育锻炼.md\"],\"2\":[\"第六章_球类运动.md\",\"第五章_田径.md\"]}'\n ].join('\\n') },\n { role: 'user', content: userContent }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) { return r.json(); })\n .then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n try { return JSON.parse(content); } catch(e) { return null; }\n }).catch(function() { return null; });\n }\n\n var fullChapters = {};\n\n function getKnowledge(q) {\n if (!relevanceMap) return '';\n var ids = relevanceMap[String(q.num)];\n if (!ids || !ids.length) return '';\n var parts = [];\n ids.forEach(function(id) {\n var text = fullChapters[id];\n if (text) parts.push(text.substring(0, 4000));\n });\n return parts.length ? '\"\"\"教材参考\"\"\"\\n' + parts.join('\\n\\n---\\n\\n') + '\\n\"\"\"教材参考结束\"\"\"\\n\\n' : '';\n }\n\n // ============ API 调用 ============\n function buildUserPrompt(qs, context) {\n context = context || '';\n return context + qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp, context) {\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n thinking: { type: 'enabled' },\n reasoning_effort: 'max',\n max_tokens: 4096,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs, context) }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1 & 2 ============\n function startVoting() {\n log(questions.length + ' 题 每题3票 共' + (questions.length*3) + '次并发' +\n (relevanceMap ? ' (RAG)' : ''), '#1a73e8');\n\n var totalOk = 0;\n var done1 = 0;\n var temps = [0.1, 0.6, 1.2];\n\n var phase1Jobs = questions.map(function(q) {\n var ctx = getKnowledge(q);\n var batch = [];\n for (var i = 0; i < 3; i++) batch.push(callAPI([q], temps[i], ctx));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done1++;\n progress('投票 ' + done1 + '/' + questions.length + ' 完成');\n if (best && max === 3) {\n if (selectAnswer(q, best)) totalOk++;\n }\n return { q: q, best: best, count: max, counts: counts };\n });\n });\n\n Promise.all(phase1Jobs).then(function(results) {\n clearProgress();\n streamClear();\n\n var disputed = [];\n results.forEach(function(r) {\n if (!r.best || r.count < 3) disputed.push(r.q);\n });\n log('完成 ' + totalOk + '/' + questions.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) return;\n\n // ============ Phase 2: 分歧题搜索+仲裁 ============\n var done2 = 0;\n log('Phase 2 仲裁 ' + disputed.length + ' 题 (搜索+每题10次)...', '#e37400');\n\n var unresolved = [];\n var phase2Ok = 0;\n var phase2Jobs = disputed.map(function(q) {\n // 先搜索,拿到结果后再投票\n var searchQuery = q.text.replace(/[^一-龥a-zA-Z0-9]/g, ' ').replace(/\\s+/g, ' ').trim().slice(0, 60);\n return webSearch(searchQuery).then(function(searchResults) {\n var searchCtx = searchResults.length ? '\"\"\"网络搜索参考\"\"\"\\n' + searchResults.join('\\n\\n') + '\\n\"\"\"搜索结束\"\"\"\\n\\n' : '';\n var ctx = getKnowledge(q) + searchCtx;\n\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.3, ctx));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done2++;\n progress('仲裁 ' + done2 + '/' + disputed.length + ' 完成');\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n });\n\n return Promise.all(phase2Jobs).then(function() {\n clearProgress();\n streamClear();\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n });\n }).catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n }\n\n // ============ 启动 ============\n function loadFullChapters(map) {\n if (!map) return Promise.resolve();\n var ids = {};\n Object.keys(map).forEach(function(k) {\n (map[k] || []).forEach(function(id) { ids[id] = true; });\n });\n var needed = Object.keys(ids);\n if (!needed.length) return Promise.resolve();\n progress('加载教材章节...');\n return Promise.all(needed.map(function(id) {\n var url = 'https://fdty.oss-cn-beijing.aliyuncs.com/chapters/' + id;\n return fetch(url).then(function(r) { return r.text(); }).then(function(t) {\n fullChapters[id] = t;\n }).catch(function() {});\n })).then(function() {\n log('加载 ' + Object.keys(fullChapters).length + ' 章教材内容');\n });\n }\n\n fetchChapters().then(function() {\n return phase0Mapping();\n }).then(function(map) {\n relevanceMap = map;\n return loadFullChapters(map);\n }).then(function() {\n clearProgress();\n startVoting();\n });\n})();\n"}, "8f8fcfc4101c295518ce59b190caa4acbb013ec4": {"t": 1785758730.342658, "n": 13986, "h": "ca9d78f8efd7174a", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '<div id=\"fdty-msg\"></div><div id=\"fdty-stream\" style=\"color:#999;font-size:11px;white-space:pre-wrap;word-break:break-all;max-height:120px;overflow:hidden;margin-top:2px;padding-top:2px;border-top:1px dashed #ddd\"></div><div id=\"fdty-progress\" style=\"color:#999;font-size:11px\"></div>';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n var stream_el = doc.getElementById('fdty-stream');\n var progress_el = doc.getElementById('fdty-progress');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n function progress(msg) {\n console.log('[fdty] ' + msg);\n progress_el.textContent = msg;\n }\n\n function clearProgress() {\n progress_el.textContent = '';\n }\n\n function streamShow(text) {\n stream_el.textContent = text;\n }\n\n function streamAppend(text) {\n stream_el.textContent += text;\n stream_el.scrollTop = stream_el.scrollHeight;\n }\n\n function streamClear() {\n stream_el.textContent = '';\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是一位复旦大学体育理论考试专业答题助手。',\n '',\n '## 知识范围',\n '运动生理学、体能训练理论、运动规则(篮球/排球/足球/田径/游泳/击剑等)、健康体能与体质评价、运动损伤预防、民族传统体育。',\n '',\n '## 答题步骤',\n '1. 仔细阅读题目,识别考点属于哪个知识领域',\n '2. 结合运动科学常识和体育规则进行推理判断',\n '3. 检查题干中是否有绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),该类表述通常为错误',\n '4. 涉及具体年份、数据、百分比的事实题,严格对照已知知识点',\n '5. 注意否定词和双重否定,避免被题干迷惑',\n '6. 给出最终答案',\n '',\n '## 输出格式',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,不要markdown包裹,不要解释。',\n '示例: {\"1\":\"对\",\"2\":\"错\",\"3\":\"C\"}'].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n\n // ============ 加载章节摘要 ============\n var CHAPTERS_URL = 'https://fdty.oss-cn-beijing.aliyuncs.com/chapters_summary.md';\n var chaptersData = null;\n var relevanceMap = null;\n\n function fetchChapters() {\n return fetch(CHAPTERS_URL).then(function(r) {\n if (!r.ok) throw new Error('HTTP ' + r.status);\n return r.text();\n }).then(function(text) {\n chaptersData = parseSummary(text);\n log('加载 ' + chaptersData.length + ' 章知识库', '#1a73e8');\n }).catch(function() {\n log('知识库加载失败,使用无上下文模式');\n });\n }\n\n function parseSummary(text) {\n var blocks = text.split(/\\n---\\n/);\n var chapters = [];\n blocks.forEach(function(block) {\n var h2 = block.match(/^## (.+\\.md)/m);\n var bold = block.match(/^\\*\\*(.+?)\\*\\*/m);\n var quote = block.match(/^> (.+)/m);\n var secs = [];\n var secMatch;\n var secRe = /^ - (.+)$/gm;\n while ((secMatch = secRe.exec(block)) !== null) {\n secs.push(secMatch[1]);\n }\n if (bold) {\n chapters.push({\n id: h2 ? h2[1] : '',\n title: bold[1],\n summary: quote ? quote[1] : '',\n sections: secs\n });\n }\n });\n return chapters;\n }\n\n function buildChapterIndex() {\n return chaptersData.map(function(ch) {\n return '## ' + ch.id + '\\n摘要: ' + ch.summary + '\\n章节: ' + ch.sections.join(', ');\n }).join('\\n\\n');\n }\n\n function phase0Mapping() {\n if (!chaptersData) return Promise.resolve(null);\n log('Phase 0: 分析章节相关性...', '#1a73e8');\n progress('Phase 0 分析中...');\n\n var userContent = '\"\"\"章节大纲\"\"\"\\n' + buildChapterIndex() +\n '\\n\\n\"\"\"题目列表\"\"\"\\n' + questions.map(function(q) {\n return '[' + q.num + '] ' + q.text;\n }).join('\\n');\n\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: 0.1,\n thinking: { type: 'disabled' },\n max_tokens: 2048,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: [\n '你是大学体育理论考试出题分析师。任务:根据题目内容,判断每道题出自教材的哪个章节。',\n '',\n '## 步骤',\n '1. 阅读题目,识别其考查的知识点',\n '2. 在章节大纲中匹配最相关的1-2个章节',\n '3. 输出关联结果',\n '',\n '## 规则',\n '- 每道题标记1-2个最相关的章节文件名',\n '- 只输出JSON,不要任何解释或markdown',\n '- 格式: {\"题号\":[\"文件名1.md\",\"文件名2.md\"]}',\n '- 示例: {\"1\":[\"第一章_体育概论与体育锻炼.md\"],\"2\":[\"第六章_球类运动.md\",\"第五章_田径.md\"]}'\n ].join('\\n') },\n { role: 'user', content: userContent }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) { return r.json(); })\n .then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n try { return JSON.parse(content); } catch(e) { return null; }\n }).catch(function() { return null; });\n }\n\n var fullChapters = {};\n\n function getKnowledge(q) {\n if (!relevanceMap) return '';\n var ids = relevanceMap[String(q.num)];\n if (!ids || !ids.length) return '';\n var parts = [];\n ids.forEach(function(id) {\n var text = fullChapters[id];\n if (text) parts.push(text.substring(0, 4000));\n });\n return parts.length ? '\"\"\"教材参考\"\"\"\\n' + parts.join('\\n\\n---\\n\\n') + '\\n\"\"\"教材参考结束\"\"\"\\n\\n' : '';\n }\n\n // ============ API 调用 ============\n function buildUserPrompt(qs, context) {\n context = context || '';\n return context + qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp, context) {\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n thinking: { type: 'enabled' },\n reasoning_effort: 'max',\n max_tokens: 4096,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs, context) }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1 & 2 ============\n function startVoting() {\n log(questions.length + ' 题 每题3票 共' + (questions.length*3) + '次并发' +\n (relevanceMap ? ' (RAG)' : ''), '#1a73e8');\n\n var totalOk = 0;\n var done1 = 0;\n var temps = [0.1, 0.6, 1.2];\n\n var phase1Jobs = questions.map(function(q) {\n var ctx = getKnowledge(q);\n var batch = [];\n for (var i = 0; i < 3; i++) batch.push(callAPI([q], temps[i], ctx));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done1++;\n progress('投票 ' + done1 + '/' + questions.length + ' 完成');\n if (best && max === 3) {\n if (selectAnswer(q, best)) totalOk++;\n }\n return { q: q, best: best, count: max, counts: counts };\n });\n });\n\n Promise.all(phase1Jobs).then(function(results) {\n clearProgress();\n streamClear();\n\n var disputed = [];\n results.forEach(function(r) {\n if (!r.best || r.count < 3) disputed.push(r.q);\n });\n log('完成 ' + totalOk + '/' + questions.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) return;\n\n // ============ Phase 2: 分歧题逐题10次仲裁 ============\n var done2 = 0;\n log('Phase 2 仲裁 ' + disputed.length + ' 题 (每题10次)...', '#e37400');\n\n var unresolved = [];\n var phase2Ok = 0;\n var phase2Jobs = disputed.map(function(q) {\n var ctx = getKnowledge(q);\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.3, ctx));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done2++;\n progress('仲裁 ' + done2 + '/' + disputed.length + ' 完成');\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n\n return Promise.all(phase2Jobs).then(function() {\n clearProgress();\n streamClear();\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n });\n }).catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n }\n\n // ============ 启动 ============\n function loadFullChapters(map) {\n if (!map) return Promise.resolve();\n var ids = {};\n Object.keys(map).forEach(function(k) {\n (map[k] || []).forEach(function(id) { ids[id] = true; });\n });\n var needed = Object.keys(ids);\n if (!needed.length) return Promise.resolve();\n progress('加载教材章节...');\n return Promise.all(needed.map(function(id) {\n var url = 'https://fdty.oss-cn-beijing.aliyuncs.com/chapters/' + id;\n return fetch(url).then(function(r) { return r.text(); }).then(function(t) {\n fullChapters[id] = t;\n }).catch(function() {});\n })).then(function() {\n log('加载 ' + Object.keys(fullChapters).length + ' 章教材内容');\n });\n }\n\n fetchChapters().then(function() {\n return phase0Mapping();\n }).then(function(map) {\n relevanceMap = map;\n return loadFullChapters(map);\n }).then(function() {\n clearProgress();\n startVoting();\n });\n})();\n"}, "680ea98f332ae5b95d36b7289d227d78e426a08d": {"t": 1785758731.8363116, "n": 13475, "h": "cbf2e9ba42216bc1", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '<div id=\"fdty-msg\"></div><div id=\"fdty-stream\" style=\"color:#999;font-size:11px;white-space:pre-wrap;word-break:break-all;max-height:120px;overflow:hidden;margin-top:2px;padding-top:2px;border-top:1px dashed #ddd\"></div><div id=\"fdty-progress\" style=\"color:#999;font-size:11px\"></div>';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n var stream_el = doc.getElementById('fdty-stream');\n var progress_el = doc.getElementById('fdty-progress');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n function progress(msg) {\n console.log('[fdty] ' + msg);\n progress_el.textContent = msg;\n }\n\n function clearProgress() {\n progress_el.textContent = '';\n }\n\n function streamShow(text) {\n stream_el.textContent = text;\n }\n\n function streamAppend(text) {\n stream_el.textContent += text;\n stream_el.scrollTop = stream_el.scrollHeight;\n }\n\n function streamClear() {\n stream_el.textContent = '';\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n\n // ============ 加载章节摘要 ============\n var CHAPTERS_URL = 'https://fdty.oss-cn-beijing.aliyuncs.com/chapters_summary.md';\n var chaptersData = null;\n var relevanceMap = null;\n\n function fetchChapters() {\n return fetch(CHAPTERS_URL).then(function(r) {\n if (!r.ok) throw new Error('HTTP ' + r.status);\n return r.text();\n }).then(function(text) {\n chaptersData = parseSummary(text);\n log('加载 ' + chaptersData.length + ' 章知识库', '#1a73e8');\n }).catch(function() {\n log('知识库加载失败,使用无上下文模式');\n });\n }\n\n function parseSummary(text) {\n var blocks = text.split(/\\n---\\n/);\n var chapters = [];\n blocks.forEach(function(block) {\n var h2 = block.match(/^## (.+\\.md)/m);\n var bold = block.match(/^\\*\\*(.+?)\\*\\*/m);\n var quote = block.match(/^> (.+)/m);\n var secs = [];\n var secMatch;\n var secRe = /^ - (.+)$/gm;\n while ((secMatch = secRe.exec(block)) !== null) {\n secs.push(secMatch[1]);\n }\n if (bold) {\n chapters.push({\n id: h2 ? h2[1] : '',\n title: bold[1],\n summary: quote ? quote[1] : '',\n sections: secs\n });\n }\n });\n return chapters;\n }\n\n function buildChapterIndex() {\n return chaptersData.map(function(ch) {\n return '## ' + ch.id + '\\n摘要: ' + ch.summary + '\\n章节: ' + ch.sections.join(', ');\n }).join('\\n\\n');\n }\n\n function phase0Mapping() {\n if (!chaptersData) return Promise.resolve(null);\n log('Phase 0: 分析章节相关性...', '#1a73e8');\n progress('Phase 0 分析中...');\n\n var userContent = '【章节大纲】\\n' + buildChapterIndex() +\n '\\n\\n【题目列表】\\n' + questions.map(function(q) {\n return '[' + q.num + '] ' + q.text;\n }).join('\\n');\n\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: 0.1,\n thinking: { type: 'disabled' },\n max_tokens: 2048,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: '你是大学体育理论考试出题分析师。根据每道题的内容,判断其出自哪个章节。每道题标记1-2个最相关的章节文件名。只输出JSON: {\"题号\":[\"文件名1\"]},不要解释。' },\n { role: 'user', content: userContent }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) { return r.json(); })\n .then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n try { return JSON.parse(content); } catch(e) { return null; }\n }).catch(function() { return null; });\n }\n\n var fullChapters = {};\n\n function getKnowledge(q) {\n if (!relevanceMap) return '';\n var ids = relevanceMap[String(q.num)];\n if (!ids || !ids.length) return '';\n var parts = [];\n ids.forEach(function(id) {\n var text = fullChapters[id];\n if (text) parts.push(text.substring(0, 4000));\n });\n return parts.length ? '【教材参考】\\n\\n' + parts.join('\\n\\n---\\n\\n') + '\\n\\n' : '';\n }\n\n // ============ API 调用 ============\n function buildUserPrompt(qs, context) {\n context = context || '';\n return context + qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp, context) {\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n thinking: { type: 'enabled' },\n reasoning_effort: 'max',\n max_tokens: 4096,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs, context) }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1 & 2 ============\n function startVoting() {\n log(questions.length + ' 题 每题3票 共' + (questions.length*3) + '次并发' +\n (relevanceMap ? ' (RAG)' : ''), '#1a73e8');\n\n var totalOk = 0;\n var done1 = 0;\n var temps = [0.1, 0.6, 1.2];\n\n var phase1Jobs = questions.map(function(q) {\n var ctx = getKnowledge(q);\n var batch = [];\n for (var i = 0; i < 3; i++) batch.push(callAPI([q], temps[i], ctx));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done1++;\n progress('投票 ' + done1 + '/' + questions.length + ' 完成');\n if (best && max === 3) {\n if (selectAnswer(q, best)) totalOk++;\n }\n return { q: q, best: best, count: max, counts: counts };\n });\n });\n\n Promise.all(phase1Jobs).then(function(results) {\n clearProgress();\n streamClear();\n\n var disputed = [];\n results.forEach(function(r) {\n if (!r.best || r.count < 3) disputed.push(r.q);\n });\n log('完成 ' + totalOk + '/' + questions.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) return;\n\n // ============ Phase 2: 分歧题逐题10次仲裁 ============\n var done2 = 0;\n log('Phase 2 仲裁 ' + disputed.length + ' 题 (每题10次)...', '#e37400');\n\n var unresolved = [];\n var phase2Ok = 0;\n var phase2Jobs = disputed.map(function(q) {\n var ctx = getKnowledge(q);\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.3, ctx));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done2++;\n progress('仲裁 ' + done2 + '/' + disputed.length + ' 完成');\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n\n return Promise.all(phase2Jobs).then(function() {\n clearProgress();\n streamClear();\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n });\n }).catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n }\n\n // ============ 启动 ============\n function loadFullChapters(map) {\n if (!map) return Promise.resolve();\n var ids = {};\n Object.keys(map).forEach(function(k) {\n (map[k] || []).forEach(function(id) { ids[id] = true; });\n });\n var needed = Object.keys(ids);\n if (!needed.length) return Promise.resolve();\n progress('加载教材章节...');\n return Promise.all(needed.map(function(id) {\n var url = 'https://fdty.oss-cn-beijing.aliyuncs.com/chapters/' + id;\n return fetch(url).then(function(r) { return r.text(); }).then(function(t) {\n fullChapters[id] = t;\n }).catch(function() {});\n })).then(function() {\n log('加载 ' + Object.keys(fullChapters).length + ' 章教材内容');\n });\n }\n\n fetchChapters().then(function() {\n return phase0Mapping();\n }).then(function(map) {\n relevanceMap = map;\n return loadFullChapters(map);\n }).then(function() {\n clearProgress();\n startVoting();\n });\n})();\n"}, "163bf1633fb3e6739bd00472056e33a31659c6da": {"t": 1785758734.2570071, "n": 12802, "h": "a300bf24f593f605", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '<div id=\"fdty-msg\"></div><div id=\"fdty-stream\" style=\"color:#999;font-size:11px;white-space:pre-wrap;word-break:break-all;max-height:120px;overflow:hidden;margin-top:2px;padding-top:2px;border-top:1px dashed #ddd\"></div><div id=\"fdty-progress\" style=\"color:#999;font-size:11px\"></div>';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n var stream_el = doc.getElementById('fdty-stream');\n var progress_el = doc.getElementById('fdty-progress');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n function progress(msg) {\n console.log('[fdty] ' + msg);\n progress_el.textContent = msg;\n }\n\n function clearProgress() {\n progress_el.textContent = '';\n }\n\n function streamShow(text) {\n stream_el.textContent = text;\n }\n\n function streamAppend(text) {\n stream_el.textContent += text;\n stream_el.scrollTop = stream_el.scrollHeight;\n }\n\n function streamClear() {\n stream_el.textContent = '';\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n\n // ============ 加载章节摘要 ============\n var CHAPTERS_URL = 'https://fdty.oss-cn-beijing.aliyuncs.com/chapters_summary.md';\n var chaptersData = null;\n var relevanceMap = null;\n\n function fetchChapters() {\n return fetch(CHAPTERS_URL).then(function(r) {\n if (!r.ok) throw new Error('HTTP ' + r.status);\n return r.text();\n }).then(function(text) {\n chaptersData = parseSummary(text);\n log('加载 ' + chaptersData.length + ' 章知识库', '#1a73e8');\n }).catch(function() {\n log('知识库加载失败,使用无上下文模式');\n });\n }\n\n function parseSummary(text) {\n var blocks = text.split(/\\n---\\n/);\n var chapters = [];\n blocks.forEach(function(block) {\n var h2 = block.match(/^## (.+\\.md)/m);\n var bold = block.match(/^\\*\\*(.+?)\\*\\*/m);\n var quote = block.match(/^> (.+)/m);\n var secs = [];\n var secMatch;\n var secRe = /^ - (.+)$/gm;\n while ((secMatch = secRe.exec(block)) !== null) {\n secs.push(secMatch[1]);\n }\n if (bold) {\n chapters.push({\n id: h2 ? h2[1] : '',\n title: bold[1],\n summary: quote ? quote[1] : '',\n sections: secs\n });\n }\n });\n return chapters;\n }\n\n function buildChapterIndex() {\n return chaptersData.map(function(ch) {\n return '## ' + ch.id + '\\n摘要: ' + ch.summary + '\\n章节: ' + ch.sections.join(', ');\n }).join('\\n\\n');\n }\n\n function phase0Mapping() {\n if (!chaptersData) return Promise.resolve(null);\n log('Phase 0: 分析章节相关性...', '#1a73e8');\n progress('Phase 0 分析中...');\n\n var userContent = '【章节大纲】\\n' + buildChapterIndex() +\n '\\n\\n【题目列表】\\n' + questions.map(function(q) {\n return '[' + q.num + '] ' + q.text;\n }).join('\\n');\n\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: 0.1,\n thinking: { type: 'disabled' },\n max_tokens: 2048,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: '你是大学体育理论考试出题分析师。根据每道题的内容,判断其出自哪个章节。每道题标记1-2个最相关的章节文件名。只输出JSON: {\"题号\":[\"文件名1\"]},不要解释。' },\n { role: 'user', content: userContent }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) { return r.json(); })\n .then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n try { return JSON.parse(content); } catch(e) { return null; }\n }).catch(function() { return null; });\n }\n\n function getKnowledge(q) {\n if (!chaptersData || !relevanceMap) return '';\n var ids = relevanceMap[String(q.num)];\n if (!ids || !ids.length) return '';\n var parts = [];\n ids.forEach(function(id) {\n var ch = chaptersData.find(function(c) { return c.id === id; });\n if (ch) {\n parts.push('【知识点 - ' + ch.title + '】\\n' + ch.summary + '\\n包含: ' + ch.sections.join('、'));\n }\n });\n return parts.length ? parts.join('\\n\\n') + '\\n\\n' : '';\n }\n\n // ============ API 调用 ============\n function buildUserPrompt(qs, context) {\n context = context || '';\n return context + qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp, context) {\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n thinking: { type: 'enabled' },\n reasoning_effort: 'max',\n max_tokens: 4096,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs, context) }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1 & 2 ============\n function startVoting() {\n log(questions.length + ' 题 每题3票 共' + (questions.length*3) + '次并发' +\n (relevanceMap ? ' (RAG)' : ''), '#1a73e8');\n\n var totalOk = 0;\n var done1 = 0;\n var temps = [0.1, 0.6, 1.2];\n\n var phase1Jobs = questions.map(function(q) {\n var ctx = getKnowledge(q);\n var batch = [];\n for (var i = 0; i < 3; i++) batch.push(callAPI([q], temps[i], ctx));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done1++;\n progress('投票 ' + done1 + '/' + questions.length + ' 完成');\n if (best && max === 3) {\n if (selectAnswer(q, best)) totalOk++;\n }\n return { q: q, best: best, count: max, counts: counts };\n });\n });\n\n Promise.all(phase1Jobs).then(function(results) {\n clearProgress();\n streamClear();\n\n var disputed = [];\n results.forEach(function(r) {\n if (!r.best || r.count < 3) disputed.push(r.q);\n });\n log('完成 ' + totalOk + '/' + questions.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) return;\n\n // ============ Phase 2: 分歧题逐题10次仲裁 ============\n var done2 = 0;\n log('Phase 2 仲裁 ' + disputed.length + ' 题 (每题10次)...', '#e37400');\n\n var unresolved = [];\n var phase2Ok = 0;\n var phase2Jobs = disputed.map(function(q) {\n var ctx = getKnowledge(q);\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.3, ctx));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done2++;\n progress('仲裁 ' + done2 + '/' + disputed.length + ' 完成');\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n\n return Promise.all(phase2Jobs).then(function() {\n clearProgress();\n streamClear();\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n });\n }).catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n }\n\n // ============ 启动 ============\n fetchChapters().then(function() {\n return phase0Mapping();\n }).then(function(map) {\n relevanceMap = map;\n clearProgress();\n startVoting();\n });\n})();\n"}, "26df350083a0cc214d638dc083e58c75e1ee0408": {"t": 1785758735.43631, "n": 12818, "h": "f417d04de6a3846d", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '<div id=\"fdty-msg\"></div><div id=\"fdty-stream\" style=\"color:#999;font-size:11px;white-space:pre-wrap;word-break:break-all;max-height:120px;overflow:hidden;margin-top:2px;padding-top:2px;border-top:1px dashed #ddd\"></div><div id=\"fdty-progress\" style=\"color:#999;font-size:11px\"></div>';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n var stream_el = doc.getElementById('fdty-stream');\n var progress_el = doc.getElementById('fdty-progress');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n function progress(msg) {\n console.log('[fdty] ' + msg);\n progress_el.textContent = msg;\n }\n\n function clearProgress() {\n progress_el.textContent = '';\n }\n\n function streamShow(text) {\n stream_el.textContent = text;\n }\n\n function streamAppend(text) {\n stream_el.textContent += text;\n stream_el.scrollTop = stream_el.scrollHeight;\n }\n\n function streamClear() {\n stream_el.textContent = '';\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n\n // ============ 加载章节摘要 ============\n var CHAPTERS_URL = 'https://fdty.oss-cn-beijing.aliyuncs.com/chapters_summary.md';\n var chaptersData = null;\n var relevanceMap = null;\n\n function fetchChapters() {\n return fetch(CHAPTERS_URL).then(function(r) {\n if (!r.ok) throw new Error('HTTP ' + r.status);\n return r.text();\n }).then(function(text) {\n chaptersData = parseSummary(text);\n log('加载 ' + chaptersData.length + ' 章知识库', '#1a73e8');\n }).catch(function() {\n log('知识库加载失败,使用无上下文模式');\n });\n }\n\n function parseSummary(text) {\n var blocks = text.split(/\\n---\\n/);\n var chapters = [];\n blocks.forEach(function(block) {\n var h2 = block.match(/^## (.+\\.md)/m);\n var bold = block.match(/^\\*\\*(.+?)\\*\\*/m);\n var quote = block.match(/^> (.+)/m);\n var secs = [];\n var secMatch;\n var secRe = /^ - (.+)$/gm;\n while ((secMatch = secRe.exec(block)) !== null) {\n secs.push(secMatch[1]);\n }\n if (bold) {\n chapters.push({\n id: h2 ? h2[1] : '',\n title: bold[1],\n summary: quote ? quote[1] : '',\n sections: secs\n });\n }\n });\n return chapters;\n }\n\n function buildChapterIndex() {\n return chaptersData.map(function(ch) {\n return '## ' + ch.id + '\\n摘要: ' + ch.summary + '\\n章节: ' + ch.sections.join(', ');\n }).join('\\n\\n');\n }\n\n function phase0Mapping() {\n if (!chaptersData) return Promise.resolve(null);\n log('Phase 0: 分析章节相关性...', '#1a73e8');\n progress('Phase 0 分析中...');\n\n var userContent = '【章节大纲】\\n' + buildChapterIndex() +\n '\\n\\n【题目列表】\\n' + questions.map(function(q) {\n return '[' + q.num + '] ' + q.text;\n }).join('\\n');\n\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: 0.1,\n thinking: { type: 'disabled' },\n max_tokens: 2048,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: '你是大学体育理论考试出题分析师。根据每道题的内容,判断其出自哪个章节。每道题标记1-2个最相关的章节文件名。只输出JSON: {\"题号\":[\"文件名1\"]},不要解释。' },\n { role: 'user', content: userContent }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: JSON.stringify(body)\n }).then(function(r) { return r.json(); })\n .then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n try { return JSON.parse(content); } catch(e) { return null; }\n }).catch(function() { return null; });\n }\n\n function getKnowledge(q) {\n if (!chaptersData || !relevanceMap) return '';\n var ids = relevanceMap[String(q.num)];\n if (!ids || !ids.length) return '';\n var parts = [];\n ids.forEach(function(id) {\n var ch = chaptersData.find(function(c) { return c.id === id; });\n if (ch) {\n parts.push('【知识点 - ' + ch.title + '】\\n' + ch.summary + '\\n包含: ' + ch.sections.join('、'));\n }\n });\n return parts.length ? parts.join('\\n\\n') + '\\n\\n' : '';\n }\n\n // ============ API 调用 ============\n function buildUserPrompt(qs, context) {\n context = context || '';\n return context + qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp, context) {\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n thinking: { type: 'enabled' },\n reasoning_effort: 'max',\n max_tokens: 4096,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs, context) }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1 & 2 ============\n function startVoting() {\n log(questions.length + ' 题 每题3票 共' + (questions.length*3) + '次并发' +\n (relevanceMap ? ' (RAG)' : ''), '#1a73e8');\n\n var totalOk = 0;\n var done1 = 0;\n var temps = [0.1, 0.6, 1.2];\n\n var phase1Jobs = questions.map(function(q) {\n var ctx = getKnowledge(q);\n var batch = [];\n for (var i = 0; i < 3; i++) batch.push(callAPI([q], temps[i], ctx));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done1++;\n progress('投票 ' + done1 + '/' + questions.length + ' 完成');\n if (best && max === 3) {\n if (selectAnswer(q, best)) totalOk++;\n }\n return { q: q, best: best, count: max, counts: counts };\n });\n });\n\n Promise.all(phase1Jobs).then(function(results) {\n clearProgress();\n streamClear();\n\n var disputed = [];\n results.forEach(function(r) {\n if (!r.best || r.count < 3) disputed.push(r.q);\n });\n log('完成 ' + totalOk + '/' + questions.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) return;\n\n // ============ Phase 2: 分歧题逐题10次仲裁 ============\n var done2 = 0;\n log('Phase 2 仲裁 ' + disputed.length + ' 题 (每题10次)...', '#e37400');\n\n var unresolved = [];\n var phase2Ok = 0;\n var phase2Jobs = disputed.map(function(q) {\n var ctx = getKnowledge(q);\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.3, ctx));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done2++;\n progress('仲裁 ' + done2 + '/' + disputed.length + ' 完成');\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n\n return Promise.all(phase2Jobs).then(function() {\n clearProgress();\n streamClear();\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n });\n }).catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n }\n\n // ============ 启动 ============\n fetchChapters().then(function() {\n return phase0Mapping();\n }).then(function(map) {\n relevanceMap = map;\n clearProgress();\n startVoting();\n });\n})();\n"}, "2a5ed1eaad754f3ec6a12979a758a2d41b1cb0e2": {"t": 1785758736.6128995, "n": 9288, "h": "9552e085c69bd246", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '<div id=\"fdty-msg\"></div><div id=\"fdty-stream\" style=\"color:#999;font-size:11px;white-space:pre-wrap;word-break:break-all;max-height:120px;overflow:hidden;margin-top:2px;padding-top:2px;border-top:1px dashed #ddd\"></div><div id=\"fdty-progress\" style=\"color:#999;font-size:11px\"></div>';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n var stream_el = doc.getElementById('fdty-stream');\n var progress_el = doc.getElementById('fdty-progress');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n function progress(msg) {\n console.log('[fdty] ' + msg);\n progress_el.textContent = msg;\n }\n\n function clearProgress() {\n progress_el.textContent = '';\n }\n\n function streamShow(text) {\n stream_el.textContent = text;\n }\n\n function streamAppend(text) {\n stream_el.textContent += text;\n stream_el.scrollTop = stream_el.scrollHeight;\n }\n\n function streamClear() {\n stream_el.textContent = '';\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n log(questions.length + ' 题 Phase 1 投票中...', '#1a73e8');\n\n // ============ API 调用 ============\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n thinking: { type: 'enabled' },\n reasoning_effort: 'max',\n max_tokens: 4096,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1: 逐题投票,每题3票并发 ============\n var totalOk = 0;\n var done1 = 0;\n var temps = [0.1, 0.6, 1.2];\n log(questions.length + ' 题 每题3票 共' + (questions.length*3) + '次并发...', '#1a73e8');\n\n var phase1Jobs = questions.map(function(q) {\n var batch = [];\n for (var i = 0; i < 3; i++) batch.push(callAPI([q], temps[i]));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done1++;\n progress('投票 ' + done1 + '/' + questions.length + ' 完成');\n if (best && max === 3) {\n if (selectAnswer(q, best)) totalOk++;\n }\n return { q: q, best: best, count: max, counts: counts };\n });\n });\n\n Promise.all(phase1Jobs).then(function(results) {\n clearProgress();\n streamClear();\n\n var disputed = [];\n results.forEach(function(r) {\n if (!r.best || r.count < 3) disputed.push(r.q);\n });\n log('完成 ' + totalOk + '/' + questions.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) return;\n\n // ============ Phase 2: 分歧题逐题10次仲裁 ============\n var done2 = 0;\n log('Phase 2 仲裁 ' + disputed.length + ' 题 (每题10次)...', '#e37400');\n\n var unresolved = [];\n var phase2Ok = 0;\n var phase2Jobs = disputed.map(function(q) {\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.3));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done2++;\n progress('仲裁 ' + done2 + '/' + disputed.length + ' 完成');\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n\n return Promise.all(phase2Jobs).then(function() {\n clearProgress();\n streamClear();\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n });\n }).catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n})();\n"}, "d0a4569e5fa90e8b580f60fe3f3ec573d9a667fe": {"t": 1785758738.0460753, "n": 9354, "h": "f61dd6bb091a64c3", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '<div id=\"fdty-msg\"></div><div id=\"fdty-stream\" style=\"color:#999;font-size:11px;white-space:pre-wrap;word-break:break-all;max-height:120px;overflow:hidden;margin-top:2px;padding-top:2px;border-top:1px dashed #ddd\"></div><div id=\"fdty-progress\" style=\"color:#999;font-size:11px\"></div>';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n var stream_el = doc.getElementById('fdty-stream');\n var progress_el = doc.getElementById('fdty-progress');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n function progress(msg) {\n console.log('[fdty] ' + msg);\n progress_el.textContent = msg;\n }\n\n function clearProgress() {\n progress_el.textContent = '';\n }\n\n function streamShow(text) {\n stream_el.textContent = text;\n }\n\n function streamAppend(text) {\n stream_el.textContent += text;\n stream_el.scrollTop = stream_el.scrollHeight;\n }\n\n function streamClear() {\n stream_el.textContent = '';\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n log(questions.length + ' 题 Phase 1 投票中...', '#1a73e8');\n\n // ============ API 调用 ============\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n thinking: { type: 'enabled' },\n reasoning_effort: 'max',\n max_tokens: 4096,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1: 逐题投票,每题3票并发 ============\n var totalOk = 0;\n var done1 = 0;\n var temps = [0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4];\n log(questions.length + ' 题 每题3票 共' + (questions.length*3) + '次并发...', '#1a73e8');\n\n var phase1Jobs = questions.map(function(q) {\n var batch = [];\n for (var i = 0; i < 3; i++) batch.push(callAPI([q], temps[i]));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done1++;\n progress('投票 ' + done1 + '/' + questions.length + ' 完成');\n if (best && max === 3) {\n if (selectAnswer(q, best)) totalOk++;\n }\n return { q: q, best: best, count: max, counts: counts };\n });\n });\n\n Promise.all(phase1Jobs).then(function(results) {\n clearProgress();\n streamClear();\n\n var disputed = [];\n results.forEach(function(r) {\n if (!r.best || r.count < 3) disputed.push(r.q);\n });\n log('完成 ' + totalOk + '/' + questions.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) return;\n\n // ============ Phase 2: 分歧题逐题10次仲裁 ============\n var done2 = 0;\n log('Phase 2 仲裁 ' + disputed.length + ' 题 (每题10次)...', '#e37400');\n\n var unresolved = [];\n var phase2Ok = 0;\n var phase2Jobs = disputed.map(function(q) {\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.7));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n done2++;\n progress('仲裁 ' + done2 + '/' + disputed.length + ' 完成');\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n\n return Promise.all(phase2Jobs).then(function() {\n clearProgress();\n streamClear();\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n });\n }).catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n})();\n"}, "f7d64d70ab31e884ab87d8ae9d1b458c804164a9": {"t": 1785758739.311823, "n": 9313, "h": "205391148c7038fb", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '<div id=\"fdty-msg\"></div><div id=\"fdty-stream\" style=\"color:#999;font-size:11px;white-space:pre-wrap;word-break:break-all;max-height:120px;overflow:hidden;margin-top:2px;padding-top:2px;border-top:1px dashed #ddd\"></div><div id=\"fdty-progress\" style=\"color:#999;font-size:11px\"></div>';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n var stream_el = doc.getElementById('fdty-stream');\n var progress_el = doc.getElementById('fdty-progress');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n function progress(msg) {\n console.log('[fdty] ' + msg);\n progress_el.textContent = msg;\n }\n\n function clearProgress() {\n progress_el.textContent = '';\n }\n\n function streamShow(text) {\n stream_el.textContent = text;\n }\n\n function streamAppend(text) {\n stream_el.textContent += text;\n stream_el.scrollTop = stream_el.scrollHeight;\n }\n\n function streamClear() {\n stream_el.textContent = '';\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n log(questions.length + ' 题 Phase 1 投票中...', '#1a73e8');\n\n // ============ API 调用 ============\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n thinking: { type: 'enabled' },\n reasoning_effort: 'max',\n max_tokens: 4096,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1: 三次投票 ============\n var temps = [0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4];\n var doneVotes = 0;\n var phase1Calls = temps.map(function(temp) {\n return callAPI(questions, temp).then(function(r) {\n doneVotes++;\n progress('投票 ' + doneVotes + '/3 完成');\n return r;\n });\n });\n\n Promise.all(phase1Calls).then(function(results) {\n clearProgress();\n streamClear();\n var votes = {};\n questions.forEach(function(q) { votes[String(q.num)] = []; });\n\n questions.forEach(function(q) {\n var key = String(q.num);\n results.forEach(function(r) {\n votes[key].push(String(r[key] || '').trim());\n });\n });\n\n var agreed = [], disputed = [];\n questions.forEach(function(q) {\n var v = votes[String(q.num)];\n if (v[0] && v[0] === v[1] && v[1] === v[2]) {\n agreed.push({ q: q, ans: v[0] });\n } else {\n disputed.push(q);\n }\n });\n\n var totalOk = 0;\n agreed.forEach(function(item) { if (selectAnswer(item.q, item.ans)) totalOk++; });\n log('一致' + agreed.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) {\n log('完成 ' + totalOk + '/' + questions.length + ' | ' + ts(), '#1a73e8');\n return;\n }\n\n // ============ Phase 2: 逐题仲裁 ============\n log('Phase 2 仲裁 ' + disputed.length + ' 题...', '#e37400');\n\n var unresolved = [];\n var phase2Ok = 0;\n var phase2Done = 0;\n var phase2Jobs = disputed.map(function(q) {\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.7));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n phase2Done++;\n progress('仲裁 ' + phase2Done + '/' + disputed.length + ' 题完成...');\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n\n return Promise.all(phase2Jobs).then(function() {\n clearProgress();\n streamClear();\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (一致' + agreed.length + ' + 仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n });\n }).catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n})();\n"}, "eb9f62b13197dad6975a543cb4631a6e01e75760": {"t": 1785758740.6806946, "n": 10517, "h": "872e5ae4b7998953", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '<div id=\"fdty-msg\"></div><div id=\"fdty-stream\" style=\"color:#999;font-size:11px;white-space:pre-wrap;word-break:break-all;max-height:120px;overflow:hidden;margin-top:2px;padding-top:2px;border-top:1px dashed #ddd\"></div><div id=\"fdty-progress\" style=\"color:#999;font-size:11px\"></div>';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n var stream_el = doc.getElementById('fdty-stream');\n var progress_el = doc.getElementById('fdty-progress');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n function progress(msg) {\n console.log('[fdty] ' + msg);\n progress_el.textContent = msg;\n }\n\n function clearProgress() {\n progress_el.textContent = '';\n }\n\n function streamShow(text) {\n stream_el.textContent = text;\n }\n\n function streamAppend(text) {\n stream_el.textContent += text;\n stream_el.scrollTop = stream_el.scrollHeight;\n }\n\n function streamClear() {\n stream_el.textContent = '';\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n log(questions.length + ' 题 Phase 1 投票中...', '#1a73e8');\n\n // ============ API 调用 ============\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n var body = JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n thinking: { type: 'enabled' },\n reasoning_effort: 'max',\n max_tokens: 4096,\n response_format: { type: 'json_object' },\n stream: true,\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n });\n\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: body\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n\n var reader = r.body.getReader();\n var decoder = new TextDecoder();\n var content = '', buf = '';\n\n return reader.read().then(function pump(result) {\n if (result.done) {\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }\n\n buf += decoder.decode(result.value, { stream: true });\n var lines = buf.split('\\n');\n buf = lines.pop();\n\n for (var i = 0; i < lines.length; i++) {\n var line = lines[i].trim();\n if (!line || line === 'data: [DONE]') continue;\n if (line.indexOf('data: ') !== 0) continue;\n try {\n var chunk = JSON.parse(line.slice(6));\n var delta = chunk.choices[0].delta;\n if (delta.content) {\n content += delta.content;\n streamShow(content);\n }\n } catch(e) {}\n }\n return reader.read().then(pump);\n });\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1: 分组并发投票 ============\n var CHUNK_SIZE = 15;\n var chunks = [];\n for (var i = 0; i < questions.length; i += CHUNK_SIZE) {\n chunks.push(questions.slice(i, i + CHUNK_SIZE));\n }\n var temps = [0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4];\n\n var totalCalls = chunks.length * temps.length;\n var doneCalls = 0;\n var phase1Calls = [];\n chunks.forEach(function(chunk) {\n temps.forEach(function(temp) {\n var p = callAPI(chunk, temp).then(function(r) {\n doneCalls++;\n progress('投票 ' + doneCalls + '/' + totalCalls + ' 完成...');\n return r;\n });\n phase1Calls.push(p);\n });\n });\n\n Promise.all(phase1Calls).then(function(allResults) {\n clearProgress();\n streamClear();\n var votes = {};\n questions.forEach(function(q) { votes[String(q.num)] = []; });\n\n chunks.forEach(function(chunk, ci) {\n temps.forEach(function(temp, ti) {\n var result = allResults[ci * temps.length + ti];\n chunk.forEach(function(q) {\n votes[String(q.num)].push(String(result[String(q.num)] || '').trim());\n });\n });\n });\n\n var agreed = [], disputed = [];\n questions.forEach(function(q) {\n var v = votes[String(q.num)];\n if (v[0] && v[0] === v[1] && v[1] === v[2]) {\n agreed.push({ q: q, ans: v[0] });\n } else {\n disputed.push(q);\n }\n });\n\n var totalOk = 0;\n agreed.forEach(function(item) { if (selectAnswer(item.q, item.ans)) totalOk++; });\n log('一致' + agreed.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) {\n log('完成 ' + totalOk + '/' + questions.length + ' | ' + ts(), '#1a73e8');\n return;\n }\n\n // ============ Phase 2: 逐题仲裁 ============\n log('Phase 2 仲裁 ' + disputed.length + ' 题...', '#e37400');\n\n var unresolved = [];\n var phase2Ok = 0;\n var phase2Done = 0;\n var phase2Jobs = disputed.map(function(q) {\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.7));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n phase2Done++;\n progress('仲裁 ' + phase2Done + '/' + disputed.length + ' 题完成...');\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n\n return Promise.all(phase2Jobs).then(function() {\n clearProgress();\n streamClear();\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (一致' + agreed.length + ' + 仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n });\n }).catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n})();\n"}, "11a327e365f0fa927acc3e92e4efb7009a85415b": {"t": 1785758741.868947, "n": 9203, "h": "a6da056c8ac25f8a", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '<div id=\"fdty-msg\"></div><div id=\"fdty-progress\" style=\"color:#999;font-size:11px\"></div>';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n var progress_el = doc.getElementById('fdty-progress');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n function progress(msg) {\n console.log('[fdty] ' + msg);\n progress_el.textContent = msg;\n }\n\n function clearProgress() {\n progress_el.textContent = '';\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n log(questions.length + ' 题 Phase 1 投票中...', '#1a73e8');\n\n // ============ API 调用 ============\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n thinking: { type: 'enabled' },\n reasoning_effort: 'max',\n max_tokens: 4096,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n })\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1: 分组并发投票 ============\n var CHUNK_SIZE = 15;\n var chunks = [];\n for (var i = 0; i < questions.length; i += CHUNK_SIZE) {\n chunks.push(questions.slice(i, i + CHUNK_SIZE));\n }\n var temps = [0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4];\n\n var totalCalls = chunks.length * temps.length;\n var doneCalls = 0;\n var phase1Calls = [];\n chunks.forEach(function(chunk) {\n temps.forEach(function(temp) {\n var p = callAPI(chunk, temp).then(function(r) {\n doneCalls++;\n progress('投票 ' + doneCalls + '/' + totalCalls + ' 完成...');\n return r;\n });\n phase1Calls.push(p);\n });\n });\n\n Promise.all(phase1Calls).then(function(allResults) {\n clearProgress();\n var votes = {};\n questions.forEach(function(q) { votes[String(q.num)] = []; });\n\n chunks.forEach(function(chunk, ci) {\n temps.forEach(function(temp, ti) {\n var result = allResults[ci * temps.length + ti];\n chunk.forEach(function(q) {\n votes[String(q.num)].push(String(result[String(q.num)] || '').trim());\n });\n });\n });\n\n var agreed = [], disputed = [];\n questions.forEach(function(q) {\n var v = votes[String(q.num)];\n if (v[0] && v[0] === v[1] && v[1] === v[2]) {\n agreed.push({ q: q, ans: v[0] });\n } else {\n disputed.push(q);\n }\n });\n\n var totalOk = 0;\n agreed.forEach(function(item) { if (selectAnswer(item.q, item.ans)) totalOk++; });\n log('一致' + agreed.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) {\n log('完成 ' + totalOk + '/' + questions.length + ' | ' + ts(), '#1a73e8');\n return;\n }\n\n // ============ Phase 2: 逐题仲裁 ============\n log('Phase 2 仲裁 ' + disputed.length + ' 题...', '#e37400');\n\n var unresolved = [];\n var phase2Ok = 0;\n var phase2Done = 0;\n var phase2Jobs = disputed.map(function(q) {\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.7));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n phase2Done++;\n progress('仲裁 ' + phase2Done + '/' + disputed.length + ' 题完成...');\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n\n return Promise.all(phase2Jobs).then(function() {\n clearProgress();\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (一致' + agreed.length + ' + 仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n });\n }).catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n})();\n"}, "d268720620ce31afadfcfe879d4a30ae5704fe6e": {"t": 1785758743.1119199, "n": 9171, "h": "44aabf99c78dddb2", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '<div id=\"fdty-msg\"></div><div id=\"fdty-progress\" style=\"color:#999;font-size:11px\"></div>';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n var progress_el = doc.getElementById('fdty-progress');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n function progress(msg) {\n console.log('[fdty] ' + msg);\n progress_el.textContent = msg;\n }\n\n function clearProgress() {\n progress_el.textContent = '';\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n log(questions.length + ' 题 Phase 1 投票中...', '#1a73e8');\n\n // ============ API 调用 ============\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n thinking: { type: 'disabled' },\n max_tokens: 4096,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n })\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1: 分组并发投票 ============\n var CHUNK_SIZE = 15;\n var chunks = [];\n for (var i = 0; i < questions.length; i += CHUNK_SIZE) {\n chunks.push(questions.slice(i, i + CHUNK_SIZE));\n }\n var temps = [0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4];\n\n var totalCalls = chunks.length * temps.length;\n var doneCalls = 0;\n var phase1Calls = [];\n chunks.forEach(function(chunk) {\n temps.forEach(function(temp) {\n var p = callAPI(chunk, temp).then(function(r) {\n doneCalls++;\n progress('投票 ' + doneCalls + '/' + totalCalls + ' 完成...');\n return r;\n });\n phase1Calls.push(p);\n });\n });\n\n Promise.all(phase1Calls).then(function(allResults) {\n clearProgress();\n var votes = {};\n questions.forEach(function(q) { votes[String(q.num)] = []; });\n\n chunks.forEach(function(chunk, ci) {\n temps.forEach(function(temp, ti) {\n var result = allResults[ci * temps.length + ti];\n chunk.forEach(function(q) {\n votes[String(q.num)].push(String(result[String(q.num)] || '').trim());\n });\n });\n });\n\n var agreed = [], disputed = [];\n questions.forEach(function(q) {\n var v = votes[String(q.num)];\n if (v[0] && v[0] === v[1] && v[1] === v[2]) {\n agreed.push({ q: q, ans: v[0] });\n } else {\n disputed.push(q);\n }\n });\n\n var totalOk = 0;\n agreed.forEach(function(item) { if (selectAnswer(item.q, item.ans)) totalOk++; });\n log('一致' + agreed.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) {\n log('完成 ' + totalOk + '/' + questions.length + ' | ' + ts(), '#1a73e8');\n return;\n }\n\n // ============ Phase 2: 逐题仲裁 ============\n log('Phase 2 仲裁 ' + disputed.length + ' 题...', '#e37400');\n\n var unresolved = [];\n var phase2Ok = 0;\n var phase2Done = 0;\n var phase2Jobs = disputed.map(function(q) {\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.7));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n phase2Done++;\n progress('仲裁 ' + phase2Done + '/' + disputed.length + ' 题完成...');\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n\n return Promise.all(phase2Jobs).then(function() {\n clearProgress();\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (一致' + agreed.length + ' + 仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n });\n }).catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n})();\n"}, "6fc8bdc251c33c5796ef48c0b4b96ea8e914b836": {"t": 1785758744.4896598, "n": 9108, "h": "bfc9a2bf4b1d0765", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '<div id=\"fdty-msg\"></div><div id=\"fdty-progress\" style=\"color:#999;font-size:11px\"></div>';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n var progress_el = doc.getElementById('fdty-progress');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n function progress(msg) {\n console.log('[fdty] ' + msg);\n progress_el.textContent = msg;\n }\n\n function clearProgress() {\n progress_el.textContent = '';\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/v1/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n log(questions.length + ' 题 Phase 1 投票中...', '#1a73e8');\n\n // ============ API 调用 ============\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n })\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1: 分组并发投票 ============\n var CHUNK_SIZE = 15;\n var chunks = [];\n for (var i = 0; i < questions.length; i += CHUNK_SIZE) {\n chunks.push(questions.slice(i, i + CHUNK_SIZE));\n }\n var temps = [0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4];\n\n var totalCalls = chunks.length * temps.length;\n var doneCalls = 0;\n var phase1Calls = [];\n chunks.forEach(function(chunk) {\n temps.forEach(function(temp) {\n var p = callAPI(chunk, temp).then(function(r) {\n doneCalls++;\n progress('投票 ' + doneCalls + '/' + totalCalls + ' 完成...');\n return r;\n });\n phase1Calls.push(p);\n });\n });\n\n Promise.all(phase1Calls).then(function(allResults) {\n clearProgress();\n var votes = {};\n questions.forEach(function(q) { votes[String(q.num)] = []; });\n\n chunks.forEach(function(chunk, ci) {\n temps.forEach(function(temp, ti) {\n var result = allResults[ci * temps.length + ti];\n chunk.forEach(function(q) {\n votes[String(q.num)].push(String(result[String(q.num)] || '').trim());\n });\n });\n });\n\n var agreed = [], disputed = [];\n questions.forEach(function(q) {\n var v = votes[String(q.num)];\n if (v[0] && v[0] === v[1] && v[1] === v[2]) {\n agreed.push({ q: q, ans: v[0] });\n } else {\n disputed.push(q);\n }\n });\n\n var totalOk = 0;\n agreed.forEach(function(item) { if (selectAnswer(item.q, item.ans)) totalOk++; });\n log('一致' + agreed.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) {\n log('完成 ' + totalOk + '/' + questions.length + ' | ' + ts(), '#1a73e8');\n return;\n }\n\n // ============ Phase 2: 逐题仲裁 ============\n log('Phase 2 仲裁 ' + disputed.length + ' 题...', '#e37400');\n\n var unresolved = [];\n var phase2Ok = 0;\n var phase2Done = 0;\n var phase2Jobs = disputed.map(function(q) {\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.7));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n phase2Done++;\n progress('仲裁 ' + phase2Done + '/' + disputed.length + ' 题完成...');\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n\n return Promise.all(phase2Jobs).then(function() {\n clearProgress();\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (一致' + agreed.length + ' + 仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n });\n }).catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n})();\n"}, "01920735ec3e8d43154cfceecfa0fae8a2a9059e": {"t": 1785758746.062085, "n": 8423, "h": "56f2883d03b7e194", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:220px;max-width:360px;pointer-events:none';\n panel_el.innerHTML = '<div id=\"fdty-msg\"></div>';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/v1/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n log(questions.length + ' 题 Phase 1 投票中...', '#1a73e8');\n\n // ============ API 调用 ============\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n })\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1: 分组并发投票 ============\n var CHUNK_SIZE = 15;\n var chunks = [];\n for (var i = 0; i < questions.length; i += CHUNK_SIZE) {\n chunks.push(questions.slice(i, i + CHUNK_SIZE));\n }\n var temps = [0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4];\n\n var phase1Calls = [];\n chunks.forEach(function(chunk) {\n temps.forEach(function(temp) { phase1Calls.push(callAPI(chunk, temp)); });\n });\n\n Promise.all(phase1Calls).then(function(allResults) {\n var votes = {};\n questions.forEach(function(q) { votes[String(q.num)] = []; });\n\n chunks.forEach(function(chunk, ci) {\n temps.forEach(function(temp, ti) {\n var result = allResults[ci * temps.length + ti];\n chunk.forEach(function(q) {\n votes[String(q.num)].push(String(result[String(q.num)] || '').trim());\n });\n });\n });\n\n var agreed = [], disputed = [];\n questions.forEach(function(q) {\n var v = votes[String(q.num)];\n if (v[0] && v[0] === v[1] && v[1] === v[2]) {\n agreed.push({ q: q, ans: v[0] });\n } else {\n disputed.push(q);\n }\n });\n\n var totalOk = 0;\n agreed.forEach(function(item) { if (selectAnswer(item.q, item.ans)) totalOk++; });\n log('一致' + agreed.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) {\n log('完成 ' + totalOk + '/' + questions.length + ' | ' + ts(), '#1a73e8');\n return;\n }\n\n // ============ Phase 2: 逐题仲裁 ============\n log('Phase 2 仲裁 ' + disputed.length + ' 题...', '#e37400');\n\n var unresolved = [];\n var phase2Ok = 0;\n var phase2Jobs = disputed.map(function(q) {\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.7));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n\n return Promise.all(phase2Jobs).then(function() {\n if (unresolved.length > 0) {\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n var line = '#' + r.q.num + ' ' + r.q.text.slice(0, 50) + ' → ' + parts.join(', ');\n log(line, '#c62828');\n console.warn('[fdty] [' + r.q.num + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (一致' + agreed.length + ' + 仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n });\n }).catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n})();\n"}, "ebb391b3ee47f3850df302bda610713cee1280fd": {"t": 1785758747.4245703, "n": 8374, "h": "0b5b2e9aa3bd5f0f", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;right:8px;z-index:99999;background:#fff;color:#333;padding:8px 12px;border:1px solid #ccc;font:12px/1.5 monospace;min-width:200px;pointer-events:none';\n panel_el.innerHTML = '<div id=\"fdty-msg\"></div>';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#333';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/v1/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n log(questions.length + ' 题 Phase 1 投票中...', '#1a73e8');\n\n // ============ API 调用 ============\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n })\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1: 分组并发投票 ============\n var CHUNK_SIZE = 15;\n var chunks = [];\n for (var i = 0; i < questions.length; i += CHUNK_SIZE) {\n chunks.push(questions.slice(i, i + CHUNK_SIZE));\n }\n var temps = [0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4];\n\n var phase1Calls = [];\n chunks.forEach(function(chunk) {\n temps.forEach(function(temp) { phase1Calls.push(callAPI(chunk, temp)); });\n });\n\n Promise.all(phase1Calls).then(function(allResults) {\n var votes = {};\n questions.forEach(function(q) { votes[String(q.num)] = []; });\n\n chunks.forEach(function(chunk, ci) {\n temps.forEach(function(temp, ti) {\n var result = allResults[ci * temps.length + ti];\n chunk.forEach(function(q) {\n votes[String(q.num)].push(String(result[String(q.num)] || '').trim());\n });\n });\n });\n\n var agreed = [], disputed = [];\n questions.forEach(function(q) {\n var v = votes[String(q.num)];\n if (v[0] && v[0] === v[1] && v[1] === v[2]) {\n agreed.push({ q: q, ans: v[0] });\n } else {\n disputed.push(q);\n }\n });\n\n var totalOk = 0;\n agreed.forEach(function(item) { if (selectAnswer(item.q, item.ans)) totalOk++; });\n log('一致' + agreed.length + ' | 分歧' + disputed.length + ' | ' + ts(), '#1a73e8');\n\n if (disputed.length === 0) {\n log('完成 ' + totalOk + '/' + questions.length + ' | ' + ts(), '#1a73e8');\n return;\n }\n\n // ============ Phase 2: 逐题仲裁 ============\n log('Phase 2 仲裁 ' + disputed.length + ' 题...', '#e37400');\n\n var unresolved = [];\n var phase2Ok = 0;\n var phase2Jobs = disputed.map(function(q) {\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.7));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n\n return Promise.all(phase2Jobs).then(function() {\n if (unresolved.length > 0) {\n log(unresolved.length + ' 题无法确定 (看控制台)');\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n console.warn('[fdty] [' + r.q.num + '] [' + (r.q.type === 'tf' ? '判' : '选') + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (一致' + agreed.length + ' + 仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#1a73e8');\n });\n }).catch(function(e) {\n log('错误: ' + e.message);\n console.error('[fdty]', e);\n });\n})();\n"}, "1d51374bb570b05d07d4cfba4764e81d96342e43": {"t": 1785758748.6907973, "n": 8634, "h": "4d2a053b16f8382c", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n if (!doc.getElementById('Panel3')) {\n for (var fi = 0; fi < window.frames.length; fi++) {\n try {\n var fd = window.frames[fi].document;\n if (fd.getElementById('Panel3')) { doc = fd; break; }\n } catch(e) {}\n }\n }\n\n // ============ 悬浮面板 ============\n var panel_el = doc.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;left:8px;z-index:99999;background:rgba(0,0,0,0.82);color:#fff;padding:10px 14px;border-radius:8px;font:12px/1.6 monospace;min-width:220px;max-width:320px;box-shadow:0 2px 12px rgba(0,0,0,0.5);border:1px solid rgba(255,255,255,0.15);pointer-events:none';\n panel_el.innerHTML = '<div style=\"font-weight:bold;color:#4fc3f7;margin-bottom:4px\">fdty 答题中...</div><div id=\"fdty-msg\"></div>';\n doc.body.appendChild(panel_el);\n var msg_el = doc.getElementById('fdty-msg');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#ccc';\n var line = doc.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/v1/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found', '#ff5252'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n log(questions.length + ' 题 Phase 1 投票中...', '#4fc3f7');\n\n // ============ API 调用 ============\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n })\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1: 分组并发投票 ============\n var CHUNK_SIZE = 15;\n var chunks = [];\n for (var i = 0; i < questions.length; i += CHUNK_SIZE) {\n chunks.push(questions.slice(i, i + CHUNK_SIZE));\n }\n var temps = [0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4];\n\n var phase1Calls = [];\n chunks.forEach(function(chunk) {\n temps.forEach(function(temp) { phase1Calls.push(callAPI(chunk, temp)); });\n });\n\n Promise.all(phase1Calls).then(function(allResults) {\n var votes = {};\n questions.forEach(function(q) { votes[String(q.num)] = []; });\n\n chunks.forEach(function(chunk, ci) {\n temps.forEach(function(temp, ti) {\n var result = allResults[ci * temps.length + ti];\n chunk.forEach(function(q) {\n votes[String(q.num)].push(String(result[String(q.num)] || '').trim());\n });\n });\n });\n\n var agreed = [], disputed = [];\n questions.forEach(function(q) {\n var v = votes[String(q.num)];\n if (v[0] && v[0] === v[1] && v[1] === v[2]) {\n agreed.push({ q: q, ans: v[0] });\n } else {\n disputed.push(q);\n }\n });\n\n var totalOk = 0;\n agreed.forEach(function(item) { if (selectAnswer(item.q, item.ans)) totalOk++; });\n log('一致' + agreed.length + ' | 分歧' + disputed.length + ' | ' + ts(), agreed.length > disputed.length ? '#69f0ae' : '#ffab40');\n\n if (disputed.length === 0) {\n log('完成 ' + totalOk + '/' + questions.length + ' | ' + ts(), '#69f0ae');\n return;\n }\n\n // ============ Phase 2: 逐题仲裁 ============\n log('Phase 2 仲裁 ' + disputed.length + ' 题...', '#ffab40');\n\n var unresolved = [];\n var phase2Ok = 0;\n var phase2Jobs = disputed.map(function(q) {\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.7));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n\n return Promise.all(phase2Jobs).then(function() {\n if (unresolved.length > 0) {\n log(unresolved.length + ' 题无法确定 (看控制台)', '#ff5252');\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n console.warn('[fdty] [' + r.q.num + '] [' + (r.q.type === 'tf' ? '判' : '选') + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (一致' + agreed.length + ' + 仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#69f0ae');\n });\n }).catch(function(e) {\n log('错误: ' + e.message, '#ff5252');\n console.error('[fdty]', e);\n });\n})();\n"}, "d6566d96b7c99962f35e163a7435fcb8448271cc": {"t": 1785758749.9739895, "n": 8410, "h": "a3448c64ebc0dfed", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n // ============ 悬浮面板 ============\n var panel_el = document.createElement('div');\n panel_el.id = 'fdty-panel';\n panel_el.style.cssText = 'position:fixed;top:8px;left:8px;z-index:99999;background:rgba(0,0,0,0.82);color:#fff;padding:10px 14px;border-radius:8px;font:12px/1.6 monospace;min-width:220px;max-width:320px;box-shadow:0 2px 12px rgba(0,0,0,0.5);border:1px solid rgba(255,255,255,0.15);pointer-events:none';\n panel_el.innerHTML = '<div style=\"font-weight:bold;color:#4fc3f7;margin-bottom:4px\">fdty 答题中...</div><div id=\"fdty-msg\"></div>';\n document.body.appendChild(panel_el);\n var msg_el = document.getElementById('fdty-msg');\n\n function log(msg, color) {\n console.log('[fdty] ' + msg);\n if (!color) color = '#ccc';\n var line = document.createElement('div');\n line.style.color = color;\n line.textContent = msg;\n msg_el.appendChild(line);\n }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/v1/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n\n var panel = doc.getElementById('Panel3');\n if (!panel) { log('Panel3 not found', '#ff5252'); return; }\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n log(questions.length + ' 题 Phase 1 投票中...', '#4fc3f7');\n\n // ============ API 调用 ============\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n })\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1: 分组并发投票 ============\n var CHUNK_SIZE = 15;\n var chunks = [];\n for (var i = 0; i < questions.length; i += CHUNK_SIZE) {\n chunks.push(questions.slice(i, i + CHUNK_SIZE));\n }\n var temps = [0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4];\n\n var phase1Calls = [];\n chunks.forEach(function(chunk) {\n temps.forEach(function(temp) { phase1Calls.push(callAPI(chunk, temp)); });\n });\n\n Promise.all(phase1Calls).then(function(allResults) {\n var votes = {};\n questions.forEach(function(q) { votes[String(q.num)] = []; });\n\n chunks.forEach(function(chunk, ci) {\n temps.forEach(function(temp, ti) {\n var result = allResults[ci * temps.length + ti];\n chunk.forEach(function(q) {\n votes[String(q.num)].push(String(result[String(q.num)] || '').trim());\n });\n });\n });\n\n var agreed = [], disputed = [];\n questions.forEach(function(q) {\n var v = votes[String(q.num)];\n if (v[0] && v[0] === v[1] && v[1] === v[2]) {\n agreed.push({ q: q, ans: v[0] });\n } else {\n disputed.push(q);\n }\n });\n\n var totalOk = 0;\n agreed.forEach(function(item) { if (selectAnswer(item.q, item.ans)) totalOk++; });\n log('一致' + agreed.length + ' | 分歧' + disputed.length + ' | ' + ts(), agreed.length > disputed.length ? '#69f0ae' : '#ffab40');\n\n if (disputed.length === 0) {\n log('完成 ' + totalOk + '/' + questions.length + ' | ' + ts(), '#69f0ae');\n return;\n }\n\n // ============ Phase 2: 逐题仲裁 ============\n log('Phase 2 仲裁 ' + disputed.length + ' 题...', '#ffab40');\n\n var unresolved = [];\n var phase2Ok = 0;\n var phase2Jobs = disputed.map(function(q) {\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.7));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n\n return Promise.all(phase2Jobs).then(function() {\n if (unresolved.length > 0) {\n log(unresolved.length + ' 题无法确定 (看控制台)', '#ff5252');\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n console.warn('[fdty] [' + r.q.num + '] [' + (r.q.type === 'tf' ? '判' : '选') + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n log('完成 ' + totalOk + '/' + questions.length +\n ' (一致' + agreed.length + ' + 仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts(), '#69f0ae');\n });\n }).catch(function(e) {\n log('错误: ' + e.message, '#ff5252');\n console.error('[fdty]', e);\n });\n})();\n"}, "7fc5b9949f1c4a50730b0714ddf1a35ac478ebec": {"t": 1785758751.6193368, "n": 7481, "h": "d53c0b8d05a772cf", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/v1/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n\n var panel = doc.getElementById('Panel3');\n if (!panel) return console.error('[fdty] Panel3 not found');\n\n // ============ 提取题目 ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue;\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break;\n node = node.previousSibling;\n }\n if (!node) continue;\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n console.log('[fdty] ' + questions.length + ' 题 Phase 1 投票中...');\n\n // ============ API 调用 ============\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n })\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n }).catch(function() { return {}; });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1: 分组并发投票 ============\n var CHUNK_SIZE = 15;\n var chunks = [];\n for (var i = 0; i < questions.length; i += CHUNK_SIZE) {\n chunks.push(questions.slice(i, i + CHUNK_SIZE));\n }\n var temps = [0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4];\n\n var phase1Calls = [];\n chunks.forEach(function(chunk) {\n temps.forEach(function(temp) { phase1Calls.push(callAPI(chunk, temp)); });\n });\n\n Promise.all(phase1Calls).then(function(allResults) {\n var votes = {};\n questions.forEach(function(q) { votes[String(q.num)] = []; });\n\n chunks.forEach(function(chunk, ci) {\n temps.forEach(function(temp, ti) {\n var result = allResults[ci * temps.length + ti];\n chunk.forEach(function(q) {\n votes[String(q.num)].push(String(result[String(q.num)] || '').trim());\n });\n });\n });\n\n var agreed = [], disputed = [];\n questions.forEach(function(q) {\n var v = votes[String(q.num)];\n if (v[0] && v[0] === v[1] && v[1] === v[2]) {\n agreed.push({ q: q, ans: v[0] });\n } else {\n disputed.push(q);\n }\n });\n\n var totalOk = 0;\n agreed.forEach(function(item) { if (selectAnswer(item.q, item.ans)) totalOk++; });\n console.log('[fdty] 一致' + agreed.length + ' | 分歧' + disputed.length + ' | ' + ts());\n\n if (disputed.length === 0) {\n console.log('[fdty] 完成 ' + totalOk + '/' + questions.length + ' 题 | ' + ts());\n return;\n }\n\n // ============ Phase 2: 逐题仲裁 ============\n console.log('[fdty] Phase 2 仲裁 ' + disputed.length + ' 题...');\n\n var unresolved = [];\n var phase2Ok = 0;\n var phase2Jobs = disputed.map(function(q) {\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.7));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n }\n });\n });\n\n return Promise.all(phase2Jobs).then(function() {\n if (unresolved.length > 0) {\n console.warn('[fdty] ' + unresolved.length + ' 题无法确定,需人工判断:');\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n console.warn(' [' + r.q.num + '] [' + (r.q.type === 'tf' ? '判' : '选') + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n }\n console.log('[fdty] 完成 ' + totalOk + '/' + questions.length +\n ' (一致' + agreed.length + ' + 仲裁' + phase2Ok + ', 未定' + unresolved.length + ') | ' + ts());\n });\n }).catch(function(e) {\n console.error('[fdty]', e);\n });\n})();\n"}, "807ad0d81d2ba1b3260ccc990aeb581b0f326128": {"t": 1785758753.2175405, "n": 10013, "h": "8ba3539eeac099fe", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/v1/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n\n var panel = doc.getElementById('Panel3');\n if (!panel) return console.error('[fdty] Panel3 not found');\n\n // ============ 提取题目 (DOM 遍历 + 正则兜底) ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n // Method 1: DOM 遍历 — 找所有 group 元素,向前找兄弟文本节点\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue; // 过滤子元素 (radio inputs)\n\n // 向前找包含数字的文本节点\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break; // TEXT_NODE\n node = node.previousSibling;\n }\n if (!node) continue;\n\n // 从文本节点末尾提取 \"题号 . 题目文本\"\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n // Method 2: 正则兜底 — innerHTML 匹配 (处理 DOM 遍历遗漏)\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n var numTF = questions.filter(function(q) { return q.type === 'tf'; }).length;\n var numChoice = questions.filter(function(q) { return q.type === 'choice'; }).length;\n questions.sort(function(a, b) { return a.num - b.num; });\n console.log('[fdty] ' + ts() + ' 提取 ' + questions.length + ' 题 (判断' + numTF + ' + 单选' + numChoice + ', DOM:' + (questions.length > 0 && isGroupId(questions[0].id) ? 'yes' : 'regex') + ')');\n console.log('[fdty] 模型: deepseek-v4-flash | Key: ' + API_KEY.slice(0,8) + '***');\n\n // ============ API 调用 ============\n var apiCallCount = 0;\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n var t0 = Date.now();\n apiCallCount++;\n var label = 'API#' + apiCallCount + ' (' + qs.length + '题, temp=' + temp.toFixed(2) + ')';\n console.log('[fdty] ' + ts() + ' ' + label + ' 发送...');\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n })\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status + ': ' + t.slice(0,200)); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n var elapsed = ((Date.now() - t0) / 1000).toFixed(1);\n var tokenInfo = d.usage ? ' | tokens: ' + d.usage.prompt_tokens + '+' + d.usage.completion_tokens + '=' + d.usage.total_tokens : '';\n if (!content) {\n console.warn('[fdty] ' + ts() + ' ' + label + ' 完成 ' + elapsed + 's, 空响应!' + tokenInfo);\n return {};\n }\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try {\n var obj = JSON.parse(content);\n var keys = Object.keys(obj).length;\n console.log('[fdty] ' + ts() + ' ' + label + ' 完成 ' + elapsed + 's, 解析 ' + keys + ' 个答案' + tokenInfo);\n return obj;\n } catch(e) {\n console.warn('[fdty] ' + ts() + ' ' + label + ' 完成 ' + elapsed + 's, JSON解析失败: ' + content.slice(0,80) + tokenInfo);\n return {};\n }\n }).catch(function(e) {\n console.error('[fdty] ' + ts() + ' ' + label + ' 失败: ' + e.message);\n return {};\n });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1: 分组并发投票 ============\n var CHUNK_SIZE = 15;\n var chunks = [];\n for (var i = 0; i < questions.length; i += CHUNK_SIZE) {\n chunks.push(questions.slice(i, i + CHUNK_SIZE));\n }\n var temps = [0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4, 0.5 + Math.random() * 0.4];\n console.log('[fdty] ' + ts() + ' Phase 1 ' + questions.length + '题 → ' + chunks.length + '组×3票, 共' + (chunks.length*3) + '次并发 (temp=' + temps.map(function(t) { return t.toFixed(2); }).join(',') + ')');\n\n var phase1Calls = [];\n chunks.forEach(function(chunk) {\n temps.forEach(function(temp) {\n phase1Calls.push(callAPI(chunk, temp));\n });\n });\n\n Promise.all(phase1Calls).then(function(allResults) {\n // allResults: [chunk0_temp0, chunk0_temp1, chunk0_temp2, chunk1_temp0, ...]\n // 合并为每题的 3 票\n var votes = {};\n questions.forEach(function(q) {\n votes[String(q.num)] = [];\n });\n\n chunks.forEach(function(chunk, ci) {\n temps.forEach(function(temp, ti) {\n var result = allResults[ci * temps.length + ti];\n chunk.forEach(function(q) {\n var ans = String(result[String(q.num)] || '').trim();\n votes[String(q.num)].push(ans);\n });\n });\n });\n\n var agreed = [], disputed = [];\n var emptyCount = 0;\n questions.forEach(function(q) {\n var key = String(q.num);\n var v = votes[key];\n if (!v[0] && !v[1] && !v[2]) {\n emptyCount++;\n }\n if (v[0] && v[0] === v[1] && v[1] === v[2]) {\n agreed.push({ q: q, ans: v[0] });\n } else {\n disputed.push(q);\n }\n });\n\n // 全票一致的直接选\n var totalOk = 0;\n agreed.forEach(function(item) {\n if (selectAnswer(item.q, item.ans)) totalOk++;\n });\n console.log('[fdty] ' + ts() + ' Phase 1 一致:' + agreed.length + ' 已选 | 分歧:' + disputed.length + ' | 全空:' + emptyCount);\n\n if (disputed.length === 0) {\n console.log('[fdty] ' + ts() + ' 完成 ' + totalOk + '/' + questions.length);\n return;\n }\n\n // ============ Phase 2: 逐题仲裁,流式输出 ============\n var unresolved = [];\n var phase2Done = 0, phase2Ok = 0;\n console.log('[fdty] Phase 2 开始 (' + disputed.length + '题, 每题10次请求)...');\n\n var phase2Jobs = disputed.map(function(q) {\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.7));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n phase2Done++;\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n console.log('[fdty] ✅ #' + q.num + ' → ' + best + ' (' + max + '/10票) [' + phase2Done + '/' + disputed.length + ']');\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n console.log('[fdty] ❓ #' + q.num + ' ' + (best||'?') + '(' + max + '/10) [' + phase2Done + '/' + disputed.length + ']');\n }\n });\n });\n\n return Promise.all(phase2Jobs).then(function() {\n if (unresolved.length > 0) {\n console.warn('[fdty] === 以下 ' + unresolved.length + ' 题无法确定,需人工判断 ===');\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n console.warn(' [' + r.q.num + '] [' + (r.q.type === 'tf' ? '判' : '选') + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n console.warn('[fdty] ==========================================');\n }\n console.log('[fdty] ' + ts() + ' 完成 ' + totalOk + '/' + questions.length +\n ' (一致' + agreed.length + ' + 仲裁' + phase2Ok + ', 未定' + unresolved.length + ', 共' + apiCallCount + '次API)');\n });\n }).catch(function(e) {\n console.error('[fdty]', e);\n });\n})();\n"}, "c20d5d943c0c8c28974b7c4d427cf6a35cc6aba5": {"t": 1785758754.4748611, "n": 9422, "h": "59e7c9cfe39030de", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/v1/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n\n var panel = doc.getElementById('Panel3');\n if (!panel) return console.error('[fdty] Panel3 not found');\n\n // ============ 提取题目 (DOM 遍历 + 正则兜底) ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n // Method 1: DOM 遍历 — 找所有 group 元素,向前找兄弟文本节点\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue; // 过滤子元素 (radio inputs)\n\n // 向前找包含数字的文本节点\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break; // TEXT_NODE\n node = node.previousSibling;\n }\n if (!node) continue;\n\n // 从文本节点末尾提取 \"题号 . 题目文本\"\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n // Method 2: 正则兜底 — innerHTML 匹配 (处理 DOM 遍历遗漏)\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n var numTF = questions.filter(function(q) { return q.type === 'tf'; }).length;\n var numChoice = questions.filter(function(q) { return q.type === 'choice'; }).length;\n questions.sort(function(a, b) { return a.num - b.num; });\n console.log('[fdty] ' + ts() + ' 提取 ' + questions.length + ' 题 (判断' + numTF + ' + 单选' + numChoice + ', DOM:' + (questions.length > 0 && isGroupId(questions[0].id) ? 'yes' : 'regex') + ')');\n console.log('[fdty] 模型: deepseek-v4-flash | Key: ' + API_KEY.slice(0,8) + '***');\n\n // ============ API 调用 ============\n var apiCallCount = 0;\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n var t0 = Date.now();\n apiCallCount++;\n var label = 'API#' + apiCallCount + ' (' + qs.length + '题, temp=' + temp.toFixed(2) + ')';\n console.log('[fdty] ' + ts() + ' ' + label + ' 发送...');\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n response_format: { type: 'json_object' },\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n })\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status + ': ' + t.slice(0,200)); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n var elapsed = ((Date.now() - t0) / 1000).toFixed(1);\n var tokenInfo = d.usage ? ' | tokens: ' + d.usage.prompt_tokens + '+' + d.usage.completion_tokens + '=' + d.usage.total_tokens : '';\n if (!content) {\n console.warn('[fdty] ' + ts() + ' ' + label + ' 完成 ' + elapsed + 's, 空响应!' + tokenInfo);\n return {};\n }\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try {\n var obj = JSON.parse(content);\n var keys = Object.keys(obj).length;\n console.log('[fdty] ' + ts() + ' ' + label + ' 完成 ' + elapsed + 's, 解析 ' + keys + ' 个答案' + tokenInfo);\n return obj;\n } catch(e) {\n console.warn('[fdty] ' + ts() + ' ' + label + ' 完成 ' + elapsed + 's, JSON解析失败: ' + content.slice(0,80) + tokenInfo);\n return {};\n }\n }).catch(function(e) {\n console.error('[fdty] ' + ts() + ' ' + label + ' 失败: ' + e.message);\n return {};\n });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1: 三次随机温度投票 ============\n var temps = [\n 0.5 + Math.random() * 0.4,\n 0.5 + Math.random() * 0.4,\n 0.5 + Math.random() * 0.4\n ];\n console.log('[fdty] ' + ts() + ' Phase 1 三次投票 (temp=' + temps.map(function(t) { return t.toFixed(2); }).join(',') + ')...');\n\n Promise.all([\n callAPI(questions, temps[0]),\n callAPI(questions, temps[1]),\n callAPI(questions, temps[2])\n ]).then(function(results) {\n // 统计每题的 3 票\n var votes = {};\n questions.forEach(function(q) {\n var key = String(q.num);\n votes[key] = results.map(function(r) { return String(r[key] || '').trim(); });\n });\n\n var agreed = [], disputed = [];\n var emptyCount = 0;\n questions.forEach(function(q) {\n var key = String(q.num);\n var v = votes[key];\n if (!v[0] && !v[1] && !v[2]) {\n emptyCount++;\n }\n if (v[0] && v[0] === v[1] && v[1] === v[2]) {\n agreed.push({ q: q, ans: v[0] });\n } else {\n disputed.push(q);\n }\n });\n\n // 全票一致的直接选\n var totalOk = 0;\n agreed.forEach(function(item) {\n if (selectAnswer(item.q, item.ans)) totalOk++;\n });\n console.log('[fdty] ' + ts() + ' Phase 1 一致:' + agreed.length + ' 已选 | 分歧:' + disputed.length + ' | 全空:' + emptyCount);\n\n if (disputed.length === 0) {\n console.log('[fdty] ' + ts() + ' 完成 ' + totalOk + '/' + questions.length);\n return;\n }\n\n // ============ Phase 2: 逐题仲裁,流式输出 ============\n var unresolved = [];\n var phase2Done = 0, phase2Ok = 0;\n console.log('[fdty] Phase 2 开始 (' + disputed.length + '题, 每题10次请求)...');\n\n var phase2Jobs = disputed.map(function(q) {\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.7));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n phase2Done++;\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n console.log('[fdty] ✅ #' + q.num + ' → ' + best + ' (' + max + '/10票) [' + phase2Done + '/' + disputed.length + ']');\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n console.log('[fdty] ❓ #' + q.num + ' ' + (best||'?') + '(' + max + '/10) [' + phase2Done + '/' + disputed.length + ']');\n }\n });\n });\n\n return Promise.all(phase2Jobs).then(function() {\n if (unresolved.length > 0) {\n console.warn('[fdty] === 以下 ' + unresolved.length + ' 题无法确定,需人工判断 ===');\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n console.warn(' [' + r.q.num + '] [' + (r.q.type === 'tf' ? '判' : '选') + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n console.warn('[fdty] ==========================================');\n }\n console.log('[fdty] ' + ts() + ' 完成 ' + totalOk + '/' + questions.length +\n ' (一致' + agreed.length + ' + 仲裁' + phase2Ok + ', 未定' + unresolved.length + ', 共' + apiCallCount + '次API)');\n });\n }).catch(function(e) {\n console.error('[fdty]', e);\n });\n})();\n"}, "1819b5103d2a07ba9b07df80b97a587bf4a71fcb": {"t": 1785758756.0573802, "n": 9372, "h": "7f922c66648bcaef", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/v1/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n\n var panel = doc.getElementById('Panel3');\n if (!panel) return console.error('[fdty] Panel3 not found');\n\n // ============ 提取题目 (DOM 遍历 + 正则兜底) ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n // Method 1: DOM 遍历 — 找所有 group 元素,向前找兄弟文本节点\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue; // 过滤子元素 (radio inputs)\n\n // 向前找包含数字的文本节点\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break; // TEXT_NODE\n node = node.previousSibling;\n }\n if (!node) continue;\n\n // 从文本节点末尾提取 \"题号 . 题目文本\"\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n // Method 2: 正则兜底 — innerHTML 匹配 (处理 DOM 遍历遗漏)\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n var numTF = questions.filter(function(q) { return q.type === 'tf'; }).length;\n var numChoice = questions.filter(function(q) { return q.type === 'choice'; }).length;\n questions.sort(function(a, b) { return a.num - b.num; });\n console.log('[fdty] ' + ts() + ' 提取 ' + questions.length + ' 题 (判断' + numTF + ' + 单选' + numChoice + ', DOM:' + (questions.length > 0 && isGroupId(questions[0].id) ? 'yes' : 'regex') + ')');\n console.log('[fdty] 模型: deepseek-v4-flash | Key: ' + API_KEY.slice(0,8) + '***');\n\n // ============ API 调用 ============\n var apiCallCount = 0;\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n var t0 = Date.now();\n apiCallCount++;\n var label = 'API#' + apiCallCount + ' (' + qs.length + '题, temp=' + temp.toFixed(2) + ')';\n console.log('[fdty] ' + ts() + ' ' + label + ' 发送...');\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n })\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status + ': ' + t.slice(0,200)); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n var elapsed = ((Date.now() - t0) / 1000).toFixed(1);\n var tokenInfo = d.usage ? ' | tokens: ' + d.usage.prompt_tokens + '+' + d.usage.completion_tokens + '=' + d.usage.total_tokens : '';\n if (!content) {\n console.warn('[fdty] ' + ts() + ' ' + label + ' 完成 ' + elapsed + 's, 空响应!' + tokenInfo);\n return {};\n }\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try {\n var obj = JSON.parse(content);\n var keys = Object.keys(obj).length;\n console.log('[fdty] ' + ts() + ' ' + label + ' 完成 ' + elapsed + 's, 解析 ' + keys + ' 个答案' + tokenInfo);\n return obj;\n } catch(e) {\n console.warn('[fdty] ' + ts() + ' ' + label + ' 完成 ' + elapsed + 's, JSON解析失败: ' + content.slice(0,80) + tokenInfo);\n return {};\n }\n }).catch(function(e) {\n console.error('[fdty] ' + ts() + ' ' + label + ' 失败: ' + e.message);\n return {};\n });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1: 三次随机温度投票 ============\n var temps = [\n 0.5 + Math.random() * 0.4,\n 0.5 + Math.random() * 0.4,\n 0.5 + Math.random() * 0.4\n ];\n console.log('[fdty] ' + ts() + ' Phase 1 三次投票 (temp=' + temps.map(function(t) { return t.toFixed(2); }).join(',') + ')...');\n\n Promise.all([\n callAPI(questions, temps[0]),\n callAPI(questions, temps[1]),\n callAPI(questions, temps[2])\n ]).then(function(results) {\n // 统计每题的 3 票\n var votes = {};\n questions.forEach(function(q) {\n var key = String(q.num);\n votes[key] = results.map(function(r) { return String(r[key] || '').trim(); });\n });\n\n var agreed = [], disputed = [];\n var emptyCount = 0;\n questions.forEach(function(q) {\n var key = String(q.num);\n var v = votes[key];\n if (!v[0] && !v[1] && !v[2]) {\n emptyCount++;\n }\n if (v[0] && v[0] === v[1] && v[1] === v[2]) {\n agreed.push({ q: q, ans: v[0] });\n } else {\n disputed.push(q);\n }\n });\n\n // 全票一致的直接选\n var totalOk = 0;\n agreed.forEach(function(item) {\n if (selectAnswer(item.q, item.ans)) totalOk++;\n });\n console.log('[fdty] ' + ts() + ' Phase 1 一致:' + agreed.length + ' 已选 | 分歧:' + disputed.length + ' | 全空:' + emptyCount);\n\n if (disputed.length === 0) {\n console.log('[fdty] ' + ts() + ' 完成 ' + totalOk + '/' + questions.length);\n return;\n }\n\n // ============ Phase 2: 逐题仲裁,流式输出 ============\n var unresolved = [];\n var phase2Done = 0, phase2Ok = 0;\n console.log('[fdty] Phase 2 开始 (' + disputed.length + '题, 每题10次请求)...');\n\n var phase2Jobs = disputed.map(function(q) {\n var batch = [];\n for (var i = 0; i < 10; i++) batch.push(callAPI([q], 0.7));\n return Promise.all(batch).then(function(answers) {\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n var best = '', max = 0;\n Object.keys(counts).forEach(function(k) { if (counts[k] > max) { max = counts[k]; best = k; } });\n\n phase2Done++;\n if (best && max >= 8) {\n if (selectAnswer(q, best)) { totalOk++; phase2Ok++; }\n console.log('[fdty] ✅ #' + q.num + ' → ' + best + ' (' + max + '/10票) [' + phase2Done + '/' + disputed.length + ']');\n } else {\n unresolved.push({ q: q, best: best, count: max, counts: counts });\n console.log('[fdty] ❓ #' + q.num + ' ' + (best||'?') + '(' + max + '/10) [' + phase2Done + '/' + disputed.length + ']');\n }\n });\n });\n\n return Promise.all(phase2Jobs).then(function() {\n if (unresolved.length > 0) {\n console.warn('[fdty] === 以下 ' + unresolved.length + ' 题无法确定,需人工判断 ===');\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) { parts.push(ans + ':' + r.counts[ans]); });\n console.warn(' [' + r.q.num + '] [' + (r.q.type === 'tf' ? '判' : '选') + '] ' + r.q.text + '\\n 票数: ' + parts.join(', '));\n });\n console.warn('[fdty] ==========================================');\n }\n console.log('[fdty] ' + ts() + ' 完成 ' + totalOk + '/' + questions.length +\n ' (一致' + agreed.length + ' + 仲裁' + phase2Ok + ', 未定' + unresolved.length + ', 共' + apiCallCount + '次API)');\n });\n }).catch(function(e) {\n console.error('[fdty]', e);\n });\n})();\n"}, "9112c12f88927b5992c097d26b779d34d6b89577": {"t": 1785758758.1313272, "n": 9928, "h": "db8dc423428c50db", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/v1/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n\n var panel = doc.getElementById('Panel3');\n if (!panel) return console.error('[fdty] Panel3 not found');\n\n // ============ 提取题目 (DOM 遍历 + 正则兜底) ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n // Method 1: DOM 遍历 — 找所有 group 元素,向前找兄弟文本节点\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue; // 过滤子元素 (radio inputs)\n\n // 向前找包含数字的文本节点\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break; // TEXT_NODE\n node = node.previousSibling;\n }\n if (!node) continue;\n\n // 从文本节点末尾提取 \"题号 . 题目文本\"\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n // Method 2: 正则兜底 — innerHTML 匹配 (处理 DOM 遍历遗漏)\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n var numTF = questions.filter(function(q) { return q.type === 'tf'; }).length;\n var numChoice = questions.filter(function(q) { return q.type === 'choice'; }).length;\n questions.sort(function(a, b) { return a.num - b.num; });\n console.log('[fdty] ' + ts() + ' 提取 ' + questions.length + ' 题 (判断' + numTF + ' + 单选' + numChoice + ', DOM:' + (questions.length > 0 && isGroupId(questions[0].id) ? 'yes' : 'regex') + ')');\n console.log('[fdty] 模型: deepseek-v4-flash | Key: ' + API_KEY.slice(0,8) + '***');\n\n // ============ API 调用 ============\n var apiCallCount = 0;\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n var t0 = Date.now();\n apiCallCount++;\n var label = 'API#' + apiCallCount + ' (' + qs.length + '题, temp=' + temp.toFixed(2) + ')';\n console.log('[fdty] ' + ts() + ' ' + label + ' 发送...');\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n })\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status + ': ' + t.slice(0,200)); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n var elapsed = ((Date.now() - t0) / 1000).toFixed(1);\n var tokenInfo = d.usage ? ' | tokens: ' + d.usage.prompt_tokens + '+' + d.usage.completion_tokens + '=' + d.usage.total_tokens : '';\n if (!content) {\n console.warn('[fdty] ' + ts() + ' ' + label + ' 完成 ' + elapsed + 's, 空响应!' + tokenInfo);\n return {};\n }\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try {\n var obj = JSON.parse(content);\n var keys = Object.keys(obj).length;\n console.log('[fdty] ' + ts() + ' ' + label + ' 完成 ' + elapsed + 's, 解析 ' + keys + ' 个答案' + tokenInfo);\n return obj;\n } catch(e) {\n console.warn('[fdty] ' + ts() + ' ' + label + ' 完成 ' + elapsed + 's, JSON解析失败: ' + content.slice(0,80) + tokenInfo);\n return {};\n }\n }).catch(function(e) {\n console.error('[fdty] ' + ts() + ' ' + label + ' 失败: ' + e.message);\n return {};\n });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1: 三次随机温度投票 ============\n var temps = [\n 0.5 + Math.random() * 0.4,\n 0.5 + Math.random() * 0.4,\n 0.5 + Math.random() * 0.4\n ];\n console.log('[fdty] ' + ts() + ' Phase 1 三次投票 (temp=' + temps.map(function(t) { return t.toFixed(2); }).join(',') + ')...');\n\n Promise.all([\n callAPI(questions, temps[0]),\n callAPI(questions, temps[1]),\n callAPI(questions, temps[2])\n ]).then(function(results) {\n // 统计每题的 3 票\n var votes = {};\n questions.forEach(function(q) {\n var key = String(q.num);\n votes[key] = results.map(function(r) { return String(r[key] || '').trim(); });\n });\n\n var agreed = [], disputed = [];\n var emptyCount = 0;\n questions.forEach(function(q) {\n var key = String(q.num);\n var v = votes[key];\n if (!v[0] && !v[1] && !v[2]) {\n emptyCount++;\n }\n if (v[0] && v[0] === v[1] && v[1] === v[2]) {\n agreed.push({ q: q, ans: v[0] });\n } else {\n disputed.push(q);\n }\n });\n\n // 全票一致的直接选\n var totalOk = 0;\n agreed.forEach(function(item) {\n if (selectAnswer(item.q, item.ans)) totalOk++;\n });\n console.log('[fdty] ' + ts() + ' Phase 1 一致:' + agreed.length + ' 已选 | 分歧:' + disputed.length + ' | 全空:' + emptyCount);\n\n if (disputed.length === 0) {\n console.log('[fdty] ' + ts() + ' 完成 ' + totalOk + '/' + questions.length);\n return;\n }\n\n // ============ Phase 2: 逐题 10 次仲裁 ============\n console.log('[fdty] ' + ts() + ' Phase 2 逐题仲裁 ' + disputed.length + ' 题 (每题 10 次请求, 共' + (disputed.length*10) + '次)...');\n\n var phase2Done = 0;\n var phase2Jobs = disputed.map(function(q, qi) {\n // 同一题发 10 次请求\n var batch = [];\n for (var i = 0; i < 10; i++) {\n batch.push(callAPI([q], 0.7));\n }\n return Promise.all(batch).then(function(answers) {\n phase2Done++;\n // 统计 10 次投票\n var counts = {}, validAnswers = 0;\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) { counts[ans] = (counts[ans] || 0) + 1; validAnswers++; }\n });\n\n var best = '', max = 0;\n Object.keys(counts).forEach(function(ans) {\n if (counts[ans] > max) { max = counts[ans]; best = ans; }\n });\n\n var result = { q: q, best: best, count: max, counts: counts };\n console.log('[fdty] ' + ts() + ' Phase2 [' + phase2Done + '/' + disputed.length +\n '] 题#' + q.num + ' (' + (q.type==='tf'?'判':'选') + ') ' +\n q.text.slice(0, 40) + '... ' + validAnswers + '/10 有效, 最佳:' + best + '(' + max + '票)');\n return result;\n });\n });\n\n return Promise.all(phase2Jobs).then(function(results) {\n var unresolved = [];\n\n results.forEach(function(r) {\n if (r.best && r.count >= 8) {\n if (selectAnswer(r.q, r.best)) totalOk++;\n } else {\n unresolved.push(r);\n }\n });\n\n var phase2ok = results.length - unresolved.length;\n console.log('[fdty] ' + ts() + ' Phase 2 确定 ' + phase2ok + ' 题, 剩余 ' + unresolved.length + ' 题');\n\n // ============ Phase 3: 输出无法确定题目 ============\n if (unresolved.length > 0) {\n console.warn('[fdty] ========== 以下 ' + unresolved.length + ' 题无法确定,需人工判断 ==========');\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) {\n parts.push(ans + ':' + r.counts[ans]);\n });\n console.warn(\n ' [' + r.q.num + '] [' + (r.q.type === 'tf' ? '判' : '选') + '] ' +\n r.q.text + '\\n 票数: ' + parts.join(', ')\n );\n });\n console.warn('[fdty] ================================================');\n }\n\n console.log('[fdty] ' + ts() + ' 完成 ' + totalOk + '/' + questions.length +\n ' (一致:' + agreed.length + ' + 仲裁:' + phase2ok + ', 未定:' + unresolved.length + ', API调用:' + apiCallCount + ')');\n });\n }).catch(function(e) {\n console.error('[fdty]', e);\n });\n})();\n"}, "9f5931fc29e45423b8707e9f943921dfd7bb2149": {"t": 1785758759.3000329, "n": 8135, "h": "7204bf4330e4f19e", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/v1/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n\n var panel = doc.getElementById('Panel3');\n if (!panel) return console.error('[fdty] Panel3 not found');\n\n // ============ 提取题目 (DOM 遍历 + 正则兜底) ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n // Method 1: DOM 遍历 — 找所有 group 元素,向前找兄弟文本节点\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue; // 过滤子元素 (radio inputs)\n\n // 向前找包含数字的文本节点\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break; // TEXT_NODE\n node = node.previousSibling;\n }\n if (!node) continue;\n\n // 从文本节点末尾提取 \"题号 . 题目文本\"\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n // Method 2: 正则兜底 — innerHTML 匹配 (处理 DOM 遍历遗漏)\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n console.log('[fdty] ' + ts() + ' 提取 ' + questions.length + ' 题 (DOM: ' + (questions.length > 0 && isGroupId(questions[0].id) ? 'yes' : 'regex') + ')');\n\n // ============ API 调用 ============\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: JSON.stringify({\n model: 'deepseek-v4-flash',\n temperature: temp,\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n })\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status + ': ' + t.slice(0,200)); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (!content) throw new Error('empty response');\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1: 三次随机温度投票 ============\n var temps = [\n 0.5 + Math.random() * 0.4,\n 0.5 + Math.random() * 0.4,\n 0.5 + Math.random() * 0.4\n ];\n console.log('[fdty] ' + ts() + ' Phase 1 三次投票 (temp=' + temps.map(function(t) { return t.toFixed(2); }).join(',') + ')...');\n\n Promise.all([\n callAPI(questions, temps[0]),\n callAPI(questions, temps[1]),\n callAPI(questions, temps[2])\n ]).then(function(results) {\n // 统计每题的 3 票\n var votes = {};\n questions.forEach(function(q) {\n var key = String(q.num);\n votes[key] = results.map(function(r) { return String(r[key] || '').trim(); });\n });\n\n var agreed = [], disputed = [];\n questions.forEach(function(q) {\n var key = String(q.num);\n var v = votes[key];\n if (v[0] && v[0] === v[1] && v[1] === v[2]) {\n agreed.push({ q: q, ans: v[0] });\n } else {\n disputed.push(q);\n }\n });\n\n // 全票一致的直接选\n var totalOk = 0;\n agreed.forEach(function(item) {\n if (selectAnswer(item.q, item.ans)) totalOk++;\n });\n console.log('[fdty] ' + ts() + ' Phase 1 一致 ' + agreed.length + ' 题已选, 分歧 ' + disputed.length + ' 题');\n\n if (disputed.length === 0) {\n console.log('[fdty] ' + ts() + ' 完成 ' + totalOk + '/' + questions.length);\n return;\n }\n\n // ============ Phase 2: 逐题 10 次仲裁 ============\n console.log('[fdty] ' + ts() + ' Phase 2 逐题仲裁 ' + disputed.length + ' 题 (每题 10 次请求)...');\n\n var phase2Jobs = disputed.map(function(q) {\n // 同一题发 10 次请求\n var batch = [];\n for (var i = 0; i < 10; i++) {\n batch.push(callAPI([q], 0.7));\n }\n return Promise.all(batch).then(function(answers) {\n // 统计 10 次投票\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n\n var best = '', max = 0;\n Object.keys(counts).forEach(function(ans) {\n if (counts[ans] > max) { max = counts[ans]; best = ans; }\n });\n\n return { q: q, best: best, count: max, counts: counts };\n });\n });\n\n return Promise.all(phase2Jobs).then(function(results) {\n var unresolved = [];\n\n results.forEach(function(r) {\n if (r.best && r.count >= 8) {\n if (selectAnswer(r.q, r.best)) totalOk++;\n } else {\n unresolved.push(r);\n }\n });\n\n var phase2ok = results.length - unresolved.length;\n console.log('[fdty] ' + ts() + ' Phase 2 确定 ' + phase2ok + ' 题, 剩余 ' + unresolved.length + ' 题');\n\n // ============ Phase 3: 输出无法确定题目 ============\n if (unresolved.length > 0) {\n console.warn('[fdty] ========== 以下 ' + unresolved.length + ' 题无法确定,需人工判断 ==========');\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) {\n parts.push(ans + ':' + r.counts[ans]);\n });\n console.warn(\n ' [' + r.q.num + '] [' + (r.q.type === 'tf' ? '判' : '选') + '] ' +\n r.q.text + '\\n 票数: ' + parts.join(', ')\n );\n });\n console.warn('[fdty] ================================================');\n }\n\n console.log('[fdty] ' + ts() + ' 完成 ' + totalOk + '/' + questions.length +\n ' (一致 ' + agreed.length + ' + 仲裁 ' + phase2ok + ', 未定 ' + unresolved.length + ')');\n });\n }).catch(function(e) {\n console.error('[fdty]', e);\n });\n})();\n"}, "ee741843ae4815e4056204cb5f7d1200ef94a578": {"t": 1785758760.6310122, "n": 8133, "h": "0fc6bcee3ca478cb", "c": "// fdty_top.js - 复旦体育理论考试 AI 自动答题(两阶段投票仲裁)\n// 用法: 考试页面控制台粘贴执行\n(function() {\n var T0 = Date.now();\n function ts() { return ((Date.now() - T0) / 1000).toFixed(1) + 's'; }\n\n var API_KEY = window.__DEEPSEEK_KEY || '';\n var API_URL = 'https://api.deepseek.com/v1/chat/completions';\n\n var SYSTEM_PROMPT = [\n '你是复旦大学体育理论考试专业答题助手。考试内容涵盖运动生理学、体能训练理论及专项运动规则。',\n '',\n '答题原则:',\n '1. 根据运动科学常识和体育规则判断正误,不要凭直觉猜测',\n '2. 注意题目中的绝对化用词(\"完全\"\"绝不\"\"一定\"\"所有\"),此类表述通常为错',\n '3. 涉及具体年份、数据、百分比的事实题,仔细核对自己的知识点',\n '4. 注意否定词和双重否定,避免被题干迷惑',\n '',\n '是非题回答\"对\"或\"错\",单选题回答字母A/B/C/D。',\n '只输出纯JSON,格式: {\"题号\":\"答案\",...},不要markdown包裹,不要解释。'\n ].join('\\n');\n\n // ============ 定位 paper frame ============\n var doc = document;\n try {\n var f = document.getElementById('paper') || document.querySelector('frame[name=\"paper\"]') || window.frames['paper'];\n if (f) doc = f.contentDocument || f.contentWindow.document;\n } catch(e) {}\n\n var panel = doc.getElementById('Panel3');\n if (!panel) return console.error('[fdty] Panel3 not found');\n\n // ============ 提取题目 (DOM 遍历 + 正则兜底) ============\n function isGroupId(id) {\n return /^rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+$/.test(id);\n }\n\n var questions = [], seen = {};\n\n // Method 1: DOM 遍历 — 找所有 group 元素,向前找兄弟文本节点\n var candidates = panel.querySelectorAll('[id^=\"repVer_rbtn_ver_\"], [id^=\"repSin_RadioButtonList1_\"]');\n for (var i = 0; i < candidates.length; i++) {\n var el = candidates[i];\n if (!isGroupId(el.id)) continue; // 过滤子元素 (radio inputs)\n\n // 向前找包含数字的文本节点\n var node = el.previousSibling;\n while (node) {\n if (node.nodeType === 3 && /\\d/.test(node.textContent)) break; // TEXT_NODE\n node = node.previousSibling;\n }\n if (!node) continue;\n\n // 从文本节点末尾提取 \"题号 . 题目文本\"\n var m = node.textContent.match(/(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*$/);\n if (!m) continue;\n\n var n = parseInt(m[1]);\n if (seen[n]) continue;\n seen[n] = true;\n questions.push({\n num: n,\n text: m[2].replace(/\\s+/g, ' ').trim(),\n type: el.id.indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: el.id\n });\n }\n\n // Method 2: 正则兜底 — innerHTML 匹配 (处理 DOM 遍历遗漏)\n if (questions.length === 0) {\n var RE = /(\\d+)\\s*\\.\\s*([\\s\\S]+?)\\s*<\\w+\\s+[^>]*\\bid\\s*=\\s*[\"'](rep(?:Ver_rbtn_ver|Sin_RadioButtonList1)_\\d+)[\"'][^>]*>/gi;\n var rm;\n while ((rm = RE.exec(panel.innerHTML))) {\n var rn = parseInt(rm[1]);\n if (seen[rn]) continue;\n seen[rn] = true;\n questions.push({\n num: rn,\n text: rm[2].replace(/<[^>]+>/g, '').replace(/\\s+/g, ' ').trim(),\n type: rm[3].indexOf('Ver') !== -1 ? 'tf' : 'choice',\n id: rm[3]\n });\n }\n }\n\n questions.sort(function(a, b) { return a.num - b.num; });\n console.log('[fdty] ' + ts() + ' 提取 ' + questions.length + ' 题 (DOM: ' + (questions.length > 0 && isGroupId(questions[0].id) ? 'yes' : 'regex') + ')');\n\n // ============ API 调用 ============\n function buildUserPrompt(qs) {\n return qs.map(function(q) {\n return '[' + q.num + '] (' + (q.type === 'tf' ? '判' : '选') + ') ' + q.text;\n }).join('\\n');\n }\n\n function callAPI(qs, temp) {\n return fetch(API_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + API_KEY },\n body: JSON.stringify({\n model: 'deepseek-v4-pro',\n temperature: temp,\n messages: [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: buildUserPrompt(qs) }\n ]\n })\n }).then(function(r) {\n if (!r.ok) return r.text().then(function(t) { throw new Error('HTTP ' + r.status + ': ' + t.slice(0,200)); });\n return r.json();\n }).then(function(d) {\n var content = (d.choices[0].message.content || '').trim();\n if (!content) throw new Error('empty response');\n if (content.slice(0, 3) === '```') content = content.replace(/^```\\w*\\n?/, '').replace(/\\n?```$/, '');\n try { return JSON.parse(content); } catch(e) { return {}; }\n });\n }\n\n // ============ 选择答案 ============\n function selectAnswer(q, answer) {\n var group = doc.getElementById(q.id);\n if (!group) return false;\n var radios = group.querySelectorAll('input[type=\"radio\"]');\n var idx;\n if (q.type === 'tf') {\n idx = /^(对|正确|是|true|T|yes|1)$/i.test(String(answer).trim()) ? 0 : 1;\n } else {\n idx = {A:0, B:1, C:2, D:3}[String(answer).trim().charAt(0).toUpperCase()];\n }\n if (idx !== undefined && radios[idx]) {\n radios[idx].checked = true;\n radios[idx].click();\n radios[idx].dispatchEvent(new Event('change', { bubbles: true }));\n return true;\n }\n return false;\n }\n\n // ============ Phase 1: 三次随机温度投票 ============\n var temps = [\n 0.5 + Math.random() * 0.4,\n 0.5 + Math.random() * 0.4,\n 0.5 + Math.random() * 0.4\n ];\n console.log('[fdty] ' + ts() + ' Phase 1 三次投票 (temp=' + temps.map(function(t) { return t.toFixed(2); }).join(',') + ')...');\n\n Promise.all([\n callAPI(questions, temps[0]),\n callAPI(questions, temps[1]),\n callAPI(questions, temps[2])\n ]).then(function(results) {\n // 统计每题的 3 票\n var votes = {};\n questions.forEach(function(q) {\n var key = String(q.num);\n votes[key] = results.map(function(r) { return String(r[key] || '').trim(); });\n });\n\n var agreed = [], disputed = [];\n questions.forEach(function(q) {\n var key = String(q.num);\n var v = votes[key];\n if (v[0] && v[0] === v[1] && v[1] === v[2]) {\n agreed.push({ q: q, ans: v[0] });\n } else {\n disputed.push(q);\n }\n });\n\n // 全票一致的直接选\n var totalOk = 0;\n agreed.forEach(function(item) {\n if (selectAnswer(item.q, item.ans)) totalOk++;\n });\n console.log('[fdty] ' + ts() + ' Phase 1 一致 ' + agreed.length + ' 题已选, 分歧 ' + disputed.length + ' 题');\n\n if (disputed.length === 0) {\n console.log('[fdty] ' + ts() + ' 完成 ' + totalOk + '/' + questions.length);\n return;\n }\n\n // ============ Phase 2: 逐题 10 次仲裁 ============\n console.log('[fdty] ' + ts() + ' Phase 2 逐题仲裁 ' + disputed.length + ' 题 (每题 10 次请求)...');\n\n var phase2Jobs = disputed.map(function(q) {\n // 同一题发 10 次请求\n var batch = [];\n for (var i = 0; i < 10; i++) {\n batch.push(callAPI([q], 0.7));\n }\n return Promise.all(batch).then(function(answers) {\n // 统计 10 次投票\n var counts = {};\n answers.forEach(function(a) {\n var ans = String(a[String(q.num)] || '').trim();\n if (ans) counts[ans] = (counts[ans] || 0) + 1;\n });\n\n var best = '', max = 0;\n Object.keys(counts).forEach(function(ans) {\n if (counts[ans] > max) { max = counts[ans]; best = ans; }\n });\n\n return { q: q, best: best, count: max, counts: counts };\n });\n });\n\n return Promise.all(phase2Jobs).then(function(results) {\n var unresolved = [];\n\n results.forEach(function(r) {\n if (r.best && r.count >= 8) {\n if (selectAnswer(r.q, r.best)) totalOk++;\n } else {\n unresolved.push(r);\n }\n });\n\n var phase2ok = results.length - unresolved.length;\n console.log('[fdty] ' + ts() + ' Phase 2 确定 ' + phase2ok + ' 题, 剩余 ' + unresolved.length + ' 题');\n\n // ============ Phase 3: 输出无法确定题目 ============\n if (unresolved.length > 0) {\n console.warn('[fdty] ========== 以下 ' + unresolved.length + ' 题无法确定,需人工判断 ==========');\n unresolved.forEach(function(r) {\n var parts = [];\n Object.keys(r.counts).sort().forEach(function(ans) {\n parts.push(ans + ':' + r.counts[ans]);\n });\n console.warn(\n ' [' + r.q.num + '] [' + (r.q.type === 'tf' ? '判' : '选') + '] ' +\n r.q.text + '\\n 票数: ' + parts.join(', ')\n );\n });\n console.warn('[fdty] ================================================');\n }\n\n console.log('[fdty] ' + ts() + ' 完成 ' + totalOk + '/' + questions.length +\n ' (一致 ' + agreed.length + ' + 仲裁 ' + phase2ok + ', 未定 ' + unresolved.length + ')');\n });\n }).catch(function(e) {\n console.error('[fdty]', e);\n });\n})();\n"}}}}