avatar

Chen Kunpeng

Tongji University. Focusing on Trustworthy AI & Federated Learning.

【LangGraph】从零构建我的专属科研 AI Agent

写在前面:以前我使用 LLM(如 ChatGPT)时,主要是简单的“问答模式”。虽然方便,但它有两个致命弱点:没有记忆(关掉窗口就忘了上下文)与无法控制(无法按固定流程“先读文件→再搜索→最后写摘要”)。今天系统学习了 LangGraph:它更像一张“建筑图纸”,允许我用状态(State)、**循环(Loop)条件分支(Conditional Edges)**搭出可控且可扩展的智能体工作流。Github链接:https://github.com/SSSuperfriendly/LangGraph_Agent.git

0. 前言:为什么要用 LangGraph?

以前我使用 LLM 时主要是“单轮/多轮问答”。它的体验很强,但当我把它真正当作“科研助手”去长期使用时,问题会很快暴露出来:

  1. 没有记忆:关掉窗口,它就忘了我是谁,也忘了我之前的研究背景。
  2. 无法控制:我没法让它按死命令执行任务(比如“先读文件,再搜索,最后写摘要”)。

LangGraph 的价值在于:我可以把 Agent 的行为写成一个有状态的图(Graph)。与“链式调用(chain)”相比,它天然支持:

  • 循环与中断(更像在写程序,而不是写脚本)
  • 条件分支决策(让 Agent 自己选择是否调用工具)
  • 持久化记忆(让它不再“失忆”)

1. 核心概念:Agent 的“三驾马车”

在 LangGraph 中,构建一个可用的 Agent,最关键的是搞懂三个概念:

1.1 状态 (State) —— 它的“大脑”

状态就是 Agent 的共享内存。所有关键数据(对话历史、当前轮次、文件内容、工具返回)都存在这里。

  • 关键点:使用 add_messages 机制,让新的聊天记录自动追加到历史中,而不是覆盖。

1.2 节点 (Nodes) —— 它的“动作”

节点就是具体的 Python 函数(或封装后的可执行单元)。

  • 比如:一个节点负责“调用 DeepSeek 生成回复”,另一个节点负责“去 E 盘读取 PDF 文件”。
  • 每个节点接收当前状态,执行动作,然后返回更新后的状态。

1.3 边 (Edges) —— 它的“决策”

边决定了下一步走哪儿。

  • 条件边:Agent 可以自己决定是“继续对话”还是“去查资料”。
  • 循环:可以设置 ITERATION_LIMIT(或在业务逻辑中限制轮次),防止 Agent 陷入死循环。

2. 我的实战成果:DeepSeek 本地科研助手

结合教程与自己的使用场景,我在 E 盘落地了一个不会失忆、能读论文、能联网的科研助手。

2.1 环境配置(E 盘避坑指南)

为了不塞满 C 盘,我采用了“全局+本地”的配置思路:

  • 虚拟环境.venv 建立在项目根目录。
  • 缓存重定向PIP_CACHEHF_HOME 等缓存目录指向 E 盘。
  • 模型调用:通过 OpenAI 兼容接口调用 DeepSeek-V3(性价比极高)。

2.2 核心功能进化史

v1.0:简单的聊天机器人

实现了基本的对话,但一关机就“失忆”。

  • 痛点:每次都要重新介绍“我是陈鲲鹏,做图联邦学习的”。

v2.0:持久化记忆 (The Game Changer)

引入 SqliteSaver,把对话状态落盘。

  • 原理:在 E 盘生成 agent_memory.db 数据库文件。
  • 效果:即使电脑重启,只要 thread_id 不变,它依然记得我的名字和研究方向。

v3.0:科研完全体 (Tools Integration)

给 Agent 装上了“手”和“眼”。

  • PyMuPDF:可以解析本地 PDF 论文(双栏排版也能读)。
  • Arxiv API:联网搜索最新的 Graph Federated Learning 论文。
  • 本地文件读取:可以直接读取 .py 代码或 .txt 笔记。

3. 最终版代码模板(可直接改造成你的项目结构)

下面是我当前使用的 ai_agent.py 核心逻辑模板,展示了:工具集成、模型绑定工具、图结构与 SQLite 持久化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import os
import sqlite3
import fitz # PyMuPDF
import arxiv
from dotenv import load_dotenv
from langgraph.graph import StateGraph, START, END
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, SystemMessage
from langgraph.checkpoint.sqlite import SqliteSaver
from langgraph.prebuilt import ToolNode, tools_condition
from langchain_core.tools import tool

# --- 1. 定义工具 (Tools) ---
@tool
def read_pdf_paper(file_path: str) -> str:
"""读取本地 E 盘 PDF 论文"""
# ... (省略具体实现,使用 fitz 读取)
return "PDF内容..."

@tool
def search_arxiv(query: str) -> str:
"""联网搜索 Arxiv"""
# ... (省略具体实现,使用 arxiv 库)
return "论文列表..."

tools = [read_pdf_paper, search_arxiv]

# --- 2. 配置模型 (DeepSeek) ---
load_dotenv()
model = ChatOpenAI(
model='deepseek-chat',
openai_api_key=os.getenv("DEEPSEEK_API_KEY"),
openai_api_base=os.getenv("DEEPSEEK_BASE_URL")
).bind_tools(tools)

# --- 3. 构建图 (Graph) ---
builder = StateGraph(State) # State 定义略
builder.add_node("chatbot", chatbot_node)
builder.add_node("tools", ToolNode(tools))

builder.add_edge(START, "chatbot")
# 核心:让模型自动判断是否调用工具
builder.add_conditional_edges("chatbot", tools_condition)
builder.add_edge("tools", "chatbot")

# --- 4. 持久化运行 ---
if __name__ == "__main__":
conn = sqlite3.connect("E:/LangGraph_Project/agent_memory.db", check_same_thread=False)
memory = SqliteSaver(conn)
graph = builder.compile(checkpointer=memory)

# 只要这个 ID 不变,记忆就在
config = {"configurable": {"thread_id": "kunpeng_research_main"}}

# ... (进入对话循环)

4. 心得与展望

LangGraph 的核心优势:相比于传统的链式调用,LangGraph 更像是在写程序而不是写脚本。由于支持循环、条件分支与状态持久化,它非常适合处理长周期任务(例如辅助完成一篇论文从调研到写作的全过程)。

下一步计划

  1. 人机协作:在 Agent 发出 Arxiv 搜索请求前增加“人工确认”(interrupt 机制),防止它搜错方向。
  2. 多智能体:尝试搞一个“写手 Agent”和一个“审核 Agent”,左手写代码,右手改 Bug。

本文基于云朵君《使用 LangGraph 构建 AI Agent》一文及个人实战总结。