AI Agent开发实战:用LangGraph打造会思考、能行动的智能助手
从零构建一个具备状态记忆、工具调用、多步推理能力的智能Agent系统
前言:为什么我们需要AI Agent?
在ChatGPT引爆AI热潮后,我们很快发现:单纯的大语言模型(LLM)只是"会说话的百科全书"。它能生成流畅的文本,却无法执行实际操作——不能查询实时数据、不能操作文件、不能与外部系统交互。
这正是AI Agent诞生的背景。Agent = LLM + 记忆 + 工具 + 规划能力,它让AI从"被动回答"进化为"主动解决问题"。
本文将带你深入LangGraph——LangChain团队推出的Agent开发框架,通过一个完整的自动化数据分析Agent项目,掌握现代AI Agent的核心开发技术。
本文你将学到:
- Agent的核心架构与设计模式
- LangGraph状态图编程模型
- 工具调用与Function Calling机制
- 多Agent协作系统设计
- 完整项目实战:从需求到部署
一、AI Agent核心概念解析
1.1 Agent ≠ ChatGPT
先通过一个例子理解Agent与传统LLM对话的本质区别:
传统LLM对话:
用户:帮我分析一下sales.csv文件,给出销售趋势
LLM:我无法访问您的本地文件,请提供文件内容或描述...
AI Agent对话:
用户:帮我分析一下sales.csv文件,给出销售趋势
Agent:[读取文件] → [解析数据结构] → [生成分析代码] → [执行分析] → [生成报告]
我已完成分析,发现以下趋势:Q3销售额同比增长23%...
核心差异:Agent具备行动能力(Action),能调用工具完成实际任务。
1.2 Agent的四大核心组件
现代AI Agent架构遵循ReAct模式(Reasoning + Acting),包含四个关键模块:
┌─────────────────────────────────────────────┐
│ AI Agent Architecture │
├─────────────────────────────────────────────┤
│ 1. 🧠 大脑 (Brain/LLM) │
│ - 理解用户意图 │
│ - 生成推理链条 │
│ - 决策下一步行动 │
├─────────────────────────────────────────────┤
│ 2. 💾 记忆系统 (Memory) │
│ - 短期记忆:当前对话上下文 │
│ - 长期记忆:历史经验、知识库 │
│ - 工作记忆:任务执行状态 │
├─────────────────────────────────────────────┤
│ 3. 🔧 工具库 (Tools) │
│ - 搜索引擎、数据库查询 │
│ - 代码执行、文件操作 │
│ - API调用、第三方服务集成 │
├─────────────────────────────────────────────┤
│ 4. 📋 规划器 (Planner) │
│ - 任务分解(复杂→简单子任务) │
│ - 执行策略(顺序/并行/条件分支) │
│ - 自我反思与纠错 │
└─────────────────────────────────────────────┘
1.3 ReAct循环:思考-行动-观察
Agent的核心执行模式是ReAct循环:
# ReAct循环伪代码
while not task_complete:
# 1. 思考(Reasoning)
thought = llm.think(current_state, goal)
# 2. 行动(Action)
action = llm.decide_action(available_tools)
result = execute_tool(action)
# 3. 观察(Observation)
observation = observe(result)
# 4. 更新状态
current_state.update(observation)
# 5. 判断是否完成
task_complete = llm.evaluate(current_state, goal)
LangGraph的价值:提供一套声明式的图结构API,让开发者可以用状态机思维设计Agent,而非编写复杂的循环控制逻辑。
二、LangGraph架构深度解析
2.1 为什么选择LangGraph?
在LangGraph之前,主流Agent框架存在明显痛点:
| 框架 | 优势 | 痛点 |
|---|---|---|
| LangChain Agent | 快速原型 | 难以定制循环逻辑,调试困难 |
| AutoGPT/BabyAGI | 自主性强 | 黑盒执行,不可控,成本高 |
| 自定义实现 | 完全控制 | 开发成本高,易出错 |
LangGraph的设计哲学:
- 显式状态管理:状态流转清晰可见
- 可控性:每个节点、边都可精确控制
- 循环支持:原生支持循环、分支结构
- 可视化调试:自动生成执行流程图
2.2 核心概念:图(Graph)、节点(Node)、边(Edge)
LangGraph基于**状态图(State Graph)**模型,三个核心概念:
from langgraph.graph import StateGraph, END
# 1. 定义状态(State)
class AgentState(TypedDict):
messages: List[BaseMessage] # 对话历史
current_task: str # 当前任务
tool_results: List[dict] # 工具执行结果
next_action: str # 下一步行动
# 2. 定义节点(Node)- 处理状态的函数
def think_node(state: AgentState) -> AgentState:
"""LLM推理节点"""
# ... 处理逻辑
return state
def act_node(state: AgentState) -> AgentState:
"""工具执行节点"""
# ... 处理逻辑
return state
# 3. 构建图(Graph)
graph = StateGraph(AgentState)
graph.add_node("think", think_node)
graph.add_node("act", act_node)
# 4. 定义边(Edge)- 状态流转路径
graph.add_edge("think", "act")
graph.add_conditional_edges(
"act",
should_continue, # 条件函数
{
"continue": "think", # 继续循环
"end": END # 结束
}
)
# 5. 编译并运行
app = graph.compile()
result = app.invoke({"messages": [], "current_task": "分析数据"})
2.3 图的结构类型
LangGraph支持多种图结构,适配不同复杂度的Agent:
2.3.1 线性图(Linear Graph)
适用于简单流水线任务:
[Start] → [Node A] → [Node B] → [Node C] → [End]
graph = StateGraph(State)
graph.add_node("a", node_a)
graph.add_node("b", node_b)
graph.add_node("c", node_c)
graph.add_edge("a", "b")
graph.add_edge("b", "c")
graph.add_edge("c", END)
graph.set_entry_point("a")
2.3.2 循环图(Cyclic Graph)
适用于ReAct模式的Agent:
[Start] → [Think] ⟷ [Act] → [End]
↑________|
graph.add_edge("think", "act")
graph.add_conditional_edges(
"act",
lambda state: "continue" if not state["done"] else "end",
{"continue": "think", "end": END}
)
2.3.3 分支图(Branching Graph)
适用于多工具选择的复杂场景:
→ [Search Tool] ↘
[Start] → [Router] → [Aggregate] → [End]
→ [Code Tool] ↗
2.4 状态管理:Agent的"工作记忆"
状态是LangGraph的核心,所有节点共享同一状态对象:
from typing import TypedDict, Annotated
from langgraph.graph import add_messages
class AgentState(TypedDict):
# Annotated类型:自动合并(而非覆盖)
messages: Annotated[List[BaseMessage], add_messages]
# 普通类型:每次更新会覆盖
current_step: int
task_list: List[str]
results: dict
状态更新策略:
- 覆盖模式:
state["current_step"] = 2 - 合并模式:
add_messages自动将新消息追加到列表
三、实战基础:构建第一个Agent
3.1 环境准备
# 创建虚拟环境
python -m venv agent_env
source agent_env/bin/activate # Windows: agent_env\Scripts\activate
# 安装依赖
pip install langgraph langchain langchain-openai python-dotenv
# 可选:安装工具相关库
pip install duckduckgo-search # 搜索工具
pip install pandas matplotlib # 数据分析工具
创建.env文件配置API密钥:
OPENAI_API_KEY=sk-xxx
# 或使用国内模型
# DEEPSEEK_API_KEY=sk-xxx
3.2 最简Agent:问答机器人
先实现一个最简单的图,理解基本流程:
"""
最简LangGraph Agent示例:基础问答机器人
演示图的基本构建和执行流程
"""
from typing import TypedDict
from langgraph.graph import StateGraph, END
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, AIMessage
# 1. 定义状态
class ChatState(TypedDict):
messages: list # 对话历史
# 2. 定义节点
def chatbot_node(state: ChatState) -> ChatState:
"""对话节点:调用LLM生成回复"""
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.7)
# 获取历史消息
messages = state["messages"]
# 调用LLM
response = llm.invoke(messages)
# 更新状态(追加AI消息)
return {"messages": messages + [response]}
# 3. 构建图
graph = StateGraph(ChatState)
graph.add_node("chatbot", chatbot_node)
graph.set_entry_point("chatbot")
graph.add_edge("chatbot", END)
# 4. 编译
app = graph.compile()
# 5. 运行
if __name__ == "__main__":
# 初始状态
initial_state = {
"messages": [HumanMessage(content="什么是LangGraph?用一句话解释")]
}
# 执行图
result = app.invoke(initial_state)
# 打印结果
print("AI回复:", result["messages"][-1].content)
运行结果:
AI回复: LangGraph是一个基于图的AI Agent开发框架,让开发者可以用状态机思维构建具有记忆、工具调用和循环推理能力的智能助手。
3.3 进阶:添加工具调用能力
现在让Agent具备搜索能力,成为真正的"能行动"的助手:
"""
工具调用Agent:具备搜索能力的智能助手
演示LangGraph的工具绑定机制
"""
from typing import TypedDict, Annotated
from langgraph.graph import StateGraph, END
from langgraph.prebuilt import ToolNode
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
from langchain_core.tools import tool
import duckduckgo_search
# 1. 定义搜索工具
@tool
def search_web(query: str) -> str:
"""搜索互联网获取实时信息
Args:
query: 搜索关键词
Returns:
搜索结果摘要
"""
from duckduckgo_search import DDGS
with DDGS() as ddgs:
results = list(ddgs.text(query, max_results=3))
if not results:
return "未找到相关结果"
# 格式化结果
output = []
for r in results:
output.append(f"【{r['title']}】\n{r['body']}\n链接: {r['href']}\n")
return "\n".join(output)
# 2. 定义状态
class AgentState(TypedDict):
messages: Annotated[list, "add_messages"] # 自动合并消息
# 3. 初始化LLM并绑定工具
tools = [search_web]
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
llm_with_tools = llm.bind_tools(tools)
# 4. 定义节点
def agent_node(state: AgentState) -> AgentState:
"""Agent推理节点:决定是否调用工具"""
messages = state["messages"]
response = llm_with_tools.invoke(messages)
return {"messages": [response]}
def should_continue(state: AgentState) -> str:
"""路由函数:判断是否需要调用工具"""
last_message = state["messages"][-1]
# 如果LLM决定调用工具,则进入工具节点
if last_message.tool_calls:
return "tools"
# 否则结束
return "end"
# 5. 构建图
graph = StateGraph(AgentState)
# 添加节点
graph.add_node("agent", agent_node)
graph.add_node("tools", ToolNode(tools)) # 内置工具节点
# 设置入口
graph.set_entry_point("agent")
# 添加条件边
graph.add_conditional_edges(
"agent",
should_continue,
{
"tools": "tools",
"end": END
}
)
# 工具执行后返回Agent继续推理
graph.add_edge("tools", "agent")
# 6. 编译
app = graph.compile()
# 7. 运行示例
if __name__ == "__main__":
question = "2024年诺贝尔物理学奖获得者是谁?"
result = app.invoke({
"messages": [HumanMessage(content=question)]
})
# 打印执行过程
print("=" * 50)
print("用户问题:", question)
print("=" * 50)
for i, msg in enumerate(result["messages"]):
msg_type = type(msg).__name__
print(f"\n[步骤{i+1}] {msg_type}:")
if hasattr(msg, 'tool_calls') and msg.tool_calls:
print(f" → 调用工具: {msg.tool_calls}")
else:
print(f" → {msg.content[:200]}...")
print("\n" + "=" * 50)
print("最终回答:", result["messages"][-1].content)
执行流程可视化:
[步骤1] HumanMessage:
→ 2024年诺贝尔物理学奖获得者是谁?
[步骤2] AIMessage:
→ 调用工具: [{'name': 'search_web', 'args': {'query': '2024年诺贝尔物理学奖获得者'}}]
[步骤3] ToolMessage:
→ 【2024年诺贝尔物理学奖公布...】
[步骤4] AIMessage:
→ 2024年诺贝尔物理学奖授予了John Hopfield和Geoffrey Hinton...
四、状态管理深度实践
4.1 为什么状态管理至关重要?
在复杂Agent系统中,状态管理直接影响:
- 上下文连贯性:多轮对话、多步骤任务
- 错误恢复:任务失败后的状态回滚
- 调试追踪:定位问题节点
- 持久化:任务中断后的恢复
4.2 状态设计最佳实践
from typing import TypedDict, Annotated, Literal
from langgraph.graph import add_messages
from langchain_core.messages import BaseMessage
class DataAnalysisState(TypedDict):
"""数据分析Agent的状态定义
设计原则:
1. 区分"累加型"和"覆盖型"字段
2. 明确每个字段的用途
3. 为复杂字段定义子结构
"""
# === 对话与推理 ===
messages: Annotated[List[BaseMessage], add_messages] # 对话历史(自动合并)
reasoning_chain: List[str] # 推理链条(手动追加)
# === 任务管理 ===
current_step: str # 当前步骤标识
task_plan: List[dict] # 任务计划列表
completed_steps: List[str] # 已完成步骤
# === 数据处理 ===
input_file: str # 输入文件路径
data_schema: dict # 数据结构信息
analysis_results: dict # 分析结果
# === 输出控制 ===
output_format: Literal["report", "chart", "json"] # 输出格式
final_output: str # 最终输出内容
# === 错误处理 ===
errors: List[dict] # 错误记录
retry_count: int # 重试计数
4.3 状态更新模式
模式1:直接覆盖
def update_step(state: DataAnalysisState) -> DataAnalysisState:
"""更新当前步骤"""
return {
"current_step": "data_cleaning" # 直接覆盖
}
模式2:追加到列表
def add_reasoning(state: DataAnalysisState) -> DataAnalysisState:
"""追加推理步骤"""
new_thought = "发现数据包含空值,需要清洗"
return {
"reasoning_chain": state["reasoning_chain"] + [new_thought]
}
模式3:合并字典
def update_results(state: DataAnalysisState) -> DataAnalysisState:
"""合并分析结果"""
new_result = {"mean_price": 150.5, "std_price": 23.8}
return {
"analysis_results": {
**state["analysis_results"], # 保留现有结果
**new_result # 添加新结果
}
}
4.4 状态持久化:Checkpointing
LangGraph内置检查点机制,支持任务中断与恢复:
from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import StateGraph
# 创建检查点存储
checkpointer = MemorySaver()
# 编译时启用检查点
app = graph.compile(checkpointer=checkpointer)
# 运行并指定thread_id(会话标识)
config = {"configurable": {"thread_id": "session-001"}}
result = app.invoke(initial_state, config=config)
# 后续可以从断点恢复
# checkpointer会自动保存每一步的状态
应用场景:
- 长时间运行的任务(数据分析、代码生成)
- 需要人工审核的步骤
- 多用户并发场景
五、多Agent协作系统
5.1 为什么需要多Agent?
单个Agent在面对复杂任务时存在局限:
- 能力边界:一个Agent难以精通所有领域
- 注意力分散:长任务链容易偏离目标
- 单点故障:一个工具失败可能导致整体失败
多Agent协作的优势:
- 专业化分工:每个Agent专注特定领域
- 并行执行:独立子任务可并行处理
- 相互校验:多Agent交叉验证提高准确性
5.2 协作模式
模式1:主从模式(Supervisor)
┌→ [Researcher Agent] →┐
[User] → [Supervisor Agent] → [Final Report]
└→ [Coder Agent] →┘
"""
多Agent协作:主从模式示例
Supervisor负责任务分配和结果整合
"""
from typing import TypedDict, Annotated, Literal
from langgraph.graph import StateGraph, END
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, AIMessage
# 定义状态
class MultiAgentState(TypedDict):
messages: Annotated[list, "add_messages"]
next_agent: str # 下一个要执行的Agent
research_result: str
code_result: str
final_report: str
# Supervisor节点
def supervisor_node(state: MultiAgentState) -> MultiAgentState:
"""主控Agent:分析任务并分配"""
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
prompt = f"""你是一个任务分配者。分析用户需求,决定下一步应该由哪个Agent处理:
当前状态:
- 已有研究结果: {state.get('research_result', '无')}
- 已有代码结果: {state.get('code_result', '无')}
可选Agent:
- researcher: 负责信息搜集和调研
- coder: 负责代码编写
- end: 任务完成,生成最终报告
输出格式(仅输出一个词):
researcher / coder / end
"""
response = llm.invoke([
HumanMessage(content=prompt)
])
decision = response.content.strip().lower()
return {"next_agent": decision}
# Researcher节点
def researcher_node(state: MultiAgentState) -> MultiAgentState:
"""研究Agent:搜集信息"""
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.7)
task = state["messages"][0].content
prompt = f"""你是一个专业研究员。针对以下任务进行调研:
任务:{task}
请提供:
1. 关键概念解释
2. 相关技术或方法
3. 实施建议
"""
response = llm.invoke([HumanMessage(content=prompt)])
return {
"research_result": response.content,
"next_agent": "supervisor" # 返回主控
}
# Coder节点
def coder_node(state: MultiAgentState) -> MultiAgentState:
"""代码Agent:编写代码"""
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.3)
task = state["messages"][0].content
research = state.get("research_result", "")
prompt = f"""你是一个专业程序员。基于研究结果编写代码:
原始任务:{task}
研究结果:
{research}
请提供:
1. 完整可运行的Python代码
2. 代码注释
3. 使用说明
"""
response = llm.invoke([HumanMessage(content=prompt)])
return {
"code_result": response.content,
"next_agent": "supervisor" # 返回主控
}
# 最终报告节点
def final_report_node(state: MultiAgentState) -> MultiAgentState:
"""生成最终报告"""
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.5)
task = state["messages"][0].content
prompt = f"""生成最终报告:
原始任务:{task}
研究结果:
{state.get('research_result', '')}
代码方案:
{state.get('code_result', '')}
请整合以上信息,生成一份完整的技术报告。
"""
response = llm.invoke([HumanMessage(content=prompt)])
return {
"final_report": response.content,
"next_agent": "end"
}
# 路由函数
def route_agent(state: MultiAgentState) -> str:
"""根据next_agent字段路由"""
next_agent = state["next_agent"]
if next_agent == "end":
return "final_report"
return next_agent
# 构建图
graph = StateGraph(MultiAgentState)
# 添加节点
graph.add_node("supervisor", supervisor_node)
graph.add_node("researcher", researcher_node)
graph.add_node("coder", coder_node)
graph.add_node("final_report", final_report_node)
# 设置入口
graph.set_entry_point("supervisor")
# 添加条件路由
graph.add_conditional_edges(
"supervisor",
route_agent,
{
"researcher": "researcher",
"coder": "coder",
"final_report": "final_report"
}
)
# Agent执行后返回Supervisor
graph.add_edge("researcher", "supervisor")
graph.add_edge("coder", "supervisor")
graph.add_edge("final_report", END)
# 编译
app = graph.compile()
模式2:对等协作(Peer-to-Peer)
"""
对等协作模式:两个专家Agent互相讨论
适用于需要多角度分析的场景
"""
from typing import TypedDict, Annotated
from langgraph.graph import StateGraph, END
class DebateState(TypedDict):
messages: Annotated[list, "add_messages"]
round_count: int
conclusion: str
def optimist_agent(state: DebateState) -> DebateState:
"""乐观派Agent"""
# ... 提供正面观点
pass
def pessimist_agent(state: DebateState) -> DebateState:
"""悲观派Agent"""
# ... 提供反面观点
pass
def mediator_agent(state: DebateState) -> DebateState:
"""调解人:综合观点"""
# ... 生成结论
pass
# 构建对等协作图
# 两个Agent轮流发言,最后由调解人总结
5.3 Agent通信机制
多Agent之间需要共享信息,常用方法:
方法1:共享状态(推荐)
class SharedState(TypedDict):
workspace: dict # 共享工作空间
agent_a_output: str
agent_b_output: str
方法2:消息传递
def agent_a_send_message(state: SharedState) -> SharedState:
"""Agent A发送消息到工作空间"""
message = {"from": "agent_a", "content": "数据分析完成"}
return {
"workspace": {
**state["workspace"],
"messages": state["workspace"].get("messages", []) + [message]
}
}
六、完整项目:自动化数据分析Agent
6.1 项目需求分析
**目标:**构建一个能自动完成数据分析全流程的Agent系统
功能需求:
- 接收用户自然语言描述的分析需求
- 自动读取数据文件(CSV/Excel)
- 自动理解数据结构
- 生成并执行分析代码
- 输出可视化图表和分析报告
技术架构:
用户输入 → [需求理解] → [数据加载] → [数据分析] → [可视化] → [报告生成]
↓ ↓ ↓ ↓
[状态管理] ← ← ← ← ← ← ← ← ← ← ← ← ← ←
6.2 项目结构
data_analysis_agent/
├── agent/
│ ├── __init__.py
│ ├── graph.py # 主图定义
│ ├── nodes/ # 各节点实现
│ │ ├── planner.py # 任务规划节点
│ │ ├── loader.py # 数据加载节点
│ │ ├── analyzer.py # 分析执行节点
│ │ └── reporter.py # 报告生成节点
│ ├── state.py # 状态定义
│ └── tools.py # 工具函数
├── utils/
│ ├── code_executor.py # 代码执行器
│ └── visualizer.py # 可视化工具
├── main.py # 入口文件
└── requirements.txt
6.3 核心代码实现
6.3.1 状态定义
# agent/state.py
from typing import TypedDict, Annotated, List, Optional
from langgraph.graph import add_messages
from langchain_core.messages import BaseMessage
class DataAnalysisState(TypedDict):
"""数据分析Agent状态
状态流转:
用户需求 → 规划 → 加载 → 分析 → 可视化 → 报告
"""
# === 输入 ===
user_request: str # 用户原始需求
input_file: Optional[str] # 数据文件路径
# === 处理过程 ===
messages: Annotated[List[BaseMessage], add_messages] # 对话历史
# 规划阶段
analysis_plan: List[str] # 分析计划步骤
current_step_index: int # 当前执行到第几步
# 数据阶段
data_info: dict # 数据基本信息(列名、类型、统计)
data_sample: str # 数据预览(前5行)
# 分析阶段
generated_code: str # 生成的分析代码
code_output: str # 代码执行输出
analysis_results: dict # 解析后的分析结果
# 可视化阶段
chart_paths: List[str] # 生成的图表文件路径
# === 输出 ===
final_report: str # 最终分析报告
# === 控制 ===
errors: List[dict] # 错误记录
retry_count: int # 当前步骤重试次数
max_retries: int # 最大重试次数
6.3.2 工具函数
# agent/tools.py
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from pathlib import Path
from typing import Optional
import json
class DataTools:
"""数据分析工具集"""
@staticmethod
def load_data(file_path: str) -> dict:
"""加载数据文件并返回基本信息
Args:
file_path: CSV或Excel文件路径
Returns:
包含数据信息的字典
"""
path = Path(file_path)
if not path.exists():
return {"error": f"文件不存在: {file_path}"}
# 根据扩展名选择加载方式
if path.suffix == '.csv':
df = pd.read_csv(file_path)
elif path.suffix in ['.xlsx', '.xls']:
df = pd.read_excel(file_path)
else:
return {"error": f"不支持的文件格式: {path.suffix}"}
# 生成数据信息
info = {
"file_name": path.name,
"rows": len(df),
"columns": list(df.columns),
"dtypes": {col: str(dtype) for col, dtype in df.dtypes.items()},
"missing_values": {col: int(df[col].isna().sum()) for col in df.columns},
"statistics": df.describe().to_dict()
}
# 数据预览
sample = df.head(5).to_string()
return {
"info": info,
"sample": sample,
"shape": df.shape,
"dataframe": df # 保留DataFrame供后续使用
}
@staticmethod
def execute_analysis_code(code: str, df: pd.DataFrame) -> dict:
"""执行分析代码
Args:
code: Python分析代码
df: 数据DataFrame
Returns:
执行结果
"""
# 创建执行环境
local_vars = {
'df': df,
'pd': pd,
'plt': plt,
'sns': sns,
'result': {}
}
try:
# 执行代码
exec(code, local_vars)
# 获取结果
result = local_vars.get('result', {})
return {
"success": True,
"output": str(result),
"result": result
}
except Exception as e:
return {
"success": False,
"error": str(e)
}
@staticmethod
def save_chart(figure: plt.Figure, output_dir: str, name: str) -> str:
"""保存图表
Args:
figure: matplotlib Figure对象
output_dir: 输出目录
name: 图表名称
Returns:
保存的文件路径
"""
output_path = Path(output_dir)
output_path.mkdir(parents=True, exist_ok=True)
file_path = output_path / f"{name}.png"
figure.savefig(file_path, dpi=150, bbox_inches='tight')
plt.close(figure)
return str(file_path)
# 定义LangGraph工具
from langchain_core.tools import tool
@tool
def analyze_data_info(file_path: str) -> str:
"""分析数据文件的基本信息
Args:
file_path: 数据文件路径
Returns:
数据信息JSON字符串
"""
result = DataTools.load_data(file_path)
return json.dumps(result["info"], indent=2, ensure_ascii=False)
@tool
def generate_sample_code(data_info: str, analysis_goal: str) -> str:
"""根据数据信息和分析目标生成分析代码
Args:
data_info: 数据基本信息JSON
analysis_goal: 分析目标描述
Returns:
Python分析代码
"""
# 这个工具实际由LLM实现,这里只是占位
return "# 代码生成由LLM完成"
6.3.3 节点实现
# agent/nodes/planner.py
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, SystemMessage
from typing import List
import json
def planner_node(state: dict) -> dict:
"""任务规划节点:分析用户需求,制定分析计划"""
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
system_prompt = """你是一个数据分析专家。根据用户需求,制定详细的分析计划。
输出JSON格式的计划列表,例如:
{
"plan": [
"1. 数据概览:查看数据基本统计信息",
"2. 数据质量:检查缺失值和异常值",
"3. 分布分析:分析关键数值列的分布",
"4. 相关性分析:探索变量间的关系",
"5. 可视化:生成关键图表",
"6. 总结:输出分析报告"
]
}
只输出JSON,不要有其他内容。"""
user_prompt = f"""用户需求:{state['user_request']}
请制定分析计划。"""
response = llm.invoke([
SystemMessage(content=system_prompt),
HumanMessage(content=user_prompt)
])
# 解析计划
try:
plan_data = json.loads(response.content)
plan = plan_data["plan"]
except:
# 如果解析失败,使用默认计划
plan = [
"1. 数据概览",
"2. 数据质量检查",
"3. 分析执行",
"4. 生成报告"
]
return {
"analysis_plan": plan,
"current_step_index": 0
}
# agent/nodes/loader.py
def loader_node(state: dict) -> dict:
"""数据加载节点:读取数据文件并提取信息"""
from agent.tools import DataTools
file_path = state.get("input_file")
if not file_path:
return {"errors": state.get("errors", []) + [{"step": "loader", "error": "未指定数据文件"}]}
result = DataTools.load_data(file_path)
if "error" in result:
return {
"errors": state.get("errors", []) + [{"step": "loader", "error": result["error"]}]
}
return {
"data_info": result["info"],
"data_sample": result["sample"],
"_dataframe": result.get("dataframe") # 临时存储
}
# agent/nodes/analyzer.py
def analyzer_node(state: dict) -> dict:
"""分析执行节点:生成并执行分析代码"""
from agent.tools import DataTools
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.3)
# 构建提示词
system_prompt = """你是一个数据分析程序员。根据数据信息和分析目标,编写Python代码进行分析。
要求:
1. 使用pandas进行数据处理
2. 使用matplotlib/seaborn生成图表
3. 将分析结果存入result字典
4. 代码要完整可运行
5. 添加必要的注释"""
user_prompt = f"""数据信息:
{json.dumps(state['data_info'], indent=2, ensure_ascii=False)}
数据预览:
{state['data_sample']}
当前分析步骤:
{state['analysis_plan'][state['current_step_index']]}
请编写分析代码。"""
response = llm.invoke([
SystemMessage(content=system_prompt),
HumanMessage(content=user_prompt)
])
generated_code = response.content
# 提取代码块
if "```python" in generated_code:
code_start = generated_code.find("```python") + 9
code_end = generated_code.find("```", code_start)
code = generated_code[code_start:code_end].strip()
elif "```" in generated_code:
code_start = generated_code.find("```") + 3
code_end = generated_code.find("```", code_start)
code = generated_code[code_start:code_end].strip()
else:
code = generated_code
# 执行代码
df = state.get("_dataframe")
execution_result = DataTools.execute_analysis_code(code, df)
return {
"generated_code": code,
"code_output": execution_result.get("output", ""),
"analysis_results": execution_result.get("result", {}),
"current_step_index": state["current_step_index"] + 1
}
# agent/nodes/reporter.py
def reporter_node(state: dict) -> dict:
"""报告生成节点:整合所有分析结果,生成最终报告"""
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.5)
system_prompt = """你是一个数据分析师,负责撰写分析报告。
报告要求:
1. 结构清晰:概述 → 数据概况 → 主要发现 → 建议
2. 突出关键发现
3. 提供可操作的建议
4. 使用Markdown格式"""
user_prompt = f"""原始需求:{state['user_request']}
数据信息:
{json.dumps(state['data_info'], indent=2, ensure_ascii=False)}
分析计划:
{json.dumps(state['analysis_plan'], indent=2, ensure_ascii=False)}
代码执行输出:
{state.get('code_output', '无')}
分析结果:
{json.dumps(state.get('analysis_results', {}), indent=2, ensure_ascii=False)}
请撰写完整的分析报告。"""
response = llm.invoke([
SystemMessage(content=system_prompt),
HumanMessage(content=user_prompt)
])
return {
"final_report": response.content
}
6.3.4 主图构建
# agent/graph.py
from langgraph.graph import StateGraph, END
from agent.state import DataAnalysisState
from agent.nodes.planner import planner_node
from agent.nodes.loader import loader_node
from agent.nodes.analyzer import analyzer_node
from agent.nodes.reporter import reporter_node
def should_continue_analysis(state: DataAnalysisState) -> str:
"""判断是否继续分析"""
# 检查错误
if state.get("errors"):
return "report" # 有错误则直接生成报告
# 检查步骤进度
current = state["current_step_index"]
total = len(state["analysis_plan"])
if current < total:
return "analyze" # 继续分析
else:
return "report" # 进入报告生成
def build_analysis_graph():
"""构建数据分析Agent图"""
# 创建图
graph = StateGraph(DataAnalysisState)
# 添加节点
graph.add_node("planner", planner_node)
graph.add_node("loader", loader_node)
graph.add_node("analyzer", analyzer_node)
graph.add_node("reporter", reporter_node)
# 设置入口
graph.set_entry_point("planner")
# 定义边
graph.add_edge("planner", "loader")
graph.add_edge("loader", "analyzer")
# 条件边:判断是否继续分析
graph.add_conditional_edges(
"analyzer",
should_continue_analysis,
{
"analyze": "analyzer", # 继续下一轮分析
"report": "reporter"
}
)
graph.add_edge("reporter", END)
# 编译
return graph.compile()
# main.py
from agent.graph import build_analysis_graph
from agent.state import DataAnalysisState
def main():
"""主入口"""
# 构建Agent
app = build_analysis_graph()
# 用户输入
user_request = input("请描述你的数据分析需求: ")
file_path = input("请输入数据文件路径: ")
# 初始状态
initial_state = {
"user_request": user_request,
"input_file": file_path,
"messages": [],
"analysis_plan": [],
"current_step_index": 0,
"data_info": {},
"data_sample": "",
"generated_code": "",
"code_output": "",
"analysis_results": {},
"chart_paths": [],
"final_report": "",
"errors": [],
"retry_count": 0,
"max_retries": 3
}
# 执行
print("\n" + "="*50)
print("开始执行数据分析...")
print("="*50 + "\n")
result = app.invoke(initial_state)
# 输出结果
print("\n" + "="*50)
print("分析报告")
print("="*50 + "\n")
print(result["final_report"])
# 保存报告
output_file = "analysis_report.md"
with open(output_file, "w", encoding="utf-8") as f:
f.write(result["final_report"])
print(f"\n报告已保存到: {output_file}")
if __name__ == "__main__":
main()
6.4 运行示例
$ python main.py
请描述你的数据分析需求: 分析销售数据,找出销售趋势和最佳产品
请输入数据文件路径: ./sales_data.csv
==================================================
开始执行数据分析...
==================================================
[规划阶段] 制定分析计划...
[加载阶段] 读取数据文件...
[分析阶段] 执行步骤 1/5...
[分析阶段] 执行步骤 2/5...
[分析阶段] 执行步骤 3/5...
==================================================
分析报告
==================================================
# 销售数据分析报告
## 一、数据概况
本次分析的数据集包含1000条销售记录,时间跨度为2023年1月至2024年6月。
数据包含以下字段:日期、产品名称、销售额、数量、客户类型...
## 二、主要发现
### 1. 销售趋势
- 整体销售额呈上升趋势,月均增长率约5.2%
- Q4为销售旺季,销售额占全年的35%
### 2. 最佳产品
- 销售额Top3产品:产品A(¥125万)、产品B(¥98万)、产品C(¥76万)
- 产品A贡献了总销售额的25%
## 三、建议
1. 加大旺季营销投入
2. 优化产品A的供应链
...
七、高级主题与优化策略
7.1 错误处理与重试机制
from typing import Literal
def robust_analyzer_node(state: DataAnalysisState) -> DataAnalysisState:
"""具备错误处理的分析节点"""
max_retries = state.get("max_retries", 3)
current_retry = state.get("retry_count", 0)
try:
# 执行分析
result = execute_analysis(state)
if result["success"]:
return {
**result,
"retry_count": 0 # 成功后重置计数器
}
else:
raise Exception(result.get("error", "Unknown error"))
except Exception as e:
# 记录错误
errors = state.get("errors", [])
errors.append({
"step": "analyzer",
"error": str(e),
"retry": current_retry
})
# 判断是否需要重试
if current_retry < max_retries:
# 生成修复后的代码
fixed_code = fix_code_with_llm(state["generated_code"], str(e))
return {
"errors": errors,
"retry_count": current_retry + 1,
"generated_code": fixed_code
}
else:
# 超过重试次数,跳过当前步骤
return {
"errors": errors,
"current_step_index": state["current_step_index"] + 1,
"retry_count": 0
}
7.2 流式输出
LangGraph支持流式执行,实时展示中间结果:
from langgraph.graph import StateGraph
app = graph.compile()
# 流式执行
for event in app.stream(initial_state):
# event是字典:{节点名: 节点输出}
for node_name, node_output in event.items():
print(f"[{node_name}] {node_output}")
7.3 可视化调试
from IPython.display import Image, display
# 生成流程图
try:
display(Image(app.get_graph().draw_mermaid_png()))
except:
# 保存为文件
graph_image = app.get_graph().draw_mermaid_png()
with open("agent_flow.png", "wb") as f:
f.write(graph_image)
7.4 性能优化
优化1:并行执行
from langgraph.graph import StateGraph
# 定义并行节点
graph.add_node("task_a", node_a)
graph.add_node("task_b", node_b)
# 从同一起点分支
graph.add_edge("start", "task_a")
graph.add_edge("start", "task_b")
# 汇聚到同一节点
graph.add_edge("task_a", "merge")
graph.add_edge("task_b", "merge")
优化2:缓存机制
from functools import lru_cache
@lru_cache(maxsize=100)
def cached_llm_call(prompt_hash: str) -> str:
"""缓存LLM调用结果"""
return llm.invoke(prompt)
优化3:Token管理
def truncate_messages(messages: list, max_tokens: int = 4000):
"""截断消息历史以控制token数"""
# 使用tiktoken计算token数
import tiktoken
encoding = tiktoken.encoding_for_model("gpt-4")
total_tokens = sum(len(encoding.encode(m.content)) for m in messages)
if total_tokens > max_tokens:
# 保留最新的消息
while total_tokens > max_tokens and len(messages) > 1:
messages.pop(0)
total_tokens = sum(len(encoding.encode(m.content)) for m in messages)
return messages
八、总结与展望
8.1 核心要点回顾
本文从理论到实践,完整介绍了LangGraph Agent开发:
- Agent本质 = LLM + 记忆 + 工具 + 规划,实现"思考-行动"循环
- LangGraph优势 = 显式状态管理 + 可控执行流程 + 循环/分支支持
- 状态设计 = 区分累加型/覆盖型字段,合理组织数据结构
- 多Agent协作 = 主从模式/对等模式,实现专业化分工
- 完整项目 = 数据分析Agent展示真实场景应用
8.2 最佳实践总结
| 维度 | 建议 |
|---|---|
| 状态设计 | 粒度适中,避免过度复杂;区分临时状态与持久状态 |
| 节点实现 | 单一职责,每个节点只做一件事;幂等性设计 |
| 错误处理 | 多层重试机制;降级策略;错误隔离 |
| 工具选择 | 优先使用成熟库;合理抽象;文档完善 |
| 调试策略 | 使用检查点机制;可视化流程图;单元测试节点 |
8.3 未来发展方向
AI Agent技术正在快速演进,值得关注的方向:
- 多模态Agent:图像、语音、视频理解与生成
- 自主学习:从执行中学习,持续优化策略
- 人机协作:人在回路(Human-in-the-loop)的混合智能
- 安全可控:输出对齐、风险评估、可解释性
附录
A. 完整项目代码
项目已开源:github.com/yourname/data-analysis-agent
B. 参考资料
C. 环境依赖
# requirements.txt
langgraph>=0.2.0
langchain>=0.3.0
langchain-openai>=0.2.0
pandas>=2.0.0
matplotlib>=3.7.0
seaborn>=0.12.0
duckduckgo-search>=4.0.0
python-dotenv>=1.0.0
更多推荐
所有评论(0)