Files
autogen/demo_visualization.html

703 lines
22 KiB
HTML
Raw Permalink Normal View History

2026-03-12 13:27:03 +08:00
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AutoGen SDLC 多智能体协作平台 - 演示</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1400px;
margin: 0 auto;
background: white;
border-radius: 20px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
overflow: hidden;
}
.header {
background: linear-gradient(135deg, #2196f3, #9c27b0);
color: white;
padding: 30px;
text-align: center;
}
.header h1 {
font-size: 2.5rem;
margin-bottom: 10px;
}
.header p {
font-size: 1.1rem;
opacity: 0.9;
}
/* Agent 状态面板 */
.agent-status-panel {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
padding: 30px;
background: #f5f5f5;
}
.agent-card {
background: white;
padding: 20px;
border-radius: 15px;
text-align: center;
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
transition: all 0.3s ease;
border: 3px solid transparent;
}
.agent-card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 25px rgba(0,0,0,0.15);
}
.agent-card.active {
animation: pulse-border 2s infinite;
}
@keyframes pulse-border {
0%, 100% { border-color: #4caf50; }
50% { border-color: #81c784; }
}
.agent-avatar {
font-size: 3rem;
margin-bottom: 10px;
}
.agent-name {
font-weight: bold;
font-size: 1.2rem;
margin-bottom: 5px;
color: #333;
}
.agent-role {
color: #666;
font-size: 0.9rem;
margin-bottom: 10px;
}
.agent-stats {
display: flex;
justify-content: space-around;
font-size: 0.85rem;
color: #999;
}
.status-indicator {
display: inline-block;
width: 10px;
height: 10px;
border-radius: 50%;
margin-right: 5px;
}
.status-active {
background: #4caf50;
animation: pulse-dot 1.5s infinite;
}
.status-waiting {
background: #ffc107;
}
.status-inactive {
background: #e0e0e0;
}
@keyframes pulse-dot {
0%, 100% { opacity: 1; }
50% { opacity: 0.4; }
}
/* 主内容区 */
.main-content {
display: grid;
grid-template-columns: 2fr 1fr;
gap: 20px;
padding: 30px;
}
/* 聊天区域 */
.chat-section {
background: #fafafa;
border-radius: 15px;
padding: 20px;
max-height: 600px;
overflow-y: auto;
}
.chat-message {
background: white;
padding: 15px;
margin-bottom: 15px;
border-radius: 10px;
border-left: 5px solid #2196f3;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
animation: slide-in 0.3s ease;
}
@keyframes slide-in {
from {
opacity: 0;
transform: translateX(-20px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
.message-header {
display: flex;
align-items: center;
margin-bottom: 10px;
}
.message-avatar {
font-size: 1.5rem;
margin-right: 10px;
}
.message-agent {
font-weight: bold;
color: #333;
}
.message-time {
margin-left: auto;
color: #999;
font-size: 0.85rem;
}
.message-content {
color: #555;
line-height: 1.6;
}
/* 侧边栏 */
.sidebar {
display: flex;
flex-direction: column;
gap: 20px;
}
/* 工作流程图 */
.workflow-section {
background: white;
padding: 20px;
border-radius: 15px;
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
}
.workflow-step {
display: flex;
align-items: center;
padding: 10px;
margin-bottom: 10px;
border-radius: 8px;
background: #f5f5f5;
transition: all 0.3s ease;
}
.workflow-step.active {
background: linear-gradient(135deg, #e3f2fd, #bbdefb);
border-left: 4px solid #2196f3;
}
.workflow-step.completed {
background: linear-gradient(135deg, #e8f5e9, #c8e6c9);
border-left: 4px solid #4caf50;
}
.step-icon {
font-size: 1.5rem;
margin-right: 10px;
}
.step-name {
flex: 1;
font-weight: bold;
}
.step-status {
font-size: 0.8rem;
padding: 3px 8px;
border-radius: 12px;
background: #e0e0e0;
}
.step-status.active {
background: #2196f3;
color: white;
}
.step-status.completed {
background: #4caf50;
color: white;
}
/* 统计卡片 */
.stats-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 15px;
}
.stat-card {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
padding: 20px;
border-radius: 10px;
text-align: center;
}
.stat-value {
font-size: 2rem;
font-weight: bold;
}
.stat-label {
font-size: 0.9rem;
opacity: 0.9;
}
/* 时间线 */
.timeline-section {
background: white;
padding: 20px;
border-radius: 15px;
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
}
.timeline-item {
padding: 10px 0;
border-left: 3px solid #2196f3;
padding-left: 15px;
position: relative;
}
.timeline-item::before {
content: '';
position: absolute;
left: -8px;
top: 15px;
width: 13px;
height: 13px;
border-radius: 50%;
background: #2196f3;
}
.timeline-time {
font-size: 0.8rem;
color: #999;
}
.timeline-event {
font-weight: bold;
color: #333;
}
.timeline-desc {
color: #666;
font-size: 0.9rem;
}
/* 控制按钮 */
.controls {
padding: 20px 30px;
background: #f5f5f5;
display: flex;
gap: 15px;
justify-content: center;
}
.btn {
padding: 12px 30px;
border: none;
border-radius: 8px;
font-size: 1rem;
font-weight: bold;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 8px;
}
.btn-primary {
background: linear-gradient(135deg, #2196f3, #9c27b0);
color: white;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(33, 150, 243, 0.4);
}
.btn-secondary {
background: #ff9800;
color: white;
}
.btn-secondary:hover {
background: #f57c00;
}
.btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* 响应式 */
@media (max-width: 1024px) {
.main-content {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="container">
<!-- 头部 -->
<div class="header">
<h1>🤖 AutoGen SDLC 多智能体协作平台</h1>
<p>基于 AutoGen + Qwen3.5-flash 的端到端软件交付系统 · 实时可视化演示</p>
</div>
<!-- Agent 状态面板 -->
<div class="agent-status-panel">
<div class="agent-card" id="agent-pm">
<div class="agent-avatar">📋</div>
<div class="agent-name">PM Agent</div>
<div class="agent-role">产品经理 · 需求分析</div>
<div class="agent-stats">
<span><span class="status-indicator status-waiting" id="status-pm"></span><span id="count-pm">0</span> 消息</span>
</div>
</div>
<div class="agent-card" id="agent-qa">
<div class="agent-avatar"></div>
<div class="agent-name">QA Agent</div>
<div class="agent-role">测试工程师 · TDD</div>
<div class="agent-stats">
<span><span class="status-indicator status-inactive" id="status-qa"></span><span id="count-qa">0</span> 消息</span>
</div>
</div>
<div class="agent-card" id="agent-dev">
<div class="agent-avatar">💻</div>
<div class="agent-name">Dev Agent</div>
<div class="agent-role">开发工程师 · 代码实现</div>
<div class="agent-stats">
<span><span class="status-indicator status-inactive" id="status-dev"></span><span id="count-dev">0</span> 消息</span>
</div>
</div>
<div class="agent-card" id="agent-orchestrator">
<div class="agent-avatar">🎯</div>
<div class="agent-name">Orchestrator</div>
<div class="agent-role">协调器 · 流程调度</div>
<div class="agent-stats">
<span><span class="status-indicator status-inactive" id="status-orchestrator"></span><span id="count-orchestrator">0</span> 消息</span>
</div>
</div>
<div class="agent-card" id="agent-user">
<div class="agent-avatar">👤</div>
<div class="agent-name">User Proxy</div>
<div class="agent-role">用户代理 · 人机交互</div>
<div class="agent-stats">
<span><span class="status-indicator status-inactive" id="status-user"></span><span id="count-user">0</span> 消息</span>
</div>
</div>
</div>
<!-- 主内容区 -->
<div class="main-content">
<!-- 聊天区域 -->
<div class="chat-section" id="chat-container">
<h2 style="margin-bottom: 20px; color: #333;">💬 Agent 对话实录</h2>
<div id="messages">
<!-- 消息将动态插入这里 -->
</div>
</div>
<!-- 侧边栏 -->
<div class="sidebar">
<!-- 统计卡片 -->
<div class="stats-grid">
<div class="stat-card">
<div class="stat-value" id="total-messages">0</div>
<div class="stat-label">总消息数</div>
</div>
<div class="stat-card">
<div class="stat-value" id="total-files">0</div>
<div class="stat-label">生成文件数</div>
</div>
</div>
<!-- 工作流程图 -->
<div class="workflow-section">
<h3 style="margin-bottom: 15px; color: #333;">🔄 工作流程</h3>
<div class="workflow-step" id="step-1">
<div class="step-icon">📋</div>
<div class="step-name">需求分析</div>
<div class="step-status" id="status-1">等待中</div>
</div>
<div class="workflow-step" id="step-2">
<div class="step-icon"></div>
<div class="step-name">测试设计</div>
<div class="step-status" id="status-2">等待中</div>
</div>
<div class="workflow-step" id="step-3">
<div class="step-icon">💻</div>
<div class="step-name">代码实现</div>
<div class="step-status" id="status-3">等待中</div>
</div>
<div class="workflow-step" id="step-4">
<div class="step-icon">🧪</div>
<div class="step-name">测试执行</div>
<div class="step-status" id="status-4">等待中</div>
</div>
<div class="workflow-step" id="step-5">
<div class="step-icon">🎯</div>
<div class="step-name">最终验证</div>
<div class="step-status" id="status-5">等待中</div>
</div>
</div>
<!-- 时间线 -->
<div class="timeline-section">
<h3 style="margin-bottom: 15px; color: #333;">⏱️ 执行时间线</h3>
<div id="timeline">
<!-- 时间线事件将动态插入这里 -->
</div>
</div>
</div>
</div>
<!-- 控制按钮 -->
<div class="controls">
<button class="btn btn-primary" onclick="startDemo()">
<span>▶️</span> 启动演示
</button>
<button class="btn btn-secondary" onclick="resetDemo()">
<span>🔄</span> 重置
</button>
</div>
</div>
<script>
// 模拟数据
const demoMessages = [
{
agent: "Orchestrator",
avatar: "🎯",
content: "🚀 启动 SDLC 工作流!用户需求:开发一个电池健康状态预测 API...",
time: new Date().toISOString(),
step: 0
},
{
agent: "PM Agent",
avatar: "📋",
content: "收到!开始分析需求... 功能性需求1. 接收电压/电流/温度数据 2. 计算 SOH 百分比 3. 异常检测。非功能性需求:响应时间<100ms符合 MISRA-C 规范...",
time: new Date(Date.now() + 5000).toISOString(),
step: 1
},
{
agent: "QA Agent",
avatar: "✅",
content: "基于 SRS 设计测试用例test_normal_operation(), test_zero_voltage(), test_extreme_temperature()... 采用 TDD 模式,测试先行!",
time: new Date(Date.now() + 10000).toISOString(),
step: 2
},
{
agent: "Dev Agent",
avatar: "💻",
content: "开始编写代码... 实现 calculate_soh() 函数,包含输入验证、边界检查、错误处理。代码符合 PEP8 规范,添加完整 docstring...",
time: new Date(Date.now() + 15000).toISOString(),
step: 3
},
{
agent: "User Proxy",
avatar: "👤",
content: "执行测试... ✅ test_normal_operation PASSED ✅ test_zero_voltage PASSED ✅ test_extreme_temperature PASSED 所有测试通过!",
time: new Date(Date.now() + 20000).toISOString(),
step: 4
},
{
agent: "Orchestrator",
avatar: "🎯",
content: "✅ SDLC 流程完成生成文件SRS.md, test_battery_health.py, src_battery_health.py。质量验证通过可以交付",
time: new Date(Date.now() + 25000).toISOString(),
step: 5
}
];
let currentStep = 0;
let messageCount = 0;
const agentStats = {
"PM Agent": 0,
"QA Agent": 0,
"Dev Agent": 0,
"Orchestrator": 0,
"User Proxy": 0
};
function updateAgentStatus(agentName, isActive) {
const card = document.getElementById(`agent-${agentName.toLowerCase().replace(' ', '-')}`);
const indicator = document.getElementById(`status-${agentName.toLowerCase().replace(' ', '-')}`);
if (card && indicator) {
if (isActive) {
card.classList.add('active');
indicator.className = 'status-indicator status-active';
} else {
card.classList.remove('active');
indicator.className = 'status-indicator status-inactive';
}
}
}
function addMessage(msg) {
const messagesDiv = document.getElementById('messages');
const time = new Date(msg.time).toLocaleTimeString('zh-CN', { hour12: false });
const messageHtml = `
<div class="chat-message" style="border-left-color: ${getAgentColor(msg.agent)}">
<div class="message-header">
<span class="message-avatar">${msg.avatar}</span>
<span class="message-agent">${msg.agent}</span>
<span class="message-time">${time}</span>
</div>
<div class="message-content">${msg.content}</div>
</div>
`;
messagesDiv.insertAdjacentHTML('beforeend', messageHtml);
messagesDiv.scrollTop = messagesDiv.scrollHeight;
// 更新统计
if (agentStats[msg.agent] !== undefined) {
agentStats[msg.agent]++;
document.getElementById(`count-${msg.agent.toLowerCase().replace(' ', '-')}`).textContent = agentStats[msg.agent];
}
messageCount++;
document.getElementById('total-messages').textContent = messageCount;
}
function getAgentColor(agent) {
const colors = {
"PM Agent": "#2196f3",
"QA Agent": "#4caf50",
"Dev Agent": "#ff9800",
"Orchestrator": "#9c27b0",
"User Proxy": "#795548"
};
return colors[agent] || "#999";
}
function updateWorkflow(step) {
for (let i = 1; i <= 5; i++) {
const stepEl = document.getElementById(`step-${i}`);
const statusEl = document.getElementById(`status-${i}`);
if (i < step) {
stepEl.className = 'workflow-step completed';
statusEl.textContent = '已完成';
statusEl.className = 'step-status completed';
} else if (i === step) {
stepEl.className = 'workflow-step active';
statusEl.textContent = '进行中';
statusEl.className = 'step-status active';
} else {
stepEl.className = 'workflow-step';
statusEl.textContent = '等待中';
statusEl.className = 'step-status';
}
}
}
function addTimelineEvent(time, event, desc) {
const timelineDiv = document.getElementById('timeline');
const timeStr = new Date(time).toLocaleTimeString('zh-CN', { hour12: false });
const eventHtml = `
<div class="timeline-item">
<div class="timeline-time">${timeStr}</div>
<div class="timeline-event">${event}</div>
<div class="timeline-desc">${desc}</div>
</div>
`;
timelineDiv.insertAdjacentHTML('beforeend', eventHtml);
}
async function startDemo() {
resetDemo();
for (let i = 0; i < demoMessages.length; i++) {
const msg = demoMessages[i];
// 激活当前 Agent
updateAgentStatus(msg.agent, true);
// 添加消息
setTimeout(() => {
addMessage(msg);
updateWorkflow(msg.step);
addTimelineEvent(msg.time, msg.agent, msg.content.substring(0, 50) + '...');
}, i * 1000);
// 等待后失活
await new Promise(resolve => setTimeout(resolve, 1500));
updateAgentStatus(msg.agent, false);
}
// 更新文件数
setTimeout(() => {
document.getElementById('total-files').textContent = '3';
}, demoMessages.length * 1000);
}
function resetDemo() {
document.getElementById('messages').innerHTML = '';
document.getElementById('timeline').innerHTML = '';
document.getElementById('total-messages').textContent = '0';
document.getElementById('total-files').textContent = '0';
for (const agent in agentStats) {
agentStats[agent] = 0;
document.getElementById(`count-${agent.toLowerCase().replace(' ', '-')}`).textContent = '0';
updateAgentStatus(agent, false);
}
updateWorkflow(0);
}
// 初始化
resetDemo();
</script>
</body>
</html>