diff --git a/docs/WEB_UI_SPEC.md b/docs/WEB_UI_SPEC.md new file mode 100644 index 0000000..5313aba --- /dev/null +++ b/docs/WEB_UI_SPEC.md @@ -0,0 +1,570 @@ +# 智队中枢 - Web 管理界面技术方案 + +> 智队中枢 Web UI 设计文档 + +## 📋 概述 + +为智队中枢添加 Web 管理界面,提供: +1. 智队频道插件配置管理 +2. 一键测试频道插件连接 +3. 会话监控与消息查看 + +--- + +## 🏗️ 技术架构 + +### 整体架构 + +``` +┌─────────────────────────────────────────────────────────────────────────────┐ +│ 智队中枢 Web UI │ +│ ┌───────────────────────────────────────────────────────────────────────┐ │ +│ │ 前端 (Jinja2 + HTMX + Tailwind) │ │ +│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ +│ │ │ 配置管理 │ │ 连接测试 │ │ 会话监控 │ │ │ +│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ +│ └───────────────────────────────────────────────────────────────────────┘ │ +│ │ │ +│ ┌───────────────────────────────────────────────────────────────────────┐ │ +│ │ 后端 API (Flask Blueprint) │ │ +│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ +│ │ │ /web/* │ │ /api/web/* │ │ /ws/* │ │ │ +│ │ │ 页面路由 │ │ 数据接口 │ │ 实时推送 │ │ │ +│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ +│ └───────────────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────────┘ +``` + +### 技术选型 + +| 层级 | 技术 | 说明 | +|------|------|------| +| 模板引擎 | Jinja2 | Flask 原生支持 | +| 交互增强 | HTMX | 无需写 JS,HTML 属性驱动 | +| 样式框架 | Tailwind CSS | 快速构建 UI | +| 图表 | Chart.js | 会话统计图表 | +| 实时更新 | WebSocket | 会话状态实时推送 | + +--- + +## 📁 项目结构 + +``` +pit-router/ +├── app/ +│ ├── web/ # Web UI 模块 (新增) +│ │ ├── __init__.py # Blueprint 注册 +│ │ ├── routes.py # 页面路由 +│ │ ├── api.py # 数据 API +│ │ └── utils.py # 工具函数 +│ ├── templates/ # 模板文件 (新增) +│ │ ├── base.html # 基础模板 +│ │ ├── index.html # 首页/仪表盘 +│ │ ├── config/ # 配置管理 +│ │ │ ├── channels.html # 频道配置列表 +│ │ │ └── channel_edit.html # 频道配置编辑 +│ │ ├── monitor/ # 会话监控 +│ │ │ ├── sessions.html # 会话列表 +│ │ │ ├── session_detail.html # 会话详情 +│ │ │ └── messages.html # 消息查看 +│ │ └── components/ # 组件模板 +│ │ ├── navbar.html # 导航栏 +│ │ ├── sidebar.html # 侧边栏 +│ │ └── toast.html # 提示消息 +│ └── static/ # 静态资源 (新增) +│ ├── css/ +│ │ └── style.css # 自定义样式 +│ └── js/ +│ └── app.js # 自定义脚本 +└── ... +``` + +--- + +## 🎨 页面设计 + +### 1. 首页/仪表盘 + +``` +┌─────────────────────────────────────────────────────────────────────────────┐ +│ 智队中枢 [用户] [设置] [登出] │ +├──────────────┬──────────────────────────────────────────────────────────────┤ +│ │ │ +│ 📊 仪表盘 │ 系统状态 │ +│ ──────── │ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ +│ │ │ 在线 Agent │ │ 活跃会话 │ │ 今日消息 │ │ +│ ⚙️ 配置管理 │ │ 5 │ │ 12 │ │ 1,234 │ │ +│ └ 频道配置 │ └────────────┘ └────────────┘ └────────────┘ │ +│ │ │ +│ 📡 连接测试 │ 会话趋势 (最近 24 小时) │ +│ └ 测试面板 │ ┌────────────────────────────────────────────────────┐ │ +│ │ │ 📈 │ │ +│ 👁️ 会话监控 │ │ ████ │ │ +│ └ 会话列表 │ │ ████████ ████████ │ │ +│ └ 消息记录 │ │ ████████████████████████████████ │ │ +│ │ └────────────────────────────────────────────────────┘ │ +│ │ │ +│ │ 最近会话 │ +│ │ ┌────────────────────────────────────────────────────┐ │ +│ │ │ 用户 Agent 消息数 状态 时间 │ │ +│ │ │ yunxiafei 小白 15 活跃 2分钟前 │ │ +│ │ │ xiaobai 小黑 8 活跃 5分钟前 │ │ +│ │ │ test_user 小花 23 已关闭 1小时前 │ │ +│ │ └────────────────────────────────────────────────────┘ │ +└──────────────┴──────────────────────────────────────────────────────────────┘ +``` + +### 2. 频道配置页面 + +``` +┌─────────────────────────────────────────────────────────────────────────────┐ +│ 智队中枢 > 配置管理 > 频道配置 │ +├──────────────┬──────────────────────────────────────────────────────────────┤ +│ │ │ +│ 📊 仪表盘 │ 智队频道插件配置 [+ 新增配置] │ +│ ──────── │ ┌────────────────────────────────────────────────────┐ │ +│ │ │ 名称 Gateway URL 状态 操作 │ │ +│ ⚙️ 配置管理 │ ├────────────────────────────────────────────────────┤ │ +│ └ 频道配置 │ │ 默认频道 ws://localhost:18888 ✅ 在线 [编辑][测试] │ +│ │ │ 工作频道 ws://work:18888 ⚠️ 离线 [编辑][测试] │ +│ 📡 连接测试 │ │ 测试频道 ws://test:18888 ✅ 在线 [编辑][测试] │ +│ └ 测试面板 │ └────────────────────────────────────────────────────┘ │ +│ │ │ +│ 👁️ 会话监控 │ 配置详情 (点击编辑后显示) │ +│ └ 会话列表 │ ┌────────────────────────────────────────────────────┐ │ +│ └ 消息记录 │ │ Gateway URL: [ws://localhost:18888 ] │ │ +│ │ │ Auth Token: [•••••••••••••••• ] │ │ +│ │ │ 重连间隔: [5000] 毫秒 │ │ +│ │ │ 心跳间隔: [30000] 毫秒 │ │ +│ │ │ 启用状态: [✓] 启用 │ │ +│ │ │ │ │ +│ │ │ [取消] [保存] │ │ +│ │ └────────────────────────────────────────────────────┘ │ +└──────────────┴──────────────────────────────────────────────────────────────┘ +``` + +### 3. 连接测试页面 + +``` +┌─────────────────────────────────────────────────────────────────────────────┐ +│ 智队中枢 > 连接测试 │ +├──────────────┬──────────────────────────────────────────────────────────────┤ +│ │ │ +│ 📊 仪表盘 │ 连接测试 │ +│ ──────── │ ┌────────────────────────────────────────────────────┐ │ +│ │ │ 选择频道: [默认频道 ▼] │ │ +│ ⚙️ 配置管理 │ │ │ │ +│ └ 频道配置 │ │ [🚀 开始测试] │ │ +│ │ └────────────────────────────────────────────────────┘ │ +│ 📡 连接测试 │ │ +│ └ 测试面板 │ 测试结果 │ +│ │ ┌────────────────────────────────────────────────────┐ │ +│ 👁️ 会话监控 │ │ ✅ WebSocket 连接成功 │ │ +│ └ 会话列表 │ │ ✅ 认证通过 │ │ +│ └ 消息记录 │ │ ✅ 心跳响应正常 │ │ +│ │ │ ✅ 消息收发测试通过 │ │ +│ │ │ │ │ +│ │ │ 响应时间: 45ms │ │ +│ │ │ 测试时间: 2026-03-14 22:10:00 │ │ +│ │ └────────────────────────────────────────────────────┘ │ +│ │ │ +│ │ 测试日志 │ +│ │ ┌────────────────────────────────────────────────────┐ │ +│ │ │ [INFO] 正在连接 ws://localhost:18888... │ │ +│ │ │ [INFO] WebSocket 连接已建立 │ │ +│ │ │ [INFO] 发送认证请求... │ │ +│ │ │ [INFO] 认证成功: agent_id=xiaobai │ │ +│ │ │ [INFO] 发送心跳... │ │ +│ │ │ [INFO] 心跳响应: pong │ │ +│ │ │ [INFO] 发送测试消息... │ │ +│ │ │ [INFO] 收到响应: Hello from Agent │ │ +│ │ │ [INFO] 测试完成 │ │ +│ │ └────────────────────────────────────────────────────┘ │ +└──────────────┴──────────────────────────────────────────────────────────────┘ +``` + +### 4. 会话监控页面 + +``` +┌─────────────────────────────────────────────────────────────────────────────┐ +│ 智队中枢 > 会话监控 │ +├──────────────┬──────────────────────────────────────────────────────────────┤ +│ │ │ +│ 📊 仪表盘 │ 会话列表 [🔄 刷新] │ +│ ──────── │ ┌────────────────────────────────────────────────────┐ │ +│ │ │ 筛选: Agent [全部▼] 状态 [全部▼] 时间 [今天▼] │ │ +│ ⚙️ 配置管理 │ ├────────────────────────────────────────────────────┤ │ +│ └ 频道配置 │ │ ID 用户 Agent 消息 状态 时间 │ │ +│ │ ├────────────────────────────────────────────────────┤ │ +│ 📡 连接测试 │ │ sess_001 yunxiafei 小白 15 🟢活跃 2分前│ │ +│ └ 测试面板 │ │ sess_002 xiaobai 小黑 8 🟢活跃 5分前│ │ +│ │ │ sess_003 test_user 小花 23 ⚪关闭 1时前│ │ +│ 👁️ 会话监控 │ │ sess_004 admin 小白 42 🟡暂停 2时前│ │ +│ └ 会话列表 │ │ sess_005 guest 小花 3 🟢活跃 10分前│ │ +│ └ 消息记录 │ └────────────────────────────────────────────────────┘ │ +│ │ │ +│ │ 会话详情: sess_001 │ +│ │ ┌────────────────────────────────────────────────────┐ │ +│ │ │ 用户: yunxiafei Agent: 小白 状态: 🟢 活跃 │ │ +│ │ │ 创建: 2026-03-14 20:00:00 │ │ +│ │ │ 最后活跃: 2026-03-14 22:08:00 │ │ +│ │ ├────────────────────────────────────────────────────┤ │ +│ │ │ 消息记录: │ │ +│ │ │ ┌──────────────────────────────────────────────┐ │ │ +│ │ │ │ [用户] 你好小白 │ │ │ +│ │ │ │ [小白] 你好!有什么可以帮你的? │ │ │ +│ │ │ │ [用户] 帮我写一个 Flask 应用 │ │ │ +│ │ │ │ [小白] 好的,我来帮你创建一个 Flask 应用... │ │ │ +│ │ │ │ ... │ │ │ +│ │ │ └──────────────────────────────────────────────┘ │ │ +│ │ │ [关闭会话] [导出记录] │ │ +│ │ └────────────────────────────────────────────────────┘ │ +└──────────────┴──────────────────────────────────────────────────────────────┘ +``` + +--- + +## 🔌 API 设计 + +### Web UI 路由 + +| 方法 | 路径 | 说明 | +|------|------|------| +| GET | /web/ | 首页/仪表盘 | +| GET | /web/config/channels | 频道配置列表 | +| GET | /web/config/channels/new | 新增频道配置 | +| GET | /web/config/channels/:id/edit | 编辑频道配置 | +| GET | /web/test | 连接测试页面 | +| GET | /web/monitor/sessions | 会话监控列表 | +| GET | /web/monitor/sessions/:id | 会话详情 | + +### Web UI 数据 API + +| 方法 | 路径 | 说明 | +|------|------|------| +| GET | /api/web/stats | 系统统计数据 | +| GET | /api/web/channels | 频道配置列表 | +| POST | /api/web/channels | 创建频道配置 | +| PUT | /api/web/channels/:id | 更新频道配置 | +| DELETE | /api/web/channels/:id | 删除频道配置 | +| POST | /api/web/channels/:id/test | 测试频道连接 | +| GET | /api/web/sessions | 会话列表 (带筛选) | +| GET | /api/web/sessions/:id | 会话详情 | +| GET | /api/web/sessions/:id/messages | 会话消息 | +| PUT | /api/web/sessions/:id/close | 关闭会话 | + +--- + +## 🔧 核心实现 + +### 1. 频道配置数据模型 + +```python +# app/models/channel_config.py + +from app import db +from datetime import datetime +import uuid + +class ChannelConfig(db.Model): + """智队频道插件配置""" + __tablename__ = 'channel_configs' + + id = db.Column(db.String(36), primary_key=True, default=lambda: str(uuid.uuid4())) + name = db.Column(db.String(80), nullable=False, unique=True) + gateway_url = db.Column(db.String(256), nullable=False) + auth_token = db.Column(db.String(256), nullable=True) + reconnect_interval = db.Column(db.Integer, default=5000) + heartbeat_interval = db.Column(db.Integer, default=30000) + enabled = db.Column(db.Boolean, default=True) + + # 状态 + status = db.Column(db.String(20), default='offline') + last_connected = db.Column(db.DateTime, nullable=True) + last_error = db.Column(db.Text, nullable=True) + + # 时间戳 + created_at = db.Column(db.DateTime, default=datetime.utcnow) + updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) + + def to_dict(self): + return { + 'id': self.id, + 'name': self.name, + 'gateway_url': self.gateway_url, + 'auth_token': '••••••••' if self.auth_token else None, + 'reconnect_interval': self.reconnect_interval, + 'heartbeat_interval': self.heartbeat_interval, + 'enabled': self.enabled, + 'status': self.status, + 'last_connected': self.last_connected.isoformat() if self.last_connected else None, + 'created_at': self.created_at.isoformat() + } +``` + +### 2. 连接测试服务 + +```python +# app/services/channel_tester.py + +import asyncio +import websockets +import json +import time +from datetime import datetime + +class ChannelTester: + """频道连接测试器""" + + def __init__(self, config): + self.config = config + self.results = [] + self.start_time = None + + async def test_connection(self): + """执行连接测试""" + self.start_time = time.time() + self.results = [] + + try: + # 1. WebSocket 连接测试 + await self._test_websocket() + + # 2. 认证测试 + await self._test_auth() + + # 3. 心跳测试 + await self._test_heartbeat() + + # 4. 消息收发测试 + await self._test_message() + + except Exception as e: + self._log('ERROR', str(e)) + + return { + 'success': all(r['success'] for r in self.results), + 'results': self.results, + 'response_time': int((time.time() - self.start_time) * 1000), + 'tested_at': datetime.utcnow().isoformat() + } + + async def _test_websocket(self): + """测试 WebSocket 连接""" + self._log('INFO', f'正在连接 {self.config.gateway_url}...') + + try: + self.ws = await websockets.connect( + self.config.gateway_url, + extra_headers={'Authorization': f'Bearer {self.config.auth_token}'} + ) + self._log('INFO', 'WebSocket 连接已建立', success=True) + except Exception as e: + self._log('ERROR', f'连接失败: {e}', success=False) + raise + + async def _test_auth(self): + """测试认证""" + self._log('INFO', '发送认证请求...') + + try: + # 发送认证消息 + await self.ws.send(json.dumps({ + 'type': 'auth', + 'token': self.config.auth_token + })) + + # 等待响应 + response = await asyncio.wait_for(self.ws.recv(), timeout=10) + data = json.loads(response) + + if data.get('type') == 'auth_success': + self._log('INFO', f"认证成功: agent_id={data.get('agent_id')}", success=True) + else: + self._log('ERROR', f"认证失败: {data}", success=False) + raise Exception('认证失败') + + except Exception as e: + self._log('ERROR', f'认证错误: {e}', success=False) + raise + + async def _test_heartbeat(self): + """测试心跳""" + self._log('INFO', '发送心跳...') + + try: + await self.ws.send(json.dumps({'type': 'ping'})) + response = await asyncio.wait_for(self.ws.recv(), timeout=10) + data = json.loads(response) + + if data.get('type') == 'pong': + self._log('INFO', '心跳响应: pong', success=True) + else: + self._log('WARNING', f'心跳响应异常: {data}', success=True) + + except Exception as e: + self._log('ERROR', f'心跳错误: {e}', success=False) + + async def _test_message(self): + """测试消息收发""" + self._log('INFO', '发送测试消息...') + + try: + test_msg = { + 'type': 'message', + 'content': '__TEST__' + } + await self.ws.send(json.dumps(test_msg)) + + response = await asyncio.wait_for(self.ws.recv(), timeout=30) + data = json.loads(response) + + self._log('INFO', f"收到响应: {data.get('content', data)[:50]}", success=True) + + except Exception as e: + self._log('WARNING', f'消息测试跳过: {e}', success=True) + + await self.ws.close() + self._log('INFO', '测试完成') + + def _log(self, level, message, success=None): + """记录日志""" + entry = { + 'level': level, + 'message': message, + 'timestamp': datetime.utcnow().isoformat(), + 'success': success if success is not None else level == 'INFO' + } + self.results.append(entry) +``` + +### 3. Web UI Blueprint + +```python +# app/web/routes.py + +from flask import Blueprint, render_template, request, redirect, url_for +from app.models import Session, Agent, Gateway, Message +from app.models.channel_config import ChannelConfig +from app import db +from datetime import datetime, timedelta + +web_bp = Blueprint('web', __name__, url_prefix='/web') + +@web_bp.route('/') +def index(): + """首页/仪表盘""" + # 统计数据 + stats = { + 'online_agents': Agent.query.filter_by(status='online').count(), + 'active_sessions': Session.query.filter_by(status='active').count(), + 'today_messages': Message.query.filter( + Message.created_at >= datetime.utcnow() - timedelta(days=1) + ).count() + } + + # 最近会话 + recent_sessions = Session.query.order_by( + Session.last_active_at.desc() + ).limit(5).all() + + return render_template('index.html', + stats=stats, + recent_sessions=recent_sessions) + +@web_bp.route('/config/channels') +def channels(): + """频道配置列表""" + configs = ChannelConfig.query.all() + return render_template('config/channels.html', configs=configs) + +@web_bp.route('/config/channels//edit') +def channel_edit(id): + """编辑频道配置""" + config = ChannelConfig.query.get_or_404(id) + return render_template('config/channel_edit.html', config=config) + +@web_bp.route('/test') +def test(): + """连接测试页面""" + configs = ChannelConfig.query.filter_by(enabled=True).all() + return render_template('test/index.html', configs=configs) + +@web_bp.route('/monitor/sessions') +def sessions(): + """会话监控""" + status = request.args.get('status') + agent_id = request.args.get('agent_id') + + query = Session.query + + if status: + query = query.filter_by(status=status) + if agent_id: + query = query.filter_by(agent_id=agent_id) + + sessions = query.order_by(Session.last_active_at.desc()).all() + agents = Agent.query.all() + + return render_template('monitor/sessions.html', + sessions=sessions, + agents=agents) + +@web_bp.route('/monitor/sessions/') +def session_detail(id): + """会话详情""" + session = Session.query.get_or_404(id) + messages = Message.query.filter_by(session_id=id).order_by( + Message.created_at.asc() + ).limit(100).all() + + return render_template('monitor/session_detail.html', + session=session, + messages=messages) +``` + +--- + +## 📦 依赖更新 + +### requirements.txt 新增 + +``` +# Web UI +flask-htmx==0.3.0 +``` + +--- + +## 📈 开发计划 + +### Phase 5: Web UI 开发 + +| 任务 | 预计时间 | 说明 | +|------|----------|------| +| 项目结构调整 | 0.5 天 | 创建 templates、static 目录 | +| 基础模板 | 0.5 天 | base.html、导航、侧边栏 | +| 首页仪表盘 | 1 天 | 统计数据、图表 | +| 频道配置 | 1 天 | CRUD、表单验证 | +| 连接测试 | 1 天 | WebSocket 测试、日志显示 | +| 会话监控 | 1 天 | 列表、详情、实时更新 | +| 样式美化 | 0.5 天 | Tailwind 样式优化 | +| 测试 | 0.5 天 | 功能测试 | + +**总计:6 天** + +--- + +## 🎯 里程碑 + +| 阶段 | 功能 | 预计完成 | +|------|------|----------| +| M1 | 基础框架 + 首页 | Day 2 | +| M2 | 频道配置管理 | Day 3 | +| M3 | 连接测试 | Day 4 | +| M4 | 会话监控 | Day 5 | +| M5 | 样式优化 + 测试 | Day 6 | + +--- + +*文档版本: v1.0 | 创建时间: 2026-03-14 | 作者: 小白 🐶*