Files
HaHafeng 96290d2f76 feat(aia): Implement Protocol Agent MVP with reusable Agent framework
Sprint 1-3 Completed (Backend + Frontend):

Backend (Sprint 1-2):
- Implement 5-layer Agent framework (Query->Planner->Executor->Tools->Reflection)
- Create agent_schema with 6 tables (agent_definitions, stages, prompts, sessions, traces, reflexion_rules)
- Create protocol_schema with 2 tables (protocol_contexts, protocol_generations)
- Implement Protocol Agent core services (Orchestrator, ContextService, PromptBuilder)
- Integrate LLM service adapter (DeepSeek/Qwen/GPT-5/Claude)
- 6 API endpoints with full authentication
- 10/10 API tests passed

Frontend (Sprint 3):
- Add Protocol Agent entry in AgentHub (indigo theme card)
- Implement ProtocolAgentPage with 3-column layout
- Collapsible sidebar (Gemini style, 48px <-> 280px)
- StatePanel with 5 stage cards (scientific_question, pico, study_design, sample_size, endpoints)
- ChatArea with sync button and action cards integration
- 100% prototype design restoration (608 lines CSS)
- Detailed endpoints structure: baseline, exposure, outcomes, confounders

Features:
- 5-stage dialogue flow for research protocol design
- Conversation-driven interaction with sync-to-protocol button
- Real-time context state management
- One-click protocol generation button (UI ready, backend pending)

Database:
- agent_schema: 6 tables for reusable Agent framework
- protocol_schema: 2 tables for Protocol Agent
- Seed data: 1 agent + 5 stages + 9 prompts + 4 reflexion rules

Code Stats:
- Backend: 13 files, 4338 lines
- Frontend: 14 files, 2071 lines
- Total: 27 files, 6409 lines

Status: MVP core functionality completed, pending frontend-backend integration testing

Next: Sprint 4 - One-click protocol generation + Word export
2026-01-24 17:29:24 +08:00

381 lines
26 KiB
HTML
Raw Permalink 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.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AIA Protocol Agent Ops - V3.0</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/lucide@latest"></script>
<!-- 引入 Alpine.js 用于简单的状态管理 -->
<script src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Noto+Sans+SC:wght@300;400;500;700&family=JetBrains+Mono:wght@400;500&display=swap');
body { font-family: 'Inter', 'Noto Sans SC', sans-serif; }
.font-mono { font-family: 'JetBrains Mono', monospace; }
/* 滚动条 */
::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 3px; }
::-webkit-scrollbar-thumb:hover { background: #94a3b8; }
[x-cloak] { display: none !important; }
</style>
</head>
<body class="bg-slate-50 h-screen flex flex-col text-slate-800" x-data="{
currentTab: 'global-persona', // global-persona, global-memory, stage-prompt, stage-cot, stage-reflexion
currentStage: 'SAMPLE_SIZE_CALC'
}">
<!-- 1. 顶部栏 (Global Status) -->
<header class="bg-white border-b border-slate-200 h-16 px-6 flex items-center justify-between shadow-sm z-20 shrink-0">
<div class="flex items-center gap-3">
<div class="w-9 h-9 bg-indigo-600 rounded-lg flex items-center justify-center text-white shadow-md">
<i data-lucide="cpu" class="w-5 h-5"></i>
</div>
<div>
<h1 class="font-bold text-lg text-slate-800">Protocol Agent Ops</h1>
<div class="flex items-center gap-2 text-[10px] text-slate-500 uppercase tracking-wider font-medium">
<span>Env: Production</span>
<span class="w-1 h-1 bg-slate-300 rounded-full"></span>
<span>Orchestrator V3.0</span>
</div>
</div>
</div>
<div class="flex items-center gap-4">
<div class="flex items-center gap-2 px-3 py-1.5 bg-slate-100 rounded text-xs text-slate-600">
<span class="w-2 h-2 bg-green-500 rounded-full"></span>
API Status: Healthy
</div>
<button class="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded-lg text-sm font-medium transition flex items-center gap-2 shadow-sm">
<i data-lucide="save" class="w-4 h-4"></i>
发布变更
</button>
</div>
</header>
<div class="flex flex-1 overflow-hidden">
<!-- 2. 左侧导航 (Global -> Stages) -->
<aside class="w-64 bg-white border-r border-slate-200 flex flex-col overflow-y-auto shrink-0">
<!-- Global Config Section -->
<div class="p-4">
<div class="text-xs font-bold text-slate-400 uppercase tracking-wider mb-3 px-2">Level 1: 全局设定</div>
<nav class="space-y-1">
<button @click="currentTab = 'global-persona'" :class="currentTab === 'global-persona' ? 'bg-indigo-50 text-indigo-700' : 'text-slate-700 hover:bg-slate-50'" class="w-full text-left block px-3 py-2 text-sm rounded-lg flex items-center gap-3 transition">
<i data-lucide="settings" class="w-4 h-4" :class="currentTab === 'global-persona' ? 'text-indigo-600' : 'text-slate-400'"></i>
全局人设 (Persona)
</button>
<button @click="currentTab = 'global-memory'" :class="currentTab === 'global-memory' ? 'bg-indigo-50 text-indigo-700' : 'text-slate-700 hover:bg-slate-50'" class="w-full text-left block px-3 py-2 text-sm rounded-lg flex items-center gap-3 transition">
<i data-lucide="database" class="w-4 h-4" :class="currentTab === 'global-memory' ? 'text-indigo-600' : 'text-slate-400'"></i>
记忆与知识库
</button>
</nav>
</div>
<!-- Stages Config Section -->
<div class="p-4 pt-0">
<div class="text-xs font-bold text-slate-400 uppercase tracking-wider mb-3 px-2 flex justify-between items-center">
Level 2: 阶段配置
<span class="bg-indigo-100 text-indigo-700 px-1.5 rounded text-[10px]">7</span>
</div>
<nav class="space-y-1">
<template x-for="(stage, idx) in ['科学问题梳理', 'PICO 分析', '选题评价', '观察指标设计', '样本量计算', 'CRF 设计', '方案撰写']">
<button
@click="currentStage = stage; currentTab = 'stage-cot'"
:class="currentStage === stage ? 'bg-indigo-50 text-indigo-700 border border-indigo-100 shadow-sm' : 'text-slate-600 hover:bg-slate-50'"
class="w-full text-left px-3 py-2 text-sm rounded-lg flex items-center gap-3 transition relative group"
>
<span
class="w-5 text-center text-xs font-mono"
:class="currentStage === stage ? 'text-indigo-500' : 'text-slate-400'"
x-text="'0' + (idx + 1)"
></span>
<span x-text="stage" :class="currentStage === stage ? 'font-medium' : ''"></span>
<span x-show="currentStage === stage" class="absolute right-3 w-1.5 h-1.5 bg-indigo-500 rounded-full"></span>
</button>
</template>
</nav>
</div>
</aside>
<!-- 3. 主内容区 -->
<main class="flex-1 flex flex-col bg-slate-50/50 overflow-hidden relative">
<!-- Global & Memory Tabs (Only show when Global is selected) -->
<div x-show="currentTab.startsWith('global')" class="px-8 py-6 border-b border-slate-200 bg-white">
<h2 class="text-xl font-bold text-slate-800 mb-1 flex items-center gap-2">
<i data-lucide="globe" class="w-5 h-5 text-indigo-600"></i>
全局配置中心
</h2>
<p class="text-sm text-slate-500">定义 Protocol Agent 的底层逻辑、记忆结构和通用知识库挂载。</p>
</div>
<!-- Stage Header (Only show when Stage is selected) -->
<div x-show="currentTab.startsWith('stage')" class="px-8 py-6 border-b border-slate-200 bg-white flex justify-between items-start">
<div>
<h2 class="text-xl font-bold text-slate-800 mb-1 flex items-center gap-2">
<span x-text="currentStage"></span>
<span class="text-xs bg-slate-100 text-slate-500 px-2 py-0.5 rounded font-mono font-normal">ID: SAMPLE_SIZE_CALC</span>
</h2>
<p class="text-sm text-slate-500">配置该阶段的任务指令、思维链逻辑和反思规则。</p>
</div>
<!-- Logic Layer Tabs -->
<div class="flex bg-slate-100 p-1 rounded-lg">
<button @click="currentTab = 'stage-prompt'" :class="currentTab === 'stage-prompt' ? 'bg-white text-indigo-600 shadow-sm' : 'text-slate-600 hover:text-slate-900'" class="px-4 py-1.5 text-sm font-medium rounded-md transition">Prompt (指令)</button>
<button @click="currentTab = 'stage-cot'" :class="currentTab === 'stage-cot' ? 'bg-white text-indigo-600 shadow-sm' : 'text-slate-600 hover:text-slate-900'" class="px-4 py-1.5 text-sm font-medium rounded-md transition flex items-center gap-2"><i data-lucide="git-merge" class="w-3 h-3"></i>思维链 (CoT)</button>
<button @click="currentTab = 'stage-reflexion'" :class="currentTab === 'stage-reflexion' ? 'bg-white text-indigo-600 shadow-sm' : 'text-slate-600 hover:text-slate-900'" class="px-4 py-1.5 text-sm font-medium rounded-md transition flex items-center gap-2"><i data-lucide="shield-check" class="w-3 h-3"></i>反思卫士</button>
</div>
</div>
<!-- Scrollable Content Area -->
<div class="flex-1 overflow-y-auto p-8">
<div class="max-w-4xl mx-auto space-y-6">
<!-- ========================================== -->
<!-- 1. 全局人设 (Global Persona) -->
<!-- ========================================== -->
<div x-show="currentTab === 'global-persona'" x-transition:enter="transition ease-out duration-200" x-transition:enter-start="opacity-0 translate-y-2">
<div class="bg-white border border-slate-200 rounded-xl p-6 shadow-sm mb-6">
<h3 class="font-bold text-slate-700 mb-4">Base System Prompt (基座人设)</h3>
<textarea class="w-full h-48 text-sm border border-slate-200 rounded-lg p-4 bg-slate-50 focus:bg-white focus:border-indigo-500 focus:ring-1 focus:ring-indigo-500 outline-none resize-none font-mono leading-relaxed">
你是一个严谨的临床研究方法学专家助手 (Protocol Agent)。
你的核心目标是辅助用户制定一份科学、合规、可执行的临床研究方案。
核心原则:
1. 循证医学:所有建议必须基于可靠的医学证据。
2. 逻辑严密:在回答前,必须先进行逻辑推演 (Chain of Thought)。
3. 状态感知:始终基于 Context 中已确认的信息PICO等进行回答不要重复索要已知信息。
4. 语气风格:专业、客观、理性,避免过度热情的营销式语气。
</textarea>
<div class="flex justify-end mt-4">
<button class="text-sm text-indigo-600 hover:underline">查看历史版本 (v1.2)</button>
</div>
</div>
</div>
<!-- ========================================== -->
<!-- 2. 记忆与知识库 (Global Memory & Knowledge)-->
<!-- ========================================== -->
<div x-show="currentTab === 'global-memory'" x-transition:enter="transition ease-out duration-200" x-transition:enter-start="opacity-0 translate-y-2">
<!-- Memory Schema -->
<div class="bg-white border border-slate-200 rounded-xl p-6 shadow-sm mb-6">
<h3 class="font-bold text-slate-700 mb-4 flex items-center gap-2">
<i data-lucide="hard-drive" class="w-4 h-4 text-indigo-500"></i>
记忆结构定义 (Memory Schema)
</h3>
<div class="bg-slate-900 rounded-lg p-4 font-mono text-xs text-slate-300 overflow-x-auto">
<pre>{
"scientific_question": "string",
"pico": {
"p": "string (Patient)",
"i": "string (Intervention)",
"c": "string (Comparison)",
"o": "string (Outcome)"
},
"sample_size": {
"n_total": "number",
"power": "number",
"alpha": "number"
},
...
}</pre>
</div>
<p class="text-xs text-slate-500 mt-2">此 Schema 定义了 Agent 可以“记住”的结构化数据字段。修改此配置需同步更新后端 Prisma Schema。</p>
</div>
<!-- Knowledge Base -->
<div class="bg-white border border-slate-200 rounded-xl p-6 shadow-sm">
<h3 class="font-bold text-slate-700 mb-4 flex items-center gap-2">
<i data-lucide="library" class="w-4 h-4 text-green-500"></i>
知识库挂载 (EKB Mounting)
</h3>
<div class="space-y-3">
<div class="flex items-center justify-between p-3 border border-slate-200 rounded-lg bg-slate-50">
<div class="flex items-center gap-3">
<div class="w-8 h-8 bg-white border border-slate-200 rounded flex items-center justify-center">
<i data-lucide="book" class="w-4 h-4 text-indigo-500"></i>
</div>
<div>
<div class="text-sm font-medium text-slate-800">Clinical Guidelines (临床指南库)</div>
<div class="text-xs text-slate-500">包含 2020-2025 全球核心指南 | 12,400 篇</div>
</div>
</div>
<div class="flex items-center gap-2">
<span class="text-xs bg-green-100 text-green-700 px-2 py-0.5 rounded">Active</span>
<button class="text-slate-400 hover:text-slate-600"><i data-lucide="more-horizontal" class="w-4 h-4"></i></button>
</div>
</div>
<div class="flex items-center justify-between p-3 border border-slate-200 rounded-lg bg-slate-50">
<div class="flex items-center gap-3">
<div class="w-8 h-8 bg-white border border-slate-200 rounded flex items-center justify-center">
<i data-lucide="file-text" class="w-4 h-4 text-indigo-500"></i>
</div>
<div>
<div class="text-sm font-medium text-slate-800">RCT Protocols (RCT 方案库)</div>
<div class="text-xs text-slate-500">高质量 RCT 注册方案 | 5,300 篇</div>
</div>
</div>
<div class="flex items-center gap-2">
<span class="text-xs bg-green-100 text-green-700 px-2 py-0.5 rounded">Active</span>
<button class="text-slate-400 hover:text-slate-600"><i data-lucide="more-horizontal" class="w-4 h-4"></i></button>
</div>
</div>
</div>
</div>
</div>
<!-- ========================================== -->
<!-- 3. 阶段 Prompt (Stage Prompt) -->
<!-- ========================================== -->
<div x-show="currentTab === 'stage-prompt'" x-transition:enter="transition ease-out duration-200" x-transition:enter-start="opacity-0 translate-y-2">
<div class="bg-white border border-slate-200 rounded-xl p-6 shadow-sm">
<div class="flex justify-between items-center mb-4">
<h3 class="font-bold text-slate-700">Task Instruction (阶段任务指令)</h3>
<div class="text-xs text-slate-500">这将覆盖到 System Prompt 的 [Stage Instruction] 部分</div>
</div>
<textarea class="w-full h-64 text-sm border border-slate-200 rounded-lg p-4 bg-slate-50 focus:bg-white focus:border-indigo-500 focus:ring-1 focus:ring-indigo-500 outline-none resize-none font-mono leading-relaxed">
当前任务阶段:【样本量计算】
你的目标是帮助用户确定合适的研究样本量。
请遵循以下逻辑:
1. 识别研究设计类型RCT、队列、病例对照
2. 询问或确认计算所需的统计学参数Alpha, Power, Effect Size, 组间比例)。
- 如果用户不知道,给出该领域的常用默认值(如 Alpha=0.05, Power=0.8)。
3. 一旦参数齐全,请生成 Action Card 引导用户调用计算器。
4. 计算完成后,对结果进行解读(是否过大/过小,是否符合伦理)。
注意:严禁自己口算样本量,必须调用工具。
</textarea>
</div>
</div>
<!-- ========================================== -->
<!-- 4. 思维链编排 (Stage CoT) -->
<!-- ========================================== -->
<div x-show="currentTab === 'stage-cot'" x-transition:enter="transition ease-out duration-200" x-transition:enter-start="opacity-0 translate-y-2">
<div class="flex items-center justify-between mb-4">
<h3 class="font-bold text-slate-700">思维链 SOP 可视化编排</h3>
<button class="text-xs bg-slate-100 hover:bg-slate-200 text-slate-600 px-3 py-1.5 rounded transition">查看 JSON 源码</button>
</div>
<!-- Step List -->
<div class="space-y-4">
<!-- Step 1 -->
<div class="bg-white border border-slate-200 rounded-xl p-5 shadow-sm hover:border-indigo-300 transition-colors group relative">
<div class="absolute left-0 top-0 bottom-0 w-1.5 bg-slate-300 rounded-l-xl group-hover:bg-indigo-500 transition-colors"></div>
<div class="flex gap-4">
<div class="shrink-0 flex flex-col items-center gap-2 pt-1">
<span class="w-6 h-6 rounded-full bg-slate-100 text-slate-500 text-xs font-bold flex items-center justify-center border border-slate-200">1</span>
</div>
<div class="flex-1 space-y-3">
<div class="flex justify-between items-center">
<input type="text" value="Check Parameters (参数完整性检查)" class="font-bold text-slate-800 bg-transparent border-none p-0 focus:ring-0 w-full text-sm">
<div class="flex items-center gap-2">
<div class="text-[10px] bg-slate-100 text-slate-500 px-2 py-0.5 rounded font-mono">Output: &lt;completeness_check&gt;</div>
<i data-lucide="trash-2" class="w-4 h-4 text-slate-300 hover:text-red-500 cursor-pointer"></i>
</div>
</div>
<textarea class="w-full text-xs border border-slate-200 rounded-lg p-2 bg-slate-50 focus:bg-white outline-none resize-none h-16">检查用户输入的 Context 中是否包含样本量计算所需的全部参数Alpha, Power, 预期效应量)。</textarea>
</div>
</div>
</div>
<!-- Step 2 -->
<div class="bg-white border border-slate-200 rounded-xl p-5 shadow-sm hover:border-indigo-300 transition-colors group relative">
<div class="absolute left-0 top-0 bottom-0 w-1.5 bg-slate-300 rounded-l-xl group-hover:bg-indigo-500 transition-colors"></div>
<div class="flex gap-4">
<div class="shrink-0 flex flex-col items-center gap-2 pt-1">
<span class="w-6 h-6 rounded-full bg-slate-100 text-slate-500 text-xs font-bold flex items-center justify-center border border-slate-200">2</span>
</div>
<div class="flex-1 space-y-3">
<div class="flex justify-between items-center">
<input type="text" value="Match Methodology (方法学匹配)" class="font-bold text-slate-800 bg-transparent border-none p-0 focus:ring-0 w-full text-sm">
<div class="flex items-center gap-2">
<div class="text-[10px] bg-slate-100 text-slate-500 px-2 py-0.5 rounded font-mono">Output: &lt;method_match&gt;</div>
<i data-lucide="trash-2" class="w-4 h-4 text-slate-300 hover:text-red-500 cursor-pointer"></i>
</div>
</div>
<textarea class="w-full text-xs border border-slate-200 rounded-lg p-2 bg-slate-50 focus:bg-white outline-none resize-none h-16">基于 PICO 中的 Study Design (RCT/Cohort),推荐最合适的样本量计算公式。若无法确定,推荐通用公式。</textarea>
</div>
</div>
</div>
<!-- Add Step -->
<button class="w-full py-3 border-2 border-dashed border-slate-300 rounded-xl text-slate-500 text-sm font-medium hover:border-indigo-500 hover:text-indigo-600 hover:bg-indigo-50 transition flex items-center justify-center gap-2">
<i data-lucide="plus" class="w-4 h-4"></i>
添加思考步骤
</button>
</div>
</div>
<!-- ========================================== -->
<!-- 5. 反思卫士 (Stage Reflexion) -->
<!-- ========================================== -->
<div x-show="currentTab === 'stage-reflexion'" x-transition:enter="transition ease-out duration-200" x-transition:enter-start="opacity-0 translate-y-2">
<div class="bg-blue-50 border border-blue-200 rounded-lg p-4 mb-6 flex gap-3">
<i data-lucide="shield-check" class="w-5 h-5 text-blue-600 shrink-0 mt-0.5"></i>
<div class="text-sm text-blue-800">
<p class="font-bold mb-1">Reflexion Guard (反思卫士)</p>
<p>当工具执行完毕并回写数据时后端会自动运行这些规则。如果校验失败Agent 将拒绝推进流程并报警。</p>
</div>
</div>
<div class="bg-white border border-slate-200 rounded-lg overflow-hidden shadow-sm">
<div class="px-4 py-3 bg-slate-50 border-b border-slate-200 flex justify-between items-center">
<h3 class="text-sm font-bold text-slate-700">规则列表 (Rule-based)</h3>
<button class="text-xs text-indigo-600 hover:underline">+ 添加规则</button>
</div>
<table class="w-full text-sm text-left">
<thead class="bg-slate-50 text-slate-500 border-b border-slate-200">
<tr>
<th class="px-4 py-3 font-medium">校验字段</th>
<th class="px-4 py-3 font-medium">逻辑</th>
<th class="px-4 py-3 font-medium">阈值</th>
<th class="px-4 py-3 font-medium">报警消息</th>
<th class="px-4 py-3 text-right">操作</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-100">
<tr>
<td class="px-4 py-3 font-mono text-indigo-600">sample_size.n</td>
<td class="px-4 py-3 text-slate-600">&lt;</td>
<td class="px-4 py-3 font-mono">10</td>
<td class="px-4 py-3 text-slate-600">样本量过小,无法通过伦理审查</td>
<td class="px-4 py-3 text-right text-slate-400 hover:text-indigo-600 cursor-pointer"><i data-lucide="edit-2" class="w-4 h-4 inline"></i></td>
</tr>
<tr>
<td class="px-4 py-3 font-mono text-indigo-600">sample_size.n</td>
<td class="px-4 py-3 text-slate-600">&gt;</td>
<td class="px-4 py-3 font-mono">100000</td>
<td class="px-4 py-3 text-slate-600">样本量过大,请确认可行性</td>
<td class="px-4 py-3 text-right text-slate-400 hover:text-indigo-600 cursor-pointer"><i data-lucide="edit-2" class="w-4 h-4 inline"></i></td>
</tr>
</tbody>
</table>
</div>
<div class="mt-6 bg-white border border-slate-200 rounded-lg p-6 shadow-sm">
<h3 class="text-sm font-bold text-slate-700 mb-3">AI 软校验 (Prompt-based)</h3>
<textarea class="w-full text-xs border border-slate-200 rounded-lg p-3 bg-slate-50 focus:bg-white outline-none resize-none h-20" placeholder="在此输入给 AI 的反思指令...">请结合用户的研究病种(如罕见病),判断该样本量是否合理。如果是罕见病,样本量小是可以接受的。</textarea>
</div>
</div>
</div>
</div>
</main>
</div>
<script>
lucide.createIcons();
</script>
</body>
</html>