🎯 课程主题
将上一节 InMemoryStore 的长期记忆方案切换到 PostgresStore,实现数据的持久化存储,并通过数据库客户端验证 Store 的底层表结构。
📝 核心知识点
1. InMemoryStore → PostgresStore 的切换要点
整体代码逻辑几乎不变,核心变更集中在 Store 的初始化部分:
| 变更项 | InMemoryStore | PostgresStore |
|---|---|---|
| 导入 | from langgraph.store.memory import InMemoryStore | from langgraph.store.postgres import PostgresStore |
| 初始化 | store = InMemoryStore() | with PostgresStore.from_conn_string(DB_URL) as store: |
| 建表 | 无需 | store.setup()(首次自动创建表) |
| 生命周期 | 手动管理 | 使用 with 上下文管理器 |
| 数据持久化 | 进程重启后丢失 | 持久化到 PostgreSQL |
2. PostgresStore 的数据库表
首次 setup() 会在数据库中创建两张 Store 相关的表:
-- 查看所有表
\dt
-- 预期比纯 State 方案多出两张表:
-- store — 长期记忆数据主表
-- store_vectors — 语义搜索向量索引表(启用了 embedding 时)
3. 数据验证
可直接通过 PostgreSQL 客户端连接 langchadb 数据库,使用 SQL 或 \dt 命令查看表结构,确认长期记忆数据已正确持久化。
🏗️ 架构与工作流
PostgresStore.from_conn_string(DB_URL)
│
▼
store.setup() ──▶ 创建 store / store_vectors 表
│
▼
create_agent(model, tools, store, state_schema)
│
▼
agent.invoke({"messages": [...], "user_id": "user-1"})
│
▼
工具中 runtime.store.put() / .get()
│
▼
PostgreSQL (langchadb) ──▶ 数据持久化
与 InMemoryStore 方案的工作流完全一致,仅底层存储介质从内存切换到 PostgreSQL。
💻 代码实战
完整代码(PostgresStore 版)
from langgraph.agent import create_agent
from langgraph.store.postgres import PostgresStore
from langgraph.prebuilt.tool_runtime import ToolRuntime
from typing import NotRequired
from langgraph.graph.state import AgentState
# 1. 初始化模型
from langchain.chat_models import init_chat_model
model = init_chat_model(model="gpt-4o-mini")
# 2. 数据库连接地址
DB_URL = "postgresql://postgres:123456@localhost:5432/langchadb"
# 3. 使用上下文管理器初始化 PostgresStore
with PostgresStore.from_conn_string(DB_URL) as store:
store.setup() # 首次运行创建相关表
# 4. 自定义 AgentState
class CustomAgentState(AgentState):
user_id: NotRequired[str]
# 5. 定义工具(与 InMemoryStore 版完全相同)
def save_user_info(name: str, runtime: ToolRuntime) -> str:
"""将客户信息保存在长期记忆中。"""
user_id = runtime.state["user_id"]
runtime.store.put(
namespace=("users",),
key=user_id,
value={"name": name}
)
return "saved"
def get_user_info(runtime: ToolRuntime) -> str:
"""从长期记忆中读取客户信息。"""
user_id = runtime.state["user_id"]
item = runtime.store.get(namespace=("users",), key=user_id)
if item:
return str(item.value)
return "INFO NOT FOUND"
# 6. 系统提示词
SYSTEM_PROMPT = """
用户提及个人信息时,使用工具保存用户信息。
如果用户询问个人信息时,尝试使用工具检索/读取用户信息。
"""
# 7. 创建 Agent
agent = create_agent(
model=model,
tools=[save_user_info, get_user_info],
store=store,
system_prompt=SYSTEM_PROMPT,
state_schema=CustomAgentState
)
# 8. 第一次会话:存储信息
response = agent.invoke({
"messages": [{"role": "user", "content": "你好,我是小花"}],
"user_id": "user-1"
})
print(response["messages"][-1].content)
# "很高兴认识你,小花!"
# 9. 第二次会话:读取信息
response = agent.invoke({
"messages": [{"role": "user", "content": "我是谁?"}],
"user_id": "user-1"
})
print(response["messages"][-1].content)
# "你是小花。"
数据库验证
# 连接到 langchadb
psql -h localhost -U postgres -d langchadb
# 查看所有表
\dt
# 预期输出:
# Schema | Name | Type | Owner
# --------+---------------+-------+----------
# public | checkpoints | table | postgres ← State 快照
# public | checkpoint_...| table | postgres
# public | checkpoint_...| table | postgres
# public | checkpoint_...| table | postgres
# public | store | table | postgres ← 长期记忆数据
# public | store_vectors | table | postgres ← 语义搜索向量
⚠️ 常见问题与避坑指南
- 必须使用
with上下文管理器:PostgresStore.from_conn_string()返回的是上下文管理器对象,需用with ... as store:确保连接正确关闭。 - setup() 只需调用一次:首次运行创建表结构,后续运行不会重复创建;但多次调用无副作用。
- PostgreSQL 服务必须运行:确保数据库服务处于开启状态,否则连接失败。
- DB_URL 格式:
postgresql://用户名:密码@主机:端口/数据库名,注意 URL 编码特殊字符。 store对象需传入create_agent():与 InMemoryStore 不同,PostgresStore 的store在with块内有效,确保create_agent()和agent.invoke()都在with块内执行。- 每次
invoke仍需传user_id:尽管 Store 是持久化的,但 State(包括 user_id)在每次 invoke 中仍需显式传入。
💡 个人总结与延伸
- 从 InMemoryStore 切换到 PostgresStore 几乎只需要改动 Store 的初始化部分,其余代码(工具定义、Agent 创建、invoke 调用)完全一致 — 体现了 LangGraph Store 接口的良好抽象。
- PostgresStore 配合
store.setup()自动管理表结构,开发者无需手写 DDL。 - 生产环境中建议使用 PostgresStore 确保长期记忆数据不丢失;开发/测试阶段用 InMemoryStore 快速迭代。
- 结合
search()+ 语义搜索时,PostgresStore 下的向量索引表(store_vectors)将由框架自动维护。