一、背景与动机
1.1 项目现状
OpsMind 项目经过几个月的开发,已完成核心功能:
| 模块 |
状态 |
说明 |
| ReAct Agent |
✅ 完成 |
智能决策引擎 |
| 三维意图识别 |
✅ 完成 |
10种分析逻辑 |
| 数据分析引擎 |
✅ 完成 |
35+种图表 |
| 上下文管理 |
✅ 完成 |
多轮对话支持 |
| Streamlit UI |
✅ 完成 |
ChatGPT风格界面 |
1.2 工程化短板
然而,项目在工程化方面存在明显短板:
┌─────────────────────────────────────────────────────────────────────────┐ │ 项目工程化短板分析 │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ │ │ 测试覆盖 │ │ 代码质量 │ │ 组件复用 │ │ │ │ │ │ │ │ │ │ │ │ ❌ 无单元测试 │ │ ❌ 无 Linter │ │ ❌ UI 代码耦合 │ │ │ │ ❌ 无集成测试 │ │ ❌ 无 Formatter │ │ ❌ 组件不独立 │ │ │ │ ❌ 无测试报告 │ │ ❌ 无 Type Check│ │ ❌ 难以维护 │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────┘ │ │ │ │ 风险:代码变更可能引入 Bug,UI 修改影响核心逻辑 │ │ │ └─────────────────────────────────────────────────────────────────────────┘
|
1.3 工程化目标
本次更新的目标:
- 建立测试体系:单元测试 + 集成测试 + 测试报告
- UI 组件化:分离 UI 组件与业务逻辑
- 完善工具链:代码格式化 + 静态检查 + 类型检查
二、测试体系建设
2.1 测试目录结构
tests/ ├── __init__.py # 测试包初始化 ├── test_all.py # 综合集成测试 ├── test_context_manager.py # 上下文管理器测试 ├── test_database.py # 数据库测试 ├── test_visualization.py # 可视化测试 ├── test_simple.py # 简单冒烟测试 ├── test_cm.py # ContextManager 快速测试 ├── check_db.py # 数据库检查工具 └── migrate_db.py # 数据库迁移工具
|
2.2 测试用例设计
2.2.1 数据库测试
def test_database(): """测试数据库初始化""" db = ChatDatabase('./data/test_opsmind.db') print('✓ 数据库初始化成功') return db
def test_session_creation(db): """测试会话创建""" session_id = db.create_session('测试会话1') print(f'✓ 创建会话: {session_id}') session = db.get_session(session_id) print(f'✓ 会话信息: {session["title"]}') return session_id
def test_messages(db, session_id): """测试消息存储""" msg_id = db.add_message('user', '你好', '10:00', session_id=session_id) print(f'✓ 添加消息 ID: {msg_id}') messages = db.get_messages(session_id) print(f'✓ 消息数量: {len(messages)}')
|
2.2.2 上下文管理器测试
def test_context_manager(): """测试 ContextManager""" cm = ContextManager(db_path='./data/test_opsmind.db') print(f'✓ 当前会话: {cm.current_session_id}') new_session = cm.create_session('新测试会话') print(f'✓ 创建新会话: {new_session}') cm.switch_session(new_session) print(f'✓ 切换到新会话') cm.add_message('user', '测试消息') cm.add_message('assistant', '测试回复', intent='DATA') context = cm.get_current_session() print(f'✓ 会话标题: {context.title}') print(f'✓ 消息数: {len(context.messages)}') print(f'✓ 意图历史: {context.intent_history}')
|
2.2.3 LLM 上下文构建测试
def test_llm_context(cm): """测试 LLM 上下文构建""" llm_context = cm.build_context_for_llm() print(f'✓ LLM 上下文消息数: {len(llm_context)}') for msg in llm_context: content = msg['content'][:30] if len(msg['content']) > 30 else msg['content'] print(f' - [{msg["role"]}] {content}...')
def test_stats(cm): """测试会话统计""" stats = cm.get_session_stats() print(f'✓ 会话ID: {stats["session_id"]}') print(f'✓ 消息数: {stats["message_count"]}') print(f'✓ RAG查询: {stats["rag_query_count"]}') print(f'✓ DATA查询: {stats["data_query_count"]}')
|
2.3 测试金字塔
┌─────────────────────────────────────────────────────────────────────────┐ │ 测试金字塔 │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────┐ │ │ │ 端到端测试 │ │ │ │ test_all.py │ │ │ └─────────────────┘ │ │ ┌─────────────────────────┐ │ │ │ 集成测试 │ │ │ │ test_context_manager │ │ │ └─────────────────────────┘ │ │ ┌───────────────────────────────────┐ │ │ │ 单元测试 │ │ │ │ test_database / test_visualization│ │ │ └───────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────┘
|
三、UI 组件化重构
3.1 重构动机
问题:原 main.py 中 UI 代码与业务逻辑耦合,难以维护和复用。
def display_charts(charts): for chart_type, fig in charts.items(): st.markdown(f"### {chart_type}") st.plotly_chart(fig)
|
解决:抽取独立的 UI 组件模块。
3.2 组件模块设计
src/ui/ ├── __init__.py └── chart_components.py # 图表相关 UI 组件
|
3.3 核心组件实现
3.3.1 图表渲染组件
def render_interactive_chart(figure_json: str, use_container_width: bool = True, height: int = None) -> None: """ 渲染交互式图表 Args: figure_json: Plotly Figure 的 JSON 字符串 use_container_width: 是否使用容器宽度 height: 图表高度 """ try: fig = pio.from_json(figure_json) if height: fig.update_layout(height=height) st.plotly_chart(fig, use_container_width=use_container_width) except Exception as e: st.error(f"图表渲染失败: {str(e)}")
|
3.3.2 推荐列表组件
def render_recommendations(recommendations: List[Dict[str, Any]]) -> None: """ 渲染图表推荐列表 Args: recommendations: 推荐列表 """ if not recommendations: return st.markdown("### 📊 智能推荐图表") for i, rec in enumerate(recommendations, 1): chart_type = rec.get("chart_type", "unknown") score = rec.get("score", 0) reason = rec.get("reason", "") score_percent = int(score * 100) col1, col2 = st.columns([0.15, 0.85]) with col1: if score >= 0.8: st.success(f"**{score_percent}%**") elif score >= 0.6: st.info(f"**{score_percent}%**") else: st.warning(f"**{score_percent}%**") with col2: st.markdown(f"**{i}. {chart_type}**") st.caption(reason)
|
3.3.3 图表画廊组件
def render_chart_gallery(interactive_charts: Dict[str, str], recommendations: Optional[List[Dict]] = None) -> None: """ 渲染图表画廊 Args: interactive_charts: 图表类型到 JSON 的映射 recommendations: 推荐列表(可选) """ if not interactive_charts: st.info("暂无图表数据") return st.markdown("### 📈 分析图表") chart_order = [] if recommendations: chart_order = [r.get("chart_type") for r in recommendations] all_types = list(interactive_charts.keys()) for chart_type in chart_order: if chart_type in all_types: all_types.remove(chart_type) chart_order.extend(all_types) for chart_type in chart_order: if chart_type not in interactive_charts: continue figure_json = interactive_charts[chart_type] with st.expander(f"📊 {chart_type}", expanded=True): render_interactive_chart(figure_json, height=500)
|
3.3.4 配置面板组件
def render_chart_config_panel() -> Dict[str, Any]: """ 渲染图表配置面板 Returns: 配置字典 """ st.markdown("### ⚙️ 图表设置") col1, col2 = st.columns(2) with col1: theme = st.selectbox( "主题", options=["plotly_white", "plotly_dark", "ggplot2", "seaborn"], index=0, ) width = st.number_input( "宽度", min_value=400, max_value=2000, value=Config.CHART_WIDTH, step=50, ) with col2: color_scale = st.selectbox( "配色方案", options=["default", "viridis", "plasma", "set1", "set2", "pastel"], index=0, ) height = st.number_input( "高度", min_value=300, max_value=1500, value=Config.CHART_HEIGHT, step=50, ) export_format = st.selectbox( "导出格式", options=["png", "svg", "html"], index=0, ) return { "theme": theme, "width": width, "height": height, "color_scale": color_scale, "export_format": export_format, }
|
3.4 组件化收益
| 方面 |
重构前 |
重构后 |
| 代码复用 |
低(重复代码多) |
高(组件可复用) |
| 可维护性 |
差(UI与逻辑耦合) |
好(职责分离) |
| 可测试性 |
困难(依赖 Streamlit) |
容易(组件独立) |
| 扩展性 |
差(修改影响大) |
好(新增组件即可) |
四、开发工具链完善
4.1 依赖管理
生产依赖 (requirements.txt):
openai>=1.3.0,<2.0.0 pandas>=2.1.0,<3.0.0 streamlit>=1.29.0,<2.0.0 plotly>=5.18.0,<6.0.0 # ... 核心依赖
|
开发依赖 (requirements-dev.txt):
-r requirements.txt
pytest>=7.4.0,<8.0.0 # 测试框架 pytest-cov>=4.1.0,<5.0.0 # 测试覆盖率 black>=23.7.0,<24.0.0 # 代码格式化 flake8>=6.1.0,<7.0.0 # 代码检查 mypy>=1.5.0,<2.0.0 # 类型检查 ipython>=8.0.0,<9.0.0 # 交互式开发 jupyter>=1.0.0,<2.0.0 # Notebook 支持
|
4.2 工具链配置
4.2.1 pytest 配置
[pytest] testpaths = tests python_files = test_*.py python_functions = test_* addopts = -v --tb=short
|
4.2.2 black 配置
[tool.black] line-length = 100 target-version = ['py38', 'py39', 'py310', 'py311'] include = '\.pyi?$' exclude = ''' /( \.eggs | \.git | \.venv | build | dist )/ '''
|
4.2.3 mypy 配置
[mypy] python_version = 3.8 warn_return_any = True warn_unused_configs = True ignore_missing_imports = True
|
4.3 开发工作流
┌─────────────────────────────────────────────────────────────────────────┐ │ 开发工作流 │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ 编码 │ ─► │ 格式化 │ ─► │ 检查 │ │ │ │ │ │ black │ │ flake8 │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ 提交 │ ◄─ │ 测试 │ ◄─ │ 类型检查 │ │ │ │ git │ │ pytest │ │ mypy │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────┘
|
五、项目成熟度评估
5.1 成熟度模型
┌─────────────────────────────────────────────────────────────────────────┐ │ 项目成熟度模型 │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ Level 5: 优化级 ░░░░░░░░░░░░░░░░░░░░░░░░░░ 待开始 │ │ Level 4: 量化级 ░░░░░░░░░░░░░░░░░░░░░░░░░░ 待开始 │ │ Level 3: 定义级 ████████████████████░░░░░░ 进行中 │ │ Level 2: 管理级 ██████████████████████████ 已完成 │ │ Level 1: 初始级 ██████████████████████████ 已完成 │ │ │ └─────────────────────────────────────────────────────────────────────────┘
|
5.2 各维度评估
| 维度 |
状态 |
说明 |
| 功能完整性 |
⭐⭐⭐⭐⭐ |
核心功能已完成 |
| 代码质量 |
⭐⭐⭐⭐ |
格式化+检查+类型注解 |
| 测试覆盖 |
⭐⭐⭐ |
基础测试已建立 |
| 文档完善 |
⭐⭐⭐⭐ |
README+博客+注释 |
| 可维护性 |
⭐⭐⭐⭐ |
组件化+职责分离 |
| 可扩展性 |
⭐⭐⭐⭐⭐ |
配置驱动+Agent架构 |
5.3 项目进度更新
总体进度: █████████████████████░░░░░ 85% (16/19)
基础架构: ██████████████████████████ 100% (5/5) UI 开发: ██████████████████████████ 100% (5/5) ← 新增 UI 组件化 功能完善: ████████████████████░░░░░ 83% (5/6) 工程化: ████████████████░░░░░░░░░ 50% (1/2) ← 新增测试体系
|
六、下一步计划
6.1 短期任务
| 任务 |
优先级 |
预计时间 |
| 提升测试覆盖率 > 80% |
🔴 高 |
2-3天 |
| CI/CD 流程配置 |
🔴 高 |
1-2天 |
| 报表生成功能 |
🟡 中 |
3-5天 |
6.2 中期规划
┌─────────────────────────────────────────────────────────────────────────┐ │ 中期规划路线图 │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ Q1 2026 Q2 2026 │ │ ───────────────────────────────────────────────────────────────── │ │ │ │ ✓ 测试体系建设 ○ 性能优化 │ │ ✓ UI 组件化 ○ 缓存机制 │ │ ○ CI/CD 配置 ○ 分布式部署 │ │ ○ 测试覆盖率提升 ○ 监控告警 │ │ │ └─────────────────────────────────────────────────────────────────────────┘
|
6.3 技术债务清单
总结
本次更新标志着 OpsMind 项目从”功能开发阶段”正式进入”工程化完善阶段”:
- 测试体系:建立了单元测试 + 集成测试的测试金字塔
- UI 组件化:分离 UI 组件与业务逻辑,提升可维护性
- 工具链完善:引入 black/flake8/mypy 等开发工具
核心收益:
- 代码变更风险降低(测试保障)
- 开发效率提升(组件复用)
- 代码质量可控(工具链检查)
项目正朝着生产就绪的方向稳步前进!
本文由 OpsMind 技术团队撰写,转载请注明出处。