Files
autogen/demo_visualization.html
2026-03-12 13:27:03 +08:00

703 lines
22 KiB
HTML
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.

<!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>