🏗️ 项目重构:模块化清理完成

This commit is contained in:
llama-research
2025-09-01 12:29:27 +00:00
parent ef7657101a
commit f9856c31e5
349 changed files with 41438 additions and 254 deletions

View File

@@ -0,0 +1 @@
# 应用模块

View File

@@ -0,0 +1,228 @@
#!/usr/bin/env python3
"""
炼妖壶 (Lianyaohu) - 稷下学宫AI辩论系统
主Streamlit应用入口
重构版本:
- 清晰的模块化结构
- 统一的配置管理
- 安全的密钥处理
"""
import streamlit as st
import sys
from pathlib import Path
# 添加项目根目录到Python路径
project_root = Path(__file__).parent.parent
sys.path.insert(0, str(project_root))
def configure_page():
"""配置页面基本设置"""
st.set_page_config(
page_title="炼妖壶 - 稷下学宫AI辩论系统",
page_icon="🏛️",
layout="wide",
initial_sidebar_state="expanded"
)
def show_header():
"""显示页面头部"""
st.title("🏛️ 炼妖壶 - 稷下学宫AI辩论系统")
st.markdown("**基于中国哲学传统的多AI智能体辩论平台**")
# 显示系统状态
col1, col2, col3 = st.columns(3)
with col1:
st.metric("系统状态", "🟢 运行中")
with col2:
st.metric("AI模型", "OpenRouter")
with col3:
# 更新数据源展示,加入 OpenBB
st.metric("数据源", "RapidAPI + OpenBB")
def show_sidebar():
"""显示侧边栏"""
with st.sidebar:
st.markdown("## 🎛️ 控制面板")
# 系统信息
st.markdown("### 📊 系统信息")
st.info("**版本**: v2.0 (重构版)")
st.info("**状态**: 迁移完成")
# 配置检查
st.markdown("### 🔧 配置状态")
try:
from config.doppler_config import validate_config
if validate_config():
st.success("✅ 配置正常")
else:
st.error("❌ 配置异常")
except Exception as e:
st.warning(f"⚠️ 配置检查失败: {str(e)}")
# 快速操作
st.markdown("### ⚡ 快速操作")
if st.button("🧪 测试API连接"):
test_api_connections()
if st.button("🏛️ 启动八仙论道"):
start_jixia_debate()
def test_api_connections():
"""测试API连接"""
with st.spinner("正在测试API连接..."):
try:
from scripts.api_health_check import test_openrouter_api, test_rapidapi_connection
openrouter_ok = test_openrouter_api()
rapidapi_ok = test_rapidapi_connection()
if openrouter_ok and rapidapi_ok:
st.success("✅ 所有API连接正常")
else:
st.error("❌ 部分API连接失败")
except Exception as e:
st.error(f"❌ API测试异常: {str(e)}")
def start_jixia_debate():
"""启动稷下学宫辩论"""
with st.spinner("正在启动稷下学宫八仙论道..."):
try:
from config.settings import get_rapidapi_key
from src.jixia.engines.perpetual_engine import JixiaPerpetualEngine
api_key = get_rapidapi_key()
engine = JixiaPerpetualEngine(api_key)
# 运行辩论
results = engine.simulate_jixia_debate('TSLA')
st.success("✅ 八仙论道完成")
st.json(results)
except Exception as e:
st.error(f"❌ 辩论启动失败: {str(e)}")
def main():
"""主函数"""
configure_page()
show_header()
show_sidebar()
# 主内容区域
st.markdown("---")
# 选项卡(新增 OpenBB 数据页签和AI协作页签
tab1, tab2, tab3, tab4, tab5 = st.tabs(["🏛️ 稷下学宫", "🌍 天下体系", "📊 数据分析", "📈 OpenBB 数据", "🤖 AI协作"])
with tab1:
st.markdown("### 🏛️ 稷下学宫 - 八仙论道")
st.markdown("**多AI智能体辩论系统基于中国传统八仙文化**")
# 辩论模式选择
debate_mode = st.selectbox(
"选择辩论模式",
["ADK模式 (太上老君主持)", "传统模式 (RapidAPI数据)"]
)
if debate_mode == "ADK模式 (太上老君主持)":
from app.tabs.adk_debate_tab import render_adk_debate_tab
render_adk_debate_tab()
else:
# 传统模式
col1, col2 = st.columns([2, 1])
with col1:
topic = st.text_input("辩论主题 (股票代码)", value="TSLA")
with col2:
if st.button("🎭 开始辩论", type="primary"):
start_debate_session(topic)
# 显示辩论历史
if 'debate_history' in st.session_state:
st.markdown("#### 📜 辩论记录")
for record in st.session_state.debate_history[-3:]: # 显示最近3次
with st.expander(f"🎭 {record['topic']} - {record['time']}"):
st.json(record['results'])
with tab2:
st.markdown("### 🌍 天下体系分析")
try:
from app.tabs.tianxia_tab import render_tianxia_tab
render_tianxia_tab()
except Exception as e:
st.error(f"❌ 天下体系模块加载失败: {str(e)}")
with tab3:
st.markdown("### 📊 数据分析")
st.info("🚧 数据分析模块开发中...")
# 显示系统统计
try:
from config.settings import get_rapidapi_key
from src.jixia.engines.perpetual_engine import JixiaPerpetualEngine
api_key = get_rapidapi_key()
engine = JixiaPerpetualEngine(api_key)
stats = engine.get_usage_stats()
col1, col2, col3 = st.columns(3)
with col1:
st.metric("API调用总数", stats['total_calls'])
with col2:
st.metric("活跃API数", f"{stats['active_apis']}/{stats['total_apis']}")
with col3:
st.metric("未使用API", stats['unused_count'])
except Exception as e:
st.warning(f"⚠️ 无法加载统计数据: {str(e)}")
with tab5:
st.markdown("### 🤖 AI协作")
try:
from app.tabs.ai_collaboration_tab import main as ai_collaboration_main
ai_collaboration_main()
except Exception as e:
st.error(f"❌ AI协作模块加载失败: {str(e)}")
def start_debate_session(topic: str):
"""启动辩论会话"""
if not topic:
st.error("请输入辩论主题")
return
with st.spinner(f"🏛️ 八仙正在就 {topic} 展开论道..."):
try:
from config.settings import get_rapidapi_key
from src.jixia.engines.perpetual_engine import JixiaPerpetualEngine
from datetime import datetime
api_key = get_rapidapi_key()
engine = JixiaPerpetualEngine(api_key)
# 运行辩论
results = engine.simulate_jixia_debate(topic)
# 保存到会话状态
if 'debate_history' not in st.session_state:
st.session_state.debate_history = []
st.session_state.debate_history.append({
'topic': topic,
'time': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
'results': {name: {'success': result.success, 'api_used': result.api_used}
for name, result in results.items()}
})
st.success(f"✅ 八仙论道完成!共有 {len(results)} 位仙人参与")
# 显示结果摘要
successful_debates = sum(1 for result in results.values() if result.success)
st.info(f"📊 成功获取数据: {successful_debates}/{len(results)} 位仙人")
except Exception as e:
st.error(f"❌ 辩论启动失败: {str(e)}")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,205 @@
import streamlit as st
import asyncio
import sys
from pathlib import Path
from typing import Dict, Any, List
# Ensure the main project directory is in the Python path
project_root = Path(__file__).parent.parent.parent
sys.path.insert(0, str(project_root))
try:
from google.adk import Agent, Runner
from google.adk.sessions import InMemorySessionService, Session
from google.genai import types
ADK_AVAILABLE = True
except ImportError:
ADK_AVAILABLE = False
# 创建占位符类
class Agent:
pass
class Runner:
pass
class InMemorySessionService:
pass
class Session:
pass
class types:
class Content:
pass
class Part:
pass
async def _get_llm_reply(runner: Runner, session: Session, prompt: str) -> str:
"""Helper function to call a Runner and get a text reply."""
content = types.Content(role='user', parts=[types.Part(text=prompt)])
response = runner.run_async(
user_id=session.user_id,
session_id=session.id,
new_message=content
)
reply = ""
async for event in response:
if hasattr(event, 'content') and event.content and hasattr(event.content, 'parts'):
for part in event.content.parts:
if hasattr(part, 'text') and part.text:
reply += str(part.text)
elif hasattr(event, 'text') and event.text:
reply += str(event.text)
return reply.strip()
async def run_adk_debate_streamlit(topic: str, participants: List[str], rounds: int):
"""
Runs the ADK turn-based debate and yields each statement for Streamlit display.
"""
try:
yield "🚀 **启动ADK八仙轮流辩论 (太上老君主持)...**"
all_immortals = ["铁拐李", "吕洞宾", "何仙姑", "张果老", "蓝采和", "汉钟离", "韩湘子", "曹国舅"]
if not participants:
participants = all_immortals
character_configs = {
"太上老君": {"name": "太上老君", "model": "gemini-1.5-pro", "instruction": "你是太上老君天道化身辩论的主持人。你的言辞沉稳、公正、充满智慧。你的任务是1. 对辩论主题进行开场介绍。2. 在每轮开始时进行引导。3. 在辩论结束后,对所有观点进行全面、客观的总结。保持中立,不偏袒任何一方。"},
"铁拐李": {"name": "铁拐李", "model": "gemini-1.5-flash", "instruction": "你是铁拐李,八仙中的逆向思维专家。你善于从批判和质疑的角度看问题,发言风格直接、犀利,但富有智慧。"},
"吕洞宾": {"name": "吕洞宾", "model": "gemini-1.5-flash", "instruction": "你是吕洞宾,八仙中的理性分析者。你善于平衡各方观点,用理性和逻辑来分析问题,发言风格温和而深刻。"},
"何仙姑": {"name": "何仙姑", "model": "gemini-1.5-flash", "instruction": "你是何仙姑,八仙中的风险控制专家。你总是从风险管理的角度思考问题,善于发现潜在危险,发言风格谨慎、细致。"},
"张果老": {"name": "张果老", "model": "gemini-1.5-flash", "instruction": "你是张果老,八仙中的历史智慧者。你善于从历史数据中寻找规律和智慧,提供长期视角,发言风格沉稳、博学。"},
"蓝采和": {"name": "蓝采和", "model": "gemini-1.5-flash", "instruction": "你是蓝采和,八仙中的创新思维者。你善于从新兴视角和非传统方法来看待问题,发言风格活泼、新颖。"},
"汉钟离": {"name": "汉钟离", "model": "gemini-1.5-flash", "instruction": "你是汉钟离,八仙中的平衡协调者。你善于综合各方观点,寻求和谐统一的解决方案,发言风格平和、包容。"},
"韩湘子": {"name": "韩湘子", "model": "gemini-1.5-flash", "instruction": "你是韩湘子,八仙中的艺术感知者。你善于从美学和感性的角度分析问题,发言风格优雅、感性。"},
"曹国舅": {"name": "曹国舅", "model": "gemini-1.5-flash", "instruction": "你是曹国舅,八仙中的实务执行者。你关注实际操作和具体细节,发言风格务实、严谨。"}
}
session_service = InMemorySessionService()
session = await session_service.create_session(state={}, app_name="稷下学宫八仙论道系统-Streamlit", user_id="st_user")
runners: Dict[str, Runner] = {}
for name, config in character_configs.items():
if name == "太上老君" or name in participants:
agent = Agent(name=config["name"], model=config["model"], instruction=config["instruction"])
runners[name] = Runner(app_name="稷下学宫八仙论道系统-Streamlit", agent=agent, session_service=session_service)
host_runner = runners.get("太上老君")
if not host_runner:
yield "❌ **主持人太上老君初始化失败。**"
return
yield f"🎯 **参与仙人**: {', '.join(participants)}"
debate_history = []
# Opening statement
opening_prompt = f"请为本次关于“{topic}”的辩论,发表一段公正、深刻的开场白,并宣布辩论开始。"
opening_statement = await _get_llm_reply(host_runner, session, opening_prompt)
yield f"👑 **太上老君**: {opening_statement}"
# Debate rounds
for round_num in range(rounds):
round_intro_prompt = f"请为第 {round_num + 1} 轮辩论说一段引导语。"
round_intro = await _get_llm_reply(host_runner, session, round_intro_prompt)
yield f"👑 **太上老君**: {round_intro}"
for name in participants:
if name not in runners: continue
history_context = f"\n最近的论道内容:\n" + "\n".join([f"- {h}" for h in debate_history[-5:]]) if debate_history else ""
prompt = f"论道主题: {topic}{history_context}\n\n请从你的角色特点出发,简洁地发表观点。"
reply = await _get_llm_reply(runners[name], session, prompt)
yield f"🗣️ **{name}**: {reply}"
debate_history.append(f"{name}: {reply}")
await asyncio.sleep(1)
# Summary
summary_prompt = f"辩论已结束。以下是完整的辩论记录:\n\n{' '.join(debate_history)}\n\n请对本次辩论进行全面、公正、深刻的总结。"
summary = await _get_llm_reply(host_runner, session, summary_prompt)
yield f"👑 **太上老君**: {summary}"
for runner in runners.values():
await runner.close()
yield "🎉 **ADK八仙轮流辩论完成!**"
except Exception as e:
yield f"❌ **运行ADK八仙轮流辩论失败**: {e}"
import traceback
st.error(traceback.format_exc())
def render_adk_debate_tab():
"""Renders the Streamlit UI for the ADK Debate tab."""
# 检查 ADK 是否可用
if not ADK_AVAILABLE:
st.error("🚫 Google ADK 模块未安装或不可用")
st.info("📦 正在安装 Google ADK请稍候...")
st.info("💡 安装完成后请刷新页面")
with st.expander("📋 安装说明"):
st.code("""
# 安装 Google ADK
pip install google-adk>=1.12.0
# 或从 GitHub 安装开发版
pip install git+https://github.com/google/adk-python.git@main
""")
return
st.markdown("### 🏛️ 八仙论道 (ADK版 - 太上老君主持)")
topic = st.text_input(
"辩论主题",
value="AI是否应该拥有创造力",
key="adk_topic_input"
)
all_immortals = ["铁拐李", "吕洞宾", "何仙姑", "张果老", "蓝采和", "汉钟离", "韩湘子", "曹国舅"]
col1, col2 = st.columns(2)
with col1:
rounds = st.number_input("辩论轮数", min_value=1, max_value=5, value=1, key="adk_rounds_input")
with col2:
participants = st.multiselect(
"选择参与的仙人 (默认全选)",
options=all_immortals,
default=all_immortals,
key="adk_participants_select"
)
if st.button("🚀 开始论道", key="start_adk_debate_button", type="primary"):
if not topic:
st.error("请输入辩论主题。")
return
if not participants:
st.error("请至少选择一位参与的仙人。")
return
st.markdown("---")
st.markdown("#### 📜 论道实录")
# Placeholder for real-time output
output_container = st.empty()
full_log = ""
# Run the async debate function
try:
# Get a new event loop for the thread
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
# Create a coroutine object
coro = run_adk_debate_streamlit(topic, participants, rounds)
# Run the coroutine until completion
for message in loop.run_until_complete(async_generator_to_list(coro)):
full_log += message + "\n\n"
output_container.markdown(full_log)
except Exception as e:
st.error(f"启动辩论时发生错误: {e}")
async def async_generator_to_list(async_gen):
"""Helper to consume an async generator and return a list of its items."""
return [item async for item in async_gen]

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()

View File

@@ -0,0 +1,184 @@
import streamlit as st
import pandas as pd
import plotly.express as px
from datetime import datetime, timedelta
def _check_openbb_installed() -> bool:
try:
# OpenBB v4 推荐用法: from openbb import obb
from openbb import obb # noqa: F401
return True
except Exception:
return False
def _load_price_data(symbol: str, days: int = 365) -> pd.DataFrame:
"""Fetch OHLCV using OpenBB v4 when available; otherwise return demo/synthetic data."""
end = datetime.utcnow().date()
start = end - timedelta(days=days)
# 优先使用 OpenBB v4
try:
from openbb import obb
# 先尝试股票路由
try:
out = obb.equity.price.historical(
symbol,
start_date=str(start),
end_date=str(end),
)
except Exception:
out = None
# 若股票无数据,再尝试 ETF 路由
if out is None or (hasattr(out, "is_empty") and out.is_empty):
try:
out = obb.etf.price.historical(
symbol,
start_date=str(start),
end_date=str(end),
)
except Exception:
out = None
if out is not None:
if hasattr(out, "to_df"):
df = out.to_df()
elif hasattr(out, "to_dataframe"):
df = out.to_dataframe()
else:
# 兜底: 有些 provider 返回可序列化对象
df = pd.DataFrame(out) # type: ignore[arg-type]
# 规格化列名
if not isinstance(df, pd.DataFrame) or df.empty:
raise ValueError("OpenBB 返回空数据")
# 有的表以 index 为日期
if 'date' in df.columns:
df['Date'] = pd.to_datetime(df['date'])
elif df.index.name in ('date', 'Date') or isinstance(df.index, pd.DatetimeIndex):
df = df.copy()
df['Date'] = pd.to_datetime(df.index)
else:
# 尝试查找常见日期列
for cand in ['timestamp', 'time', 'datetime']:
if cand in df.columns:
df['Date'] = pd.to_datetime(df[cand])
break
# 归一化收盘价列
close_col = None
for cand in ['adj_close', 'close', 'Close', 'price', 'close_price', 'c']:
if cand in df.columns:
close_col = cand
break
if close_col is None:
raise ValueError("未找到收盘价列")
df['Close'] = pd.to_numeric(df[close_col], errors='coerce')
# 仅保留需要列并清洗
if 'Date' not in df.columns:
raise ValueError("未找到日期列")
df = df[['Date', 'Close']].dropna()
df = df.sort_values('Date').reset_index(drop=True)
# 限定时间窗口(有些 provider 可能返回更长区间)
df = df[df['Date'].dt.date.between(start, end)]
if df.empty:
raise ValueError("清洗后为空")
return df
except Exception:
# 如果 OpenBB 不可用或调用失败,进入本地演示/合成数据兜底
pass
# Fallback to demo from examples/data
try:
from pathlib import Path
root = Path(__file__).resolve().parents[2]
demo_map = {
'AAPL': root / 'examples' / 'data' / 'demo_results_aapl.json',
'MSFT': root / 'examples' / 'data' / 'demo_results_msft.json',
'TSLA': root / 'examples' / 'data' / 'demo_results_tsla.json',
}
path = demo_map.get(symbol.upper())
if path and path.exists():
df = pd.read_json(path)
if 'date' in df.columns:
df['Date'] = pd.to_datetime(df['date'])
if 'close' in df.columns:
df['Close'] = df['close']
df = df[['Date', 'Close']].dropna().sort_values('Date').reset_index(drop=True)
# 裁剪到时间窗口
df = df[df['Date'].dt.date.between(start, end)]
return df
except Exception:
pass
# Last resort: minimal synthetic data避免 FutureWarning
dates = pd.date_range(end=end, periods=min(days, 180))
return pd.DataFrame({
'Date': dates,
'Close': pd.Series(range(len(dates))).rolling(5).mean().bfill()
})
def _kpis_from_df(df: pd.DataFrame) -> dict:
if df.empty or 'Close' not in df.columns:
return {"最新价": "-", "近30日涨幅": "-", "最大回撤(近90日)": "-"}
latest = float(df['Close'].iloc[-1])
last_30 = df.tail(30)
if len(last_30) > 1:
pct_30 = (last_30['Close'].iloc[-1] / last_30['Close'].iloc[0] - 1) * 100
else:
pct_30 = 0.0
# max drawdown over last 90 days
lookback = df.tail(90)['Close']
roll_max = lookback.cummax()
drawdown = (lookback / roll_max - 1).min() * 100
return {
"最新价": f"{latest:,.2f}",
"近30日涨幅": f"{pct_30:.2f}%",
"最大回撤(近90日)": f"{drawdown:.2f}%",
}
def render_openbb_tab():
st.write("使用 OpenBB如可用或演示数据展示市场概览。")
col_a, col_b = st.columns([2, 1])
with col_b:
symbol = st.text_input("股票/ETF 代码", value="AAPL")
days = st.slider("时间窗口(天)", 90, 720, 365, step=30)
obb_ready = _check_openbb_installed()
if obb_ready:
st.success("OpenBB 已安装 ✅")
else:
st.info("未检测到 OpenBB将使用演示数据。可在 requirements.txt 中加入 openbb 后安装启用。")
with col_a:
df = _load_price_data(symbol, days)
if df is None or df.empty:
st.warning("未获取到数据")
return
# 绘制收盘价
if 'Date' in df.columns and 'Close' in df.columns:
fig = px.line(df, x='Date', y='Close', title=f"{symbol.upper()} 收盘价")
st.plotly_chart(fig, use_container_width=True)
else:
st.dataframe(df.head())
# KPI 卡片
st.markdown("#### 关键指标")
kpis = _kpis_from_df(df)
k1, k2, k3 = st.columns(3)
k1.metric("最新价", kpis["最新价"])
k2.metric("近30日涨幅", kpis["近30日涨幅"])
k3.metric("最大回撤(近90日)", kpis["最大回撤(近90日)"])
# 未来:基本面、新闻、情绪等组件占位
with st.expander("🚧 更多组件(即将推出)"):
st.write("基本面卡片、新闻与情绪、宏观指标、策略筛选等将逐步接入。")

View File

@@ -0,0 +1,436 @@
"""
天下体系 - 儒门天下观资本生态分析Tab
基于"天命树"结构模型分析全球资本市场权力结构
重构版本:
- 移除硬编码API密钥
- 使用统一配置管理
- 改进数据结构
- 增强错误处理
"""
import streamlit as st
import pandas as pd
import plotly.express as px
from datetime import datetime
import time
import random
from typing import Dict, List, Any, Optional
from dataclasses import dataclass
# 导入配置管理
try:
from config.settings import get_rapidapi_key
except ImportError:
# 如果配置模块不可用,使用环境变量
import os
def get_rapidapi_key():
return os.getenv('RAPIDAPI_KEY', '')
@dataclass
class StockEntity:
"""股票实体数据类"""
symbol: str
name: str
role: str
dependency: Optional[str] = None
serves: Optional[str] = None
type: Optional[str] = None
@dataclass
class EcosystemData:
"""生态系统数据类"""
tianzi: Dict[str, str]
dafu: List[StockEntity]
shi: List[StockEntity]
jiajie: List[StockEntity]
class TianxiaAnalyzer:
"""天下体系分析器 - 天命树结构分析"""
def __init__(self):
"""初始化分析器"""
try:
self.rapidapi_key = get_rapidapi_key()
except Exception:
self.rapidapi_key = ""
st.warning("⚠️ 未配置RapidAPI密钥将使用模拟数据")
# 定义三大天命树生态系统
self.ecosystems = self._initialize_ecosystems()
def _initialize_ecosystems(self) -> Dict[str, EcosystemData]:
"""初始化生态系统数据"""
return {
'AI': EcosystemData(
tianzi={'symbol': 'NVDA', 'name': 'NVIDIA', 'tianming': 'CUDA + GPU硬件定义AI计算范式'},
dafu=[
StockEntity('TSM', 'TSMC', '芯片代工', '高端芯片唯一代工厂'),
StockEntity('000660.SZ', 'SK Hynix', 'HBM内存', 'GPU性能关键'),
StockEntity('MU', 'Micron', 'HBM内存', 'GPU性能关键'),
StockEntity('SMCI', 'Supermicro', '服务器集成', 'GPU转化为计算能力')
],
shi=[
StockEntity('ASML', 'ASML', '光刻设备', serves='TSMC'),
StockEntity('AMAT', 'Applied Materials', '半导体设备', serves='TSMC')
],
jiajie=[
StockEntity('AMD', 'AMD', '竞争对手', type='竞争天子'),
StockEntity('GOOGL', 'Google', '云计算', type='云计算天子'),
StockEntity('AMZN', 'Amazon', '云计算', type='云计算天子')
]
),
'EV': EcosystemData(
tianzi={'symbol': 'TSLA', 'name': 'Tesla', 'tianming': '软件定义汽车 + 超级充电网络'},
dafu=[
StockEntity('300750.SZ', 'CATL', '动力电池', '动力系统基石'),
StockEntity('6752.T', 'Panasonic', '动力电池', '动力系统基石'),
StockEntity('ALB', 'Albemarle', '锂矿', '源头命脉'),
StockEntity('002460.SZ', 'Ganfeng Lithium', '锂矿', '源头命脉')
],
shi=[
StockEntity('002497.SZ', 'Yahua Industrial', '氢氧化锂', serves='CATL'),
StockEntity('002850.SZ', 'Kedali', '精密结构件', serves='CATL')
],
jiajie=[
StockEntity('002594.SZ', 'BYD', '电动车', type='诸侯'),
StockEntity('VWAGY', 'Volkswagen', '传统车企', type='诸侯'),
StockEntity('F', 'Ford', '传统车企', type='诸侯')
]
),
'Consumer_Electronics': EcosystemData(
tianzi={'symbol': 'AAPL', 'name': 'Apple', 'tianming': 'iOS + App Store生态系统'},
dafu=[
StockEntity('2317.TW', 'Foxconn', '代工制造', '物理执行者'),
StockEntity('TSM', 'TSMC', '芯片代工', '性能优势保障'),
StockEntity('005930.KS', 'Samsung Display', '屏幕供应', '显示技术'),
StockEntity('QCOM', 'Qualcomm', '基带芯片', '通信命脉')
],
shi=[
StockEntity('002475.SZ', 'Luxshare', '精密制造', serves='Foxconn'),
StockEntity('002241.SZ', 'Goertek', '声学器件', serves='Foxconn')
],
jiajie=[
StockEntity('005930.KS', 'Samsung', '手机制造', type='亦敌亦友天子'),
StockEntity('1810.HK', 'Xiaomi', '手机制造', type='诸侯'),
StockEntity('NVDA', 'NVIDIA', 'AI芯片', type='跨生态天子')
]
)
}
def get_stock_data(self, symbol: str) -> Dict[str, Any]:
"""
获取股票数据
Args:
symbol: 股票代码
Returns:
股票数据字典
"""
# TODO: 实现真实API调用
# 目前使用模拟数据
try:
return {
'price': round(random.uniform(50, 500), 2),
'change_pct': round(random.uniform(-5, 5), 2),
'market_cap': f"{random.randint(100, 3000)}B",
'volume': random.randint(1000000, 100000000)
}
except Exception:
return {
'price': 'N/A',
'change_pct': 0,
'market_cap': 'N/A',
'volume': 'N/A'
}
def create_tianming_card(self, ecosystem_name: str, ecosystem_data: EcosystemData) -> None:
"""
创建天命卡片
Args:
ecosystem_name: 生态系统名称
ecosystem_data: 生态系统数据
"""
tianzi = ecosystem_data.tianzi
stock_data = self.get_stock_data(tianzi['symbol'])
st.markdown(f"### 👑 {ecosystem_name} 天命树")
# 天子信息
col1, col2, col3 = st.columns([1, 2, 1])
with col1:
st.markdown("#### 🌟 天子")
st.markdown(f"**{tianzi['name']}** ({tianzi['symbol']})")
with col2:
st.markdown("#### 📜 天命")
st.info(tianzi['tianming'])
with col3:
st.metric(
label="股价",
value=f"${stock_data['price']}",
delta=f"{stock_data['change_pct']:+.2f}%"
)
# 大夫层级
if ecosystem_data.dafu:
st.markdown("#### 🏛️ 大夫 (核心依赖)")
dafu_cols = st.columns(min(len(ecosystem_data.dafu), 4))
for i, dafu in enumerate(ecosystem_data.dafu):
col_index = i % 4
with dafu_cols[col_index]:
data = self.get_stock_data(dafu.symbol)
st.metric(
label=f"{dafu.name}",
value=f"${data['price']}",
delta=f"{data['change_pct']:+.2f}%"
)
st.caption(f"**{dafu.role}**: {dafu.dependency}")
# 士层级
if ecosystem_data.shi:
st.markdown("#### ⚔️ 士 (专业供应商)")
shi_cols = st.columns(min(len(ecosystem_data.shi), 3))
for i, shi in enumerate(ecosystem_data.shi):
col_index = i % 3
with shi_cols[col_index]:
data = self.get_stock_data(shi.symbol)
st.metric(
label=f"{shi.name}",
value=f"${data['price']}",
delta=f"{data['change_pct']:+.2f}%"
)
st.caption(f"**{shi.role}** → 服务于{shi.serves}")
# 嫁接关系
if ecosystem_data.jiajie:
st.markdown("#### 🔗 嫁接关系 (跨生态链接)")
jiajie_cols = st.columns(min(len(ecosystem_data.jiajie), 4))
for i, jiajie in enumerate(ecosystem_data.jiajie):
col_index = i % 4
with jiajie_cols[col_index]:
data = self.get_stock_data(jiajie.symbol)
st.metric(
label=f"{jiajie.name}",
value=f"${data['price']}",
delta=f"{data['change_pct']:+.2f}%"
)
st.caption(f"**{jiajie.type}**")
st.markdown("---")
def create_tianming_tree_table(self) -> pd.DataFrame:
"""
创建天命树完整表格 - 用于投资组合去相关性分析
Returns:
包含所有股票信息的DataFrame
"""
st.markdown("### 📋 天命树完整表格 - 投资组合去相关性分析")
st.markdown("**核心理念**: 投资组合的本质是去相关性 - 从不同root下的不同spine下的不同leaf进行配置")
all_stocks = []
for eco_name, eco_data in self.ecosystems.items():
# 天子
tianzi = eco_data.tianzi
stock_data = self.get_stock_data(tianzi['symbol'])
all_stocks.append({
'Root': eco_name,
'Level': '👑 天子',
'Symbol': tianzi['symbol'],
'Company': tianzi['name'],
'Role': '定义范式',
'Dependency_Path': f"{eco_name}",
'Price': stock_data['price'],
'Change%': stock_data['change_pct'],
'Market_Cap': stock_data['market_cap'],
'Correlation_Risk': '极高 - 生态核心'
})
# 大夫
for dafu in eco_data.dafu:
stock_data = self.get_stock_data(dafu.symbol)
all_stocks.append({
'Root': eco_name,
'Level': '🏛️ 大夫',
'Symbol': dafu.symbol,
'Company': dafu.name,
'Role': dafu.role,
'Dependency_Path': f"{eco_name}{tianzi['name']}{dafu.name}",
'Price': stock_data['price'],
'Change%': stock_data['change_pct'],
'Market_Cap': stock_data['market_cap'],
'Correlation_Risk': '高 - 深度绑定天子'
})
# 士
for shi in eco_data.shi:
stock_data = self.get_stock_data(shi.symbol)
all_stocks.append({
'Root': eco_name,
'Level': '⚔️ 士',
'Symbol': shi.symbol,
'Company': shi.name,
'Role': shi.role,
'Dependency_Path': f"{eco_name}{shi.serves}{shi.name}",
'Price': stock_data['price'],
'Change%': stock_data['change_pct'],
'Market_Cap': stock_data['market_cap'],
'Correlation_Risk': '中 - 专业供应商'
})
# 嫁接
for jiajie in eco_data.jiajie:
stock_data = self.get_stock_data(jiajie.symbol)
all_stocks.append({
'Root': '🔗 跨生态',
'Level': '🔗 嫁接',
'Symbol': jiajie.symbol,
'Company': jiajie.name,
'Role': jiajie.type or jiajie.role,
'Dependency_Path': f"多生态嫁接 → {jiajie.name}",
'Price': stock_data['price'],
'Change%': stock_data['change_pct'],
'Market_Cap': stock_data['market_cap'],
'Correlation_Risk': '低 - 多元化依赖'
})
df = pd.DataFrame(all_stocks)
# 显示表格
st.dataframe(
df,
use_container_width=True,
column_config={
"Root": st.column_config.TextColumn("生态根节点", width="small"),
"Level": st.column_config.TextColumn("层级", width="small"),
"Symbol": st.column_config.TextColumn("代码", width="small"),
"Company": st.column_config.TextColumn("公司", width="medium"),
"Role": st.column_config.TextColumn("角色", width="medium"),
"Dependency_Path": st.column_config.TextColumn("依赖路径", width="large"),
"Price": st.column_config.NumberColumn("股价", format="$%.2f"),
"Change%": st.column_config.NumberColumn("涨跌幅", format="%.2f%%"),
"Market_Cap": st.column_config.TextColumn("市值", width="small"),
"Correlation_Risk": st.column_config.TextColumn("相关性风险", width="medium")
}
)
return df
def render_tianxia_tab() -> None:
"""渲染天下体系Tab"""
# 页面标题
st.markdown("### 🏛️ 天下体系 - 儒门天下观资本生态分析")
st.markdown("**基于'天命树'结构模型,穿透市场表象,绘制全球资本市场真实的权力结构**")
st.markdown("---")
# 初始化分析器
analyzer = TianxiaAnalyzer()
# 控制面板
col1, col2, col3 = st.columns([1, 1, 2])
with col1:
auto_refresh = st.checkbox("🔄 自动刷新", value=False, key="tianxia_auto_refresh")
with col2:
if st.button("🏛️ 扫描天下", type="primary", key="tianxia_scan_btn"):
st.session_state.trigger_tianxia_scan = True
with col3:
st.markdown("*正在分析全球资本生态权力结构...*")
# 理论介绍
with st.expander("📚 天命树理论基础"):
st.markdown("""
### 🏛️ 儒门天下观核心思想
**两大哲学基石:**
1. **结构非平权**: 资本宇宙本质是不平权的、层级森严的树状结构
2. **天命与脉络**: 每个生态都有唯一的"根节点"(天子),拥有定义整个生态的"天命"
**四层架构:**
- **👑 天子**: 定义范式的平台型公司 (如Apple, NVIDIA, Tesla)
- **🏛️ 大夫**: 深度绑定天子的核心供应商 (如TSMC, CATL)
- **⚔️ 士**: 专业供应商和服务商 (如ASML, Luxshare)
- **🔗 嫁接**: 跨生态的策略性链接关系
""")
# 自动刷新逻辑
if auto_refresh:
time.sleep(60)
st.rerun()
# 触发扫描或显示数据
if st.session_state.get('trigger_tianxia_scan', False) or 'tianxia_scan_time' not in st.session_state:
with st.spinner("🏛️ 正在扫描天下体系..."):
st.session_state.tianxia_scan_time = datetime.now()
st.session_state.trigger_tianxia_scan = False
# 显示扫描时间
if 'tianxia_scan_time' in st.session_state:
st.info(f"📅 最后扫描时间: {st.session_state.tianxia_scan_time.strftime('%Y-%m-%d %H:%M:%S')}")
# 显示三大生态系统
st.markdown("## 🌍 三大天命树生态系统")
# 分析模式选择
analysis_mode = st.selectbox(
"选择分析模式",
["生态系统分析", "投资组合去相关性分析"],
key="tianxia_analysis_mode"
)
if analysis_mode == "生态系统分析":
# 生态系统选择
selected_ecosystem = st.selectbox(
"选择要分析的生态系统",
["全部", "AI", "EV", "Consumer_Electronics"],
format_func=lambda x: {
"全部": "🌍 全部生态系统",
"AI": "🤖 AI人工智能生态",
"EV": "⚡ 电动汽车生态",
"Consumer_Electronics": "📱 消费电子生态"
}[x],
key="tianxia_ecosystem_select"
)
if selected_ecosystem == "全部":
# 显示所有生态系统
for eco_name, eco_data in analyzer.ecosystems.items():
analyzer.create_tianming_card(eco_name, eco_data)
else:
# 显示选定的生态系统
analyzer.create_tianming_card(selected_ecosystem, analyzer.ecosystems[selected_ecosystem])
else: # 投资组合去相关性分析
st.markdown("## 🎯 投资组合去相关性分析")
st.info("**核心理念**: 真正的分散投资是从不同的root天子下的不同spine大夫下的不同leaf进行配置")
# 创建完整天命树表格
df = analyzer.create_tianming_tree_table()
# 页面底部说明
st.markdown("---")
st.markdown("""
### 🎯 天下体系核心洞察
**权力结构分析**
- **AI生态**: NVIDIA通过CUDA平台统治AI计算TSMC是关键"嫁接"节点
- **电动车生态**: Tesla定义软件汽车范式CATL掌握电池命脉
- **消费电子生态**: Apple建立iOS护城河供应链高度集中化
**投资策略启示**
1. **投资天子**: 寻找定义范式的平台型公司
2. **关注大夫**: 深度绑定天子的核心供应商往往被低估
3. **警惕嫁接**: 被多个天子"嫁接"的公司风险与机会并存
4. **避开士层**: 缺乏议价能力的专业供应商投资价值有限
⚠️ **免责声明**: 天下体系分析仅供参考,投资有风险,决策需谨慎!
""")

View File

@@ -0,0 +1,283 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI Agent协作框架 - 真实的多Agent协作系统</title>
<link rel="stylesheet" href="styles.css">
<link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
</head>
<body>
<!-- Navigation -->
<nav class="navbar">
<div class="nav-container">
<div class="nav-logo">
<span class="logo-text">🤖 AI Agent协作</span>
</div>
<ul class="nav-menu">
<li><a href="#home">首页</a></li>
<li><a href="#features">特性</a></li>
<li><a href="#demo">演示</a></li>
<li><a href="#docs">文档</a></li>
<li><a href="#github" class="github-link">GitHub</a></li>
</ul>
<div class="hamburger">
<span></span>
<span></span>
<span></span>
</div>
</div>
</nav>
<!-- Hero Section -->
<section id="home" class="hero">
<div class="hero-container">
<div class="hero-content">
<h1 class="hero-title">
从模拟到真实<br>
<span class="gradient-text">让每个AI Agent都拥有独立Git身份</span>
</h1>
<p class="hero-subtitle">
不是让AI Agent假装协作而是让每个Agent都有真实的Git身份<br>
独立的SSH密钥、GPG签名、用户名和邮箱实现可追溯的团队协作历史
</p>
<div class="hero-buttons">
<button class="btn btn-primary" onclick="startDemo()">
🚀 立即体验
</button>
<button class="btn btn-secondary" onclick="scrollToSection('features')">
📖 了解更多
</button>
</div>
<div class="hero-stats">
<div class="stat">
<span class="stat-number">4</span>
<span class="stat-label">预定义Agent</span>
</div>
<div class="stat">
<span class="stat-number">100%</span>
<span class="stat-label">真实Git提交</span>
</div>
<div class="stat">
<span class="stat-number">一键</span>
<span class="stat-label">快速启动</span>
</div>
</div>
</div>
<div class="hero-visual">
<div class="terminal-window">
<div class="terminal-header">
<div class="terminal-buttons">
<span></span>
<span></span>
<span></span>
</div>
<div class="terminal-title">Agent协作演示</div>
</div>
<div class="terminal-content">
<div class="terminal-line">
<span class="prompt">$</span>
<span class="command">./agents/switch_agent.sh claude-ai</span>
</div>
<div class="terminal-line">
<span class="prompt">claude-ai@framework</span>
<span class="command">git commit -m "设计系统架构"</span>
</div>
<div class="terminal-line">
<span class="prompt">$</span>
<span class="command">./agents/switch_agent.sh gemini-dev</span>
</div>
<div class="terminal-line">
<span class="prompt">gemini-dev@framework</span>
<span class="command">git commit -m "实现核心功能"</span>
</div>
<div class="terminal-line success">
✅ 真实的协作历史已创建
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Features Section -->
<section id="features" class="features">
<div class="container">
<h2 class="section-title">核心特性</h2>
<div class="features-grid">
<div class="feature-card">
<div class="feature-icon">🔐</div>
<h3>真实身份系统</h3>
<p>每个Agent拥有独立的SSH密钥、GPG签名和Git配置确保每次提交都可追溯到具体Agent</p>
<ul>
<li>独立SSH密钥对</li>
<li>GPG签名验证</li>
<li>独立Git配置</li>
<li>完整审计日志</li>
</ul>
</div>
<div class="feature-card">
<div class="feature-icon">🎭</div>
<h3>专业化角色</h3>
<p>预定义四种专业化Agent角色每个都有明确的责任边界和专长领域</p>
<div class="roles-list">
<div class="role-item">
<strong>claude-ai</strong> - 架构师
</div>
<div class="role-item">
<strong>gemini-dev</strong> - 开发者
</div>
<div class="role-item">
<strong>qwen-ops</strong> - 运维
</div>
<div class="role-item">
<strong>llama-research</strong> - 研究员
</div>
</div>
</div>
<div class="feature-card">
<div class="feature-icon">📊</div>
<h3>实时协作监控</h3>
<p>实时查看所有Agent的协作状态、提交统计和代码贡献分析</p>
<div class="stats-preview">
<div class="stat-bar">
<span>claude-ai</span>
<div class="bar"><div style="width: 25%"></div></div>
<span>5次提交</span>
</div>
<div class="stat-bar">
<span>gemini-dev</span>
<div class="bar"><div style="width: 40%"></div></div>
<span>8次提交</span>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Demo Section -->
<section id="demo" class="demo">
<div class="container">
<h2 class="section-title">实时演示</h2>
<div class="demo-container">
<div class="demo-panel">
<h3>Agent状态监控</h3>
<div id="agent-status" class="agent-grid">
<!-- 动态加载Agent状态 -->
</div>
</div>
<div class="demo-panel">
<h3>最近活动</h3>
<div id="recent-activity" class="activity-feed">
<!-- 动态加载活动数据 -->
</div>
</div>
</div>
<div class="demo-actions">
<button class="btn btn-primary" onclick="runDemo()">
🎬 运行演示
</button>
<button class="btn btn-secondary" onclick="resetDemo()">
🔄 重置演示
</button>
</div>
</div>
</section>
<!-- Quick Start Section -->
<section class="quick-start">
<div class="container">
<h2 class="section-title">快速开始</h2>
<div class="install-options">
<div class="install-card">
<h3>🚀 一键安装</h3>
<div class="code-block">
<pre><code class="language-bash">curl -fsSL https://raw.githubusercontent.com/your-org/agent-collaboration-framework/main/install.sh | bash</code></pre>
</div>
</div>
<div class="install-card">
<h3>📦 手动安装</h3>
<div class="code-block">
<pre><code class="language-bash">git clone https://github.com/your-org/agent-collaboration-framework.git
cd agent-collaboration-framework
./install.sh</code></pre>
</div>
</div>
<div class="install-card">
<h3>🐳 Docker</h3>
<div class="code-block">
<pre><code class="language-bash">docker run -it -v $(pwd):/workspace agent-collaboration:latest</code></pre>
</div>
</div>
</div>
</div>
</section>
<!-- Documentation Section -->
<section id="docs" class="documentation">
<div class="container">
<h2 class="section-title">文档中心</h2>
<div class="docs-grid">
<div class="doc-card">
<h3>📚 快速入门</h3>
<p>从零开始了解AI Agent协作框架</p>
<a href="docs/quickstart.html" class="doc-link">开始阅读 →</a>
</div>
<div class="doc-card">
<h3>🏗️ 架构指南</h3>
<p>深入理解系统架构和设计原理</p>
<a href="docs/architecture.html" class="doc-link">查看详情 →</a>
</div>
<div class="doc-card">
<h3>🔧 API文档</h3>
<p>完整的API参考和开发指南</p>
<a href="docs/api.html" class="doc-link">浏览API →</a>
</div>
<div class="doc-card">
<h3>🎯 使用案例</h3>
<p>真实场景下的使用示例和最佳实践</p>
<a href="docs/examples.html" class="doc-link">查看案例 →</a>
</div>
</div>
</div>
</section>
<!-- Footer -->
<footer class="footer">
<div class="container">
<div class="footer-content">
<div class="footer-section">
<h4>AI Agent协作框架</h4>
<p>让AI Agent拥有真实的Git身份实现可追溯的团队协作</p>
</div>
<div class="footer-section">
<h4>快速链接</h4>
<ul>
<li><a href="#home">首页</a></li>
<li><a href="#features">特性</a></li>
<li><a href="#demo">演示</a></li>
<li><a href="#docs">文档</a></li>
</ul>
</div>
<div class="footer-section">
<h4>社区</h4>
<ul>
<li><a href="https://github.com/your-org/agent-collaboration-framework">GitHub</a></li>
<li><a href="https://github.com/your-org/agent-collaboration-framework/issues">问题反馈</a></li>
<li><a href="https://github.com/your-org/agent-collaboration-framework/discussions">讨论</a></li>
</ul>
</div>
</div>
<div class="footer-bottom">
<p>&copy; 2024 AI Agent协作框架. 开源项目,欢迎贡献</p>
</div>
</div>
</footer>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-bash.min.js"></script>
<script src="script.js"></script>
</body>
</html>

View File

@@ -0,0 +1,345 @@
// 全局变量
let agents = [
{ id: 'code-reviewer', name: 'Code Reviewer', status: 'active', commits: 12, tasks: 3 },
{ id: 'test-runner', name: 'Test Runner', status: 'active', commits: 8, tasks: 5 },
{ id: 'deploy-bot', name: 'Deploy Bot', status: 'inactive', commits: 3, tasks: 0 },
{ id: 'doc-writer', name: 'Doc Writer', status: 'active', commits: 15, tasks: 2 }
];
let activities = [
{ type: 'commit', message: 'Code Reviewer pushed 3 new commits', time: '2 minutes ago' },
{ type: 'test', message: 'Test Runner completed 5 test cases', time: '5 minutes ago' },
{ type: 'deploy', message: 'Deploy Bot deployed to staging', time: '10 minutes ago' },
{ type: 'docs', message: 'Doc Writer updated API documentation', time: '15 minutes ago' }
];
// 初始化
document.addEventListener('DOMContentLoaded', function() {
initializeDemo();
initializeTerminal();
initializeStats();
setupScrollEffects();
});
// 演示区域初始化
function initializeDemo() {
updateAgentGrid();
updateActivityFeed();
// 每3秒更新一次演示数据
setInterval(updateDemoData, 3000);
}
// 更新代理网格
function updateAgentGrid() {
const grid = document.getElementById('agent-grid');
if (!grid) return;
grid.innerHTML = '';
agents.forEach(agent => {
const card = createAgentCard(agent);
grid.appendChild(card);
});
}
// 创建代理卡片
function createAgentCard(agent) {
const card = document.createElement('div');
card.className = `agent-card agent-status-${agent.status}`;
const statusIcon = agent.status === 'active' ? '🟢' : '🔴';
card.innerHTML = `
<div>
<strong>${agent.name}</strong>
<div style="font-size: 0.9rem; color: #666;">
${statusIcon} ${agent.status}
</div>
</div>
<div style="text-align: right; font-size: 0.9rem;">
<div>📊 ${agent.commits} commits</div>
<div>📝 ${agent.tasks} tasks</div>
</div>
`;
return card;
}
// 更新活动流
function updateActivityFeed() {
const feed = document.getElementById('activity-feed');
if (!feed) return;
feed.innerHTML = '';
activities.forEach(activity => {
const item = createActivityItem(activity);
feed.appendChild(item);
});
}
// 创建活动项
function createActivityItem(activity) {
const item = document.createElement('div');
item.className = 'activity-item';
const icon = getActivityIcon(activity.type);
item.innerHTML = `
<div style="display: flex; align-items: center; gap: 0.5rem;">
<span>${icon}</span>
<span>${activity.message}</span>
</div>
<div style="font-size: 0.8rem; color: #666; margin-top: 0.5rem;">
${activity.time}
</div>
`;
return item;
}
// 获取活动图标
function getActivityIcon(type) {
const icons = {
'commit': '💻',
'test': '🧪',
'deploy': '🚀',
'docs': '📚'
};
return icons[type] || '📝';
}
// 更新演示数据
function updateDemoData() {
// 随机更新代理状态
agents.forEach(agent => {
if (Math.random() > 0.7) {
agent.status = agent.status === 'active' ? 'inactive' : 'active';
}
if (Math.random() > 0.5) {
agent.commits += Math.floor(Math.random() * 3);
}
});
// 添加新活动
const newActivity = generateRandomActivity();
activities.unshift(newActivity);
if (activities.length > 5) {
activities.pop();
}
updateAgentGrid();
updateActivityFeed();
updateStats();
}
// 生成随机活动
function generateRandomActivity() {
const types = ['commit', 'test', 'deploy', 'docs'];
const messages = {
'commit': ['Code Reviewer pushed new features', 'Test Runner fixed bugs', 'Deploy Bot merged PR'],
'test': ['Test Runner passed all tests', 'Code Reviewer added unit tests', 'Doc Writer tested examples'],
'deploy': ['Deploy Bot deployed to production', 'Code Reviewer released v2.0', 'Test Runner deployed hotfix'],
'docs': ['Doc Writer updated README', 'Code Reviewer improved comments', 'Deploy Bot added changelog']
};
const type = types[Math.floor(Math.random() * types.length)];
const message = messages[type][Math.floor(Math.random() * messages[type].length)];
return {
type: type,
message: message,
time: 'just now'
};
}
// 终端模拟
function initializeTerminal() {
const terminal = document.getElementById('terminal-content');
if (!terminal) return;
const commands = [
{ prompt: 'user@spore-colony:~$', command: 'spore init', output: '✓ Initializing Spore Colony...\n✓ Creating agent identities...\n✓ Setting up Git repositories...\n✓ Spore Colony initialized successfully!' },
{ prompt: 'user@spore-colony:~$', command: 'spore agent list', output: '🟢 code-reviewer (Active)\n🟢 test-runner (Active)\n🔴 deploy-bot (Inactive)\n🟢 doc-writer (Active)' },
{ prompt: 'user@spore-colony:~$', command: 'spore task create "Add new feature"', output: '✓ Task created and assigned to code-reviewer\n📊 Progress: 0% → 25% → 50% → 75% → 100%\n✅ Task completed successfully!' }
];
let currentCommand = 0;
function typeCommand() {
if (currentCommand >= commands.length) return;
const cmd = commands[currentCommand];
const line = document.createElement('div');
line.className = 'terminal-line';
line.innerHTML = `<span class="prompt">${cmd.prompt}</span> <span class="command">${cmd.command}</span>`;
terminal.appendChild(line);
setTimeout(() => {
const outputLine = document.createElement('div');
outputLine.className = 'terminal-line';
outputLine.innerHTML = `<span class="success">${cmd.output}</span>`;
terminal.appendChild(outputLine);
currentCommand++;
if (currentCommand < commands.length) {
setTimeout(typeCommand, 2000);
}
}, 1500);
}
typeCommand();
}
// 统计信息
function initializeStats() {
updateStats();
}
function updateStats() {
const totalCommits = agents.reduce((sum, agent) => sum + agent.commits, 0);
const activeAgents = agents.filter(agent => agent.status === 'active').length;
const totalTasks = agents.reduce((sum, agent) => sum + agent.tasks, 0);
animateCounter('total-commits', totalCommits);
animateCounter('active-agents', activeAgents);
animateCounter('total-tasks', totalTasks);
updateProgressBars();
}
// 数字动画
function animateCounter(elementId, targetValue) {
const element = document.getElementById(elementId);
if (!element) return;
const startValue = parseInt(element.textContent) || 0;
const duration = 1000;
const startTime = performance.now();
function updateCounter(currentTime) {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
const currentValue = Math.floor(startValue + (targetValue - startValue) * progress);
element.textContent = currentValue;
if (progress < 1) {
requestAnimationFrame(updateCounter);
}
}
requestAnimationFrame(updateCounter);
}
// 进度条更新
function updateProgressBars() {
const progressBars = document.querySelectorAll('.bar div');
progressBars.forEach(bar => {
const width = Math.floor(Math.random() * 30) + 70; // 70-100%
bar.style.width = width + '%';
});
}
// 滚动效果
function setupScrollEffects() {
const observerOptions = {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
};
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('animate-in');
}
});
}, observerOptions);
// 观察所有需要动画的元素
document.querySelectorAll('.feature-card, .demo-panel, .install-card, .doc-card').forEach(el => {
observer.observe(el);
});
}
// 平滑滚动到锚点
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
target.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
});
});
// 响应式菜单
function toggleMobileMenu() {
const navMenu = document.querySelector('.nav-menu');
navMenu.classList.toggle('active');
}
// 复制代码功能
function copyToClipboard(elementId) {
const element = document.getElementById(elementId);
if (!element) return;
const text = element.textContent;
navigator.clipboard.writeText(text).then(() => {
// 显示复制成功提示
const toast = document.createElement('div');
toast.textContent = 'Copied to clipboard!';
toast.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: #27ca3f;
color: white;
padding: 1rem;
border-radius: 0.5rem;
z-index: 1000;
animation: slideIn 0.3s ease;
`;
document.body.appendChild(toast);
setTimeout(() => {
toast.remove();
}, 2000);
});
}
// 添加CSS动画
const style = document.createElement('style');
style.textContent = `
@keyframes slideIn {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
.animate-in {
animation: fadeInUp 0.6s ease forwards;
}
@media (max-width: 768px) {
.nav-menu.active {
display: flex;
flex-direction: column;
position: absolute;
top: 100%;
left: 0;
width: 100%;
background: white;
padding: 1rem;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
}
`;
document.head.appendChild(style);

View File

@@ -0,0 +1,659 @@
/* Reset and Base Styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
line-height: 1.6;
color: #333;
background: #fafafa;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
/* Navigation */
.navbar {
position: fixed;
top: 0;
width: 100%;
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border-bottom: 1px solid #e0e0e0;
z-index: 1000;
transition: all 0.3s ease;
}
.nav-container {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem 2rem;
}
.logo-text {
font-size: 1.5rem;
font-weight: 700;
color: #2563eb;
}
.nav-menu {
display: flex;
list-style: none;
gap: 2rem;
}
.nav-menu a {
text-decoration: none;
color: #333;
font-weight: 500;
transition: color 0.3s ease;
}
.nav-menu a:hover {
color: #2563eb;
}
.github-link {
background: #2563eb;
color: white !important;
padding: 0.5rem 1rem;
border-radius: 0.5rem;
transition: background 0.3s ease;
}
.github-link:hover {
background: #1d4ed8;
}
.hamburger {
display: none;
flex-direction: column;
cursor: pointer;
}
.hamburger span {
width: 25px;
height: 3px;
background: #333;
margin: 3px 0;
transition: 0.3s;
}
/* Hero Section */
.hero {
min-height: 100vh;
display: flex;
align-items: center;
padding: 6rem 0 4rem;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.hero-container {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 4rem;
align-items: center;
}
.hero-title {
font-size: 3.5rem;
font-weight: 700;
line-height: 1.2;
margin-bottom: 1.5rem;
}
.gradient-text {
background: linear-gradient(45deg, #ff6b6b, #4ecdc4);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.hero-subtitle {
font-size: 1.25rem;
margin-bottom: 2rem;
opacity: 0.9;
line-height: 1.6;
}
.hero-buttons {
display: flex;
gap: 1rem;
margin-bottom: 3rem;
}
.btn {
padding: 1rem 2rem;
border: none;
border-radius: 0.5rem;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
text-decoration: none;
display: inline-block;
}
.btn-primary {
background: #ff6b6b;
color: white;
}
.btn-primary:hover {
background: #ff5252;
transform: translateY(-2px);
}
.btn-secondary {
background: transparent;
color: white;
border: 2px solid white;
}
.btn-secondary:hover {
background: white;
color: #667eea;
}
.hero-stats {
display: flex;
gap: 2rem;
}
.stat {
text-align: center;
}
.stat-number {
display: block;
font-size: 2rem;
font-weight: 700;
color: #ff6b6b;
}
.stat-label {
font-size: 0.9rem;
opacity: 0.8;
}
/* Terminal Window */
.terminal-window {
background: #1a1a1a;
border-radius: 1rem;
overflow: hidden;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
}
.terminal-header {
background: #2d2d2d;
padding: 1rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.terminal-buttons {
display: flex;
gap: 0.5rem;
}
.terminal-buttons span {
width: 12px;
height: 12px;
border-radius: 50%;
}
.terminal-buttons span:nth-child(1) { background: #ff5f56; }
.terminal-buttons span:nth-child(2) { background: #ffbd2e; }
.terminal-buttons span:nth-child(3) { background: #27ca3f; }
.terminal-title {
flex: 1;
text-align: center;
color: #888;
font-size: 0.9rem;
}
.terminal-content {
padding: 1.5rem;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 0.9rem;
line-height: 1.6;
}
.terminal-line {
margin-bottom: 0.5rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.prompt {
color: #27ca3f;
}
.command {
color: #fff;
}
.success {
color: #27ca3f;
}
/* Features Section */
.features {
padding: 6rem 0;
background: white;
}
.section-title {
text-align: center;
font-size: 2.5rem;
font-weight: 700;
margin-bottom: 3rem;
color: #333;
}
.features-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
gap: 3rem;
}
.feature-card {
background: white;
border-radius: 1rem;
padding: 2.5rem;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease;
}
.feature-card:hover {
transform: translateY(-5px);
}
.feature-icon {
font-size: 3rem;
margin-bottom: 1rem;
}
.feature-card h3 {
font-size: 1.5rem;
font-weight: 600;
margin-bottom: 1rem;
color: #333;
}
.feature-card p {
color: #666;
margin-bottom: 1.5rem;
line-height: 1.6;
}
.feature-card ul {
list-style: none;
}
.feature-card li {
padding: 0.25rem 0;
color: #555;
}
.feature-card li:before {
content: "✓";
color: #27ca3f;
font-weight: bold;
margin-right: 0.5rem;
}
.roles-list {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.role-item {
padding: 0.5rem;
background: #f8f9fa;
border-radius: 0.5rem;
font-size: 0.9rem;
}
/* Demo Section */
.demo {
padding: 6rem 0;
background: #f8f9fa;
}
.demo-container {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 3rem;
}
.demo-panel {
background: white;
border-radius: 1rem;
padding: 2rem;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
}
.demo-panel h3 {
font-size: 1.5rem;
font-weight: 600;
margin-bottom: 1.5rem;
color: #333;
}
.agent-grid {
display: grid;
gap: 1rem;
}
.agent-card {
padding: 1rem;
background: #f8f9fa;
border-radius: 0.5rem;
display: flex;
justify-content: space-between;
align-items: center;
}
.agent-status-active {
border-left: 4px solid #27ca3f;
}
.agent-status-inactive {
border-left: 4px solid #ff5f56;
}
.activity-feed {
display: flex;
flex-direction: column;
gap: 1rem;
}
.activity-item {
padding: 1rem;
background: #f8f9fa;
border-radius: 0.5rem;
border-left: 4px solid #667eea;
}
.demo-actions {
text-align: center;
margin-top: 2rem;
display: flex;
gap: 1rem;
justify-content: center;
}
/* Quick Start Section */
.quick-start {
padding: 6rem 0;
background: white;
}
.install-options {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
}
.install-card {
background: white;
border-radius: 1rem;
padding: 2rem;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
border: 1px solid #e0e0e0;
}
.install-card h3 {
font-size: 1.5rem;
font-weight: 600;
margin-bottom: 1rem;
color: #333;
}
.code-block {
background: #1a1a1a;
border-radius: 0.5rem;
padding: 1rem;
overflow-x: auto;
}
.code-block pre {
margin: 0;
color: #fff;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 0.9rem;
}
/* Documentation Section */
.documentation {
padding: 6rem 0;
background: #f8f9fa;
}
.docs-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 2rem;
}
.doc-card {
background: white;
border-radius: 1rem;
padding: 2rem;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease;
}
.doc-card:hover {
transform: translateY(-5px);
}
.doc-card h3 {
font-size: 1.25rem;
font-weight: 600;
margin-bottom: 1rem;
color: #333;
}
.doc-card p {
color: #666;
margin-bottom: 1rem;
}
.doc-link {
color: #2563eb;
text-decoration: none;
font-weight: 600;
}
.doc-link:hover {
text-decoration: underline;
}
/* Footer */
.footer {
background: #1a1a1a;
color: white;
padding: 3rem 0 1rem;
}
.footer-content {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 2rem;
margin-bottom: 2rem;
}
.footer-section h4 {
font-size: 1.25rem;
font-weight: 600;
margin-bottom: 1rem;
}
.footer-section ul {
list-style: none;
}
.footer-section ul li {
margin-bottom: 0.5rem;
}
.footer-section a {
color: #ccc;
text-decoration: none;
transition: color 0.3s ease;
}
.footer-section a:hover {
color: white;
}
.footer-bottom {
text-align: center;
padding-top: 2rem;
border-top: 1px solid #333;
color: #888;
}
/* Responsive Design */
@media (max-width: 768px) {
.hamburger {
display: flex;
}
.nav-menu {
display: none;
}
.hero-container {
grid-template-columns: 1fr;
text-align: center;
}
.hero-title {
font-size: 2.5rem;
}
.hero-buttons {
justify-content: center;
flex-direction: column;
align-items: center;
}
.features-grid {
grid-template-columns: 1fr;
}
.demo-container {
grid-template-columns: 1fr;
}
.install-options {
grid-template-columns: 1fr;
}
.docs-grid {
grid-template-columns: 1fr;
}
}
@media (max-width: 480px) {
.hero-title {
font-size: 2rem;
}
.hero-stats {
flex-direction: column;
gap: 1rem;
}
.container {
padding: 0 1rem;
}
}
/* Animations */
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.feature-card {
animation: fadeInUp 0.6s ease forwards;
}
.feature-card:nth-child(2) {
animation-delay: 0.1s;
}
.feature-card:nth-child(3) {
animation-delay: 0.2s;
}
/* Stats Animation */
.stats-preview {
margin-top: 1rem;
}
.stat-bar {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 0.5rem;
}
.stat-bar span {
font-size: 0.9rem;
min-width: 80px;
}
.bar {
flex: 1;
height: 8px;
background: #e0e0e0;
border-radius: 4px;
overflow: hidden;
}
.bar div {
height: 100%;
background: linear-gradient(90deg, #667eea, #764ba2);
transition: width 1s ease;
}
/* Scrollbar Styling */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: #f1f1f1;
}
::-webkit-scrollbar-thumb {
background: #888;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: #555;
}