Files
tianxuan/.omo/plans/tianxiu-v5.md
T

249 lines
13 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.
# 天璇 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 版本锁定