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

187 lines
9.3 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.
# 天璇 v4 修复计划
## TL;DR (For humans)
7个问题,24个todo。核心改动:IP子网实体聚合、多列实体、地球手动缩放、异步聚类修复、列类型值优先检测。
---
## Problem & Approach
Intent: **CLEAR** — 用户列出7个具体问题,方案可从前端/后端代码推导。无需高精度审查。
### Issue 1 — 聚类选非数值列报错
**根因**: `_run_analysis_worker` (views.py:502) 默认取前10非_开头的列;手动向导步骤2未过滤非数值列。`_handle_run_clustering` (tool_registry.py:787-791) 虽有数值过滤,但无列给用户时返回"无可用维度"且不提示哪些可用。
**方案**:
- manual.html 步骤2特征列只展示数值列(标注dtype)
- `_handle_run_clustering` 报错时返回可用数值列名清单
- views.py 默认特征列选择排除实体列自身
### Issue 2 — 地球问题 (4个子问题)
**根因**:
- TLS流不显示 → globe_view 只用GeoIP,未用数据列经纬度
- 经纬线 → Three.js未画辅助线
- 滚轮缩放 → 无wheel事件处理
- 复选框状态丢失 → GET表单→刷新页面→选中状态丢失
**方案**:
- globe_view 优先用数据列`src_latitude/src_longitude/dst_latitude/dst_longitude`,回退GeoIP
- 增强现有手动拖拽JS + 添加滚轮事件(不引入OrbitControls——three.min.js是UMD构建,OrbitControls需要ESM,不兼容)
- 用 LineLoop 绘10度间隔经纬线
- 模板端将 `?runs=1,2` 参数回填到checkbox checked属性
### Issue 3 — Django异步聚类失败
**根因**: `_run_analysis_worker` (views.py:503) 在线程中调 `asyncio.run()``_handle_run_clustering` 本质是同步代码(polars/numpy/sklearn,无await)`_handle_extract_features` 内有 `sync_to_async` 用于ORM。
**方案**:
- `_handle_run_clustering` 提取同步核心函数 `_cluster_sync()`worker直接调同步版
- `_handle_extract_features``asgiref.sync.async_to_sync` 包装
### Issue 4 — 列类型靠列名而非数值
**根因**: `type_classifier.py` 优先级: config→名称匹配→值检测。名称匹配(如 _ip→IPv4)过于宽泛。
**方案**: 改为 config→值检测→名称匹配(仅值检测返回STRING时作为二次提示)。增加MAC地址、端口值模式。
### Issue 5 — 数据列初步分析
**方案**: `scripts/column_survey.py`——standalone脚本,输出每列名称/dtype/唯一值数/空值率/前5非空样本。
### Issue 6 — src_ip和dst_ip都作为实体
**根因**: `entity_detector.py` 只返回单 `recommended``_background_process` (views.py:367) 只用单列。
**实际状态**: manual.html line 46 `<select multiple>` 已支持多选!`_run_analysis_worker` (views.py:464) 已处理 `entity_columns` 列表。gap仅在于 `_background_process``entity_detector`
**方案**:
- `entity_detector` 增加 `recommended_multi` (前N候选)
- `_background_process` 支持多列实体聚合
- manual.html 确认页(步骤3)修复多选显示
### Issue 7 — IP子网实体聚合 (/24 + /28)
**根因**: 无子网处理。IP直接作为实体标识。
**方案**:
- Config新增 `Entity` 类,含 `subnet_masks``ip_columns`
- `entity_aggregator.py` 新增 `ip_to_subnet()``apply_subnet_aggregation()`
- 在 group_by 之前将IP转换为子网前缀
- 支持 /24 (0-255) 和 /28 (240-255)
---
## Architecture & Design Decisions
### Decision 1: IP子网聚合设计
- 位置: entity_aggregator.py`aggregate_by_entity` 之前
- mask从 config 读取,支持 [24, 28]
- 转换后的子网前缀替代原始IP:
- `ip_to_subnet('10.0.1.15', 24)``'10.0.1.0/24'`
- `ip_to_subnet('10.0.1.250', 28)``'10.0.1.240/28'`
- 无效IP/None → 原值
```python
# IP → subnet prefix
def ip_to_subnet(ip_str: str, mask: int) -> str:
"""Convert IP to CIDR subnet prefix."""
if not ip_str or not isinstance(ip_str, str):
return ip_str
try:
parts = [int(o) for o in ip_str.strip().split('.')]
if len(parts) != 4:
return ip_str
if any(p < 0 or p > 255 for p in parts):
return ip_str
ip_int = (parts[0]<<24) + (parts[1]<<16) + (parts[2]<<8) + parts[3]
subnet_int = ip_int & (0xFFFFFFFF << (32 - mask))
return f"{(subnet_int>>24)&255}.{(subnet_int>>16)&255}.{(subnet_int>>8)&255}.{subnet_int&255}/{mask}"
except (ValueError, IndexError, TypeError):
return ip_str
```
### Decision 2: 多列实体
- `EntityProfile.entity_value` (max_length=512) 存储多列值: `"src_ip=10.0.0.1||dst_ip=203.0.113.5"`
- `AnalysisRun.entity_column` (max_length=256) 存储逗号分隔: `"src_ip,dst_ip"`
- `unique_together = ['run', 'entity_value']` 自然支持多列唯一性
### Decision 3: 值优先类型检测
优先级:
1. Config override → 立即返回
2. 值采样 → 按顺序检测: IPv4→MAC→HEX→URL→Port→BOOL_ENUM→ENUM→Float→STRING
3. 名称匹配 → 仅值检测返回STRING时作为二次提示(如 `_ip` 后缀提示IPv4)
4. STRING作为最终兜底
新增模式:
- MAC: `r'^([0-9A-Fa-f]{2}[:.-]){5}[0-9A-Fa-f]{2}$'`
- Port: 非空值全部在 1-65535 且全为整数 → ENUM
- Float: >80% 样本可 `float()` 解析 → FLOAT
### Decision 4: 地球缩放+旋转 (不引入OrbitControls)
**重要**: three.min.js (603KB) 是 UMD 独立构建,OrbitControls 是 three/examples/jsm/ 的 ESM 格式,
无法直接加载。两个可行方案已评估:
a) ❌ 下载 OrbitControls 到 static/ - 需要处理模块依赖链(Controls→Euler→...),不适用于离线场景
b) ✅ 增强现有手动拖拽JS + 添加滚轮缩放事件 (globe.html lines 101-110 已有拖拽骨架)
实施:
- 保留并增强现有 mousedown/mousemove/mouseup 逻辑(已验证可用)
- 添加 `wheel` 事件: `camera.position.z += event.deltaY * 0.01` + clamp(3, 30)
- 鼠标中键重置视角
---
## Dependency Graph
```
独立阶段(可并行):
Todo 1-2: 列分析脚本 ──── (无依赖)
Todo 3-4: 值类型检测 ──── (无依赖)
Todo 5-8: 地球修复 ────── (无依赖)
Todo 9-10: 异步修复 ───── (无依赖)
顺序阶段:
Todo 11-13: 子网聚合 ──── (无依赖,但避免与类型检测冲突,需确保IP列识别的正确性)
Todo 14-17: 多列实体 ──── (依赖子网聚合的 IP→subnet 转换,因为多列中的IP列需要被子网处理)
Todo 18-20: 聚类列过滤 ── (依赖多列实体,因为 entity 列需要从 feature 列中排除)
最终:
Todo 21-24: 测试+文档 ─── (依赖所有)
F1-F3: 最终验证 ───────── (依赖所有)
```
---
## Todos
- [x] 1. 创建 `scripts/column_survey.py`: 对每列输出名称/dtype/唯一值数/空值率/前5样本
- [x] 2. QA column_survey: 在现有测试数据上有合理输出
- [x] 3. `type_classifier.py`: 值优先检测(IPv4→MAC→HEX→URL→Port→BOOL→ENUM→Float→STRING);名称降级为二次提示
- [x] 4. QA type_classifier: `pytest tests/test_type_classifier.py`
- [x] 5. `globe.html`: 添加wheel滚轮缩放事件 + 增强现有mousedown/move/up拖拽(不引入OrbitControls
- [x] 6. `globe.html`: 添加经纬线网格(10度间隔 LineLoop 绘制纬线圆+经线弧)
- [x] 7. `globe.html` + `globe_view` 修复: (a) 复选框提交后保留选中状态 (b) globe_view 优先用数据列经纬度
- [x] 8. QA globe: 打开 `/globe/` 交互验证滚轮缩放+经纬线+复选框
- [x] 9. `views.py` + `tool_registry.py`: 提取 `_cluster_sync()` 同步核心 + `async_to_sync(_handle_extract_features)`
- [x] 10. QA async: 手动分析流程聚类步骤不报 `asyncio.run()` 错误
- [x] 11. `config/loader.py`: 新增 `Config.Entity` 类含 `subnet_masks`(list[int]) 和 `ip_columns`
- [x] 12. `entity_aggregator.py`: 新增 `ip_to_subnet()` 函数(含空值/无效IP处理)+ `apply_subnet_aggregation()`
- [x] 13. QA subnet: `ip_to_subnet('10.0.1.15', 24) == '10.0.1.0/24'` + `ip_to_subnet(None, 24) == None`
- [x] 14. `entity_detector.py`: 返回 `recommended_multi`(前3候选list);对IP列自动推荐多列
- [x] 15. `views.py`: `_background_process` 支持多列实体聚合;`AnalysisRun.entity_column` 存逗号分隔
- [x] 16. `manual.html`: 步骤3确认页展示多选实体列(`entity_columns|join:", "`)
- [x] 17. QA multi-col: 选 src_ip+dst_ip → 确认页显示两个 → 聚合 → 聚类 → 成功
- [x] 18. `manual.html`: 步骤2特征列只展示数值列(标注dtype如"bytes_sent (Int64)");排除entity列
- [x] 19. `views.py` + `tool_registry.py`: 聚类报错时返回可用数值列清单;views.py默认排除entity列
- [x] 20. QA cluster-col: 手动向导验证特征列仅数值列
- [x] 21. `tests/test_type_classifier.py`: 添加 MAC/端口/值优先测试
- [x] 22. `tests/test_entity.py`: 添加多列实体+IP子网测试
- [x] 23. `tests/test_e2e.py`: 添加多列实体→聚类端到端
- [x] 24. 更新 AGENTS.md 和 docs/工作流.md: 新增架构决策+已知问题表
## Final Verification Wave
- [x] F1. 全量测试: `pytest tests -q` → 全部通过
- [x] F2. 解除占用: 清理临时文件
- [x] F3. 更新所有 MD 文件到最终状态
---
## Approval Gate
状态: 等待批准
待执行: 24个todo,预计500-800行变更
关键变更: 10个文件(views.py/tool_registry.py/type_classifier.py/entity_aggregator.py/
entity_detector.py/globe.html/manual.html/config.yaml/loader.py/models.py)
同意后开始执行?