数据分析 Agent
构建能够自动编写和执行代码进行数据分析的智能 Agent
📚 学习目标
学完这篇文章后,你将能够:
- 构建一个能够生成 Python/JS 代码的 Agent
- 安全地执行生成的代码(Sandboxing)
- 实现“代码生成 -> 执行 -> 修正”的自我迭代循环
前置知识
在开始学习之前,建议先阅读:
你需要了解:
- REPL (Read-Eval-Print Loop) 的概念
1 核心理念:Code Interpreter
数据分析 Agent 的核心是 Code Interpreter 模式:让 LLM 不直接“读完所有数据然后回答”,而是像数据分析师一样:
- Analyze:理解问题与约束(要算什么?要画什么图?要输出什么格式?)
- Plan:拆解分析步骤(清洗、统计、分组、建模、可视化)
- Code:生成可执行代码(Python/JS)
- Execute:在受控环境执行代码,拿到结构化结果
- Refine:如果报错/结果不对,修复并重试
- Answer:把结果转成“人能读懂”的洞察
循环流程
2 实现代码执行节点
LangGraph 本身不提供沙箱。你需要自己决定“怎么执行代码”,并把它封装成工具。
⚠️ 注意
千万别把 LLM 生成的代码直接在生产环境执行。真实项目里,你需要:容器隔离、资源限额、白名单依赖、网络隔离、超时与审计日志。
一个务实的教学实现是:
- 本地/开发环境:用受控的执行器(例如 Docker / 本地子进程 + 超时 + 只读数据目录)
- 线上:用独立的“代码执行服务”(沙箱)承接执行
下面我们用“工具调用”的思路,把执行器包成 Tool。
import { tool } from '@langchain/core/tools';
import { z } from 'zod';
export const execPythonTool = tool(
async ({ code }) => {
// 伪代码:你需要把这里替换成真实的沙箱执行器
// - 写入临时文件
// - 调用 python 解释器/容器
// - 捕获 stdout/stderr
return {
stdout: '...',
stderr: '',
exitCode: 0,
};
},
{
name: 'exec_python',
description: '在受控环境执行 Python 代码,并返回 stdout/stderr。',
schema: z.object({
code: z.string().describe('要执行的 Python 代码(必须可独立运行)'),
}),
},
);在 Agent 节点里,System Prompt 需要非常明确:
- 你可以使用
exec_python执行代码 - 每次执行前先解释“你为什么要这么做”
- 如果报错:读取 stderr,修复后重试(不要重复同一段错误代码)
2.1 执行器接口约定
建议给执行器设定统一返回格式:
type ExecResult = {
stdout: string;
stderr: string;
exitCode: number;
artifacts?: string[];
};💡 说明
统一格式可以让“总结节点”稳定解析结果。
2.2 规划节点示例
规划节点输出“分析步骤清单”,再交给 coder:
const plannerNode = async (state: typeof AnalysisState.State) => {
const prompt = `请为以下分析任务输出 3-5 步计划:${state.datasetPath}`;
const response = await model.invoke([new HumanMessage(prompt)]);
return { plan: String(response.content) };
};2.3 代码修复路由
当执行失败时,可以通过路由重试修复:
const routeAfterExec = (state: typeof AnalysisState.State) => {
if (state.error && state.retryCount < 2) return 'fix';
return 'summarize';
};3 状态设计与输出
对于数据分析,State 中不仅要有 messages,还建议有:
dataset:数据源位置/元信息plan:分析计划(可复用、可审计)artifacts:图表、临时文件、SQL、代码片段analysis:结构化结论(方便 UI 渲染)
import { Annotation } from '@langchain/langgraph';
import { MessagesAnnotation } from '@langchain/langgraph';
const AnalysisState = Annotation.Root({
...MessagesAnnotation.spec,
datasetPath: Annotation<string>(),
plan: Annotation<string>({
default: () => '',
}),
code: Annotation<string>({
default: () => '',
}),
artifacts: Annotation<{ images: string[]; tables: string[] }>({
reducer: (a, b) => ({
images: [...(a.images || []), ...(b.images || [])],
tables: [...(a.tables || []), ...(b.tables || [])],
}),
default: () => ({ images: [], tables: [] }),
}),
insights: Annotation<string[]>({
reducer: (a, b) => a.concat(b),
default: () => [],
}),
});4 把它串成 Graph:规划 -> 执行 -> 修正
你可以复用工具调用章节的 “Agent + ToolNode + conditional edges” 结构:
planner:让 LLM 输出分析计划(步骤列表)coder:根据计划生成代码tools:执行代码summarizer:把结果转成洞察(避免把 stdout 原样丢给用户)
这类应用的关键不是“代码多”,而是闭环:失败能自动修复,成功能给出结构化洞察。
5 安全执行与沙箱设计
执行器必须有“硬边界”,例如:
- 超时终止
- 只读数据目录
- 禁止网络访问
- 依赖白名单
const execWithTimeout = async (code: string) => {
const result = await sandbox.run(code, { timeoutMs: 3000, readOnly: true });
return result;
};💡 提示
把沙箱封装成工具,避免在 agent 节点直接执行代码。
6 自动修复循环
让模型根据 stderr 自动修复:
const fixNode = async (state: typeof AnalysisState.State) => {
const prompt = `根据错误信息修复代码:\n${state.code}\n错误:${state.error}`;
const response = await model.invoke([new HumanMessage(prompt)]);
return { code: String(response.content), retryCount: state.retryCount + 1 };
};⚠️ 注意
建议限制 retryCount,避免无限重试。
7 结构化输出,便于 UI 展示
数据分析结果最好是结构化的:
- 表格:rows + columns
- 图表:series + labels
- 洞察:bullet list
return {
artifacts: {
tables: ['summary-table'],
images: ['chart-1.png'],
},
insights: ['增长率在 Q3 提升明显'],
};ℹ️ 说明
结构化输出可以直接驱动前端组件,无需再解析文本。
8 简单的前端集成思路
前端只负责上传数据与展示结果,执行与分析由后端完成:
// 前端伪代码
const resp = await fetch('/api/analyze', { method: 'POST', body: file });
const data = await resp.json();
renderCharts(data.artifacts.images);9 结果汇总与报告生成
把执行结果转换成“人能读懂”的报告是关键步骤:
const summarizeNode = async (state: typeof AnalysisState.State) => {
const prompt = `请根据以下结果生成结论:\n${state.insights.join('\n')}`;
const response = await model.invoke([new HumanMessage(prompt)]);
return { summary: String(response.content) };
};💡 输出建议
- 结论要短、明确、可执行
- 每条结论最好对应一项数据依据
10 可视化产物
如果需要图表,可以让代码生成图片并返回路径:
return {
artifacts: { images: ['chart-sales.png'], tables: ['sales-table'] },
insights: ['Q3 增长 12%'],
};ℹ️ 说明
把图表视为“产物”,再由前端决定如何展示。
11 Python vs JS 执行
选择执行语言时可以参考:
- Python:生态丰富(pandas、numpy、matplotlib)
- JS:更易与前端一体化(node + d3)
💡 建议
大多数数据分析任务优先用 Python,前端展示用 JS。
💡 练习题
-
场景题:用户上传了一个包含乱码的 CSV。Agent 第一次尝试读取失败了。在 LangGraph 中,如何设计流程让 Agent 自动尝试不同的编码格式(utf-8, gbk, latin1)?
点击查看答案
在 state 里保存
retryCount与encodingCandidates,失败后切换编码并重试。 -
操作题:给执行器增加 3 秒超时与只读目录限制。
点击查看答案
在 sandbox 运行时传入
timeoutMs和readOnly参数。 -
思考题:为什么分析结果要结构化输出,而不是纯文本?
点击查看答案
结构化数据更易渲染、复用和二次分析,减少前端解析成本。
-
操作题:为自动修复循环加入
retryCount上限。点击查看答案
在路由中判断
retryCount >= maxRetries时直接结束或降级。 -
思考题:哪些场景不适合让 LLM 自动执行代码?
点击查看答案
涉及敏感数据、生产数据库写入、或安全边界不清晰的场景。
📚 参考资源
官方文档
本项目相关内容
✅ 总结
本章要点:
- 数据分析 Agent 本质上是一个具备代码执行能力的 ReAct Agent。
- 只有通过“编写代码”才能处理大规模数据,而不是让 LLM 直接“阅读”数据。
- 错误恢复机制(Error Correction)在代码生成场景中尤为重要。
下一步:除了分析数据,Agent 还能帮我们写代码吗?继续学习:代码生成与辅助。
登录以继续阅读
解锁完整文档、代码示例及更多高级功能。