跳到主要内容

系统架构全面评审 - 三轮讨论记录

评审对象: Tier6-Model LLM 推理部署分析平台 评审日期: 2026-03-06 代码基准: commit 323c54c (v2.4.4)


Round 1 — 独立代码探查(现状扫描)

前端页面清单

页面入口文件核心功能行数估计
Dashboardpages/Dashboard/index.tsx快速操作卡、统计摘要、最近任务~80
TopologySetuppages/TopologySetup/index.tsx3D 视图 + 2D 拓扑图 + 配置面板456 (超规则)
DeploymentAnalysispages/DeploymentAnalysis/index.tsx任务配置、提交、进度监控~135
Resultspages/Results/index.tsx实验列表、详情、任务分析视图473 (超规则)
Knowledgepages/Knowledge/index.tsx知识图谱浏览~30
ThemeLabpages/ThemeLab/index.tsx主题调试(开发态)~20

页面导航机制: 非路由,采用全量挂载 + CSS display:none/block 切换(MainLayout/index.tsx)。 所有 6 个页面在应用启动时一次性挂载。3D 场景通过 React.lazy() 懒加载,但 TopologySetup 的其他代码会立即初始化。

证据:

// MainLayout/index.tsx:29
<div className={`${ui.viewMode === 'dashboard' ? 'block' : 'hidden'} absolute inset-0`}>
<Dashboard />
</div>

前端模块清单

状态管理层 (contexts/)

5 个独立 Context 通过 WorkbenchContext 聚合(门面模式):

Context职责关键状态
TopologyContext拓扑配置读写 + 展开topology, expandedTopology
ConnectionContext连接编辑、布局类型connectionMode, manualConnectionConfig, layoutType
AnalysisContext部署分析结果 + 历史记录deploymentAnalysisData, analysisHistory
KnowledgeGraphContext知识图谱节点/边选中selectedNodes
UIStateContext页面路由状态 + 选中节点/链路viewMode, selectedNode, selectedLink

嵌套层级:

TopologyProvider > AnalysisProvider > KnowledgeGraphProvider > UIStateProvider > ConnectionProvider > WorkbenchInner

组件层 (components/)

  • ConfigPanel/ — 配置面板(DeploymentAnalysisPanel、ParallelismConfigPanel、ModelPresetEditor、ChipPresetEditor、TopologyEditor、ParameterSweep)
  • Scene3D/ — Three.js 3D 拓扑渲染(react-three-fiber)
  • TopologyGraph/ — 2D 力导向图(d3-force + react-force-graph)
  • KnowledgeGraph/ — 知识图谱(react-force-graph-2d)
  • ui/ — shadcn/ui 基础组件(20+ 个)
  • common/ — BaseCard、DeleteConfirmButton

API 层 (api/ + utils/llmDeployment/)

  • api/client.ts — axios 实例
  • api/results.ts / tasks.ts / topology.ts / model.ts / math_model.ts — 各域 API 函数
  • utils/llmDeployment/backendApi.ts — 仿真/评估提交逻辑
  • utils/llmDeployment/types.ts — 核心类型定义(~875 行,类型密集)

工具层

  • utils/topologyGenerator/ — 拓扑生成(expander, directConnections, switchConnections, regenerateConnections)
  • utils/llmDeployment/ — 预设管理、分数计算、chipMapper、trafficMapper
  • hooks/ — useTaskWebSocket, useDebouncedCallback, useGlobalTimer, useViewNavigation, useNodePositions

前端依赖与框架

// package.json 关键依赖(问题标注)
"react": "^18.2.0", // 合理
"react-router-dom": "^7.12.0", // [!] 安装但代码中未使用任何路由 API
"three": "^0.158.0", // 合理,3D
"@react-three/fiber": "^8", // 合理
"echarts": "^6.0.0", // 大型图表库(~3MB gzip)
"d3-force": "^3", // 力导向布局
"react-force-graph": "^1", // 封装 d3-force,与 d3-force 重叠
"react-force-graph-2d": "^1", // 知识图谱用,与上重叠
"flame-chart-js": "^3", // Gantt 甘特图(独特,无替代)
"katex": "^0.16", // 公式渲染
"@dnd-kit/*": "^6-10", // 拖拽
"@tanstack/react-table": "^8", // 结果表格

可疑点

  • react-router-dom v7 已安装,但 App.tsx、main.tsx、所有页面均不使用 <BrowserRouter>useNavigate 等 API。死依赖
  • react-force-graphreact-force-graph-2d 分别用于拓扑图和知识图谱,可统一为一个。

后端模块清单

目录结构(L0-L5 分层)

backend/perf_model/
├── L0_entry/ 入口层
│ ├── api/ API 路由(presets, benchmarks, topologies, simulation, tasks, experiments, column_config)
│ ├── database.py [主用] 新版 ORM(Experiment + EvaluationResult + ColumnConfig)
│ ├── storage/ [废弃] 旧版 ORM(Experiment + EvaluationTask + EvaluationResult),仍导出
│ ├── tasks.py ThreadPoolExecutor 任务队列
│ ├── engine.py L1-L5 集成入口
│ ├── compat.py Gantt/Stats 格式转换(300+ 行)
│ ├── config_loader.py YAML 配置加载
│ ├── config_schema.py Pydantic 请求/响应模型
│ └── websocket.py WebSocket 广播管理
├── L1_workload/ 模型定义、算子 IR、层级建模(MLA/MoE/MLP/Embedding)
├── L2_arch/ 芯片规格、拓扑 TopologySpec
├── L3_mapping/ 并行策略、tiling、调度
├── L4_evaluation/ 精评估引擎(math 路径 + g5 指令级路径)
└── L5_reporting/ 结果格式化(ReportingEngine, assembler, gantt, roofline, cost_analysis)

关键发现:双 ORM 模型并存

文件数据库路径表结构使用状态
L0_entry/database.pydata/llm_evaluations.dbExperiment + EvaluationResult + ColumnConfig主路径,API 层直接 import
L0_entry/storage/database.pytier6.dbExperiment + EvaluationTask + EvaluationResult残留,storage/init.py 仍导出

证据:

# main.py:18 - 主用新库
from perf_model.L0_entry.database import init_db

# storage/__init__.py:6 - 旧库仍被导出
from perf_model.L0_entry.storage.database import (Experiment, EvaluationTask, ...)

两个 ORM 定义不同的 SQLAlchemy metadata,若同时调用 create_all() 可能产生冲突或数据分散在两个文件。

关键发现:config_snapshot 向后兼容字段

# tasks.py:45 — 明确注释"前端兼容字段",违反 no-backward-compat 规则
config_snapshot = {
"topology_config": request.topology_config,
"topology": request.topology_config, # 别名,前端使用 topology
"model": model_config, # 前端使用 model
"inference": inference_config, # 前端使用 inference
...
}

关键发现:executor config 更新无效

# tasks.py:256-260 - 注释已承认,但问题从未修复
task_manager.max_workers = request.max_workers
task_manager.max_queued = request.max_queued
# 注意: 更新配置需要重建线程池,这里简单更新属性
# 实际生产环境需要更复杂的处理

关键发现:_temp_imports 内存泄漏

# experiments.py:31 — 全局字典,无过期清理
_temp_imports: dict[str, dict[str, Any]] = {}

每次 check-import 调用会向此字典写入条目,但 execute-import 清理只能在成功路径触发,服务重启前数据不会自动清理。

关键发现:simulation.py 使用 .get(key, default)

# simulation.py:69 — 违反 config-loading.md 规则
hidden_size = model_config.get("hidden_size", 4096)
num_layers = model_config.get("num_layers", 32)

注释说明"使用典型默认值用于快速估算",但规则要求缺少必需字段时 raise ValueError。

后端 API 端点清单

模块路径前缀主要端点
presets/api/presetsGET chips/models/runtime, CRUD chip-presets/model-presets
benchmarks/api/benchmarksCRUD
topologies/api/topologiesCRUD
simulation/apiPOST simulate, POST validate, POST model/calculate-params
tasks/api/evaluationPOST submit, GET tasks, GET tasks/{id}, POST cancel, DELETE, WebSocket /ws/tasks
experiments/api/evaluation/experimentsGET list, GET detail, PATCH update, DELETE, POST batch-delete, GET export, POST check-import, POST execute-import
column_config/api/evaluation/column-configGET, PUT

后端框架与依赖

fastapi>=0.115.6        合理
uvicorn>=0.34.0 合理
pydantic>=2.10.0 合理
sqlalchemy>=2.0.0 合理
websockets>=12.0 合理
pyyaml>=6.0.1 合理
python-multipart>=0.0.21 文件上传

极简依赖,未使用 Redis/Celery 等重量级组件。任务队列基于内存 ThreadPoolExecutor,重启丢失所有待执行任务


Round 2 — 问题清单与优先级分析

审查原则

本轮对照以下标准对 Round 1 发现进行归类:

  1. 正确性 — 代码逻辑是否存在 bug 或数据丢失风险
  2. 规则合规 — 是否违反 .claude/rules/ 规则文件
  3. 可维护性 — 是否产生未来维护负担
  4. 性能 — 是否存在可感知的性能问题
  5. 框架契合度 — 框架选型是否适合使用场景

P0 — 正确性/数据安全(必须修复)

P0-1: 双 ORM 模型可能写入不同数据库文件

  • 位置: L0_entry/storage/database.py vs L0_entry/database.py
  • 问题: 旧库写入 tier6.db,新库写入 data/llm_evaluations.db。若有代码同时调用两个模块的 create_tables(),会产生两个独立数据库,数据分散。
  • 证据: storage/__init__.py 仍导出旧 ORM,存在被意外 import 的风险。
  • 规则违反: 无(规则未覆盖),但属于正确性问题。
  • 影响: 数据丢失或数据分叉。

P0-2: executor config 更新不生效

  • 位置: tasks.py:256-260
  • 问题: PUT /evaluation/config 直接修改 task_manager.max_workers 属性,但 ThreadPoolExecutor 实例已创建,修改属性不会改变实际并发数。
  • 影响: 用户配置 max_workers 后行为与预期不符,无错误提示。

P0-3: _temp_imports 无过期清理

  • 位置: experiments.py:31
  • 问题: 全局内存字典存储上传的导入文件(JSON blob),无 TTL 或 LRU 清理。
  • 影响: 长时间运行的服务内存持续增长,大文件导入可能加速内存耗尽。

P1 — 规则违反(应修复)

P1-1: simulation.py 使用 .get() 默认值(违反 config-loading.md)

  • 位置: simulation.py:69-128 (calculate_model_params)
  • 规则: .claude/rules/config-loading.md 禁止 dict.get(key, default) 并要求 raise ValueError
  • 反驳: 端点注释明确说明"使用典型默认值用于快速估算",这是辅助工具端点,非仿真关键路径。
  • 结论: 该端点与规则存在已知张力,需决策是否豁免或补全 ValueError。

P1-2: config_snapshot 中保留向后兼容字段(违反 no-backward-compat.md)

  • 位置: tasks.py:44-51
  • 规则: .claude/rules/no-backward-compat.md 禁止为旧接口写兼容分支
  • 影响: 前端同时读 topology_configtopology 两个字段,字段语义重复。

P1-3: Results/index.tsx 超过 400 行(违反 code-style.md)

  • 位置: pages/Results/index.tsx(473 行,20+ useState)
  • 规则: .claude/rules/code-style.md TSX 超 400 行应拆分
  • 影响: 状态管理混乱,可读性差,二次开发困难。

P1-4: TopologySetup/index.tsx 超过 400 行(违反 code-style.md)

  • 位置: pages/TopologySetup/index.tsx(456 行)
  • 规则: 同上
  • 影响: 包含 3D/2D 两个视图的全部交互逻辑,责任过重。

P2 — 可维护性/架构债务(建议修复)

P2-1: react-router-dom 安装但完全未使用

  • 位置: package.json,代码中搜索无任何 useNavigate/BrowserRouter/Route 调用
  • 问题: 死依赖,增加 bundle 构建时间,引入潜在安全漏洞面。
  • 影响: 若不使用可直接移除;若要使用需评估对全量挂载方案的影响。

P2-2: 全量页面挂载模式(memory footprint 问题)

  • 位置: MainLayout/index.tsx:25-58
  • 问题: 6 个页面(含知识图谱 WebGL、3D Canvas、力导向模拟)全部在 App 挂载时初始化,仅通过 CSS hidden 切换。
  • 辩护: 代码注释说明目的是保留知识图谱节点位置状态。
  • 实际代价: 初始渲染时间增加;隐藏页面仍占用内存;Three.js 虽 lazy 但 TopologyGraph WebGL context 立即初始化。

P2-3: 5 层嵌套 Provider 导致不必要的重渲染

  • 位置: WorkbenchContext.tsx:174-188
  • 问题: 任一子 Context 变化(如 UIState.viewMode)会触发 WorkbenchInner re-render,而后者将所有 context 合并为一个对象,所有消费者都可能因此重渲染。
  • 影响: 随着状态增多,性能退化风险上升。

P2-4: 可视化库重复且体积庞大

  • 问题: 同时引入 echarts(~3MB)、d3-force、react-force-graph、react-force-graph-2d、flame-chart-js、@react-three/fiber/drei
  • 分析:
    • echarts 用于 KPI 图表(可替换为轻量 recharts 或 chart.js)
    • react-force-graph + react-force-graph-2d 功能高度重叠(可合并)
    • flame-chart-js 用于 Gantt,无简单替代
    • Three.js 已 manual chunks 分割,问题不大

P2-5: ThemeLab 开发调试页面暴露在生产构建中

  • 位置: MainLayout/index.tsx:55-57, UIStateContext ViewMode 类型中含 'theme-lab'
  • 问题: 无条件渲染的 ThemeLab 组件,Sidebar 中也可能有入口,用户可通过直接修改 viewMode 访问。

P2-6: 旧 storage/ 目录仍有导出,造成混淆

  • 位置: L0_entry/storage/
  • 问题: 仍有 __init__.py 导出旧 ORM 符号,随时有代码 import 旧模型写入错误的数据库文件。

P2-7: 任务队列为纯内存,重启丢失任务

  • 位置: L0_entry/tasks.py
  • 问题: ThreadPoolExecutor 状态不持久化,服务重启后 pending/running 任务消失,数据库也不记录任务状态。
  • 当前影响: 仅影响未完成任务,已完成结果已入库,影响有限。

P3 — 低优先级改善(可选)

P3-1: DATABASE_URL 有默认值(轻微违反 config-loading 精神)

# database.py:27
DATABASE_URL = os.getenv("DATABASE_URL", f"sqlite:///{DB_PATH}")

当前 fallback 是合理的开发便利,但严格按规则需显式配置。

P3-2: localStorage 键名不统一

  • CLAUDE.md 中记录 tier6_sider_width_cache,实际代码用 tier6_topology_sider_width

P3-3: TopologySpec 默认值可能掩盖配置缺失

  • L2_arch/topology.py:34-47:所有字段有默认值,config-loading 规则要求缺失字段时 raise。

P3-4: 无自动化测试

  • 规模已较大(后端 90+ Python 文件,前端 100+ TSX/TS),无任何 pytest/vitest 测试覆盖。

P2 竞争分析:全量挂载 vs React Router

支持保留全量挂载

  • 知识图谱 WebGL 场景的节点位置状态确实难以快速重建
  • 深度交互状态(拓扑编辑历史)不需序列化

支持迁移到路由

  • react-router-dom 已安装,白费资源
  • 内存浪费:6 页同时存在,含多个 WebGL context
  • 无法通过 URL 分享特定视图
  • 浏览器前进/后退按钮无效

结论: 建议混合方案——使用 React Router 做页面级导航,用 useRef 或状态持久化解决知识图谱坐标问题(实际上只需要用 useRef 保存上次坐标即可)。


Round 3 — 最佳实践方案与改造路线

详见 02-final-recommendation.md


附录:关键代码位置索引

问题ID位置具体行号
P0-1L0_entry/storage/database.py + L0_entry/database.py两文件全部
P0-2L0_entry/api/tasks.py256-260
P0-3L0_entry/api/experiments.py31
P1-1L0_entry/api/simulation.py69-128
P1-2L0_entry/api/tasks.py44-51
P1-3pages/Results/index.tsx1-473
P1-4pages/TopologySetup/index.tsx1-456
P2-1frontend/package.jsonreact-router-dom
P2-2layouts/MainLayout/index.tsx25-58
P2-3contexts/WorkbenchContext.tsx134, 174-188
P2-4frontend/package.jsonecharts, d3-force, react-force-graph
P2-5layouts/MainLayout/index.tsx55-57
P2-6L0_entry/storage/__init__.py全部
P2-7L0_entry/tasks.pyTaskManager 类