refactor(officefile): 按 md/latex/word 三层结构重组文档目录
将 Markdown 源文件移入 md/,LaTeX 工作目录保留在 latex/, Word 导出移入 word/;删除临时脚本、调试截图和空 stub。 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,562 @@
|
||||
# 01.1 智能的模块化视角
|
||||
|
||||
## 核心问题
|
||||
|
||||
> 为什么智能系统需要模块化设计?
|
||||
> 技能(Skill)的本质是什么?如何设计可复用的智能组件?
|
||||
> 函数式组合思想如何应用于AI系统?
|
||||
|
||||
---
|
||||
|
||||
## 概念讲解
|
||||
|
||||
### 模块化的必要性
|
||||
|
||||
随着系统复杂度增加,模块化变得必不可少:
|
||||
|
||||
```
|
||||
复杂度与模块化的关系
|
||||
|
||||
低复杂度 ──→ [单体脚本] ──→ 可维护
|
||||
↑
|
||||
简单直接
|
||||
|
||||
中复杂度 ──→ [函数库] ──→ 需要组织
|
||||
↑
|
||||
按功能分类
|
||||
|
||||
高复杂度 ──→ [模块化系统] ──→ 必须模块化
|
||||
↑
|
||||
清晰边界,可组合
|
||||
```
|
||||
|
||||
**模块化的收益**:
|
||||
|
||||
| 收益类型 | 说明 | 例子 |
|
||||
|---------|------|------|
|
||||
| 可理解性 | 每个模块可独立理解 | 理解缓冲区分析不需要理解投影变换 |
|
||||
| 可测试性 | 模块可单独测试 | 测试空间索引不需要完整工作流 |
|
||||
| 可复用性 | 模块可在不同场景使用 | 缓冲区算法用于多个项目 |
|
||||
| 可替换性 | 模块可用等价实现替换 | QGIS ↔ ArcGIS 同一功能 |
|
||||
| 可维护性 | 修改局限在模块内 | 修复bug不影响其他模块 |
|
||||
|
||||
### 函数式组合思想
|
||||
|
||||
函数式编程的核心:**组合小函数构建复杂行为**
|
||||
|
||||
```
|
||||
简单函数 ──┬─── buffer(geom, distance)
|
||||
├─── intersect(a, b)
|
||||
├─── centroid(geom)
|
||||
└─── distance(a, b)
|
||||
│
|
||||
↓ 组合
|
||||
│
|
||||
complex_operation = pipe(
|
||||
load_data,
|
||||
clean_geometry,
|
||||
buffer(100),
|
||||
intersect(study_area),
|
||||
calculate_area,
|
||||
format_output
|
||||
)
|
||||
```
|
||||
|
||||
**关键特性**:
|
||||
1. **纯函数**:相同输入→相同输出,无副作用
|
||||
2. **高阶函数**:函数可以作为参数和返回值
|
||||
3. **不可变数据**:数据不修改,而是创建新版本
|
||||
|
||||
### 技能即能力封装
|
||||
|
||||
在Claude Code中,技能(Skill)是智能的模块化单元:
|
||||
|
||||
```yaml
|
||||
# 技能的结构
|
||||
---
|
||||
name: skill-name # 技能名称
|
||||
description: 技能描述 # 何时使用
|
||||
parameters: # 输入参数
|
||||
- param1: type
|
||||
- param2: type
|
||||
returns: # 输出
|
||||
- result: type
|
||||
---
|
||||
|
||||
## 技能逻辑
|
||||
|
||||
具体的执行步骤...
|
||||
```
|
||||
|
||||
**技能设计的三个层次**:
|
||||
|
||||
```
|
||||
Level 1: 原子技能
|
||||
└── 单一功能,不可再分
|
||||
例如:buffer, intersect, dissolve
|
||||
|
||||
Level 2: 组合技能
|
||||
└── 由原子技能组合而成
|
||||
例如:site_selection = buffer + intersect + rank
|
||||
|
||||
Level 3: 工作流技能
|
||||
└── 完整的决策流程
|
||||
例如:ecological_network_analysis
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 设计原理
|
||||
|
||||
### 模块化的设计原则
|
||||
|
||||
**1. 单一职责原则 (SRP)**
|
||||
|
||||
每个模块只做一件事,做好一件事:
|
||||
|
||||
```python
|
||||
# 好的设计:每个函数职责单一
|
||||
def calculate_distance(geom1, geom2):
|
||||
"""只计算距离"""
|
||||
return geom1.distance(geom2)
|
||||
|
||||
def format_distance(distance, unit='m'):
|
||||
"""只格式化输出"""
|
||||
if distance > 1000:
|
||||
return f"{distance/1000:.2f} km"
|
||||
return f"{distance:.0f} m"
|
||||
|
||||
# 使用
|
||||
dist = calculate_distance(point_a, point_b)
|
||||
formatted = format_distance(dist)
|
||||
|
||||
# 不好的设计:混合了计算和格式化
|
||||
def calculate_and_format_distance(geom1, geom2):
|
||||
distance = geom1.distance(geom2)
|
||||
# 格式化逻辑混在一起
|
||||
if distance > 1000:
|
||||
return f"{distance/1000:.2f} km"
|
||||
return f"{distance:.0f} m"
|
||||
```
|
||||
|
||||
**2. 开闭原则 (OCP)**
|
||||
|
||||
对扩展开放,对修改关闭:
|
||||
|
||||
```python
|
||||
# 使用抽象基类实现扩展性
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
class SpatialOperation(ABC):
|
||||
"""空间操作的抽象基类"""
|
||||
|
||||
@abstractmethod
|
||||
def execute(self, data):
|
||||
pass
|
||||
|
||||
class BufferOperation(SpatialOperation):
|
||||
"""缓冲区操作"""
|
||||
def __init__(self, distance):
|
||||
self.distance = distance
|
||||
|
||||
def execute(self, data):
|
||||
return data.buffer(self.distance)
|
||||
|
||||
class IntersectOperation(SpatialOperation):
|
||||
"""相交操作"""
|
||||
def __init__(self, other_data):
|
||||
self.other_data = other_data
|
||||
|
||||
def execute(self, data):
|
||||
return data.intersection(self.other_data)
|
||||
|
||||
# 可以添加新操作而不修改现有代码
|
||||
class UnionOperation(SpatialOperation):
|
||||
"""合并操作"""
|
||||
def execute(self, data):
|
||||
return data.union(self.other_data)
|
||||
```
|
||||
|
||||
**3. 依赖倒置原则 (DIP)**
|
||||
|
||||
依赖抽象而非具体实现:
|
||||
|
||||
```python
|
||||
# 好的设计:依赖抽象
|
||||
class WorkflowProcessor:
|
||||
def __init__(self, operation: SpatialOperation):
|
||||
self.operation = operation # 依赖抽象
|
||||
|
||||
def process(self, data):
|
||||
return self.operation.execute(data)
|
||||
|
||||
# 可以轻松替换具体实现
|
||||
processor = WorkflowProcessor(BufferOperation(100))
|
||||
|
||||
# 不好的设计:依赖具体实现
|
||||
class WorkflowProcessor:
|
||||
def __init__(self, buffer_distance):
|
||||
self.buffer_distance = buffer_distance
|
||||
|
||||
def process(self, data):
|
||||
# 硬编码了具体操作
|
||||
return data.buffer(self.buffer_distance)
|
||||
```
|
||||
|
||||
### 技能接口设计
|
||||
|
||||
良好的技能接口设计:
|
||||
|
||||
```python
|
||||
from typing import Protocol, TypeVar, Generic
|
||||
|
||||
T = TypeVar('T')
|
||||
|
||||
class SkillInput(Protocol[T]):
|
||||
"""技能输入协议"""
|
||||
def validate(self) -> bool:
|
||||
"""验证输入有效性"""
|
||||
...
|
||||
|
||||
class SkillOutput(Protocol[T]):
|
||||
"""技能输出协议"""
|
||||
def to_dict(self) -> dict:
|
||||
"""转换为可序列化格式"""
|
||||
...
|
||||
|
||||
class Skill(Generic[T]):
|
||||
"""技能基类"""
|
||||
|
||||
name: str
|
||||
description: str
|
||||
|
||||
def can_handle(self, input_data: T) -> bool:
|
||||
"""判断是否能处理此输入"""
|
||||
pass
|
||||
|
||||
def execute(self, input_data: T) -> SkillOutput[T]:
|
||||
"""执行技能"""
|
||||
pass
|
||||
|
||||
def estimate_cost(self, input_data: T) -> float:
|
||||
"""估算执行成本(时间/资源)"""
|
||||
pass
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 代码示例
|
||||
|
||||
### 模块化的空间分析系统
|
||||
|
||||
```python
|
||||
"""
|
||||
模块化空间分析系统示例
|
||||
展示如何用函数式组合构建复杂分析
|
||||
"""
|
||||
from typing import Callable, List, Any, TypeVar
|
||||
from functools import reduce
|
||||
import geopandas as gpd
|
||||
|
||||
T = TypeVar('T')
|
||||
|
||||
class SpatialPipeline:
|
||||
"""空间分析流水线"""
|
||||
|
||||
def __init__(self):
|
||||
self.steps: List[Callable] = []
|
||||
|
||||
def add_step(self, step: Callable, name: str = None):
|
||||
"""添加处理步骤"""
|
||||
step.name = name or step.__name__
|
||||
self.steps.append(step)
|
||||
return self
|
||||
|
||||
def execute(self, initial_data):
|
||||
"""执行流水线"""
|
||||
result = initial_data
|
||||
|
||||
for step in self.steps:
|
||||
print(f"执行步骤: {getattr(step, 'name', step.__name__)}")
|
||||
result = step(result)
|
||||
|
||||
return result
|
||||
|
||||
def pipe(*functions):
|
||||
"""函数式组合工具"""
|
||||
return reduce(lambda f, g: lambda x: g(f(x)), functions)
|
||||
|
||||
# === 原子操作 ===
|
||||
|
||||
def load_data(path: str) -> gpd.GeoDataFrame:
|
||||
"""加载数据"""
|
||||
print(f"加载: {path}")
|
||||
return gpd.read_file(path)
|
||||
|
||||
def clean_geometry(gdf: gpd.GeoDataFrame) -> gpd.GeoDataFrame:
|
||||
"""清理几何"""
|
||||
print("清理几何")
|
||||
# 修复无效几何
|
||||
gdf['geometry'] = gdf.geometry.buffer(0)
|
||||
return gdf[gdf.geometry.is_valid]
|
||||
|
||||
def reproject(gdf: gpd.GeoDataFrame, target_crs: str = 'EPSG:3857') -> gpd.GeoDataFrame:
|
||||
"""重投影"""
|
||||
print(f"重投影到: {target_crs}")
|
||||
return gdf.to_crs(target_crs)
|
||||
|
||||
def buffer(gdf: gpd.GeoDataFrame, distance: float) -> gpd.GeoDataFrame:
|
||||
"""缓冲区分析"""
|
||||
print(f"缓冲距离: {distance}")
|
||||
return gdf.buffer(distance)
|
||||
|
||||
def intersect(gdf: gpd.GeoDataFrame, other: gpd.GeoDataFrame) -> gpd.GeoDataFrame:
|
||||
"""相交分析"""
|
||||
print("相交分析")
|
||||
return gdf.overlay(other, how='intersection')
|
||||
|
||||
def calculate_area(gdf: gpd.GeoDataFrame) -> float:
|
||||
"""计算面积"""
|
||||
area = gdf.geometry.area.sum()
|
||||
print(f"总面积: {area:.2f} 平方米")
|
||||
return area
|
||||
|
||||
# === 高阶操作 ===
|
||||
|
||||
def make_buffer(distance: float) -> Callable:
|
||||
"""缓冲操作工厂函数"""
|
||||
return lambda gdf: buffer(gdf, distance)
|
||||
|
||||
def make_reproject(crs: str) -> Callable:
|
||||
"""重投影工厂函数"""
|
||||
return lambda gdf: reproject(gdf, crs)
|
||||
|
||||
def make_intersect(other_data: gpd.GeoDataFrame) -> Callable:
|
||||
"""相交工厂函数"""
|
||||
return lambda gdf: intersect(gdf, other_data)
|
||||
|
||||
# === 使用示例 ===
|
||||
|
||||
def example_pipeline_usage():
|
||||
"""流水线使用示例"""
|
||||
|
||||
# 方式1:使用Pipeline类
|
||||
pipeline = SpatialPipeline()
|
||||
pipeline.add_step(load_data, "加载数据")
|
||||
pipeline.add_step(clean_geometry, "清理几何")
|
||||
pipeline.add_step(lambda gdf: reproject(gdf, 'EPSG:3857'), "重投影")
|
||||
pipeline.add_step(lambda gdf: buffer(gdf, 100), "缓冲")
|
||||
pipeline.add_step(calculate_area, "计算面积")
|
||||
|
||||
# result = pipeline.execute("data.geojson")
|
||||
|
||||
# 方式2:使用函数式组合
|
||||
analysis_pipeline = pipe(
|
||||
load_data,
|
||||
clean_geometry,
|
||||
lambda gdf: reproject(gdf, 'EPSG:3857'),
|
||||
lambda gdf: buffer(gdf, 100),
|
||||
calculate_area
|
||||
)
|
||||
|
||||
# result = analysis_pipeline("data.geojson")
|
||||
|
||||
return pipeline
|
||||
|
||||
# === 技能封装 ===
|
||||
|
||||
class BufferSkill:
|
||||
"""缓冲区技能"""
|
||||
|
||||
name = "buffer_analysis"
|
||||
description = "执行缓冲区分析"
|
||||
|
||||
def __init__(self, distance: float, unit: str = 'm'):
|
||||
self.distance = distance
|
||||
self.unit = unit
|
||||
|
||||
def execute(self, data: gpd.GeoDataFrame) -> gpd.GeoDataFrame:
|
||||
"""执行技能"""
|
||||
# 确保在合适的坐标系中
|
||||
if data.crs and data.crs.is_geographic:
|
||||
data = data.to_crs('EPSG:3857')
|
||||
|
||||
result = data.buffer(self.distance)
|
||||
|
||||
return gpd.GeoDataFrame(
|
||||
geometry=result,
|
||||
crs=data.crs
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return f"BufferSkill(distance={self.distance}{self.unit})"
|
||||
|
||||
class SiteSelectionSkill:
|
||||
"""选址技能:组合多个原子操作"""
|
||||
|
||||
name = "site_selection"
|
||||
description = "基于多准则的选址分析"
|
||||
|
||||
def __init__(self,
|
||||
distance_from_road: float,
|
||||
distance_from_water: float,
|
||||
min_area: float):
|
||||
self.road_distance = distance_from_road
|
||||
self.water_distance = distance_from_water
|
||||
self.min_area = min_area
|
||||
|
||||
def execute(self,
|
||||
sites: gpd.GeoDataFrame,
|
||||
roads: gpd.GeoDataFrame,
|
||||
water: gpd.GeoDataFrame) -> gpd.GeoDataFrame:
|
||||
"""
|
||||
执行选址分析
|
||||
|
||||
组合操作:
|
||||
1. 找到距离道路指定范围内的区域
|
||||
2. 排除距离水体太近的区域
|
||||
3. 筛选面积满足要求的区域
|
||||
"""
|
||||
|
||||
# 1. 道路缓冲
|
||||
road_buffer = roads.buffer(self.road_distance)
|
||||
|
||||
# 2. 水体缓冲(排除区)
|
||||
water_buffer = water.buffer(self.water_distance)
|
||||
|
||||
# 3. 找到满足条件的site
|
||||
suitable = sites[
|
||||
sites.geometry.intersects(road_buffer.union_all()) &
|
||||
~sites.geometry.intersects(water_buffer.union_all())
|
||||
]
|
||||
|
||||
# 4. 面积筛选
|
||||
suitable = suitable[suitable.geometry.area >= self.min_area]
|
||||
|
||||
return suitable
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 示例:构建一个选址分析流水线
|
||||
print("=== 模块化空间分析系统 ===\n")
|
||||
|
||||
# 创建技能
|
||||
buffer_skill = BufferSkill(distance=500, unit='m')
|
||||
print(f"创建技能: {buffer_skill}")
|
||||
|
||||
# 技能可以独立测试
|
||||
print("\n技能的核心优势:")
|
||||
print("1. 可理解性 - 每个技能职责单一")
|
||||
print("2. 可测试性 - 独立测试每个技能")
|
||||
print("3. 可复用性 - 在不同场景中使用")
|
||||
print("4. 可组合性 - 小技能组合成大技能")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 案例分析
|
||||
|
||||
### QGIS插件架构分析
|
||||
|
||||
QGIS的模块化设计是学习的好例子:
|
||||
|
||||
```
|
||||
QGIS架构
|
||||
│
|
||||
├── Core (核心库)
|
||||
│ ├── QgsGeometry - 几何操作
|
||||
│ ├── QgsVectorLayer - 矢量图层
|
||||
│ ├── QgsRasterLayer - 栅格图层
|
||||
│ └── QgsProcessing - 处理框架
|
||||
│
|
||||
├── Providers (数据提供者)
|
||||
│ ├── OGR Provider - 矢量数据
|
||||
│ ├── GDAL Provider - 栅格数据
|
||||
│ └── PostGIS Provider - 数据库
|
||||
│
|
||||
├── Plugins (插件)
|
||||
│ ├── 每个插件独立模块
|
||||
│ ├── 通过接口访问核心功能
|
||||
│ └── 可单独安装/卸载
|
||||
│
|
||||
└── Processing Algorithms (处理算法)
|
||||
├── 算法库(600+算法)
|
||||
├── 可组合使用
|
||||
└── 模型构建器
|
||||
```
|
||||
|
||||
**关键设计模式**:
|
||||
|
||||
1. **Provider模式**:数据访问抽象
|
||||
2. **Plugin模式**:功能扩展
|
||||
3. **Algorithm模式**:处理步骤封装
|
||||
|
||||
**AI系统的启发**:
|
||||
|
||||
```python
|
||||
# 类似QGIS的AI技能架构
|
||||
class AISkillRegistry:
|
||||
"""AI技能注册表"""
|
||||
|
||||
def __init__(self):
|
||||
self.skills = {}
|
||||
|
||||
def register(self, skill):
|
||||
"""注册技能"""
|
||||
self.skills[skill.name] = skill
|
||||
|
||||
def get(self, name: str):
|
||||
"""获取技能"""
|
||||
return self.skills.get(name)
|
||||
|
||||
def list_by_category(self, category: str):
|
||||
"""按类别列出技能"""
|
||||
return [s for s in self.skills.values()
|
||||
if s.category == category]
|
||||
|
||||
# 使用
|
||||
registry = AISkillRegistry()
|
||||
registry.register(BufferSkill(distance=100))
|
||||
registry.register(SiteSelectionSkill(...))
|
||||
|
||||
# 查找和使用技能
|
||||
buffer = registry.get("buffer_analysis")
|
||||
result = buffer.execute(data)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 反思与延伸
|
||||
|
||||
### 思考问题
|
||||
|
||||
1. **边界划分**:如何确定一个模块的边界?太小的模块和太大的模块各有什么问题?
|
||||
|
||||
2. **接口设计**:设计一个技能接口时,应该考虑哪些因素?
|
||||
|
||||
3. **复用性**:什么代码值得复用?什么不值得?
|
||||
|
||||
4. **组合爆炸**:当模块数量很大时,如何管理模块之间的依赖?
|
||||
|
||||
### 实践练习
|
||||
|
||||
1. **重构练习**:找一个你写的复杂函数,将其拆分为多个小函数
|
||||
|
||||
2. **接口设计**:为你熟悉的空间分析操作设计技能接口
|
||||
|
||||
3. **组合挑战**:用5个以下的基本操作组合出10个不同的分析流程
|
||||
|
||||
### 延伸阅读
|
||||
|
||||
- **"Refactoring"** (Martin Fowler) - 重构与模块化
|
||||
- **"The Art of Unix Programming"** - 模块化哲学
|
||||
- QGIS Plugin开发指南
|
||||
|
||||
---
|
||||
|
||||
## 关键要点
|
||||
|
||||
1. **模块化是管理复杂性的核心方法**
|
||||
2. **函数式组合让小函数构建大功能**
|
||||
3. **技能是智能的封装单元**
|
||||
4. **良好的接口设计是模块化的关键**
|
||||
5. **QGIS的架构是学习的优秀范例**
|
||||
Reference in New Issue
Block a user