这个操作指南将带你一步步完成 PostgreSQL 的安装与配置,并最终整合到 LangGraph 中,让你的 Agent 拥有基于数据库的、可靠的"长期记忆"。
💻 准备工作
开始前,请确认你的电脑满足以下条件:
- 操作系统:Windows 10/11。
- 环境:已安装 Python 3.9 或更高版本。
- 工具:拥有管理员权限,并且可以访问互联网以下载软件。
🗄️ 第一步:安装与配置 PostgreSQL
我们从官网下载并安装 PostgreSQL。
- 下载安装包 :访问 PostgreSQL 官网,下载适合你系统的 Windows 版本(推荐最新版本)。下载的文件通常是一个名为
postgresql-xx.x-windows-x64.exe的安装程序。 - 运行安装程序 :双击运行安装程序,按照向导的指引进行安装。在安装过程中,请务必记住你设置的数据库超级用户密码。
- 选择组件和目录 :
- 选择组件:按默认设置安装即可,但推荐将"Command Line Tools"、"PostgreSQL Server"和"pgAdmin 4"全部安装。
- 选择数据目录 :尽量选择一个非系统盘(如
D:\PostgreSQL\xx\data)来存放数据,这样可以避免系统盘空间不足的问题。 - 完成安装 :安装结束后,PostgreSQL 服务通常会自动启动并设为开机自启。
- 配置环境变量 :为了让命令行能识别
psql等命令,需要将 PostgreSQL 的bin目录(例如C:\Program Files\PostgreSQL\xx\bin)添加到系统的PATH环境变量中。 - 验证安装 :打开命令提示符,输入
psql -V。如果正确返回版本信息,则环境变量配置成功。
🗄️ 第二步:创建数据库与用户
这一步是为我们的 LangGraph 应用在数据库中创建"专属空间"。
-
打开
psql命令行:在开始菜单找到 "SQL Shell (psql)",并运行它。 -
连接到服务器:在提示符下,依次输入你在安装时设置的密码等信息,端口保持默认(通常是5432)。
-
创建数据库用户 :
sqlCREATE USER langgraph_user WITH PASSWORD 'your_strong_password'; -
创建数据库 :
sqlCREATE DATABASE langgraph_db; -
授予权限 :将新数据库的所有权限授予刚刚创建的用户。
sqlGRANT ALL PRIVILEGES ON DATABASE langgraph_db TO langgraph_user;
现在,我们的数据库和用户已经准备好了。你可以使用 \q 命令退出 psql。
🐍 第三步:配置 LangGraph 开发环境
现在切换到 Python 环境,安装 LangGraph 与 PostgreSQL 集成所需要的库。
-
激活虚拟环境:打开你的项目终端,并激活之前课程中使用的虚拟环境。
-
安装依赖包 :
bashpip install langgraph langgraph-checkpoint-postgres psycopg[binary,pool] langchain-openai python-dotenvlanggraph-checkpoint-postgres:实现 LangGraph 与 PostgreSQL 连接的关键库。psycopg[binary,pool]:Python 连接 PostgreSQL 的驱动。langchain-openai和python-dotenv:用于配置和使用我们熟悉的 Qwen 模型。
🚀 第四步:编写 LangGraph 代码
接下来,我们用代码将这一切串联起来。
-
配置连接 :在
.env文件中,存放数据库连接字符串(Database URI)。DATABASE_URI=postgresql://langgraph_user:your_strong_password@localhost:5432/langgraph_db这个URI的格式是:
postgresql://用户名:密码@地址:端口/数据库名。具体说明如下:- postgresql://:固定协议头,用于连接 PostgreSQL 数据库。
langgraph_user:your_strong_password:替换成你之前创建的用户名和密码。@localhost:5432:指定数据库服务器地址。这里是连接本机的数据库服务,5432是 PostgreSQL 的默认端口。/langgraph_db:指定要连接的数据库名称。?sslmode=disable:一个常见参数,表示禁用 SSL 加密。
-
编写 Agent 代码:创建一个新的 Python 文件,然后将下面的完整代码复制进去。
pythonimport os from dotenv import load_dotenv from langchain_openai import ChatOpenAI from langgraph.graph import StateGraph, MessagesState, START from langgraph.checkpoint.postgres import PostgresSaver load_dotenv() # 1. 获取数据库连接字符串 DB_URI = os.getenv("DATABASE_URI") # 2. 初始化 LLM (以千问为例) llm = ChatOpenAI( model="qwen-plus", temperature=0.7, api_key=os.getenv("DASHSCOPE_API_KEY"), base_url=os.getenv("DASHSCOPE_BASE_URL"), model_kwargs={"extra_body": {"enable_thinking": False}} ) # 3. 定义 Agent 节点 def call_agent(state: MessagesState): response = llm.invoke(state["messages"]) return {"messages": [response]} # 4. 集成 PostgreSQL Checkpointer print("正在连接 PostgreSQL 数据库...") with PostgresSaver.from_conn_string(DB_URI) as checkpointer: # *首次运行时,必须调用 setup() 以在数据库中创建 LangGraph 所需的表* checkpointer.setup() print("数据库检查点(Checkpointer)初始化完成。") builder = StateGraph(MessagesState) builder.add_node("call_agent", call_agent) builder.add_edge(START, "call_agent") # 编译 Graph 时,将 checkpointer 传入 graph = builder.compile(checkpointer=checkpointer) thread_id = "user_12345" config = {"configurable": {"thread_id": thread_id}} # 第一轮对话 user_input_1 = "你好,我叫虎哥,我最喜欢编程。" print(f"\n用户: {user_input_1}") for event in graph.stream( {"messages": [("user", user_input_1)]}, config=config, stream_mode="values" ): event["messages"][-1].pretty_print() # 第二轮对话(检验记忆) user_input_2 = "你还记得我叫什么名字吗?" print(f"\n用户: {user_input_2}") for event in graph.stream( {"messages": [("user", user_input_2)]}, config=config, stream_mode="values" ): event["messages"][-1].pretty_print() print("\n程序执行完毕,状态已存入 PostgreSQL。")
▶️ 第五步:运行与验证
- 运行代码 :在终端执行
python your_script_name.py。 - 查看结果:你应该能看到 Agent 成功地"记住"了你的名字。
- 验证数据库 :你可以用
pgAdmin或psql连接到数据库langgraph_db,查看langgraph_checkpoints等表里是否新增了记录,这证实了状态已被持久化。
🩺 第六步:常见问题排查
如果在运行中遇到问题,可以参考下面的排查步骤:
| 问题现象 | 可能原因与解决方法 |
|---|---|
ModuleNotFoundError: No module named 'langgraph.checkpoint.postgres' |
LangGraph 的 PostgreSQL 功能需要单独安装,请在终端执行 pip install langgraph-checkpoint-postgres 命令。 |
psycopg.OperationalError: FATAL: password authentication failed for user ... |
代码里的数据库密码和创建时输入的不一致,请仔细核对。 |
psycopg.OperationalError: could not connect to server: ... |
表示没有找到数据库服务,请检查本地 PostgreSQL 服务是否已启动。 |
| 代码运行后,数据库里没有新表? | 在代码中,checkpointer.setup() 至关重要,这是 LangGraph 在数据库中自动创建表的指令,请确认这行代码已被执行。 |
💎 总结与面试准备
通过以上步骤,你的 LangGraph Agent 已经拥有了一个由 PostgreSQL 支撑的、牢靠的"记忆"。这不仅是技术的实践,也是面试中展示你工程能力的重要砝码。
| 面试问题 | 核心回答要点 |
|---|---|
| LangGraph 的 PostgresSaver 在生产环境有何优势? | PostgresSaver 通过数据库实现状态持久化、并发支持和故障恢复 。相比开发的 InMemorySaver,它能保证在服务重启后不丢失对话状态,是企业级部署的基础。 |
checkpointer.setup() 的作用是什么? |
setup() 方法会在 PostgreSQL 数据库中自动创建所需的系统表 (如 checkpoints 表),初始化持久化环境。 |
| 如何为不同的用户隔离会话 (session)? | 通过在调用 graph.invoke 时,为 config 参数中的 thread_id 提供唯一标识符 。LangGraph 会使用该 thread_id 来区分和存取不同用户的状态。 |
| PostgreSQL 在 LangGraph 中,核心是作为生产环境的状态持久化引擎,用来解决像 SQLite 这样的本地存储方案在生产环境中难以胜任的问题。 |
简单来说,PostgreSQL 承担了三大核心职责:
- 状态持久化(Checkpoint):存储 LangGraph 在执行过程中的所有状态快照,是实现长对话、任务中断恢复和"时间旅行"调试的关键。
- 长期记忆存储(Store):以结构化的方式,存储 Agent 跨会话的长期记忆,如用户画像或知识。
- 语义向量存储 :通过扩展
pgvector,在数据库中直接存储和检索向量数据,简化了系统架构。
🆚 为什么是 PostgreSQL?它与 SQLite 有何不同?
| 对比维度 | ✅ PostgreSQL (生产环境首选) | ⚠️ SQLite (学习/原型首选) |
|---|---|---|
| 定位 | 生产级数据库管理系统 | 轻量级嵌入式数据库 |
| 并发性能 | 高。支持高并发读写,多服务实例可同时连接。 | 低。写入操作是全局锁定的,不适合高并发场景。 |
| 数据规模 | 大。单表可存储海量数据,一个 checkpoint 字段支持最大 1GB。 |
小。数据库文件大小通常受限,不适合存储大量对话历史。 |
| 可靠性 | 高。具备完善的 ACID 事务、故障恢复等企业级特性。 | 基础。可靠性依赖于文件系统。 |
| 扩展性 | 强。支持复杂查询、自定义类型、pgvector 扩展等。 |
弱。功能相对基础。 |
| 部署与运维 | 需要独立安装、配置和管理数据库服务。 | 无需安装,直接通过文件使用,运维成本为零。 |
| 学习成本 | 较高。需要掌握 SQL 基础和数据库管理知识。 | 极低。 |
🛠️ LangGraph 与 PostgreSQL 集成实战
在 LangGraph 中集成 PostgreSQL 主要有两种方式:使用 Checkpointer 和 Store。
1. 集成 Checkpointer(状态检查点)
这是最基础也是最重要的应用,用于保存工作流的状态。
-
安装依赖:
bashpip install langgraph langgraph-checkpoint-postgres psycopg[binary,pool] -
代码示例:
pythonfrom langgraph.checkpoint.postgres import PostgresSaver from psycopg_pool import ConnectionPool # PostgreSQL 数据库连接字符串 DB_URI = "postgresql://user:pass@host:5432/langgraph_db?sslmode=require" # 创建连接池(生产环境最佳实践) pool = ConnectionPool(conninfo=DB_URI, max_size=10) # 创建并初始化检查点存储器 checkpointer = PostgresSaver(pool) # 注意:首次使用时需要调用 checkpointer.setup() 创建数据表 checkpointer.setup() # 假设你已经构建好了图(builder),编译时注入 checkpointer graph = builder.compile(checkpointer=checkpointer) # 创建线程配置,thread_id 是会话的唯一标识 config = {"configurable": {"thread_id": "user_123"}} # 第一次执行,状态会被保存到 PostgreSQL # graph.invoke(initial_input, config=config) # 下一次执行时,使用相同的 thread_id 即可自动从 PostgreSQL 恢复状态 # graph.invoke({"messages": [HumanMessage(content="继续刚刚的话题")]}, config=config)
2. 集成 Store(长期记忆)
Store 用于保存和管理 Agent 的跨会话长期记忆。
-
安装依赖:
bashpip install langgraph langgraph-store-postgres psycopg[binary,pool] -
代码示例:
pythonfrom langgraph.store.postgres import PostgresStore store = PostgresStore.from_conn_string(DB_URI) store.setup() # 首次使用时需要创建数据表 # 在图的编译或运行时,将 store 对象注入 graph = builder.compile(store=store)
🔗 图解:PostgreSQL 如何与 LangGraph 的 Checkpointer 协同工作
为了帮助你理解整个流程,下图展示了当用户与 LangGraph 驱动的 Agent 交互时,PostgreSQL 在其中扮演的角色:
PostgreSQL LangGraph 应用 User PostgreSQL LangGraph 应用 User #mermaid-svg-2DCLG5FKG09tCFea{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-2DCLG5FKG09tCFea .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-2DCLG5FKG09tCFea .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-2DCLG5FKG09tCFea .error-icon{fill:#552222;}#mermaid-svg-2DCLG5FKG09tCFea .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-2DCLG5FKG09tCFea .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-2DCLG5FKG09tCFea .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-2DCLG5FKG09tCFea .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-2DCLG5FKG09tCFea .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-2DCLG5FKG09tCFea .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-2DCLG5FKG09tCFea .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-2DCLG5FKG09tCFea .marker{fill:#333333;stroke:#333333;}#mermaid-svg-2DCLG5FKG09tCFea .marker.cross{stroke:#333333;}#mermaid-svg-2DCLG5FKG09tCFea svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-2DCLG5FKG09tCFea p{margin:0;}#mermaid-svg-2DCLG5FKG09tCFea .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-2DCLG5FKG09tCFea text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-2DCLG5FKG09tCFea .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-2DCLG5FKG09tCFea .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-2DCLG5FKG09tCFea .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-2DCLG5FKG09tCFea .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-2DCLG5FKG09tCFea #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-2DCLG5FKG09tCFea .sequenceNumber{fill:white;}#mermaid-svg-2DCLG5FKG09tCFea #sequencenumber{fill:#333;}#mermaid-svg-2DCLG5FKG09tCFea #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-2DCLG5FKG09tCFea .messageText{fill:#333;stroke:none;}#mermaid-svg-2DCLG5FKG09tCFea .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-2DCLG5FKG09tCFea .labelText,#mermaid-svg-2DCLG5FKG09tCFea .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-2DCLG5FKG09tCFea .loopText,#mermaid-svg-2DCLG5FKG09tCFea .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-2DCLG5FKG09tCFea .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-2DCLG5FKG09tCFea .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-2DCLG5FKG09tCFea .noteText,#mermaid-svg-2DCLG5FKG09tCFea .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-2DCLG5FKG09tCFea .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-2DCLG5FKG09tCFea .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-2DCLG5FKG09tCFea .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-2DCLG5FKG09tCFea .actorPopupMenu{position:absolute;}#mermaid-svg-2DCLG5FKG09tCFea .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-2DCLG5FKG09tCFea .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-2DCLG5FKG09tCFea .actor-man circle,#mermaid-svg-2DCLG5FKG09tCFea line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-2DCLG5FKG09tCFea :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 状态: {"messages": ...} 1. 发起新对话 (thread_id="user_123") 创建初始状态 2. 存储状态快照 (insert checkpoint) 3. 继续对话 (相同 thread_id) 4. 查询最新状态 (select * ...) 5. 返回历史状态 6. 基于历史状态继续执行 7. 存储新的状态快照 (update checkpoint)
- 用户发起新对话,
LangGraph应用会为该线程创建初始状态。 - 这个初始状态会作为一个
checkpoint存入PostgreSQL数据库。 - 用户稍后继续对话。
LangGraph应用会带着唯一的thread_id去PostgreSQL查询。PostgreSQL找到该线程的历史状态并返回。LangGraph应用得以在上次的"记忆"基础上继续对话。- 执行完新一轮的操作后,最新的状态会再次存入
PostgreSQL,覆盖或新增checkpoint。
💎 面试知识点总结
| 面试问题 | 核心回答要点 |
|---|---|
| 你理解LangGraph的Checkpointer吗?它和Memory的关系是什么? | Checkpointer 是LangGraph的核心概念,用于持久化整个图的状态 。它和 Memory(如ConversationBufferMemory)不是二选一的关系,而是互补关系 。Memory是图中状态(State)的一部分,Checkpointer是负责把整个State保存下来的机制。 |
| PostgreSQL在LangGraph中具体扮演什么角色? | PostgreSQL是一种高性能、可扩展的Checkpointer后端存储 。它将LangGraph的状态数据持久化到磁盘,确保了即使在应用重启或发生故障后,Agent的对话历史和状态也能完整恢复,是实现生产级应用的关键。 |
| PostgreSQL相比SQLite有哪些优势? | SQLite适合本地开发和测试,而PostgreSQL是生产环境的首选 。它支持高并发 访问,数据存储容量更大 ,并提供更强大的数据一致性和故障恢复 能力,还能通过pgvector等扩展支持向量存储。 |
| 如何为LangGraph选择Memory后端(PostgreSQL vs. SQLite)? | 开发/学习/小规模单机部署 :SQLite最简单。生产/高并发/多服务实例/大规模数据:必须选择PostgreSQL以保障性能和可靠性。 |