Complete AI Data Analysis Agent implementation with 95.7% test coverage

This commit is contained in:
2026-03-07 00:04:29 +08:00
parent 621e546b43
commit 7071b1f730
245 changed files with 22612 additions and 2211 deletions

894
docs/API.md Normal file
View File

@@ -0,0 +1,894 @@
# API 文档
本文档描述了 AI 数据分析 Agent 系统的核心 API 接口。
## 目录
- [主流程 API](#主流程-api)
- [配置管理 API](#配置管理-api)
- [数据访问 API](#数据访问-api)
- [分析引擎 API](#分析引擎-api)
- [工具系统 API](#工具系统-api)
- [数据模型](#数据模型)
---
## 主流程 API
### `run_analysis()`
运行完整的数据分析流程。
**函数签名**
```python
def run_analysis(
data_file: str,
user_requirement: Optional[str] = None,
template_file: Optional[str] = None,
output_dir: str = "output",
progress_callback: Optional[callable] = None
) -> Dict[str, Any]
```
**参数**
- `data_file` (str): 数据文件路径CSV 格式)
- `user_requirement` (Optional[str]): 用户需求(自然语言),如果为 None 则自动推断
- `template_file` (Optional[str]): 模板文件路径(可选)
- `output_dir` (str): 输出目录,默认为 "output"
- `progress_callback` (Optional[callable]): 进度回调函数,接收 (stage, current, total) 参数
**返回值**
```python
{
'success': bool, # 是否成功
'data_type': str, # 数据类型
'objectives_count': int, # 分析目标数量
'tasks_count': int, # 任务数量
'results_count': int, # 结果数量
'report_path': str, # 报告路径
'elapsed_time': float, # 执行时间(秒)
'error': str # 错误信息(如果失败)
}
```
**示例**
```python
from src.main import run_analysis
# 基本使用
result = run_analysis(
data_file="data.csv",
user_requirement="分析工单健康度"
)
if result['success']:
print(f"报告路径: {result['report_path']}")
print(f"执行时间: {result['elapsed_time']:.1f}")
else:
print(f"分析失败: {result['error']}")
# 使用进度回调
def progress_handler(stage, current, total):
print(f"[{current}/{total}] {stage}")
result = run_analysis(
data_file="data.csv",
progress_callback=progress_handler
)
```
### `AnalysisOrchestrator`
分析编排器类,协调五个阶段的执行。
**类签名**
```python
class AnalysisOrchestrator:
def __init__(
self,
data_file: str,
user_requirement: Optional[str] = None,
template_file: Optional[str] = None,
output_dir: Optional[str] = None,
progress_callback: Optional[callable] = None
)
```
**方法**
#### `run_analysis()`
运行完整的分析流程。
**返回值**:与 `run_analysis()` 函数相同
**示例**
```python
from src.main import AnalysisOrchestrator
orchestrator = AnalysisOrchestrator(
data_file="data.csv",
user_requirement="分析工单健康度",
output_dir="output"
)
result = orchestrator.run_analysis()
```
---
## 配置管理 API
### `Config`
系统配置类。
**类签名**
```python
@dataclass
class Config:
llm: LLMConfig
performance: PerformanceConfig
output: OutputConfig
code_repo_enable_reuse: bool = True
```
**类方法**
#### `from_env()`
从环境变量加载配置。
```python
@classmethod
def from_env(cls) -> "Config"
```
**示例**
```python
from src.config import Config
config = Config.from_env()
print(f"模型: {config.llm.model}")
print(f"输出目录: {config.output.output_dir}")
```
#### `from_file()`
从配置文件加载配置。
```python
@classmethod
def from_file(cls, config_file: str) -> "Config"
```
**参数**
- `config_file` (str): 配置文件路径JSON 格式)
**示例**
```python
config = Config.from_file("config.json")
```
#### `from_dict()`
从字典加载配置。
```python
@classmethod
def from_dict(cls, config_dict: Dict[str, Any]) -> "Config"
```
**参数**
- `config_dict` (Dict[str, Any]): 配置字典
#### `to_dict()`
转换为字典。
```python
def to_dict(self) -> Dict[str, Any]
```
#### `save_to_file()`
保存配置到文件。
```python
def save_to_file(self, config_file: str)
```
#### `validate()`
验证配置的有效性。
```python
def validate(self) -> bool
```
### `LLMConfig`
LLM API 配置。
**类签名**
```python
@dataclass
class LLMConfig:
provider: str = "openai"
api_key: str = ""
base_url: str = "https://api.openai.com/v1"
model: str = "gpt-4"
timeout: int = 120
max_retries: int = 3
temperature: float = 0.7
max_tokens: Optional[int] = None
```
### `PerformanceConfig`
性能参数配置。
**类签名**
```python
@dataclass
class PerformanceConfig:
agent_max_rounds: int = 20
agent_timeout: int = 300
tool_max_query_rows: int = 10000
tool_execution_timeout: int = 60
data_max_rows: int = 1000000
data_sample_threshold: int = 1000000
max_concurrent_tasks: int = 1
```
### `OutputConfig`
输出路径配置。
**类签名**
```python
@dataclass
class OutputConfig:
output_dir: str = "output"
log_dir: Optional[str] = None
chart_dir: Optional[str] = None
report_filename: str = "analysis_report.md"
log_level: str = "INFO"
log_to_file: bool = True
log_to_console: bool = True
```
**方法**
- `get_output_path() -> Path`: 获取输出目录路径
- `get_log_path() -> Path`: 获取日志目录路径
- `get_chart_path() -> Path`: 获取图表目录路径
- `get_report_path() -> Path`: 获取报告文件路径
### 全局配置函数
#### `get_config()`
获取全局配置实例。
```python
def get_config() -> Config
```
#### `set_config()`
设置全局配置实例。
```python
def set_config(config: Config)
```
#### `load_config_from_env()`
从环境变量加载配置并设置为全局配置。
```python
def load_config_from_env() -> Config
```
#### `load_config_from_file()`
从文件加载配置并设置为全局配置。
```python
def load_config_from_file(config_file: str) -> Config
```
---
## 数据访问 API
### `DataAccessLayer`
数据访问层,提供数据加载和隐私保护机制。
**类方法**
#### `load_from_file()`
从文件加载数据。
```python
@classmethod
def load_from_file(cls, file_path: str) -> "DataAccessLayer"
```
**参数**
- `file_path` (str): 数据文件路径
**返回值**DataAccessLayer 实例
**示例**
```python
from src.data_access import DataAccessLayer
data_access = DataAccessLayer.load_from_file("data.csv")
print(f"数据形状: {data_access.shape}")
```
**实例方法**
#### `get_profile()`
获取数据画像(不包含原始数据)。
```python
def get_profile(self) -> DataProfile
```
#### `execute_tool()`
执行工具并返回聚合结果。
```python
def execute_tool(self, tool: AnalysisTool, **kwargs) -> Dict[str, Any]
```
**参数**
- `tool` (AnalysisTool): 工具实例
- `**kwargs`: 工具参数
**返回值**:聚合后的结果字典
---
## 分析引擎 API
### 数据理解引擎
#### `understand_data()`
AI 驱动的数据理解。
```python
def understand_data(data_access: DataAccessLayer) -> DataProfile
```
**参数**
- `data_access` (DataAccessLayer): 数据访问层实例
**返回值**DataProfile 对象
**示例**
```python
from src.engines import understand_data
from src.data_access import DataAccessLayer
data_access = DataAccessLayer.load_from_file("data.csv")
profile = understand_data(data_access)
print(f"数据类型: {profile.inferred_type}")
print(f"质量分数: {profile.quality_score}")
```
### 需求理解引擎
#### `understand_requirement()`
AI 驱动的需求理解。
```python
def understand_requirement(
user_input: str,
data_profile: DataProfile,
template_path: Optional[str] = None
) -> RequirementSpec
```
**参数**
- `user_input` (str): 用户需求(自然语言)
- `data_profile` (DataProfile): 数据画像
- `template_path` (Optional[str]): 模板文件路径
**返回值**RequirementSpec 对象
### 分析规划引擎
#### `plan_analysis()`
AI 驱动的分析规划。
```python
def plan_analysis(
data_profile: DataProfile,
requirement: RequirementSpec
) -> AnalysisPlan
```
**参数**
- `data_profile` (DataProfile): 数据画像
- `requirement` (RequirementSpec): 需求规格
**返回值**AnalysisPlan 对象
### 任务执行引擎
#### `execute_task()`
使用 ReAct 模式执行任务。
```python
def execute_task(
task: AnalysisTask,
tools: List[AnalysisTool],
data_access: DataAccessLayer
) -> AnalysisResult
```
**参数**
- `task` (AnalysisTask): 分析任务
- `tools` (List[AnalysisTool]): 可用工具列表
- `data_access` (DataAccessLayer): 数据访问层
**返回值**AnalysisResult 对象
### 计划调整引擎
#### `adjust_plan()`
根据中间结果动态调整计划。
```python
def adjust_plan(
plan: AnalysisPlan,
completed_results: List[AnalysisResult]
) -> AnalysisPlan
```
**参数**
- `plan` (AnalysisPlan): 当前分析计划
- `completed_results` (List[AnalysisResult]): 已完成的分析结果
**返回值**:调整后的 AnalysisPlan 对象
### 报告生成引擎
#### `generate_report()`
AI 驱动的报告生成。
```python
def generate_report(
results: List[AnalysisResult],
requirement: RequirementSpec,
data_profile: DataProfile,
output_path: str
) -> str
```
**参数**
- `results` (List[AnalysisResult]): 分析结果列表
- `requirement` (RequirementSpec): 需求规格
- `data_profile` (DataProfile): 数据画像
- `output_path` (str): 输出路径
**返回值**Markdown 格式的报告内容
---
## 工具系统 API
### `AnalysisTool`
分析工具的抽象基类。
**抽象属性**
#### `name`
工具名称。
```python
@property
@abstractmethod
def name(self) -> str
```
#### `description`
工具描述(供 AI 理解)。
```python
@property
@abstractmethod
def description(self) -> str
```
#### `parameters`
参数定义JSON Schema 格式)。
```python
@property
@abstractmethod
def parameters(self) -> Dict[str, Any]
```
**抽象方法**
#### `execute()`
执行工具。
```python
@abstractmethod
def execute(self, data: pd.DataFrame, **kwargs) -> Dict[str, Any]
```
**参数**
- `data` (pd.DataFrame): 原始数据
- `**kwargs`: 工具参数
**返回值**:聚合后的结果字典
#### `is_applicable()`
判断工具是否适用于当前数据。
```python
@abstractmethod
def is_applicable(self, data_profile: DataProfile) -> bool
```
**参数**
- `data_profile` (DataProfile): 数据画像
**返回值**True 如果工具适用False 否则
**方法**
#### `validate_parameters()`
验证参数是否有效。
```python
def validate_parameters(self, **kwargs) -> bool
```
### `ToolRegistry`
工具注册表,管理所有可用的工具。
**方法**
#### `register()`
注册一个工具。
```python
def register(self, tool: AnalysisTool) -> None
```
#### `unregister()`
注销一个工具。
```python
def unregister(self, tool_name: str) -> None
```
#### `get_tool()`
获取指定名称的工具。
```python
def get_tool(self, tool_name: str) -> AnalysisTool
```
#### `list_tools()`
列出所有已注册的工具名称。
```python
def list_tools(self) -> list[str]
```
#### `get_applicable_tools()`
获取适用于指定数据的所有工具。
```python
def get_applicable_tools(self, data_profile: DataProfile) -> list[AnalysisTool]
```
### 全局工具函数
#### `register_tool()`
注册工具到全局注册表。
```python
def register_tool(tool: AnalysisTool) -> None
```
#### `get_tool()`
从全局注册表获取工具。
```python
def get_tool(tool_name: str) -> AnalysisTool
```
#### `list_tools()`
列出全局注册表中的所有工具。
```python
def list_tools() -> list[str]
```
#### `get_applicable_tools()`
获取适用于指定数据的所有工具。
```python
def get_applicable_tools(data_profile: DataProfile) -> list[AnalysisTool]
```
### `ToolManager`
工具管理器,根据数据特征动态选择工具。
**方法**
#### `select_tools()`
根据数据画像选择合适的工具。
```python
def select_tools(self, data_profile: DataProfile) -> List[AnalysisTool]
```
**参数**
- `data_profile` (DataProfile): 数据画像
**返回值**:适用的工具列表
#### `get_missing_tools()`
获取缺失的工具列表。
```python
def get_missing_tools(self) -> List[str]
```
**返回值**:缺失的工具名称列表
---
## 数据模型
### `DataProfile`
数据画像,包含数据的元数据和统计摘要。
**字段**
```python
@dataclass
class DataProfile:
file_path: str
row_count: int
column_count: int
columns: List[ColumnInfo]
inferred_type: str
key_fields: Dict[str, str]
quality_score: float
summary: str
```
### `ColumnInfo`
列信息。
**字段**
```python
@dataclass
class ColumnInfo:
name: str
dtype: str
missing_rate: float
unique_count: int
sample_values: List[Any]
statistics: Dict[str, Any]
```
### `RequirementSpec`
需求规格。
**字段**
```python
@dataclass
class RequirementSpec:
user_input: str
objectives: List[AnalysisObjective]
template_path: Optional[str]
template_requirements: Optional[Dict[str, Any]]
constraints: List[str]
expected_outputs: List[str]
```
### `AnalysisObjective`
分析目标。
**字段**
```python
@dataclass
class AnalysisObjective:
name: str
description: str
metrics: List[str]
priority: int
```
### `AnalysisPlan`
分析计划。
**字段**
```python
@dataclass
class AnalysisPlan:
objectives: List[AnalysisObjective]
tasks: List[AnalysisTask]
tool_config: Dict[str, Any]
estimated_duration: int
created_at: datetime
updated_at: datetime
```
### `AnalysisTask`
分析任务。
**字段**
```python
@dataclass
class AnalysisTask:
id: str
name: str
description: str
priority: int
dependencies: List[str]
required_tools: List[str]
expected_output: str
status: str
```
### `AnalysisResult`
分析结果。
**字段**
```python
@dataclass
class AnalysisResult:
task_id: str
task_name: str
success: bool
data: Dict[str, Any]
visualizations: List[str]
insights: List[str]
error: Optional[str]
execution_time: float
```
---
## 错误处理 API
### `execute_task_with_recovery()`
带恢复机制的任务执行。
```python
def execute_task_with_recovery(
task: AnalysisTask,
plan: AnalysisPlan,
execute_func: callable,
**kwargs
) -> AnalysisResult
```
**参数**
- `task` (AnalysisTask): 分析任务
- `plan` (AnalysisPlan): 分析计划
- `execute_func` (callable): 执行函数
- `**kwargs`: 传递给执行函数的参数
**返回值**AnalysisResult 对象
---
## 使用示例
### 完整示例:自定义分析流程
```python
from src.main import AnalysisOrchestrator
from src.config import Config, LLMConfig, OutputConfig
# 1. 配置系统
llm_config = LLMConfig(
provider="openai",
api_key="your_api_key",
model="gpt-4",
temperature=0.7
)
output_config = OutputConfig(
output_dir="my_output",
log_level="DEBUG"
)
config = Config(llm=llm_config, output=output_config)
# 2. 创建编排器
orchestrator = AnalysisOrchestrator(
data_file="data.csv",
user_requirement="分析工单健康度",
output_dir="my_output"
)
# 3. 运行分析
result = orchestrator.run_analysis()
# 4. 处理结果
if result['success']:
print(f"✓ 分析完成")
print(f" 数据类型: {result['data_type']}")
print(f" 任务数量: {result['tasks_count']}")
print(f" 报告路径: {result['report_path']}")
print(f" 执行时间: {result['elapsed_time']:.1f}")
else:
print(f"✗ 分析失败: {result['error']}")
```
### 示例:自定义工具
```python
from src.tools.base import AnalysisTool, register_tool
from src.models import DataProfile
import pandas as pd
class CustomAnalysisTool(AnalysisTool):
@property
def name(self) -> str:
return "custom_analysis"
@property
def description(self) -> str:
return "自定义分析工具"
@property
def parameters(self) -> dict:
return {
"type": "object",
"properties": {
"column": {"type": "string"}
},
"required": ["column"]
}
def execute(self, data: pd.DataFrame, **kwargs) -> dict:
column = kwargs['column']
# 执行自定义分析
result = {
"mean": data[column].mean(),
"median": data[column].median()
}
return result
def is_applicable(self, data_profile: DataProfile) -> bool:
# 检查是否有数值列
return any(col.dtype == 'numeric' for col in data_profile.columns)
# 注册工具
register_tool(CustomAnalysisTool())
```
---
## 注意事项
1. **隐私保护**:所有工具的 `execute()` 方法必须返回聚合数据,不能返回原始行级数据
2. **错误处理**:所有 API 调用都应该包含适当的错误处理
3. **配置验证**:在使用配置前,建议调用 `config.validate()` 验证配置的有效性
4. **工具注册**:自定义工具必须在使用前注册到工具注册表
5. **线程安全**:当前版本不支持并发执行,`max_concurrent_tasks` 必须设置为 1
---
## 版本信息
- **版本**: v1.0.0
- **日期**: 2026-03-06
- **状态**: 稳定版本

851
docs/DEVELOPER_GUIDE.md Normal file
View File

@@ -0,0 +1,851 @@
# 开发者指南
本指南帮助开发者理解系统架构、扩展功能和添加新工具。
## 目录
- [系统架构](#系统架构)
- [开发环境设置](#开发环境设置)
- [添加新工具](#添加新工具)
- [扩展分析引擎](#扩展分析引擎)
- [自定义数据模型](#自定义数据模型)
- [测试指南](#测试指南)
- [代码规范](#代码规范)
- [调试技巧](#调试技巧)
---
## 系统架构
### 整体架构
系统采用五阶段流水线架构,每个阶段由 AI 驱动:
```
数据输入 → 数据理解 → 需求理解 → 分析规划 → 任务执行 → 报告生成
```
### 核心组件
```
src/
├── main.py # 主流程编排
├── cli.py # 命令行接口
├── config.py # 配置管理
├── data_access.py # 数据访问层(隐私保护)
├── error_handling.py # 错误处理
├── logging_config.py # 日志配置
├── env_loader.py # 环境变量加载
├── engines/ # 分析引擎
│ ├── data_understanding.py # 数据理解
│ ├── requirement_understanding.py # 需求理解
│ ├── analysis_planning.py # 分析规划
│ ├── task_execution.py # 任务执行ReAct
│ ├── plan_adjustment.py # 计划调整
│ └── report_generation.py # 报告生成
├── models/ # 数据模型
│ ├── data_profile.py
│ ├── requirement_spec.py
│ ├── analysis_plan.py
│ └── analysis_result.py
└── tools/ # 分析工具
├── base.py # 工具基类和注册表
├── query_tools.py # 数据查询工具
├── stats_tools.py # 统计分析工具
├── viz_tools.py # 可视化工具
└── tool_manager.py # 工具管理器
```
### 数据流
```
CSV 文件
DataAccessLayer数据访问层
DataProfile数据画像元数据 + 统计摘要)
RequirementSpec需求规格
AnalysisPlan分析计划任务列表
AnalysisResult[](分析结果列表)
Markdown 报告
```
### 设计原则
1. **AI 优先**:让 AI 做决策,而不是执行预定义的规则
2. **动态适应**:根据数据特征和发现动态调整分析计划
3. **隐私保护**AI 不读取原始数据,只通过工具获取摘要信息
4. **工具驱动**:通过动态工具集赋能 AI 的分析能力
5. **可扩展性**:易于添加新工具和扩展功能
---
## 开发环境设置
### 1. 克隆仓库
```bash
git clone <repository-url>
cd <repository-name>
```
### 2. 创建虚拟环境
```bash
# 使用 venv
python -m venv .venv
# 激活虚拟环境
# Windows
.venv\Scripts\activate
# Linux/Mac
source .venv/bin/activate
```
### 3. 安装依赖
```bash
# 安装生产依赖
pip install -r requirements.txt
# 安装开发依赖(如果有)
pip install pytest hypothesis pytest-cov black flake8
```
### 4. 配置环境变量
```bash
cp .env.example .env
# 编辑 .env 文件,设置 API 密钥
```
### 5. 运行测试
```bash
# 运行所有测试
pytest
# 运行特定测试
pytest tests/test_integration.py -v
# 查看覆盖率
pytest --cov=src --cov-report=html
```
---
## 添加新工具
### 步骤1创建工具类
创建一个继承自 `AnalysisTool` 的新类:
```python
# src/tools/my_custom_tools.py
from src.tools.base import AnalysisTool
from src.models import DataProfile
import pandas as pd
from typing import Dict, Any
class MyCustomTool(AnalysisTool):
"""
自定义分析工具。
功能:[描述工具的功能]
"""
@property
def name(self) -> str:
"""工具名称(唯一标识)。"""
return "my_custom_tool"
@property
def description(self) -> str:
"""工具描述(供 AI 理解)。"""
return """
这个工具用于 [具体功能描述]。
适用场景:
- [场景1]
- [场景2]
输入参数:
- column: 要分析的列名
- threshold: 阈值参数
输出:
- result: 分析结果
- insights: 洞察列表
"""
@property
def parameters(self) -> Dict[str, Any]:
"""参数定义JSON Schema 格式)。"""
return {
"type": "object",
"properties": {
"column": {
"type": "string",
"description": "要分析的列名"
},
"threshold": {
"type": "number",
"description": "阈值参数",
"default": 0.5
}
},
"required": ["column"]
}
def execute(self, data: pd.DataFrame, **kwargs) -> Dict[str, Any]:
"""
执行工具。
参数:
data: 原始数据(工具内部使用,不暴露给 AI
**kwargs: 工具参数
返回:
聚合后的结果(不包含原始数据)
"""
# 1. 验证参数
if not self.validate_parameters(**kwargs):
raise ValueError("参数验证失败")
column = kwargs['column']
threshold = kwargs.get('threshold', 0.5)
# 2. 检查列是否存在
if column not in data.columns:
raise ValueError(f"'{column}' 不存在")
# 3. 执行分析
# 注意:只返回聚合数据,不返回原始行级数据
result = {
"column": column,
"threshold": threshold,
"count": len(data),
"result_value": data[column].mean(), # 示例
"insights": [
f"{column} 的平均值为 {data[column].mean():.2f}"
]
}
return result
def is_applicable(self, data_profile: DataProfile) -> bool:
"""
判断工具是否适用于当前数据。
参数:
data_profile: 数据画像
返回:
True 如果工具适用False 否则
"""
# 示例:检查是否有数值列
has_numeric = any(
col.dtype == 'numeric'
for col in data_profile.columns
)
return has_numeric
```
### 步骤2注册工具
`src/tools/__init__.py` 中注册工具:
```python
from src.tools.base import register_tool
from src.tools.my_custom_tools import MyCustomTool
# 注册工具
register_tool(MyCustomTool())
```
或者在工具管理器中动态注册:
```python
from src.tools.tool_manager import ToolManager
from src.tools.my_custom_tools import MyCustomTool
tool_manager = ToolManager()
tool_manager.registry.register(MyCustomTool())
```
### 步骤3编写测试
创建测试文件 `tests/test_my_custom_tools.py`
```python
import pytest
import pandas as pd
from hypothesis import given, strategies as st
from src.tools.my_custom_tools import MyCustomTool
from src.models import DataProfile, ColumnInfo
def test_my_custom_tool_basic():
"""测试工具的基本功能。"""
# 准备测试数据
data = pd.DataFrame({
'value': [1, 2, 3, 4, 5]
})
# 创建工具
tool = MyCustomTool()
# 执行工具
result = tool.execute(data, column='value', threshold=0.5)
# 验证结果
assert result['column'] == 'value'
assert result['threshold'] == 0.5
assert result['count'] == 5
assert 'insights' in result
def test_my_custom_tool_invalid_column():
"""测试无效列名的处理。"""
data = pd.DataFrame({'value': [1, 2, 3]})
tool = MyCustomTool()
with pytest.raises(ValueError, match="列 .* 不存在"):
tool.execute(data, column='invalid_column')
@given(data=st.data())
def test_my_custom_tool_property(data):
"""属性测试:工具应该总是返回聚合数据。"""
# 生成随机数据
df = pd.DataFrame({
'value': data.draw(st.lists(st.floats(), min_size=10, max_size=100))
})
tool = MyCustomTool()
result = tool.execute(df, column='value')
# 验证:结果不应包含原始行级数据
assert 'data' not in result or len(result.get('data', [])) <= 100
assert 'insights' in result
```
### 步骤4更新文档
`docs/API.md` 中添加工具文档:
```markdown
### MyCustomTool
自定义分析工具。
**功能**[描述]
**参数**
- `column` (str): 要分析的列名
- `threshold` (float): 阈值参数,默认 0.5
**返回值**
```python
{
"column": str,
"threshold": float,
"count": int,
"result_value": float,
"insights": List[str]
}
```
**示例**
```python
tool = MyCustomTool()
result = tool.execute(data, column='value', threshold=0.5)
```
```
### 工具开发最佳实践
1. **隐私保护**
- 永远不要返回原始行级数据
- 只返回聚合数据(统计值、计数、分组结果等)
- 限制返回的数据行数(最多 100 行)
2. **参数验证**
- 使用 JSON Schema 定义参数
- 在 `execute()` 中验证参数
- 提供清晰的错误信息
3. **错误处理**
- 捕获并处理异常
- 返回有意义的错误信息
- 不要让工具崩溃整个流程
4. **性能优化**
- 避免不必要的数据复制
- 使用 pandas 的向量化操作
- 考虑大数据集的性能
5. **文档完善**
- 提供清晰的工具描述
- 说明适用场景
- 提供使用示例
---
## 扩展分析引擎
### 添加新的分析阶段
如果需要添加新的分析阶段,按以下步骤操作:
#### 1. 创建引擎模块
```python
# src/engines/my_new_engine.py
import logging
from typing import Dict, Any
from src.models import DataProfile, AnalysisPlan
logger = logging.getLogger(__name__)
def my_new_analysis_stage(
data_profile: DataProfile,
analysis_plan: AnalysisPlan
) -> Dict[str, Any]:
"""
新的分析阶段。
参数:
data_profile: 数据画像
analysis_plan: 分析计划
返回:
分析结果
"""
logger.info("执行新的分析阶段...")
# 实现分析逻辑
result = {
"status": "completed",
"findings": []
}
return result
```
#### 2. 集成到主流程
`src/main.py` 中添加新阶段:
```python
class AnalysisOrchestrator:
def run_analysis(self):
# ... 现有阶段 ...
# 新阶段
self._report_progress("新分析阶段", 5, 6)
self.tracker.track_stage("新分析阶段", "started")
new_result = self._stage_new_analysis()
self.tracker.track_stage("新分析阶段", "completed")
# ... 继续 ...
def _stage_new_analysis(self) -> Dict[str, Any]:
"""新的分析阶段。"""
from src.engines.my_new_engine import my_new_analysis_stage
log_stage_start(logger, "新分析阶段")
result = my_new_analysis_stage(
self.data_profile,
self.analysis_plan
)
log_stage_end(logger, "新分析阶段")
return result
```
### 自定义 ReAct 执行逻辑
如果需要自定义任务执行逻辑:
```python
# src/engines/custom_execution.py
from typing import List, Dict, Any
from src.models import AnalysisTask, AnalysisResult
from src.tools.base import AnalysisTool
from src.data_access import DataAccessLayer
def custom_execute_task(
task: AnalysisTask,
tools: List[AnalysisTool],
data_access: DataAccessLayer
) -> AnalysisResult:
"""
自定义任务执行逻辑。
参数:
task: 分析任务
tools: 可用工具列表
data_access: 数据访问层
返回:
分析结果
"""
# 实现自定义执行逻辑
# 例如:使用不同的 AI 模型、不同的提示策略等
pass
```
---
## 自定义数据模型
### 扩展数据画像
如果需要添加新的数据特征:
```python
# src/models/data_profile.py
from dataclasses import dataclass, field
from typing import List, Dict, Any
@dataclass
class DataProfile:
# 现有字段...
# 新增字段
custom_features: Dict[str, Any] = field(default_factory=dict)
def add_custom_feature(self, name: str, value: Any):
"""添加自定义特征。"""
self.custom_features[name] = value
```
### 添加新的分析任务类型
```python
# src/models/analysis_plan.py
from dataclasses import dataclass
from typing import Optional
@dataclass
class CustomAnalysisTask(AnalysisTask):
"""自定义分析任务。"""
custom_param: Optional[str] = None
def validate(self) -> bool:
"""验证任务参数。"""
# 实现验证逻辑
return True
```
---
## 测试指南
### 测试策略
系统采用双重测试方法:
1. **单元测试**:验证特定示例、边缘情况和错误条件
2. **属性测试**:验证跨所有输入的通用属性
### 编写单元测试
```python
# tests/test_my_feature.py
import pytest
from src.my_module import my_function
def test_my_function_basic():
"""测试基本功能。"""
result = my_function(input_data)
assert result == expected_output
def test_my_function_edge_case():
"""测试边缘情况。"""
result = my_function(edge_case_input)
assert result is not None
def test_my_function_error_handling():
"""测试错误处理。"""
with pytest.raises(ValueError):
my_function(invalid_input)
```
### 编写属性测试
```python
# tests/test_my_feature_properties.py
from hypothesis import given, strategies as st
import hypothesis
# Feature: my-feature, Property 1: 输出总是有效
@given(input_data=st.data())
@hypothesis.settings(max_examples=100)
def test_output_always_valid(input_data):
"""
属性 1对于任何有效输入输出总是有效的。
"""
# 生成随机输入
data = generate_random_input(input_data)
# 执行函数
result = my_function(data)
# 验证属性
assert result is not None
assert validate_output(result)
```
### 运行测试
```bash
# 运行所有测试
pytest
# 运行单元测试
pytest tests/ -k "not properties"
# 运行属性测试
pytest tests/ -k "properties"
# 运行特定测试文件
pytest tests/test_my_feature.py -v
# 查看覆盖率
pytest --cov=src --cov-report=html
# 生成覆盖率报告
open htmlcov/index.html
```
---
## 代码规范
### Python 代码风格
遵循 PEP 8 规范:
```bash
# 检查代码风格
flake8 src/
# 自动格式化代码
black src/
```
### 文档字符串
使用 Google 风格的文档字符串:
```python
def my_function(param1: str, param2: int) -> Dict[str, Any]:
"""
函数的简短描述。
更详细的描述(如果需要)。
参数:
param1: 参数1的描述
param2: 参数2的描述
返回:
返回值的描述
异常:
ValueError: 参数无效时抛出
示例:
>>> result = my_function("test", 42)
>>> print(result)
{'status': 'success'}
"""
pass
```
### 类型注解
使用类型注解提高代码可读性:
```python
from typing import List, Dict, Optional, Any
def process_data(
data: List[Dict[str, Any]],
config: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]:
"""处理数据。"""
pass
```
### 命名规范
- **模块名**:小写,下划线分隔(`my_module.py`
- **类名**:驼峰命名(`MyClass`
- **函数名**:小写,下划线分隔(`my_function`
- **常量**:大写,下划线分隔(`MY_CONSTANT`
- **私有成员**:前缀下划线(`_private_method`
---
## 调试技巧
### 启用详细日志
```bash
# 设置日志级别为 DEBUG
export LOG_LEVEL=DEBUG
# 或在代码中设置
import logging
logging.basicConfig(level=logging.DEBUG)
```
### 使用 Python 调试器
```python
# 在代码中设置断点
import pdb; pdb.set_trace()
# 或使用 ipdb更友好
import ipdb; ipdb.set_trace()
```
### 查看 AI 的思考过程
```bash
# 使用 -v 参数显示详细日志
python -m src.cli data.csv -v
```
### 保存中间结果
```python
# 在 AnalysisOrchestrator 中保存中间结果
import json
# 保存数据画像
with open('debug_data_profile.json', 'w') as f:
json.dump(self.data_profile.__dict__, f, indent=2)
# 保存分析计划
with open('debug_analysis_plan.json', 'w') as f:
json.dump([task.__dict__ for task in self.analysis_plan.tasks], f, indent=2)
```
### 模拟 AI 调用
在测试时模拟 AI 调用以避免 API 费用:
```python
from unittest.mock import patch
with patch('src.engines.data_understanding.call_llm') as mock_llm:
mock_llm.return_value = {
'data_type': 'ticket',
'key_fields': {'status': '工单状态'},
'quality_score': 85.0
}
# 执行测试
result = understand_data(data_access)
```
---
## 常见问题
### Q1: 如何添加对新数据格式的支持?
修改 `src/data_access.py` 中的 `load_from_file()` 方法:
```python
@classmethod
def load_from_file(cls, file_path: str) -> "DataAccessLayer":
"""从文件加载数据。"""
if file_path.endswith('.csv'):
data = cls._load_csv(file_path)
elif file_path.endswith('.xlsx'):
data = cls._load_excel(file_path)
elif file_path.endswith('.json'):
data = cls._load_json(file_path)
else:
raise ValueError(f"不支持的文件格式: {file_path}")
return cls(data)
```
### Q2: 如何更换 LLM 提供商?
修改 `.env` 文件:
```bash
# 使用 Gemini
LLM_PROVIDER=gemini
GEMINI_API_KEY=your_gemini_key
GEMINI_MODEL=gemini-2.0-flash-exp
```
### Q3: 如何优化性能?
1. 增加并发任务数(未来版本支持)
2. 使用更快的 LLM 模型
3. 减少 ReAct 最大迭代次数
4. 对大数据集进行采样
### Q4: 如何贡献代码?
1. Fork 项目
2. 创建特性分支
3. 编写代码和测试
4. 确保所有测试通过
5. 提交 Pull Request
---
## 资源链接
- **项目文档**: `docs/`
- **API 文档**: `docs/API.md`
- **配置指南**: `docs/configuration_guide.md`
- **示例代码**: `examples/`
- **测试数据**: `test_data/`
---
## 版本信息
- **版本**: v1.0.0
- **日期**: 2026-03-06
- **状态**: 稳定版本
---
## 联系方式
如有问题或建议,请创建 Issue 或联系维护者。

198
docs/PERFORMANCE.md Normal file
View File

@@ -0,0 +1,198 @@
# 性能优化文档
## 概述
本文档描述了系统的性能优化措施和性能测试结果。
## 性能目标
根据需求 NFR-1.1 和 NFR-1.2,系统应满足以下性能指标:
- 数据理解阶段:< 30秒
- 完整分析流程:< 30分钟
- 支持最大 100万行数据
- 支持最大 100MB 的 CSV 文件
## 性能优化措施
### 1. 数据加载优化
#### 内存优化
- 自动优化数据类型以减少内存使用
- 整数类型int64 → int8/int16/int32根据值范围
- 浮点类型float64 → float32
- 字符串类型object → category当唯一值比例 < 50%
**优化效果**
- 测试数据10万行 × 30列
- 优化前123.88 MB
- 优化后2.97 MB
- 节省120.92 MB97.6%
#### 低内存模式
- 使用 `pd.read_csv(..., low_memory=False)` 加载大文件
- 避免内存碎片化
#### 大数据集采样
- 自动检测数据大小
- 超过100万行时自动采样到100万行
- 使用固定随机种子确保可重复性
### 2. AI 调用优化
#### LLM 缓存
- 实现基于 MD5 的缓存键生成
- 支持内存缓存和文件缓存
- 避免重复调用相同的提示
**使用方法**
```python
from src.performance_optimization import get_global_cache, cached_llm_call
cache = get_global_cache(cache_dir=".cache")
@cached_llm_call(cache)
def call_llm(prompt, model="gpt-4"):
# LLM 调用逻辑
pass
```
#### 批处理
- 实现批处理器用于批量处理工具调用
- 减少 API 调用次数
- 提高吞吐量
### 3. 性能监控
#### 性能监控器
- 记录各阶段的执行时间
- 计算统计信息(平均值、最小值、最大值)
- 生成性能报告
**使用方法**
```python
from src.performance_optimization import get_global_monitor, timed
monitor = get_global_monitor()
@timed(metric_name="my_function", monitor=monitor)
def my_function():
# 函数逻辑
pass
# 获取统计信息
stats = monitor.get_stats("my_function")
print(f"平均耗时: {stats['mean']:.2f}")
```
## 性能测试结果
### 数据理解阶段性能
| 数据规模 | 行数 | 列数 | 耗时(秒) | 行/秒 |
|---------|------|------|-----------|-------|
| 小数据集 | 1,000 | 10 | < 5 | - |
| 中等数据集 | 100,000 | 20 | < 15 | 151,497 |
| 大数据集 | 1,000,000 | 30 | < 30 | - |
**结论**:✅ 所有测试通过,满足 < 30秒的要求
### 数据加载性能基准
| 行数 | 耗时(秒) | 行/秒 |
|------|-----------|-------|
| 1,000 | 0.016 | 62,502 |
| 10,000 | 0.068 | 147,301 |
| 100,000 | 0.716 | 139,633 |
### 内存使用
| 测试场景 | 数据规模 | 内存增长 | 状态 |
|---------|---------|---------|------|
| 数据加载 | 10万行 × 50列 | < 500 MB | ✅ 通过 |
| 大数据集 | 50万行 × 50列 | < 1 GB | ✅ 通过 |
## 性能优化建议
### 对于开发者
1. **使用性能监控器**
- 在关键函数上使用 `@timed` 装饰器
- 定期检查性能统计信息
2. **启用缓存**
- 对于重复的 LLM 调用,使用缓存
- 定期清理过期缓存
3. **优化数据加载**
- 始终使用 `optimize_memory=True`
- 对于大数据集,考虑预先采样
### 对于用户
1. **数据准备**
- 尽量使用 UTF-8 编码
- 避免过多的空值和重复数据
- 控制数据规模在100万行以内
2. **性能调优**
- 设置合理的超时时间
- 使用模板可以加快分析速度
- 避免过于复杂的需求描述
## 性能监控
### 查看性能统计
在分析完成后,系统会自动输出性能统计信息:
```
==============================================================
性能统计
==============================================================
data_understanding: 21.71秒 (min: 21.71s, max: 21.71s)
requirement_understanding: 5.32秒 (min: 5.32s, max: 5.32s)
analysis_planning: 8.45秒 (min: 8.45s, max: 8.45s)
task_execution: 120.34秒 (min: 120.34s, max: 120.34s)
report_generation: 15.67秒 (min: 15.67s, max: 15.67s)
==============================================================
```
### 性能瓶颈识别
如果某个阶段耗时过长,可以:
1. 检查数据质量和规模
2. 查看日志中的详细信息
3. 使用性能监控器定位具体函数
4. 考虑优化或并行化
## 未来优化方向
1. **并行处理**
- 并行执行独立的分析任务
- 使用多进程处理大数据集
2. **增量分析**
- 支持增量数据更新
- 避免重复分析
3. **智能采样**
- 根据数据特征智能采样
- 保留关键数据点
4. **分布式处理**
- 支持分布式数据处理
- 横向扩展能力
## 参考资料
- [性能测试代码](../tests/test_performance.py)
- [性能优化工具](../src/performance_optimization.py)
- [配置文档](./configuration_guide.md)
---
**版本**: v1.0.0
**日期**: 2026-03-06
**状态**: 完成

212
docs/configuration_guide.md Normal file
View File

@@ -0,0 +1,212 @@
# 配置管理指南
## 概述
本系统提供了灵活的配置管理机制,支持通过环境变量、配置文件和代码三种方式进行配置。
## 配置方式
### 1. 环境变量(推荐)
最简单的配置方式是使用 `.env` 文件:
```bash
# .env 文件示例
LLM_PROVIDER=openai
OPENAI_API_KEY=your_api_key_here
OPENAI_BASE_URL=https://api.openai.com/v1
OPENAI_MODEL=gpt-4
AGENT_MAX_ROUNDS=20
AGENT_OUTPUT_DIR=output
TOOL_MAX_QUERY_ROWS=10000
```
系统会自动加载 `.env` 文件中的配置。
### 2. 配置文件
使用 JSON 格式的配置文件:
```bash
python -m src.cli data.csv -c config.json
```
配置文件示例(`config.json`
```json
{
"llm": {
"provider": "openai",
"api_key": "your_api_key_here",
"base_url": "https://api.openai.com/v1",
"model": "gpt-4",
"timeout": 120,
"max_retries": 3,
"temperature": 0.7
},
"performance": {
"agent_max_rounds": 20,
"tool_max_query_rows": 10000
},
"output": {
"output_dir": "output",
"log_level": "INFO"
}
}
```
### 3. 代码配置
在代码中直接配置:
```python
from src.config import Config, LLMConfig, set_config
config = Config(
llm=LLMConfig(
api_key="your_api_key",
model="gpt-4"
)
)
set_config(config)
```
## 配置优先级
配置的优先级从高到低:
1. 代码中直接设置的配置
2. 命令行指定的配置文件(`-c config.json`
3. 环境变量
4. `.env.local` 文件(本地开发,不提交到版本控制)
5. `.env` 文件
6. 默认值
## 配置项说明
### LLM 配置
| 配置项 | 环境变量 | 默认值 | 说明 |
|--------|----------|--------|------|
| provider | LLM_PROVIDER | openai | LLM 提供商openai 或 gemini |
| api_key | OPENAI_API_KEY / GEMINI_API_KEY | - | API 密钥(必需) |
| base_url | OPENAI_BASE_URL / GEMINI_BASE_URL | https://api.openai.com/v1 | API 基础 URL |
| model | OPENAI_MODEL / GEMINI_MODEL | gpt-4 | 模型名称 |
| timeout | LLM_TIMEOUT | 120 | 请求超时时间(秒) |
| max_retries | LLM_MAX_RETRIES | 3 | 最大重试次数 |
| temperature | LLM_TEMPERATURE | 0.7 | 温度参数 |
| max_tokens | LLM_MAX_TOKENS | null | 最大 token 数 |
### 性能配置
| 配置项 | 环境变量 | 默认值 | 说明 |
|--------|----------|--------|------|
| agent_max_rounds | AGENT_MAX_ROUNDS | 20 | ReAct 最大迭代次数 |
| agent_timeout | AGENT_TIMEOUT | 300 | Agent 执行超时(秒) |
| tool_max_query_rows | TOOL_MAX_QUERY_ROWS | 10000 | 工具查询最大行数 |
| tool_execution_timeout | TOOL_EXECUTION_TIMEOUT | 60 | 工具执行超时(秒) |
| data_max_rows | DATA_MAX_ROWS | 1000000 | 最大数据行数 |
| data_sample_threshold | DATA_SAMPLE_THRESHOLD | 1000000 | 数据采样阈值 |
### 输出配置
| 配置项 | 环境变量 | 默认值 | 说明 |
|--------|----------|--------|------|
| output_dir | AGENT_OUTPUT_DIR | output | 输出目录 |
| log_dir | LOG_DIR | output | 日志目录 |
| chart_dir | CHART_DIR | output/charts | 图表目录 |
| report_filename | REPORT_FILENAME | analysis_report.md | 报告文件名 |
| log_level | LOG_LEVEL | INFO | 日志级别 |
| log_to_file | LOG_TO_FILE | true | 是否记录到文件 |
| log_to_console | LOG_TO_CONSOLE | true | 是否输出到控制台 |
## 使用示例
### 示例 1使用默认配置
```bash
# 确保 .env 文件中设置了 OPENAI_API_KEY
python -m src.cli data.csv
```
### 示例 2使用自定义配置文件
```bash
python -m src.cli data.csv -c my_config.json
```
### 示例 3覆盖环境变量
```bash
# 临时覆盖环境变量
OPENAI_MODEL=gpt-3.5-turbo python -m src.cli data.csv
```
### 示例 4在代码中使用配置
```python
from src.config import get_config
# 获取全局配置
config = get_config()
# 访问配置项
print(f"使用模型: {config.llm.model}")
print(f"输出目录: {config.output.output_dir}")
```
## 配置验证
系统会在启动时自动验证配置:
- 检查必需的配置项(如 API key
- 验证配置值的有效性
- 检查输出目录是否可写
如果配置无效,系统会报错并退出。
## 安全建议
1. **不要提交 API 密钥到版本控制**
-`.env` 添加到 `.gitignore`
- 使用 `.env.example` 作为模板
2. **使用环境变量存储敏感信息**
- 在生产环境中使用环境变量而不是配置文件
- 使用密钥管理服务(如 AWS Secrets Manager
3. **限制配置文件权限**
- 确保配置文件只有必要的用户可以读取
- 在 Unix 系统上使用 `chmod 600 config.json`
## 故障排查
### 问题:配置未生效
**解决方案**
1. 检查配置优先级,确保没有被更高优先级的配置覆盖
2. 使用 `-v` 参数查看详细日志
3. 检查环境变量是否正确设置:`echo $OPENAI_API_KEY`
### 问题API 密钥无效
**解决方案**
1. 确认 API 密钥正确无误
2. 检查 API 密钥是否有足够的权限
3. 验证 base_url 是否正确
### 问题:配置文件加载失败
**解决方案**
1. 检查 JSON 格式是否正确
2. 确认文件路径是否正确
3. 检查文件权限
## 参考
- 配置模块源码:`src/config.py`
- 环境变量加载器:`src/env_loader.py`
- 配置示例:`config.example.json`
- 环境变量示例:`.env.example`