Phase 2A: WorkflowPlannerService, WorkflowExecutorService, Python data quality, 6 bug fixes, DescriptiveResultView, multi-step R code/Word export, MVP UI reuse. V11 UI: Gemini-style, multi-task, single-page scroll, Word export. Architecture: Block-based rendering consensus (4 block types). New R tools: chi_square, correlation, descriptive, logistic_binary, mann_whitney, t_test_paired. Docs: dev summary, block-based plan, status updates, task list v2.0. Co-authored-by: Cursor <cursoragent@cursor.com>
546 lines
36 KiB
HTML
546 lines
36 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>SSA-Pro 智能统计工作台 V12.0 (Agentic Pipeline)</title>
|
||
<script src="https://cdn.tailwindcss.com"></script>
|
||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||
|
||
<style>
|
||
body { font-family: 'Inter', sans-serif; background-color: #f8fafc; color: #334155; overflow: hidden; }
|
||
::-webkit-scrollbar { width: 6px; }
|
||
::-webkit-scrollbar-track { background: transparent; }
|
||
::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 3px; }
|
||
::-webkit-scrollbar-thumb:hover { background: #94a3b8; }
|
||
|
||
.fade-in { animation: fadeIn 0.4s ease-out forwards; opacity: 0; }
|
||
.slide-up { animation: slideUp 0.4s cubic-bezier(0.16, 1, 0.3, 1) forwards; opacity: 0; transform: translateY(15px); }
|
||
.pop-in { animation: popIn 0.3s cubic-bezier(0.16, 1, 0.3, 1) forwards; opacity: 0; transform: scale(0.95); }
|
||
|
||
@keyframes fadeIn { to { opacity: 1; } }
|
||
@keyframes slideUp { to { transform: translateY(0); opacity: 1; } }
|
||
@keyframes popIn { to { transform: scale(1); opacity: 1; } }
|
||
|
||
/* 科学表格样式 (V9 风格) */
|
||
.sci-table { width: 100%; border-collapse: collapse; font-size: 0.875rem; }
|
||
.sci-table th { border-top: 2px solid #1e293b; border-bottom: 1px solid #1e293b; padding: 10px 12px; text-align: left; font-weight: 600; color: #1e293b; }
|
||
.sci-table td { border-bottom: 1px solid #e2e8f0; padding: 10px 12px; color: #475569; }
|
||
.sci-table tr:last-child td { border-bottom: 2px solid #1e293b; }
|
||
|
||
.status-dot { height: 8px; width: 8px; border-radius: 50%; display: inline-block; margin-right: 6px; }
|
||
.status-success { background-color: #22c55e; box-shadow: 0 0 0 2px #dcfce7; }
|
||
|
||
pre code { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-size: 0.85rem; line-height: 1.5; }
|
||
|
||
.artifact-shadow { box-shadow: 0 4px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1); }
|
||
|
||
/* Agent 工作流连线 */
|
||
.agent-line { position: absolute; left: 11px; top: 24px; bottom: -12px; width: 2px; background-color: #e2e8f0; z-index: 0; }
|
||
.agent-step:last-child .agent-line { display: none; }
|
||
|
||
/* 物理垫片:彻底解决底部输入框遮挡问题 */
|
||
.scroll-spacer { height: 220px; width: 100%; flex-shrink: 0; }
|
||
</style>
|
||
</head>
|
||
<body class="h-screen flex text-slate-800">
|
||
|
||
<!-- 全局 Toast 容器 -->
|
||
<div id="toast-container" class="fixed top-6 left-1/2 transform -translate-x-1/2 z-50 flex flex-col gap-2 pointer-events-none"></div>
|
||
|
||
<!-- 左侧:抽屉侧边栏 (V11/V10 风格) -->
|
||
<aside id="sidebar" class="w-16 bg-slate-50 border-r border-slate-200 flex flex-col z-30 flex-shrink-0 transition-all duration-300 ease-in-out relative">
|
||
<div class="h-16 flex items-center px-3 flex-shrink-0 overflow-hidden whitespace-nowrap border-b border-slate-200 bg-white">
|
||
<button onclick="toggleSidebar()" class="w-10 h-10 rounded-lg hover:bg-slate-200 flex items-center justify-center text-slate-600 transition flex-shrink-0">
|
||
<i class="fa-solid fa-bars text-lg"></i>
|
||
</button>
|
||
<div class="sidebar-text hidden ml-3 flex items-center gap-2 fade-in">
|
||
<div class="w-7 h-7 bg-slate-800 rounded flex items-center justify-center text-white shadow-sm">
|
||
<i class="fa-solid fa-brain text-xs"></i>
|
||
</div>
|
||
<span class="font-bold text-slate-800 tracking-tight text-lg">SSA-V12</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="px-3 mt-4 flex flex-col gap-3">
|
||
<button id="new-chat-btn" class="w-10 h-10 bg-white border border-slate-200 hover:border-blue-300 shadow-sm rounded-lg flex items-center justify-center text-slate-700 transition-all duration-300 overflow-hidden whitespace-nowrap group">
|
||
<i class="fa-solid fa-plus text-blue-600"></i>
|
||
<span class="sidebar-text hidden ml-3 font-medium text-sm text-slate-700 group-hover:text-blue-600 transition">新建智能分析</span>
|
||
</button>
|
||
<button id="history-icon-only" onclick="toggleSidebar()" class="w-10 h-10 rounded-lg hover:bg-slate-200 flex items-center justify-center text-slate-500 transition-all duration-300" title="查看历史记录">
|
||
<i class="fa-solid fa-clock-rotate-left"></i>
|
||
</button>
|
||
</div>
|
||
|
||
<div id="history-list" class="flex-1 overflow-y-auto px-3 mt-6 hidden opacity-0 transition-opacity duration-300 pb-4">
|
||
<div class="px-2 py-1 text-[10px] font-bold text-slate-400 uppercase tracking-wider mb-2">近期分析</div>
|
||
<div class="space-y-1">
|
||
<button class="w-full text-left px-3 py-2.5 rounded-lg bg-white border border-slate-200 text-blue-700 font-medium text-sm truncate flex items-center gap-3 shadow-sm">
|
||
<i class="fa-solid fa-network-wired text-blue-500"></i> 血压疗效研究
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</aside>
|
||
|
||
<!-- 动态主容器 -->
|
||
<div class="flex-1 flex overflow-hidden relative bg-white" id="main-container">
|
||
|
||
<!-- 左半部分:沉浸式居中对话区 -->
|
||
<section id="chat-pane" class="flex-1 flex flex-col h-full transition-all duration-500 ease-in-out relative bg-[#f8fafc]">
|
||
|
||
<!-- Chat Header -->
|
||
<header class="h-16 flex items-center justify-between px-6 z-10 border-b border-slate-100 bg-white/90 backdrop-blur flex-shrink-0 absolute top-0 left-0 right-0">
|
||
<div class="font-semibold text-slate-800 text-lg flex items-center gap-2">
|
||
血压下降疗效分析
|
||
<span class="text-[10px] bg-slate-100 text-slate-500 px-2 py-0.5 rounded border border-slate-200 uppercase tracking-wide">Multi-Agent</span>
|
||
</div>
|
||
<div class="flex items-center gap-2 text-xs font-medium text-slate-500 bg-slate-50 px-3 py-1.5 rounded-full border border-slate-100 shadow-sm">
|
||
<span class="status-dot status-success animate-pulse"></span> Pipeline Ready
|
||
</div>
|
||
</header>
|
||
|
||
<!-- Chat Messages (包含物理垫片) -->
|
||
<div id="chat-container" class="flex-1 overflow-y-auto px-4 md:px-8 pt-24 flex flex-col items-center">
|
||
<div class="w-full max-w-3xl space-y-8 min-h-full">
|
||
|
||
<!-- 欢迎语 -->
|
||
<div class="flex gap-4 slide-up">
|
||
<div class="w-8 h-8 rounded-full bg-slate-800 flex items-center justify-center flex-shrink-0 mt-0.5"><i class="fa-solid fa-robot text-white text-xs"></i></div>
|
||
<div class="bg-white text-slate-700 p-4 rounded-2xl rounded-tl-none text-sm leading-relaxed border border-slate-200 shadow-sm">
|
||
你好!我是 <b>SSA-Pro 智能流水线</b>。<br>我由「意图理解」、「数据诊断」、「路径规划」等多个专家智能体组成。请点击下方 📎 <b>上传数据文件</b> 并描述需求,我们将协同为您服务。
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 会话内容锚点 -->
|
||
<div id="chat-anchor"></div>
|
||
|
||
<!-- 终极武器:物理垫片,确保滚动到底部时内容不会被悬浮输入框遮挡 -->
|
||
<div class="scroll-spacer"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Chat Input (悬浮输入框,已修复透明墙遮挡问题) -->
|
||
<!-- 修复点:外层 pointer-events-none,内层 pointer-events-auto -->
|
||
<div class="flex-shrink-0 w-full flex justify-center pb-6 px-4 absolute bottom-0 bg-gradient-to-t from-[#f8fafc] via-[#f8fafc] to-transparent pt-16 z-20 pointer-events-none">
|
||
<div class="w-full max-w-3xl relative pointer-events-auto">
|
||
|
||
<!-- 包裹挂载区和输入框的容器 -->
|
||
<div class="shadow-lg rounded-2xl bg-white border border-slate-200 focus-within:border-blue-400 focus-within:ring-4 focus-within:ring-blue-50 transition-all flex flex-col p-2">
|
||
|
||
<!-- 数据挂载区 (嵌入式) -->
|
||
<div id="data-mount-zone" class="hidden items-center mb-1 pl-1 pop-in w-full">
|
||
<div class="bg-slate-50 border border-slate-200 rounded-lg px-2 py-1.5 flex items-center gap-3 w-max transition-all hover:border-blue-300">
|
||
<div class="w-8 h-8 rounded bg-green-50 flex items-center justify-center border border-green-100">
|
||
<i class="fa-solid fa-file-csv text-green-600 text-sm"></i>
|
||
</div>
|
||
<div class="flex flex-col pr-2">
|
||
<span class="text-xs font-bold text-slate-700">BP_Trial_Data.csv</span>
|
||
</div>
|
||
<div class="h-6 w-px bg-slate-200 mx-1"></div>
|
||
<button onclick="removeData(event)" class="text-slate-400 hover:text-red-500 transition px-1" title="移除数据">
|
||
<i class="fa-solid fa-xmark text-sm"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 文本输入行 -->
|
||
<div class="relative flex items-end w-full">
|
||
<button onclick="simulateAgentPipeline()" id="btn-upload" class="absolute left-1 bottom-1 text-slate-400 hover:text-blue-600 transition w-9 h-9 flex items-center justify-center rounded-lg hover:bg-slate-100 z-10" title="上传数据">
|
||
<i class="fa-solid fa-paperclip text-lg"></i>
|
||
</button>
|
||
|
||
<textarea id="user-input" class="w-full bg-transparent py-2.5 pl-11 pr-12 focus:outline-none resize-none text-sm text-slate-700 placeholder-slate-400" rows="1" placeholder="发送消息,或点击回形针 📎 启动多智能体协作流..." style="min-height: 44px;"></textarea>
|
||
|
||
<button onclick="simulateAgentPipeline()" class="absolute right-1 bottom-1 w-9 h-9 bg-slate-800 text-white rounded-lg hover:bg-slate-700 transition flex items-center justify-center shadow-sm z-10">
|
||
<i class="fa-solid fa-arrow-up text-sm"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- 右半部分:动态工作区 (Workspace Pane) -->
|
||
<section id="workspace-pane" class="w-0 overflow-hidden bg-slate-50 transition-all duration-500 ease-in-out flex flex-col flex-shrink-0 border-l border-transparent relative">
|
||
|
||
<div class="w-full min-w-[600px] h-full flex flex-col absolute inset-0">
|
||
|
||
<!-- 顶部工具栏 -->
|
||
<header class="h-14 flex items-center justify-between px-4 border-b border-slate-200 bg-slate-100/50 flex-shrink-0">
|
||
<div class="flex items-center gap-2">
|
||
<span id="workspace-badge" class="px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-wider bg-purple-100 text-purple-700 border border-purple-200">
|
||
Execution
|
||
</span>
|
||
<span id="workspace-title" class="text-sm font-semibold text-slate-700">流程执行器 (Pipeline Executor)</span>
|
||
</div>
|
||
<div class="flex gap-2">
|
||
<button onclick="closeWorkspace()" class="w-8 h-8 bg-white border border-slate-200 text-slate-500 rounded-lg hover:bg-slate-100 hover:text-slate-800 transition shadow-sm flex items-center justify-center" title="收起工作区">
|
||
<i class="fa-solid fa-right-to-bracket"></i>
|
||
</button>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- 工作区主画布 -->
|
||
<div class="flex-1 p-4 md:p-6 overflow-y-auto">
|
||
<div class="bg-white rounded-2xl artifact-shadow border border-slate-200/60 min-h-full flex flex-col relative overflow-hidden">
|
||
|
||
<!-- 视图:执行与结论生成 (Executor & Critic) -->
|
||
<div id="view-execution" class="p-8 w-full flex flex-col h-full relative">
|
||
|
||
<!-- 执行引擎头部 -->
|
||
<div class="flex items-center justify-between mb-6 pb-4 border-b border-slate-100">
|
||
<div class="flex items-center gap-3">
|
||
<div class="w-10 h-10 bg-slate-900 rounded-lg flex items-center justify-center text-white"><i class="fa-solid fa-gears"></i></div>
|
||
<div>
|
||
<h2 class="text-lg font-bold text-slate-800">R 编排引擎运行中</h2>
|
||
<p class="text-xs text-slate-500" id="exec-subtitle">正在按 SAP 计划逐步执行统计检验...</p>
|
||
</div>
|
||
</div>
|
||
<div id="exec-spinner" class="text-blue-600 text-2xl"><i class="fa-solid fa-circle-notch fa-spin"></i></div>
|
||
</div>
|
||
|
||
<!-- 步骤追踪区 -->
|
||
<div class="flex-1">
|
||
<div class="bg-slate-50 rounded-xl p-6 border border-slate-200 font-mono text-sm text-slate-600 relative overflow-hidden min-h-[250px]">
|
||
<div class="absolute left-[39px] top-8 bottom-8 w-px bg-slate-300"></div>
|
||
<div id="trace-logs" class="space-y-6 relative z-10">
|
||
<!-- 日志动态插入 -->
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 结论生成器区域 (初始隐藏) -->
|
||
<div id="critic-section" class="mt-6 pt-6 border-t border-slate-100 hidden">
|
||
<div class="flex items-center gap-2 mb-4 text-purple-700 font-bold">
|
||
<i class="fa-solid fa-pen-nib"></i> 结论生成器 (Critic Agent) 工作区
|
||
</div>
|
||
|
||
<!-- 综合报告骨架屏/最终结果 -->
|
||
<div class="bg-gradient-to-r from-purple-50 to-blue-50 border border-purple-100 p-5 rounded-xl shadow-sm">
|
||
<div id="report-typing" class="text-sm text-slate-700 leading-relaxed">
|
||
<i class="fa-solid fa-pen text-purple-400 mr-2 animate-bounce"></i> 正在根据 R 执行结果 (P=0.002) 和临床解读模板合成综合报告...
|
||
</div>
|
||
<div id="report-final" class="hidden">
|
||
<h4 class="font-bold text-slate-900 mb-2">📄 综合统计分析报告</h4>
|
||
<p class="text-sm text-slate-700 mb-4 leading-relaxed">
|
||
本研究纳入了 120 例样本。由于数据未能通过 Shapiro-Wilk 正态性检验(P < 0.05),采用非参数的 Wilcoxon 秩和检验进行差异推断。
|
||
结果显示,新药组的血压下降幅度(中位数 14.5)显著高于对照组(中位数 8.2),差异具有统计学意义 (<span class="text-red-600 font-bold bg-red-50 px-1 rounded">P = 0.002</span>)。
|
||
</p>
|
||
|
||
<!-- 三线表 -->
|
||
<table class="sci-table bg-white border border-slate-200 rounded overflow-hidden">
|
||
<thead>
|
||
<tr class="bg-slate-50">
|
||
<th>组别</th>
|
||
<th>N</th>
|
||
<th>下降值 (Median [IQR])</th>
|
||
<th>W 统计量</th>
|
||
<th>P 值</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>新药组</td>
|
||
<td>60</td>
|
||
<td>14.5 [12.1 - 16.8]</td>
|
||
<td rowspan="2" class="align-middle border-l border-slate-100">2845.5</td>
|
||
<td rowspan="2" class="align-middle border-l border-slate-100 text-red-600 font-bold">< 0.01 **</td>
|
||
</tr>
|
||
<tr>
|
||
<td>对照组</td>
|
||
<td>60</td>
|
||
<td>8.2 [6.5 - 10.4]</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<div class="mt-4 flex gap-2">
|
||
<button class="px-3 py-1.5 bg-white border border-slate-200 text-slate-600 text-xs font-medium rounded hover:bg-slate-50 transition shadow-sm"><i class="fa-solid fa-chart-bar text-blue-500 mr-1"></i> 查看分布图</button>
|
||
<button class="px-3 py-1.5 bg-slate-800 text-white text-xs font-medium rounded hover:bg-slate-700 transition shadow-sm"><i class="fa-solid fa-download mr-1"></i> 下载完整报告</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
</div>
|
||
|
||
<script>
|
||
const chatAnchor = document.getElementById('chat-anchor');
|
||
const workspacePane = document.getElementById('workspace-pane');
|
||
|
||
// 侧边栏切换
|
||
let isSidebarExpanded = false;
|
||
function toggleSidebar() {
|
||
const sidebar = document.getElementById('sidebar');
|
||
const texts = document.querySelectorAll('.sidebar-text');
|
||
const historyList = document.getElementById('history-list');
|
||
const newChatBtn = document.getElementById('new-chat-btn');
|
||
const historyIconBtn = document.getElementById('history-icon-only');
|
||
|
||
isSidebarExpanded = !isSidebarExpanded;
|
||
if(isSidebarExpanded) {
|
||
sidebar.classList.replace('w-16', 'w-64');
|
||
texts.forEach(el => el.classList.remove('hidden'));
|
||
newChatBtn.classList.replace('w-10', 'w-full');
|
||
newChatBtn.classList.replace('justify-center', 'justify-start');
|
||
newChatBtn.classList.add('px-3');
|
||
historyIconBtn.classList.add('hidden');
|
||
historyList.classList.remove('hidden');
|
||
setTimeout(() => historyList.classList.replace('opacity-0', 'opacity-100'), 150);
|
||
} else {
|
||
sidebar.classList.replace('w-64', 'w-16');
|
||
texts.forEach(el => el.classList.add('hidden'));
|
||
newChatBtn.classList.replace('w-full', 'w-10');
|
||
newChatBtn.classList.replace('justify-start', 'justify-center');
|
||
newChatBtn.classList.remove('px-3');
|
||
historyList.classList.add('hidden');
|
||
historyList.classList.replace('opacity-100', 'opacity-0');
|
||
historyIconBtn.classList.remove('hidden');
|
||
}
|
||
}
|
||
|
||
// 工作区拉出与收起
|
||
function openWorkspace() {
|
||
workspacePane.classList.remove('w-0', 'border-transparent');
|
||
workspacePane.classList.add('w-[60%]', 'border-slate-200');
|
||
setTimeout(scrollToBottom, 300);
|
||
}
|
||
|
||
function closeWorkspace() {
|
||
workspacePane.classList.remove('w-[60%]', 'border-slate-200');
|
||
workspacePane.classList.add('w-0', 'border-transparent');
|
||
}
|
||
|
||
// ==========================================
|
||
// 核心流程:多智能体协作 Pipeline
|
||
// ==========================================
|
||
function simulateAgentPipeline() {
|
||
// 0. UI 预处理
|
||
const mountZone = document.getElementById('data-mount-zone');
|
||
mountZone.classList.remove('hidden');
|
||
mountZone.classList.add('flex');
|
||
document.getElementById('btn-upload').classList.add('opacity-50', 'pointer-events-none');
|
||
document.getElementById('user-input').value = "比较新药组和对照组的血压下降值。";
|
||
|
||
// 插入用户的对话
|
||
const userMsg = `
|
||
<div class="flex gap-4 flex-row-reverse slide-up w-full">
|
||
<div class="w-8 h-8 rounded-full bg-slate-200 flex items-center justify-center flex-shrink-0 mt-0.5"><i class="fa-solid fa-user text-slate-500 text-xs"></i></div>
|
||
<div class="bg-blue-600 text-white p-4 rounded-2xl rounded-tr-none text-sm shadow-md">比较新药组和对照组的血压下降值。</div>
|
||
</div>`;
|
||
chatAnchor.insertAdjacentHTML('beforebegin', userMsg);
|
||
scrollToBottom();
|
||
|
||
// 1. 生成 Agent 网络工作流容器
|
||
setTimeout(() => {
|
||
const agentContainerId = 'agent-flow-' + Date.now();
|
||
const agentFlowHTML = `
|
||
<div class="flex gap-4 slide-up w-full" id="${agentContainerId}">
|
||
<div class="w-8 h-8 rounded-full bg-slate-800 flex items-center justify-center flex-shrink-0 mt-0.5"><i class="fa-solid fa-network-wired text-white text-xs"></i></div>
|
||
<div class="w-full max-w-2xl bg-white border border-slate-200 p-5 rounded-2xl rounded-tl-none shadow-sm">
|
||
<div class="text-xs font-bold text-slate-400 uppercase tracking-wider mb-5 flex items-center gap-2">
|
||
<i class="fa-solid fa-microchip"></i> Agent Pipeline 启动
|
||
</div>
|
||
<div class="relative space-y-5" id="${agentContainerId}-steps">
|
||
<!-- 动态插入各个 Agent 节点 -->
|
||
</div>
|
||
</div>
|
||
</div>`;
|
||
chatAnchor.insertAdjacentHTML('beforebegin', agentFlowHTML);
|
||
scrollToBottom();
|
||
|
||
const stepsBox = document.getElementById(`${agentContainerId}-steps`);
|
||
|
||
// Step 1: 意图理解器
|
||
setTimeout(() => {
|
||
const step1 = `
|
||
<div class="agent-step relative pl-8 slide-up">
|
||
<div class="agent-line"></div>
|
||
<div class="absolute left-0 top-0.5 w-6 h-6 rounded-full bg-blue-100 text-blue-600 flex items-center justify-center z-10"><i class="fa-solid fa-brain text-[10px]"></i></div>
|
||
<div class="text-sm font-bold text-slate-800 mb-1">意图理解器 (Intent Parser) <span class="text-xs font-normal text-slate-400 ml-2">LLM + 分类</span></div>
|
||
<div class="bg-slate-50 border border-slate-100 p-2.5 rounded-lg flex gap-2 text-xs text-slate-600">
|
||
<span class="bg-white px-2 py-0.5 rounded border border-slate-200">Goal: <b class="text-blue-600">差异比较</b></span>
|
||
<span class="bg-white px-2 py-0.5 rounded border border-slate-200">X: <b class="text-slate-800">Group</b></span>
|
||
<span class="bg-white px-2 py-0.5 rounded border border-slate-200">Y: <b class="text-slate-800">BP_Change</b></span>
|
||
<span class="bg-white px-2 py-0.5 rounded border border-slate-200">Design: <b class="text-slate-800">独立两组</b></span>
|
||
</div>
|
||
</div>`;
|
||
stepsBox.insertAdjacentHTML('beforeend', step1);
|
||
scrollToBottom();
|
||
}, 800);
|
||
|
||
// Step 2: 数据诊断器
|
||
setTimeout(() => {
|
||
const step2 = `
|
||
<div class="agent-step relative pl-8 slide-up">
|
||
<div class="agent-line"></div>
|
||
<div class="absolute left-0 top-0.5 w-6 h-6 rounded-full bg-green-100 text-green-600 flex items-center justify-center z-10"><i class="fa-solid fa-stethoscope text-[10px]"></i></div>
|
||
<div class="text-sm font-bold text-slate-800 mb-1">数据诊断器 (Data Profiler) <span class="text-xs font-normal text-slate-400 ml-2">R 统计检验</span></div>
|
||
<div class="bg-slate-50 border border-slate-100 p-2.5 rounded-lg text-xs text-slate-600 space-y-1">
|
||
<div class="flex justify-between"><span>样本量: N=120</span> <span class="text-green-600"><i class="fa-solid fa-check"></i> 无缺失值</span></div>
|
||
<div class="flex justify-between"><span>Group: 分类 (2 Levels)</span> <span>BP_Change: 连续数值</span></div>
|
||
</div>
|
||
</div>`;
|
||
stepsBox.insertAdjacentHTML('beforeend', step2);
|
||
scrollToBottom();
|
||
}, 2000);
|
||
|
||
// Step 3: 路径规划器 & 最终 SAP 卡片
|
||
setTimeout(() => {
|
||
const step3 = `
|
||
<div class="agent-step relative pl-8 slide-up">
|
||
<div class="absolute left-0 top-0.5 w-6 h-6 rounded-full bg-purple-100 text-purple-600 flex items-center justify-center z-10"><i class="fa-solid fa-route text-[10px]"></i></div>
|
||
<div class="text-sm font-bold text-slate-800 mb-2">路径规划器 (Planner) <span class="text-xs font-normal text-slate-400 ml-2">决策表匹配</span></div>
|
||
|
||
<!-- SAP 卡片:展示 workflow_steps[] -->
|
||
<div class="bg-white border border-purple-200 rounded-xl overflow-hidden shadow-sm">
|
||
<div class="bg-purple-50 px-3 py-2 border-b border-purple-100 text-xs font-bold text-purple-800 flex items-center gap-2">
|
||
<i class="fa-solid fa-clipboard-list"></i> 统计分析计划书 (SAP Workflow)
|
||
</div>
|
||
<div class="p-4 space-y-3">
|
||
<div class="flex gap-2 items-start text-sm">
|
||
<div class="w-5 h-5 rounded bg-slate-100 text-slate-500 flex items-center justify-center text-xs font-bold shrink-0">1</div>
|
||
<div><span class="font-semibold text-slate-700">数据核验:</span> 诊断缺失值与异常分布。</div>
|
||
</div>
|
||
<div class="flex gap-2 items-start text-sm">
|
||
<div class="w-5 h-5 rounded bg-slate-100 text-slate-500 flex items-center justify-center text-xs font-bold shrink-0">2</div>
|
||
<div><span class="font-semibold text-slate-700">统计护栏:</span> 执行 Shapiro-Wilk 正态性检验。</div>
|
||
</div>
|
||
<div class="flex gap-2 items-start text-sm bg-blue-50 p-2 rounded border border-blue-100">
|
||
<div class="w-5 h-5 rounded bg-blue-600 text-white flex items-center justify-center text-xs font-bold shrink-0">3</div>
|
||
<div>
|
||
<span class="font-semibold text-blue-800">核心推断:</span> 独立样本 T 检验。<br>
|
||
<span class="text-xs text-blue-600">※ 降级策略:若正态性检验 P<0.05,则使用 Wilcoxon 秩和检验。</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="p-3 bg-slate-50 border-t border-slate-100 flex justify-end">
|
||
<button onclick="runExecutorPipeline(this)" class="bg-purple-600 text-white px-4 py-2 rounded-lg text-sm font-medium hover:bg-purple-700 transition flex items-center gap-2 shadow-sm">
|
||
<i class="fa-solid fa-play text-xs"></i> 确认并执行此流水线
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
</div>`;
|
||
stepsBox.insertAdjacentHTML('beforeend', step3);
|
||
scrollToBottom();
|
||
}, 3500);
|
||
|
||
}, 500);
|
||
}
|
||
|
||
// ==========================================
|
||
// 阶段 4 & 5: 执行器与结论生成
|
||
// ==========================================
|
||
function runExecutorPipeline(btn) {
|
||
// 左侧按钮状态变更
|
||
btn.innerHTML = '<i class="fa-solid fa-circle-notch fa-spin"></i> 调度引擎中...';
|
||
btn.classList.replace('bg-purple-600', 'bg-slate-400');
|
||
btn.classList.replace('hover:bg-purple-700', 'hover:bg-slate-400');
|
||
btn.disabled = true;
|
||
|
||
// 展开右侧工作区
|
||
openWorkspace();
|
||
|
||
// 模拟 Executor 的 step_results[] 动态输出
|
||
const logBox = document.getElementById('trace-logs');
|
||
logBox.innerHTML = '';
|
||
|
||
setTimeout(() => { addTraceLog(logBox, 'text-slate-400', 'fa-database', '[Step 1] 读取并加载预处理数据...', 'done'); }, 600);
|
||
setTimeout(() => { addTraceLog(logBox, 'text-blue-500', 'fa-shield', '[Step 2] 护栏: 执行 Shapiro-Wilk 检验...', 'spin'); }, 1500);
|
||
setTimeout(() => {
|
||
logBox.lastElementChild.querySelector('.fa-spin').classList.replace('fa-spinner', 'fa-times-circle');
|
||
logBox.lastElementChild.querySelector('.fa-spin').classList.remove('fa-spin');
|
||
logBox.lastElementChild.querySelector('.fa-times-circle').classList.replace('text-blue-500', 'text-red-500');
|
||
logBox.lastElementChild.querySelector('.log-text').innerHTML = '[Step 2] 护栏: 正态性检验未通过 <span class="bg-red-100 text-red-600 px-1 rounded ml-1 font-bold">P=0.002</span>';
|
||
|
||
addTraceLog(logBox, 'text-amber-500', 'fa-code-branch', '[Router] 触发流程编排降级机制: 指向 Wilcoxon 方法', 'done');
|
||
}, 2800);
|
||
|
||
setTimeout(() => { addTraceLog(logBox, 'text-blue-500', 'fa-calculator', '[Step 3] 核心: 运行 wilcox.test()...', 'spin'); }, 3800);
|
||
|
||
setTimeout(() => {
|
||
logBox.lastElementChild.querySelector('.fa-spin').classList.replace('fa-spinner', 'fa-check-circle');
|
||
logBox.lastElementChild.querySelector('.fa-spin').classList.remove('fa-spin');
|
||
logBox.lastElementChild.querySelector('.fa-check-circle').classList.replace('text-blue-500', 'text-green-500');
|
||
logBox.lastElementChild.querySelector('.log-text').innerHTML = '[Step 3] 核心: wilcox.test() 计算成功 (W=2845.5, P<0.001)';
|
||
|
||
document.getElementById('exec-spinner').innerHTML = '<i class="fa-solid fa-check-circle text-green-500"></i>';
|
||
document.getElementById('exec-subtitle').innerText = "编排引擎执行完毕,等待结论生成...";
|
||
|
||
// 启动结论生成器 (Step 5)
|
||
startCriticAgent(btn);
|
||
}, 5000);
|
||
}
|
||
|
||
function addTraceLog(container, colorClass, iconClass, text, state) {
|
||
const div = document.createElement('div');
|
||
div.className = 'flex items-center gap-4 fade-in bg-white p-2 rounded-lg border border-slate-100 shadow-sm';
|
||
|
||
let iconState = state === 'spin' ? 'fa-spinner fa-spin' : iconClass;
|
||
|
||
div.innerHTML = `
|
||
<div class="w-6 h-6 rounded-full bg-slate-50 flex items-center justify-center z-10 border border-slate-200">
|
||
<i class="fa-solid ${iconState} ${colorClass} text-[10px]"></i>
|
||
</div>
|
||
<span class="log-text font-medium text-slate-700">${text}</span>
|
||
`;
|
||
container.appendChild(div);
|
||
}
|
||
|
||
function startCriticAgent(btn) {
|
||
const criticSection = document.getElementById('critic-section');
|
||
criticSection.classList.remove('hidden');
|
||
criticSection.classList.add('fade-in');
|
||
|
||
// 模拟 LLM 思考生成的时间
|
||
setTimeout(() => {
|
||
document.getElementById('report-typing').classList.add('hidden');
|
||
document.getElementById('report-final').classList.remove('hidden');
|
||
document.getElementById('report-final').classList.add('fade-in');
|
||
|
||
// 左侧对话区反馈完成
|
||
btn.innerHTML = '<i class="fa-solid fa-check"></i> 流程已闭环';
|
||
btn.classList.replace('bg-slate-400', 'bg-green-500');
|
||
btn.classList.replace('hover:bg-slate-400', 'hover:bg-green-600');
|
||
|
||
const finalMsg = `
|
||
<div class="flex gap-4 slide-up w-full mt-4 border-t border-slate-100 pt-6">
|
||
<div class="w-8 h-8 rounded-full bg-slate-800 flex items-center justify-center flex-shrink-0 mt-0.5"><i class="fa-solid fa-check-double text-white text-xs"></i></div>
|
||
<div class="bg-green-50 border border-green-200 text-green-800 p-4 rounded-2xl rounded-tl-none shadow-sm text-sm w-full">
|
||
<b>综合报告已生成!</b><br>
|
||
5 大智能体协作完毕。请在右侧工作区查看包含方法学说明的完整论文级报告。
|
||
</div>
|
||
</div>`;
|
||
chatAnchor.insertAdjacentHTML('beforebegin', finalMsg);
|
||
scrollToBottom();
|
||
|
||
// 更新右侧 Badge
|
||
document.getElementById('workspace-badge').innerText = "Completed";
|
||
document.getElementById('workspace-badge').className = "px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-wider bg-green-100 text-green-700 border border-green-200";
|
||
}, 2500);
|
||
}
|
||
|
||
function removeData(e) {
|
||
e.stopPropagation();
|
||
document.getElementById('data-mount-zone').classList.add('hidden');
|
||
document.getElementById('data-mount-zone').classList.remove('flex');
|
||
document.getElementById('btn-upload').classList.remove('opacity-50', 'pointer-events-none');
|
||
}
|
||
|
||
function scrollToBottom() {
|
||
setTimeout(() => {
|
||
const c = document.getElementById('chat-container');
|
||
c.scrollTo({ top: c.scrollHeight, behavior: 'smooth' });
|
||
}, 50);
|
||
}
|
||
</script>
|
||
</body>
|
||
</html> |