feat(ui): 添加AI协作页签

新增AI协作功能模块,并在主界面中添加了对应的页签。
更新了OpenBB集成文档的路径,将其从单独的Markdown文件迁移到目录结构中。
为项目添加了新的测试依赖,包括pytest相关工具、locust和memory-profiler等。
This commit is contained in:
ben
2025-08-23 14:06:22 +00:00
parent 21128299a4
commit 09de1f782a
61 changed files with 5324 additions and 11 deletions

View File

@@ -0,0 +1,509 @@
#!/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()