1. 项目概述
在数据科学领域,Jupyter Notebook已经成为数据分析师和研究人员不可或缺的工具。但每次面对新数据集时,我们都需要重复编写相似的预处理代码、可视化脚本和分析流程,这不仅效率低下,也容易出错。这个项目就是要解决这个痛点——通过构建一个基于prompt的智能数据分析Agent,让Jupyter Notebook具备自然语言交互能力,实现"用说话的方式做数据分析"。
我花了三个月时间迭代这个项目,最终实现了一个可以理解用户自然语言需求、自动生成并执行代码、还能根据结果优化后续分析的智能体。实测下来,它能够处理80%的常规数据分析任务,从数据清洗到特征工程,从统计分析到可视化呈现,整个过程就像有个专业的数据助手在帮你写代码。
2. 核心架构设计
2.1 系统组成模块
这个智能体由四个核心组件构成:
-
自然语言理解模块:基于微调的GPT模型,专门针对数据分析场景优化。我收集了超过5000条数据分析相关的对话语料进行训练,使其能准确识别诸如"帮我看看销售额的月度趋势"这类需求背后的分析意图。
-
代码生成引擎:采用分层设计:
- 第一层判断分析类型(统计/可视化/机器学习)
- 第二层选择具体算法(如折线图/柱状图)
- 第三层填充参数(时间字段、数值字段等)
-
执行环境桥接:通过Jupyter Kernel Gateway API实现代码注入和结果获取。这里有个关键技巧是维护一个会话级的变量状态表,避免重复计算。
-
反馈优化系统:当代码执行报错时,会自动分析错误日志并尝试修复。我实现了三种修复策略:
- 语法错误:直接修正
- 数据错误:建议预处理步骤
- 逻辑错误:要求用户澄清需求
2.2 关键技术选型
在模型选择上,我对比了多个方案:
| 模型类型 | 优点 | 缺点 | 最终选择 |
|---|---|---|---|
| GPT-3.5 | 通用性强 | 数据分析专业度不够 | ❌ |
| Codex | 代码生成优秀 | 不擅长需求理解 | ❌ |
| LLaMA-2微调 | 可定制化 | 需要大量训练数据 | ✅ |
| Claude Instant | 逻辑清晰 | 生成代码风格不一致 | ❌ |
最终选择LLaMA-2-13B作为基础模型,通过以下方式增强:
- 注入pandas/numpy等库的API文档
- 添加300个数据分析代码模板
- 使用RLHF优化对话逻辑
3. 实现细节解析
3.1 Prompt工程实践
设计有效的prompt是这个项目的核心难点。经过反复测试,我总结出数据分析场景的prompt模板:
python复制"""
你是一个专业的数据分析助手,当前工作环境:
- Python 3.9
- pandas 1.5.3
- matplotlib 3.7.1
- 数据集变量名:df
请根据用户需求生成可直接执行的代码,要求:
1. 优先使用向量化操作
2. 添加必要的异常处理
3. 包含中文注释
用户需求:{{ user_input }}
"""
关键技巧:
- 显式声明环境版本避免兼容性问题
- 指定数据集变量名防止混淆
- 要求中文注释提升可读性
- 限制代码风格保证一致性
3.2 代码生成逻辑
当用户输入"分析各产品线的销售额分布"时,系统内部处理流程:
- 意图识别:分类为"可视化-分布分析"
- 参数提取:
- 分析维度:产品线
- 指标:销售额
- 生成代码:
python复制# 计算各产品线销售额占比
sales_by_product = df.groupby('产品线')['销售额'].sum()
plt.figure(figsize=(10,6))
sales_by_product.plot(kind='pie', autopct='%1.1f%%')
plt.title('各产品线销售额占比')
plt.show()
- 执行后自动添加Markdown说明:
markdown复制## 分析结果
- 总销售额:{total_sales}
- 占比最高产品线:{top_product} ({percentage}%)
3.3 状态管理实现
通过上下文管理器维护分析状态:
python复制class AnalysisContext:
def __init__(self):
self.variables = {
'df': None, # 主数据集
'tmp': {}, # 临时变量
'plots': [], # 生成的图表
'stats': {} # 统计结果
}
def update(self, code):
# 解析代码中新建的变量
new_vars = parse_created_vars(code)
self.variables['tmp'].update(new_vars)
# 记录可视化输出
if 'plt.show()' in code:
self.variables['plots'].append(
save_current_plot()
)
4. 实战应用案例
4.1 销售数据分析场景
用户输入:"帮我找出销售额异常波动的日期"
系统处理流程:
- 生成统计代码:
python复制# 计算每日销售额
daily_sales = df.resample('D', on='日期')['销售额'].sum()
# 使用Z-score检测异常值
from scipy import stats
z_scores = stats.zscore(daily_sales)
anomalies = daily_sales[abs(z_scores) > 3]
- 输出结果:
code复制发现3个异常日期:
- 2023-05-18:销售额骤降(可能原因:系统故障)
- 2023-06-11:销售额激增(可能原因:促销活动)
- 2023-07-03:数据采集异常(建议核查)
- 自动生成验证代码:
python复制# 验证2023-05-18数据
df[df['日期'] == '2023-05-18'].describe()
4.2 用户行为分析场景
用户输入:"分析用户活跃时段的分布规律"
系统响应:
- 生成处理代码:
python复制# 提取小时维度
df['活跃小时'] = df['登录时间'].dt.hour
# 绘制24小时分布
hourly_active = df['活跃小时'].value_counts().sort_index()
hourly_active.plot(kind='bar', figsize=(12,6))
plt.xlabel('小时')
plt.ylabel('活跃用户数')
plt.title('用户活跃时段分布')
- 智能解读:
code复制分析显示:
- 早高峰:9-11点(上班时段)
- 午间低谷:13-14点(午休时间)
- 晚高峰:20-22点(下班后)
建议在高峰前30分钟推送通知获取最佳响应率
5. 性能优化技巧
5.1 缓存机制实现
为避免重复计算,我设计了三级缓存:
- 代码缓存:对相似需求生成代码签名
python复制def get_code_signature(prompt):
return hashlib.md5(
re.sub(r'\s+', '', prompt).encode()
).hexdigest()
- 数据缓存:对中间结果持久化
python复制@cache_dataframe
def preprocess_data(raw_df):
# 耗时处理逻辑
return processed_df
- 可视化缓存:存储渲染过的图表
5.2 大文件处理策略
当数据量超过内存时:
- 自动切换为分块处理模式
python复制chunk_size = 100000
for chunk in pd.read_csv('large_file.csv', chunksize=chunk_size):
process_chunk(chunk)
- 建议使用Dask或Modin替代pandas
python复制# 自动检测数据大小
if df.memory_usage().sum() > 2e9: # >2GB
suggest_use('dask')
- 采样分析模式
python复制if len(df) > 1e6:
sample_df = df.sample(frac=0.1)
run_analysis(sample_df)
6. 常见问题排查
6.1 代码生成失败场景
问题现象:Agent返回"无法理解该需求"
排查步骤:
-
检查需求是否包含明确的分析对象和指标
- 错误示例:"分析数据"
- 正确示例:"分析用户留存率的变化趋势"
-
确认数据集中存在相关字段
python复制if '留存率' not in df.columns: suggest_columns(df.columns) -
尝试分步提问:
- 第一步:"需要分析哪个指标?"
- 第二步:"希望用什么方式呈现?"
6.2 执行报错处理
典型错误1:KeyError
解决方案:
- 自动检查列名相似度:
python复制def find_similar_columns(df, target):
from difflib import get_close_matches
return get_close_matches(target, df.columns)
- 建议预处理步骤:
code复制检测到"用户ID"列不存在,相似列有:
- user_id
- user_name
是否要使用user_id进行分析?
典型错误2:MemoryError
自动响应:
- 建议采样分析
- 推荐使用更高效的数据类型
python复制# 自动检测可优化的列
for col in df.select_dtypes('object'):
if df[col].nunique() < 50:
suggest_convert_to_category(col)
7. 部署与集成方案
7.1 JupyterLab扩展开发
通过JupyterLab Extension API实现深度集成:
typescript复制class DataAssistantWidget extends Widget {
constructor() {
this._input = new InputBox({
placeholder: '输入分析需求...',
handler: (query) => this._handleQuery(query)
});
}
private async _handleQuery(query: string) {
const code = await requestAnalysisCode(query);
this._insertCodeCell(code);
}
}
关键功能点:
- 快捷键唤起(Ctrl+Shift+A)
- 分析历史记录
- 代码diff查看
7.2 本地化部署方案
使用Docker打包完整环境:
dockerfile复制FROM jupyter/scipy-notebook
# 安装模型
RUN pip install llama-cpp-python==0.1.77
# 添加预训练权重
COPY --chown=$NB_USER models/ggml-model-q4_0.bin /home/$NB_USER/
# 安装扩展
COPY extension/ /opt/conda/share/jupyter/lab/extensions/
启动命令:
bash复制docker run -p 8888:8888 -v $(pwd)/data:/home/jovyan/work data-assistant
8. 效果评估与优化
建立自动化测试框架:
python复制test_cases = [
{
"input": "分析销售额趋势",
"expect": ["groupby", "plot", "时间序列"],
"timeout": 10
},
{
"input": "找出异常订单",
"expect": ["Z-score", "IQR", "异常检测"],
"timeout": 15
}
]
def run_regression():
for case in test_cases:
code = generate_code(case["input"])
assert all(keyword in code for keyword in case["expect"])
持续优化方向:
- 增加测试用例覆盖率(当前85%)
- 减少代码生成延迟(目标<2s)
- 提升复杂需求处理能力
在实际使用中,这个智能体已经能够处理我们团队80%的常规分析需求,使探索性分析的效率提升了3-5倍。特别是在处理陌生数据集时,不需要再反复查阅文档,直接用自然语言描述需求就能快速获得分析结果。