Files
pit-router/docs/WEB_UI_SPEC.md
yunxiafei 0003e5f69f docs: 新增 Web 管理界面技术方案
功能设计:
1. 智队频道插件配置管理
2. 一键测试频道插件连接
3. 会话监控与消息查看

技术栈:
- Jinja2 + HTMX + Tailwind CSS
- Chart.js 图表
- WebSocket 实时推送

预计开发时间: 6 天

作者: 小白 🐶
2026-03-14 22:06:31 +08:00

571 lines
31 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
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.
# 智队中枢 - Web 管理界面技术方案
> 智队中枢 Web UI 设计文档
## 📋 概述
为智队中枢添加 Web 管理界面,提供:
1. 智队频道插件配置管理
2. 一键测试频道插件连接
3. 会话监控与消息查看
---
## 🏗️ 技术架构
### 整体架构
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ 智队中枢 Web UI │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ 前端 (Jinja2 + HTMX + Tailwind) │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ 配置管理 │ │ 连接测试 │ │ 会话监控 │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ 后端 API (Flask Blueprint) │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ /web/* │ │ /api/web/* │ │ /ws/* │ │ │
│ │ │ 页面路由 │ │ 数据接口 │ │ 实时推送 │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
```
### 技术选型
| 层级 | 技术 | 说明 |
|------|------|------|
| 模板引擎 | Jinja2 | Flask 原生支持 |
| 交互增强 | HTMX | 无需写 JSHTML 属性驱动 |
| 样式框架 | 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/<id>/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/<id>')
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 | 作者: 小白 🐶*