249 lines
13 KiB
Markdown
249 lines
13 KiB
Markdown
# 天璇 v5 — 18 Issues Full Fix Plan
|
||
|
||
## TL;DR (For humans)
|
||
修复上传管理、列检测、大规模数据处理、Globe可视化、聚类可视化等18个问题。
|
||
|
||
---
|
||
|
||
## Workstream A: 上传管理修复 (Issues 1, 2, 3, 10, 11)
|
||
|
||
### A1 重启后端后旧数据可分析
|
||
**Files**: `analysis/views.py`, `analysis/session_store.py`
|
||
**Root Cause**: 旧分析的 `AnalysisRun` 记录在 DB 中,但 `csv_glob` 指向的目录是 `%APPDATA%/TianXuan/data/uploads/{timestamp}/`。重启后 session_store 内存清空,但上传文件仍在磁盘。`globe_view` 和 `run_detail` 尝试加载旧运行时会调用 `load_csv_directory(run.csv_glob)` 重新读取 CSV。
|
||
**Fix**: 确保 `load_csv_directory` 能处理已删除的上传目录 → 优雅降级。在 `globe_view` 和 `_background_process` 中添加 `os.path.isdir()` 检查,目录不存在时将 run 标记为 `failed` 并设置友好的错误消息。
|
||
|
||
### A2 Web图标在所有页面显示
|
||
**Files**: `templates/base.html`, `static/tianxuan/`
|
||
**Root Cause**: `base.html` 没有 `<link rel="icon">`。浏览器自动请求 `/favicon.ico` 但项目无此文件。
|
||
**Fix**:
|
||
1. 创建 `static/tianxuan/favicon.svg` 或使用基于主题的 HTML favicon
|
||
2. `base.html` 的 `<head>` 中添加 `<link rel="icon" href="{% static 'tianxuan/favicon.ico' %}">`
|
||
|
||
### A3 上传按钮在页面顶部
|
||
**File**: `templates/tianxuan/upload.html`
|
||
**Root Cause**: 上传 `<form>` 在 "已上传数据集" 表格之前,但上传按钮 (`#submitBtn`) 初始 `display:none`,只有在选择文件后才显示。
|
||
**Fix**: 将上传按钮移到 dropZone 下方、紧跟在选择文件后显示。或者将上传区域移到页面顶部的固定/粘性位置。
|
||
|
||
### A10 Django文件上传无限制
|
||
**Files**: `tianxuan/settings.py`, `analysis/views.py`
|
||
**Root Cause**: 当前限制:`DATA_UPLOAD_MAX_NUMBER_FIELDS=10000`、`DATA_UPLOAD_MAX_MEMORY_SIZE=50MB`、`views.py` 中硬编码 2000 文件/10GB 总大小限制。用户说"只能上传100文件"可能是由于 `FILE_UPLOAD_MAX_MEMORY_SIZE=10MB` 导致大文件被写入临时文件时出现问题。
|
||
**Fix**:
|
||
- 移除 `DATA_UPLOAD_MAX_NUMBER_FIELDS` 限制(设为 0 或大幅增加)
|
||
- 增大 `DATA_UPLOAD_MAX_MEMORY_SIZE` 到 256MB
|
||
- 移除 `views.py` 中的 2000 文件限制(改为仅警告)
|
||
|
||
### A11 上传页删除功能完善
|
||
**Files**: `templates/tianxuan/upload.html`, `analysis/views.py`
|
||
**Root Cause**: 删除按钮只显示在 `status` 为 `ready/completed/failed` 的运行上。`loading/profiling/aggregating` 状态的运行没有删除按钮(可能是为了安全)。
|
||
**Fix**:
|
||
- 修改模板,在所有状态下显示删除按钮(`loading` 状态时弹出确认:"后台处理中,删除将中断分析")
|
||
- 确保 `delete_upload` 视图能安全处理正在处理的运行(`shutil.rmtree` + ORM delete)
|
||
|
||
---
|
||
|
||
## Workstream B: 列检测与类型分类修复 (Issues 7, 15, 18)
|
||
|
||
### B7 TLS 0ver hex格式识别
|
||
**Files**: `analysis/type_classifier.py`, `analysis/views.py`, `templates/tianxuan/globe.html`
|
||
**Root Cause**: `0ver` 列名不在名称启发式规则中(需要 `^ver_` 前缀)。TLS版本检测在 `views.py` 中硬编码为 `'1.3' in tls` / `'1.2' in tls`,不知道 hex 格式如 `03 03` (TLS 1.2) / `03 04` (TLS 1.3)。
|
||
**Fix**:
|
||
1. `type_classifier.py` 的 `_NAME_TYPE_MAP` 添加 `0ver` → ENUM 映射
|
||
2. `views.py` 的 `globe_view` 中添加 hex→TLS版本映射:
|
||
```python
|
||
TLS_HEX_MAP = {'0303': 'TLSv1.2', '0304': 'TLSv1.3', '0302': 'TLSv1.1', '0301': 'TLSv1.0'}
|
||
hex_val = tls.replace(' ', '') # "03 03" → "0303"
|
||
tls_label = TLS_HEX_MAP.get(hex_val, 'other')
|
||
```
|
||
3. Globe legend 添加对 hex 版本的说明
|
||
|
||
### B15 "+"字符串被识别为数字
|
||
**Files**: `analysis/type_classifier.py`, `analysis/data_loader.py`
|
||
**Root Cause**: 独立 `+` 在 `_coerce_to_float` 中被列为 artifact(正常),但值如 `+5`、`+0.5` 等前导加号的值会通过 `float("+5")` 解析。在 `_values_to_type` 中,LAT_LON 检测会过滤独立 `+`,但列中混有 `+` 和数字的列可能被分类为 FLOAT。
|
||
**Fix**:
|
||
1. 在 `_values_to_type` 的 FLOAT 检测步骤中,检查采样中是否有 `+` 或 `-` 开头的字符串值
|
||
2. 如果存在前导 `+`/`-` 且并非所有值都是有效数字,降级为 STRING
|
||
3. 在 `_coerce_to_float` 中添加对 `+` 前缀值的更严格检查
|
||
|
||
### B18 根据真实列名配置实体检测
|
||
**Files**: `analysis/entity_detector.py`, `analysis/entity_aggregator.py`, `config/config.yaml`
|
||
**Changes**:
|
||
1. `entity_detector.py` 的 `ENTITY_KEYWORDS` 添加用户全部列名
|
||
2. `entity_aggregator.py` 的关键词集合添加用户列名
|
||
3. `config.yaml` 更新 `entity.ip_columns` 和 column 覆盖
|
||
4. `type_classifier.py` 的 `_NAME_TYPE_MAP` 添加全部列名映射
|
||
|
||
**用户列名映射表**:
|
||
| 列名 | 类型 | 聚合用途 | 检测关键词 |
|
||
|------|------|---------|-----------|
|
||
| `:ips` | IPv4 (已存在) | src IP | `:ips` |
|
||
| `:ipd` | IPv4 (已存在) | dst IP | `:ipd` |
|
||
| `:prs` | ENUM/端口 | src port | `:prs` |
|
||
| `:prd` | ENUM/端口 | dst port | `:prd` |
|
||
| `scnt` | STRING/ENUM | source country | `scnt` |
|
||
| `dcnt` | STRING/ENUM | dest country | `dcnt` |
|
||
| `server-ip` | IPv4 | server IP | `server_ip` |
|
||
| `client-ip` | IPv4 | client IP | `client_ip` |
|
||
| `0ver` | ENUM | TLS version | `0ver` |
|
||
| `cnam` | STRING | cert common name | `cnam` |
|
||
| `snam` | STRING | server name | `snam` |
|
||
| `4dur` | FLOAT | duration | `4dur` |
|
||
| `8ses` | FLOAT | session offset | `8ses` |
|
||
| `2tmo` | FLOAT | timeout offset | `2tmo` |
|
||
| `4ksz` | INT | key size | `4ksz` |
|
||
| `cnrs` | BOOL_ENUM | recoverable | `cnrs` |
|
||
| `isrs` | BOOL_ENUM | is recovered | `isrs` |
|
||
| `8ack` | INT | backward bytes | `8ack` |
|
||
| `8ppk` | INT | payload packets | `8ppk` |
|
||
| `8dbd` | TIMESTAMP | db timestamp | `8dbd` |
|
||
| `1ipp` | ENUM | IP protocol | `1ipp` |
|
||
| `4dbn` | ENUM | database number | `4dbn` |
|
||
| `tabl` | STRING | table name | `tabl` |
|
||
| `name` | STRING | link name | `name` |
|
||
| `source-node` | STRING | source node | `source_node` |
|
||
| `cipher-suite` | HEX | TLS cipher | `cipher_suite` |
|
||
| `ecdhe-named-curve` | STRING | ECDHE curve | `ecdhe_named_curve` |
|
||
| `0cph` | HEX | TLS cipher hex | `0cph` |
|
||
| `0crv` | HEX | TLS curve hex | `0crv` |
|
||
| `0rnd` | STRING | TLS random | `0rnd` |
|
||
| `0rnt` | FLOAT/TIMESTAMP | TLS random time | `0rnt` |
|
||
| `4dbn` | STRING | db number | `4dbn` |
|
||
| `time` | TIMESTAMP | time | `time` |
|
||
| `timestamp` | TIMESTAMP | timestamp | `timestamp` |
|
||
| `row` | INT | row number | `row` |
|
||
| `+.latd` | LAT_LON | latitude (entity_aggregator 子串匹配) | `latd` |
|
||
| `+.lond` | LAT_LON | longitude | `lond` |
|
||
| `+.ispn` | STRING | ISP name | `ispn` |
|
||
| `+.orgn` | STRING | org name | `orgn` |
|
||
| `+.city` | STRING | city | `city` |
|
||
|
||
**关键的 `_find_column` 改进**:
|
||
`entity_aggregator.py` 中的 `_find_column` 使用严格匹配。对于 `:prs` 需要匹配 dst_port keywords → 不匹配。需要在 `_find_column` 中添加 `+` 和 `:` 前缀剥离:
|
||
```python
|
||
normalised = name.lower().lstrip(':+').replace('-', '_').replace(' ', '_')
|
||
```
|
||
|
||
**Lat/Lon 列检测修复**:
|
||
`+.latd` / `+.lond` 列:
|
||
- `type_classifier._values_to_type` 中如果值 max_abs ≤ 10 则 LAT_LON 检测失败
|
||
- 需要在 `_NAME_TYPE_MAP` 中添加 `latd|lond` 模式 → LAT_LON
|
||
- 或在 `_name_to_type` 中添加对 `+.` 前缀的感知
|
||
|
||
---
|
||
|
||
## Workstream C: Traceback截断修复 (Issue 4)
|
||
|
||
### C4 清理所有 traceback 截断
|
||
**File**: `analysis/tool_registry.py` line 1138
|
||
**Current**: `traceback.format_exc()[:200]` 截断 PCA 错误到 200 字符
|
||
**Fix**: 改为完整 traceback。同时审计整个项目:
|
||
- `tianxuan/llm_orchestrator.py` line 71: `json.dumps(...)[:1200]` — 这是工具结果截断,合理,保留
|
||
- `analysis/tool_registry.py` line 43: `_truncate_response()` — 工具响应截断,合理,保留
|
||
- **只修复** line 1138 的 `[:200]`
|
||
|
||
---
|
||
|
||
## Workstream D: 大规模数据处理 (Issues 5, 6, 14, 16)
|
||
|
||
### D5 大数据量卡顿优化 (1M-20M行)
|
||
**Root Cause**: 当前 `MAX_ROWS_PER_RUN = 300` 但分析流水线是一次性 collect 所有数据到内存。
|
||
**Fix**:
|
||
1. `data_loader.py` 的 `load_csv_directory` 添加 `head=N` 参数用于预览场景
|
||
2. 聚类分析使用 Polars streaming (`engine='streaming'`)
|
||
3. 在 `tool_registry.py` 的 `_cluster_sync` 中添加数据降采样:>50K 行时随机采样
|
||
4. `views.py` 中 globe_view 的 `MAX_ROWS_PER_RUN` 从 300 改为 500(对于 20M 数据来说 300 行太稀疏了)
|
||
|
||
### D6 进度条 + 静默处理
|
||
**Root Cause**: 当前状态机只有 `loading→profiling→aggregating→ready→clustering→extracting→completed/failed`,无百分比。
|
||
**Fix**:
|
||
1. 在 `AnalysisRun` model 添加 `progress_pct` (IntegerField, default=0) 和 `progress_msg` (CharField)
|
||
2. 后台线程在每一步更新 `progress_pct`(loading=10, profiling=30, aggregating=50, clustering=70, extracting=90, completed=100)
|
||
3. `run_status_api` 返回 `progress_pct` 和 `progress_msg`
|
||
4. `upload.html` 中的进度条显示实际百分比
|
||
5. **无前端静默处理**:添加 `?background=true` 参数,后端仍全速分析但前端不轮询
|
||
|
||
### D14 低内存设备聚类崩溃
|
||
**Root Cause**: 8GB 设备上,20M 行 × 30 列的数据在聚类时会产生巨大 float64 ndarray。
|
||
**Fix**:
|
||
1. `_cluster_sync` 中添加内存检查:`psutil.virtual_memory().available < 2GB` 时自动启用流式模式
|
||
2. 自动降采样:>100K 行时使用 `sklearn.utils.resample` 采样到 50K 行
|
||
3. 使用 `MiniBatchKMeans` 替代 `KMeans`(内存效率更高)
|
||
4. 在聚类前使用 `sklearn.decomposition.PCA(n_components=min(50, n_features))` 降维
|
||
|
||
### D16 空闲时高磁盘IO
|
||
**Root Cause**: `session_store.py` 是纯内存的,不应有磁盘 IO。但 Django 日志 `RotatingFileHandler` 可能在后台写入。另外 SQLite 使用 DELETE 模式(非 WAL)可能导致写入阻塞。
|
||
**Fix**:
|
||
1. 审计后台线程是否有不必要的 ORM 轮询
|
||
2. 如果 `settings.py` 中的 `LOGGING` 配置了低级别(如 DEBUG),改为 INFO+
|
||
3. 检查是否有任何定时任务或 watchdog
|
||
|
||
---
|
||
|
||
## Workstream E: Globe可视化修复 (Issues 8, 9)
|
||
|
||
### E8 Globe响应式缩放
|
||
**File**: `templates/tianxuan/globe.html`
|
||
**Root Cause**: 当前高度固定为 700px(`H = 700`),resize 只更新宽度和 aspect。缩放(wheel)只移动 `camera.position.z`(zoom),不按比例调整数据点大小。
|
||
**Fix**:
|
||
1. resize handler 中根据容器尺寸动态计算高度:`H = container.clientHeight || window.innerHeight * 0.8`
|
||
2. 添加设备像素比处理:`renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))`
|
||
3. 弧线粗细使用相对比例(根据 zoom level 调整 `TubeGeometry.radius` 或 `LineBasicMaterial.linewidth`)
|
||
|
||
### E9 `?data=` 访问拒绝支持
|
||
**File**: `analysis/views.py` (globe_view), `templates/tianxuan/globe.html`
|
||
**Root Cause**: globe_view 只识别 `?runs=1&runs=2` 格式,没有 `?data=` 参数的处理逻辑。
|
||
**Fix**:
|
||
1. `globe_view` 添加 `?data=dataset_id` 参数支持
|
||
2. 当提供 `data=` 时,直接从 `SessionStore` 按 dataset_id 读取数据
|
||
3. Globe 模板更新 JavaScript 以支持 dataset 模式
|
||
|
||
---
|
||
|
||
## Workstream F: LLM日志 (Issue 12)
|
||
|
||
### F12 LLM运行过程打印到前端
|
||
**Files**: `tianxuan/llm_orchestrator.py`, `analysis/views.py`, `templates/tianxuan/auto.html`
|
||
**Root Cause**: LLM 在 thread 中运行,`logger.info` 写入日志文件,但前端 `auto.html` 通过轮询 `/logs/?pos=N` 显示日志。问题是日志缓冲可能延迟写入。
|
||
**Fix**:
|
||
1. 添加 `llm_status_api` 视图返回当前 LLM 运行状态
|
||
2. `llm_orchestrator` 使用回调函数报告进度(步骤/工具调用/中间结果)
|
||
3. `_run_llm_analysis` worker 写入 `AnalysisRun.run_log` 字段
|
||
|
||
---
|
||
|
||
## Workstream G: 聚类可视化修复 (Issue 13)
|
||
|
||
### G13 聚类图表完善
|
||
**Files**: `templates/analysis/cluster_overview.html`, `analysis/views.py`
|
||
**Root Cause**: 当前只有 PCA 散点图 + 地理散点图(两个 Canvas)。用户要求更丰富的图表。
|
||
**Fix**:
|
||
1. 添加簇大小饼图/柱状图(Canvas)
|
||
2. 添加 Silhouette 得分的簇间对比图
|
||
3. 添加特征重要性水平条形图(top 10 features per cluster)
|
||
4. 确保 PCA 坐标在 web 分析流程中正确保存(当前 `_handle_extract_features` 在 tool_registry.py 中保存,但异常被 `[:200]` 截断 → 先修 C4)
|
||
|
||
---
|
||
|
||
## Workstream H: 聚类质量改进 (Issue 17)
|
||
|
||
### H17 预处理降维+特征工程后聚类
|
||
**Files**: `analysis/tool_registry.py`, `analysis/entity_aggregator.py`
|
||
**Root Cause**: 当前聚类直接对原始聚合特征(flow_count, total_bytes, unique_dst_ips 等)做 StandardScaler + HDBSCAN。没有特征选择、PCA/UMAP 预处理。
|
||
**Fix**:
|
||
1. 在 `_cluster_sync` 中添加可选的 PCA 预处理步骤
|
||
2. 添加方差过滤:去除方差为 0 或接近 0 的特征
|
||
3. 添加相关性过滤:去除相关性 >0.95 的特征对中的冗余特征
|
||
4. HDBSCAN 超参数根据数据规模自动调整
|
||
|
||
---
|
||
|
||
## Dependencies
|
||
- C4 (traceback fix) is prerequisite for G13 (PCA fix won't be debuggable without full traceback)
|
||
- B18 (column mapping) is prerequisite for B7 (0ver hex detection needs 0ver in column map) and G17 (proper features need proper column detection)
|
||
- D5 (optimization) is partially prerequisite for D14 (memory handling both affect clustering path)
|
||
|
||
## Must-Not-Have
|
||
- 不修改 `runtime/` 目录下的任何文件
|
||
- 不删除或重命名现有数据库表
|
||
- 不添加新的 Python 包依赖(除非绝对必要且用户批准)
|
||
- 不修改 Polars 版本锁定
|