liurenchaxin/jixia_academy/ui/streamlit/tabs/ai_collaboration_tab.py

509 lines
17 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.

#!/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()