#!/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"{tag}" 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()