509 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			509 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Python
		
	
	
	
#!/usr/bin/env python3
 | 
						||
# -*- coding: utf-8 -*-
 | 
						||
"""
 | 
						||
四AI团队协作Web界面
 | 
						||
基于Streamlit的实时协作监控和管理界面
 | 
						||
"""
 | 
						||
 | 
						||
import streamlit as st
 | 
						||
import asyncio
 | 
						||
import json
 | 
						||
import pandas as pd
 | 
						||
import plotly.express as px
 | 
						||
import plotly.graph_objects as go
 | 
						||
from datetime import datetime, timedelta
 | 
						||
import sys
 | 
						||
from pathlib import Path
 | 
						||
 | 
						||
# 添加项目路径到sys.path
 | 
						||
project_root = Path(__file__).parent.parent.parent.parent
 | 
						||
sys.path.insert(0, str(project_root))
 | 
						||
 | 
						||
from src.jixia.coordination.ai_team_collaboration import (
 | 
						||
    AITeamCollaboration, AIRole, MessageType, CollaborationType, WorkPhase
 | 
						||
)
 | 
						||
 | 
						||
# 页面配置
 | 
						||
st.set_page_config(
 | 
						||
    page_title="四AI团队协作中心", 
 | 
						||
    page_icon="🤖",
 | 
						||
    layout="wide",
 | 
						||
    initial_sidebar_state="expanded"
 | 
						||
)
 | 
						||
 | 
						||
# 初始化协作系统
 | 
						||
@st.cache_resource
 | 
						||
def init_collaboration_system():
 | 
						||
    """初始化协作系统"""
 | 
						||
    return AITeamCollaboration()
 | 
						||
 | 
						||
def main():
 | 
						||
    """主界面"""
 | 
						||
    st.title("🤖 四AI团队协作中心")
 | 
						||
    st.markdown("### OpenBB集成项目实时协作监控")
 | 
						||
    
 | 
						||
    # 初始化系统
 | 
						||
    collab = init_collaboration_system()
 | 
						||
    
 | 
						||
    # 侧边栏
 | 
						||
    with st.sidebar:
 | 
						||
        st.header("🎯 项目状态")
 | 
						||
        
 | 
						||
        # 当前阶段
 | 
						||
        current_phase = st.selectbox(
 | 
						||
            "当前工作阶段",
 | 
						||
            [phase.value for phase in WorkPhase],
 | 
						||
            index=list(WorkPhase).index(collab.current_phase)
 | 
						||
        )
 | 
						||
        
 | 
						||
        if st.button("更新阶段"):
 | 
						||
            new_phase = WorkPhase(current_phase)
 | 
						||
            asyncio.run(collab.advance_phase(new_phase))
 | 
						||
            st.success(f"阶段已更新为: {current_phase}")
 | 
						||
            st.rerun()
 | 
						||
        
 | 
						||
        st.divider()
 | 
						||
        
 | 
						||
        # AI状态概览
 | 
						||
        st.subheader("🤖 AI状态概览")
 | 
						||
        for ai_role, status in collab.ai_status.items():
 | 
						||
            status_color = {
 | 
						||
                "ready": "🟢",
 | 
						||
                "active": "🔵", 
 | 
						||
                "waiting": "🟡",
 | 
						||
                "completed_handoff": "✅",
 | 
						||
                "received_handoff": "📥"
 | 
						||
            }.get(status["status"], "⚪")
 | 
						||
            
 | 
						||
            st.write(f"{status_color} **{ai_role.value}**")
 | 
						||
            st.write(f"   📋 {status['current_task']}")
 | 
						||
            st.write(f"   🎯 {status['role']}")
 | 
						||
    
 | 
						||
    # 主要内容区域
 | 
						||
    tab1, tab2, tab3, tab4, tab5 = st.tabs([
 | 
						||
        "📢 主协作频道", "📊 AI仪表板", "🔄 工作流管理", "📈 协作分析", "⚙️ 系统管理"
 | 
						||
    ])
 | 
						||
    
 | 
						||
    with tab1:
 | 
						||
        render_main_collaboration(collab)
 | 
						||
    
 | 
						||
    with tab2:
 | 
						||
        render_ai_dashboard(collab)
 | 
						||
    
 | 
						||
    with tab3:
 | 
						||
        render_workflow_management(collab)
 | 
						||
    
 | 
						||
    with tab4:
 | 
						||
        render_collaboration_analytics(collab)
 | 
						||
    
 | 
						||
    with tab5:
 | 
						||
        render_system_management(collab)
 | 
						||
 | 
						||
def render_main_collaboration(collab):
 | 
						||
    """渲染主协作频道"""
 | 
						||
    st.header("📢 主协作频道")
 | 
						||
    
 | 
						||
    # 频道选择
 | 
						||
    channel_options = {
 | 
						||
        channel.name: channel_id 
 | 
						||
        for channel_id, channel in collab.channels.items()
 | 
						||
    }
 | 
						||
    
 | 
						||
    selected_channel_name = st.selectbox(
 | 
						||
        "选择频道",
 | 
						||
        list(channel_options.keys()),
 | 
						||
        index=0
 | 
						||
    )
 | 
						||
    
 | 
						||
    selected_channel_id = channel_options[selected_channel_name]
 | 
						||
    channel = collab.channels[selected_channel_id]
 | 
						||
    
 | 
						||
    col1, col2 = st.columns([2, 1])
 | 
						||
    
 | 
						||
    with col1:
 | 
						||
        # 消息历史
 | 
						||
        st.subheader(f"💬 {channel.name}")
 | 
						||
        
 | 
						||
        if channel.message_history:
 | 
						||
            for msg in channel.message_history[-10:]:  # 显示最近10条消息
 | 
						||
                sender_emoji = {
 | 
						||
                    AIRole.QWEN: "🏗️",
 | 
						||
                    AIRole.CLAUDE: "💻", 
 | 
						||
                    AIRole.GEMINI: "🧪",
 | 
						||
                    AIRole.ROVODEV: "📚"
 | 
						||
                }.get(msg.sender, "🤖")
 | 
						||
                
 | 
						||
                with st.chat_message(msg.sender.value, avatar=sender_emoji):
 | 
						||
                    st.write(f"**{msg.message_type.value}** - {msg.timestamp.strftime('%H:%M')}")
 | 
						||
                    st.write(msg.content)
 | 
						||
                    
 | 
						||
                    if msg.attachments:
 | 
						||
                        st.write("📎 附件:")
 | 
						||
                        for attachment in msg.attachments:
 | 
						||
                            st.write(f"  • {attachment}")
 | 
						||
                    
 | 
						||
                    if msg.tags:
 | 
						||
                        tag_html = " ".join([f"<span style='background-color: #e1f5fe; padding: 2px 6px; border-radius: 4px; font-size: 0.8em;'>{tag}</span>" for tag in msg.tags])
 | 
						||
                        st.markdown(tag_html, unsafe_allow_html=True)
 | 
						||
        else:
 | 
						||
            st.info("暂无消息")
 | 
						||
    
 | 
						||
    with col2:
 | 
						||
        # 频道信息
 | 
						||
        st.subheader("ℹ️ 频道信息")
 | 
						||
        st.write(f"**类型**: {channel.channel_type.value}")
 | 
						||
        st.write(f"**参与者**: {len(channel.participants)}人")
 | 
						||
        st.write(f"**主持人**: {channel.moderator.value}")
 | 
						||
        st.write(f"**消息数**: {len(channel.message_history)}")
 | 
						||
        st.write(f"**最后活动**: {channel.last_activity.strftime('%Y-%m-%d %H:%M')}")
 | 
						||
        
 | 
						||
        # 参与者列表
 | 
						||
        st.write("**参与者列表**:")
 | 
						||
        for participant in channel.participants:
 | 
						||
            role_emoji = {
 | 
						||
                AIRole.QWEN: "🏗️",
 | 
						||
                AIRole.CLAUDE: "💻",
 | 
						||
                AIRole.GEMINI: "🧪", 
 | 
						||
                AIRole.ROVODEV: "📚"
 | 
						||
            }.get(participant, "🤖")
 | 
						||
            st.write(f"{role_emoji} {participant.value}")
 | 
						||
    
 | 
						||
    # 发送消息区域
 | 
						||
    st.divider()
 | 
						||
    st.subheader("📝 发送消息")
 | 
						||
    
 | 
						||
    col1, col2, col3 = st.columns([2, 1, 1])
 | 
						||
    
 | 
						||
    with col1:
 | 
						||
        message_content = st.text_area("消息内容", height=100)
 | 
						||
    
 | 
						||
    with col2:
 | 
						||
        sender = st.selectbox(
 | 
						||
            "发送者",
 | 
						||
            [role.value for role in AIRole]
 | 
						||
        )
 | 
						||
        
 | 
						||
        message_type = st.selectbox(
 | 
						||
            "消息类型",
 | 
						||
            [msg_type.value for msg_type in MessageType]
 | 
						||
        )
 | 
						||
    
 | 
						||
    with col3:
 | 
						||
        receiver = st.selectbox(
 | 
						||
            "接收者",
 | 
						||
            ["广播"] + [role.value for role in AIRole]
 | 
						||
        )
 | 
						||
        
 | 
						||
        priority = st.slider("优先级", 1, 5, 1)
 | 
						||
    
 | 
						||
    if st.button("发送消息", type="primary"):
 | 
						||
        if message_content:
 | 
						||
            try:
 | 
						||
                receiver_role = None if receiver == "广播" else AIRole(receiver)
 | 
						||
                
 | 
						||
                asyncio.run(collab.send_message(
 | 
						||
                    sender=AIRole(sender),
 | 
						||
                    content=message_content,
 | 
						||
                    message_type=MessageType(message_type),
 | 
						||
                    channel_id=selected_channel_id,
 | 
						||
                    receiver=receiver_role,
 | 
						||
                    priority=priority
 | 
						||
                ))
 | 
						||
                
 | 
						||
                st.success("消息发送成功!")
 | 
						||
                st.rerun()
 | 
						||
            except Exception as e:
 | 
						||
                st.error(f"发送失败: {str(e)}")
 | 
						||
        else:
 | 
						||
            st.warning("请输入消息内容")
 | 
						||
 | 
						||
def render_ai_dashboard(collab):
 | 
						||
    """渲染AI仪表板"""
 | 
						||
    st.header("📊 AI工作仪表板")
 | 
						||
    
 | 
						||
    # AI选择
 | 
						||
    selected_ai = st.selectbox(
 | 
						||
        "选择AI",
 | 
						||
        [role.value for role in AIRole]
 | 
						||
    )
 | 
						||
    
 | 
						||
    ai_role = AIRole(selected_ai)
 | 
						||
    dashboard = collab.get_ai_dashboard(ai_role)
 | 
						||
    
 | 
						||
    # 基本信息
 | 
						||
    col1, col2, col3, col4 = st.columns(4)
 | 
						||
    
 | 
						||
    with col1:
 | 
						||
        st.metric("当前状态", dashboard["status"]["status"])
 | 
						||
    
 | 
						||
    with col2:
 | 
						||
        st.metric("活跃频道", len(dashboard["active_channels"]))
 | 
						||
    
 | 
						||
    with col3:
 | 
						||
        st.metric("待处理任务", len(dashboard["pending_tasks"]))
 | 
						||
    
 | 
						||
    with col4:
 | 
						||
        st.metric("协作得分", dashboard["collaboration_stats"]["collaboration_score"])
 | 
						||
    
 | 
						||
    # 详细信息
 | 
						||
    col1, col2 = st.columns(2)
 | 
						||
    
 | 
						||
    with col1:
 | 
						||
        # 待处理任务
 | 
						||
        st.subheader("📋 待处理任务")
 | 
						||
        if dashboard["pending_tasks"]:
 | 
						||
            for task in dashboard["pending_tasks"]:
 | 
						||
                with st.expander(f"{task['type']} - 优先级 {task['priority']}"):
 | 
						||
                    st.write(f"**来自**: {task['from']}")
 | 
						||
                    st.write(f"**频道**: {task['channel']}")
 | 
						||
                    st.write(f"**创建时间**: {task['created']}")
 | 
						||
                    st.write(f"**描述**: {task['description']}")
 | 
						||
        else:
 | 
						||
            st.info("暂无待处理任务")
 | 
						||
    
 | 
						||
    with col2:
 | 
						||
        # 最近消息
 | 
						||
        st.subheader("📨 最近消息")
 | 
						||
        if dashboard["recent_messages"]:
 | 
						||
            for msg in dashboard["recent_messages"][:5]:
 | 
						||
                priority_color = {
 | 
						||
                    1: "🔵", 2: "🟢", 3: "🟡", 4: "🟠", 5: "🔴"
 | 
						||
                }.get(msg["priority"], "⚪")
 | 
						||
                
 | 
						||
                st.write(f"{priority_color} **{msg['sender']}** 在 **{msg['channel']}**")
 | 
						||
                st.write(f"   {msg['content']}")
 | 
						||
                st.write(f"   ⏰ {msg['timestamp']}")
 | 
						||
                st.divider()
 | 
						||
        else:
 | 
						||
            st.info("暂无最近消息")
 | 
						||
    
 | 
						||
    # 协作统计
 | 
						||
    st.subheader("📈 协作统计")
 | 
						||
    
 | 
						||
    stats = dashboard["collaboration_stats"]
 | 
						||
    col1, col2, col3 = st.columns(3)
 | 
						||
    
 | 
						||
    with col1:
 | 
						||
        st.metric("发送消息", stats["messages_sent"])
 | 
						||
    
 | 
						||
    with col2:
 | 
						||
        st.metric("接收消息", stats["messages_received"])
 | 
						||
    
 | 
						||
    with col3:
 | 
						||
        st.metric("总消息数", stats["total_messages"])
 | 
						||
 | 
						||
def render_workflow_management(collab):
 | 
						||
    """渲染工作流管理"""
 | 
						||
    st.header("🔄 工作流管理")
 | 
						||
    
 | 
						||
    # 工作流规则
 | 
						||
    st.subheader("📜 工作流规则")
 | 
						||
    
 | 
						||
    rules_data = []
 | 
						||
    for rule_id, rule in collab.workflow_rules.items():
 | 
						||
        rules_data.append({
 | 
						||
            "规则ID": rule.id,
 | 
						||
            "规则名称": rule.name,
 | 
						||
            "触发阶段": rule.trigger_phase.value,
 | 
						||
            "目标AI": rule.target_ai.value if rule.target_ai else "无",
 | 
						||
            "状态": "✅ 激活" if rule.is_active else "❌ 禁用"
 | 
						||
        })
 | 
						||
    
 | 
						||
    if rules_data:
 | 
						||
        st.dataframe(pd.DataFrame(rules_data), use_container_width=True)
 | 
						||
    
 | 
						||
    # 手动工作交接
 | 
						||
    st.divider()
 | 
						||
    st.subheader("🤝 手动工作交接")
 | 
						||
    
 | 
						||
    col1, col2, col3 = st.columns(3)
 | 
						||
    
 | 
						||
    with col1:
 | 
						||
        from_ai = st.selectbox("交接方", [role.value for role in AIRole])
 | 
						||
        to_ai = st.selectbox("接收方", [role.value for role in AIRole])
 | 
						||
    
 | 
						||
    with col2:
 | 
						||
        task_desc = st.text_input("任务描述")
 | 
						||
        deliverables = st.text_area("交付物列表 (每行一个)")
 | 
						||
    
 | 
						||
    with col3:
 | 
						||
        notes = st.text_area("备注")
 | 
						||
    
 | 
						||
    if st.button("执行工作交接"):
 | 
						||
        if task_desc and from_ai != to_ai:
 | 
						||
            deliverable_list = [d.strip() for d in deliverables.split('\n') if d.strip()]
 | 
						||
            
 | 
						||
            try:
 | 
						||
                asyncio.run(collab.handoff_work(
 | 
						||
                    from_ai=AIRole(from_ai),
 | 
						||
                    to_ai=AIRole(to_ai),
 | 
						||
                    task_description=task_desc,
 | 
						||
                    deliverables=deliverable_list,
 | 
						||
                    notes=notes
 | 
						||
                ))
 | 
						||
                st.success("工作交接完成!")
 | 
						||
                st.rerun()
 | 
						||
            except Exception as e:
 | 
						||
                st.error(f"交接失败: {str(e)}")
 | 
						||
        else:
 | 
						||
            st.warning("请填写完整信息,且交接方和接收方不能相同")
 | 
						||
 | 
						||
def render_collaboration_analytics(collab):
 | 
						||
    """渲染协作分析"""
 | 
						||
    st.header("📈 协作分析")
 | 
						||
    
 | 
						||
    # 消息统计
 | 
						||
    st.subheader("💬 消息统计")
 | 
						||
    
 | 
						||
    # 收集所有消息数据
 | 
						||
    message_data = []
 | 
						||
    for channel_id, channel in collab.channels.items():
 | 
						||
        for msg in channel.message_history:
 | 
						||
            message_data.append({
 | 
						||
                "频道": channel.name,
 | 
						||
                "发送者": msg.sender.value,
 | 
						||
                "消息类型": msg.message_type.value,
 | 
						||
                "优先级": msg.priority,
 | 
						||
                "时间": msg.timestamp,
 | 
						||
                "日期": msg.timestamp.date(),
 | 
						||
                "小时": msg.timestamp.hour
 | 
						||
            })
 | 
						||
    
 | 
						||
    if message_data:
 | 
						||
        df = pd.DataFrame(message_data)
 | 
						||
        
 | 
						||
        col1, col2 = st.columns(2)
 | 
						||
        
 | 
						||
        with col1:
 | 
						||
            # 按AI发送者统计
 | 
						||
            sender_counts = df.groupby("发送者").size().reset_index()
 | 
						||
            sender_counts.columns = ["AI", "消息数量"]
 | 
						||
            
 | 
						||
            fig = px.bar(sender_counts, x="AI", y="消息数量", 
 | 
						||
                        title="各AI发送消息统计")
 | 
						||
            st.plotly_chart(fig, use_container_width=True)
 | 
						||
        
 | 
						||
        with col2:
 | 
						||
            # 按消息类型统计
 | 
						||
            type_counts = df.groupby("消息类型").size().reset_index()
 | 
						||
            type_counts.columns = ["消息类型", "数量"]
 | 
						||
            
 | 
						||
            fig = px.pie(type_counts, values="数量", names="消息类型",
 | 
						||
                        title="消息类型分布")
 | 
						||
            st.plotly_chart(fig, use_container_width=True)
 | 
						||
        
 | 
						||
        # 时间线分析
 | 
						||
        st.subheader("⏰ 活跃度时间线")
 | 
						||
        
 | 
						||
        if len(df) > 1:
 | 
						||
            daily_counts = df.groupby("日期").size().reset_index()
 | 
						||
            daily_counts.columns = ["日期", "消息数量"]
 | 
						||
            
 | 
						||
            fig = px.line(daily_counts, x="日期", y="消息数量",
 | 
						||
                         title="每日消息数量趋势")
 | 
						||
            st.plotly_chart(fig, use_container_width=True)
 | 
						||
        
 | 
						||
        # 频道活跃度
 | 
						||
        st.subheader("📢 频道活跃度")
 | 
						||
        channel_counts = df.groupby("频道").size().reset_index()
 | 
						||
        channel_counts.columns = ["频道", "消息数量"]
 | 
						||
        channel_counts = channel_counts.sort_values("消息数量", ascending=True)
 | 
						||
        
 | 
						||
        fig = px.bar(channel_counts, x="消息数量", y="频道", 
 | 
						||
                    orientation='h', title="各频道消息数量")
 | 
						||
        st.plotly_chart(fig, use_container_width=True)
 | 
						||
    
 | 
						||
    else:
 | 
						||
        st.info("暂无消息数据用于分析")
 | 
						||
 | 
						||
def render_system_management(collab):
 | 
						||
    """渲染系统管理"""
 | 
						||
    st.header("⚙️ 系统管理")
 | 
						||
    
 | 
						||
    # 系统状态
 | 
						||
    st.subheader("🔍 系统状态")
 | 
						||
    
 | 
						||
    col1, col2, col3 = st.columns(3)
 | 
						||
    
 | 
						||
    with col1:
 | 
						||
        st.metric("活跃频道", len([c for c in collab.channels.values() if c.is_active]))
 | 
						||
    
 | 
						||
    with col2:
 | 
						||
        total_messages = sum(len(c.message_history) for c in collab.channels.values())
 | 
						||
        st.metric("总消息数", total_messages)
 | 
						||
    
 | 
						||
    with col3:
 | 
						||
        st.metric("工作流规则", len(collab.workflow_rules))
 | 
						||
    
 | 
						||
    # 频道管理
 | 
						||
    st.subheader("📢 频道管理")
 | 
						||
    
 | 
						||
    for channel_id, channel in collab.channels.items():
 | 
						||
        with st.expander(f"{channel.name} ({channel.channel_type.value})"):
 | 
						||
            col1, col2 = st.columns(2)
 | 
						||
            
 | 
						||
            with col1:
 | 
						||
                st.write(f"**描述**: {channel.description}")
 | 
						||
                st.write(f"**参与者**: {len(channel.participants)}人")
 | 
						||
                st.write(f"**消息数**: {len(channel.message_history)}")
 | 
						||
                st.write(f"**状态**: {'🟢 活跃' if channel.is_active else '🔴 禁用'}")
 | 
						||
            
 | 
						||
            with col2:
 | 
						||
                st.write("**参与者列表**:")
 | 
						||
                for participant in channel.participants:
 | 
						||
                    st.write(f"  • {participant.value}")
 | 
						||
                
 | 
						||
                st.write(f"**主持人**: {channel.moderator.value}")
 | 
						||
                st.write(f"**最后活动**: {channel.last_activity.strftime('%Y-%m-%d %H:%M')}")
 | 
						||
    
 | 
						||
    # 数据导出
 | 
						||
    st.divider()
 | 
						||
    st.subheader("📤 数据导出")
 | 
						||
    
 | 
						||
    if st.button("导出协作数据"):
 | 
						||
        # 准备导出数据
 | 
						||
        export_data = {
 | 
						||
            "channels": {},
 | 
						||
            "ai_status": {},
 | 
						||
            "workflow_rules": {},
 | 
						||
            "system_info": {
 | 
						||
                "current_phase": collab.current_phase.value,
 | 
						||
                "export_time": datetime.now().isoformat()
 | 
						||
            }
 | 
						||
        }
 | 
						||
        
 | 
						||
        # 频道数据
 | 
						||
        for channel_id, channel in collab.channels.items():
 | 
						||
            export_data["channels"][channel_id] = {
 | 
						||
                "name": channel.name,
 | 
						||
                "type": channel.channel_type.value,
 | 
						||
                "participants": [p.value for p in channel.participants],
 | 
						||
                "message_count": len(channel.message_history),
 | 
						||
                "last_activity": channel.last_activity.isoformat()
 | 
						||
            }
 | 
						||
        
 | 
						||
        # AI状态数据
 | 
						||
        for ai_role, status in collab.ai_status.items():
 | 
						||
            export_data["ai_status"][ai_role.value] = status
 | 
						||
        
 | 
						||
        # 工作流规则
 | 
						||
        for rule_id, rule in collab.workflow_rules.items():
 | 
						||
            export_data["workflow_rules"][rule_id] = {
 | 
						||
                "name": rule.name,
 | 
						||
                "description": rule.description,
 | 
						||
                "trigger_phase": rule.trigger_phase.value,
 | 
						||
                "action": rule.action,
 | 
						||
                "is_active": rule.is_active
 | 
						||
            }
 | 
						||
        
 | 
						||
        # 创建下载链接
 | 
						||
        json_str = json.dumps(export_data, indent=2, ensure_ascii=False)
 | 
						||
        st.download_button(
 | 
						||
            label="下载协作数据 (JSON)",
 | 
						||
            data=json_str,
 | 
						||
            file_name=f"ai_collaboration_data_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json",
 | 
						||
            mime="application/json"
 | 
						||
        )
 | 
						||
 | 
						||
if __name__ == "__main__":
 | 
						||
    main() |