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:
2026-05-29 14:25:21 +08:00
parent de7a47db9d
commit a90f7adfa1
64 changed files with 7 additions and 58 deletions
@@ -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的架构是学习的优秀范例**