LangGraph从新手到老师傅 - 12 - 存储功能详解

160 阅读8分钟

前言

在构建复杂的AI应用时,数据持久化和检索是至关重要的。LangGraph提供了强大的存储功能,可以帮助我们管理各种类型的数据,包括文本、向量和复杂数据结构。本文将深入探讨LangGraph的存储模块,通过实际示例展示如何有效地使用这些功能。

什么是 LangGraph 存储模块?

LangGraph的存储模块是一个灵活的数据持久化系统,专为AI应用设计。它提供了统一的接口来存储、检索和管理数据,支持:

  • 键值存储操作
  • 层次化命名空间管理
  • 向量嵌入存储与搜索
  • 自定义数据序列化
  • 支持多种存储后端

在本文中,我们将通过实用示例,学习如何使用LangGraph的存储模块实现基本的数据管理功能。

示例功能概述

我们将创建一个用户消息管理系统,展示LangGraph存储模块的核心功能:

  • 创建和管理内存存储实例
  • 向存储中添加、获取和更新项目
  • 使用命名空间组织数据
  • 处理复杂数据结构

完整代码实现

让我们先来看完整的代码实现:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""LangGraph 存储功能基础操作示例

本示例展示了 LangGraph 存储模块的基本用法,包括:
1. 创建一个基础内存存储实例
2. 向存储中添加项目(Put 操作)
3. 从存储中获取项目(Get 操作)
4. 更新存储中的项目

注意:此示例是基于 LangGraph 0.6.3 版本修改的正确实现。
"""

from langgraph.store.memory import InMemoryStore
from langgraph.store.base import Item  
import datetime

def basic_store_operations():
    """演示 LangGraph 存储的基本操作"""
    # 创建一个内存存储实例
    # InMemoryStore 是一个基于内存的存储实现
    store = InMemoryStore()
    
    # 准备要存储的项目数据
    # Item 代表一个存储的项目,包含以下必需字段:
    # - value: 存储的数据(字典形式,键可用于过滤)
    # - key: 命名空间内的唯一标识符
    # - namespace: 定义项目所属集合的层次路径(元组形式)
    # - created_at: 创建时间戳
    # - updated_at: 最后更新时间戳
    item1 = Item(
        value={"text": "这是第一条消息", "priority": "high"},
        key="message_1",
        namespace=("users", "user123", "messages"),
        created_at=datetime.datetime.now(),
        updated_at=datetime.datetime.now()
    )
    
    item2 = Item(
        value={"text": "这是第二条消息", "priority": "medium"},
        key="message_2",
        namespace=("users", "user123", "messages"),
        created_at=datetime.datetime.now(),
        updated_at=datetime.datetime.now()
    )
    
    item3 = Item(
        value={"title": "项目计划", "content": "每周会议安排"},
        key="doc_1",
        namespace=("users", "user123", "documents"),
        created_at=datetime.datetime.now(),
        updated_at=datetime.datetime.now()
    )
    
    # 执行 Put 操作,将项目添加到存储中
    store.put(key=item1.key, value=item1.value, namespace=item1.namespace)
    store.put(key=item2.key, value=item2.value, namespace=item2.namespace)
    store.put(key=item3.key, value=item3.value, namespace=item3.namespace)
    
    print("已成功添加 3 个项目到存储中")
    print()

    # 执行 Get 操作,通过命名空间和键获取特定项目
    print("获取的项目:")
    
    # 获取第一条消息
    message_1 = store.get(key='message_1', namespace=('users', 'user123', 'messages'))
    if message_1 is not None:
        print(f"- 键: message_1, 值: {message_1}")
    else:
        print("- 找不到消息: message_1")
    print()
    
    # 获取文档
    doc_1 = store.get(key='doc_1', namespace=('users', 'user123', 'documents'))
    if doc_1 is not None:
        print(f"- 键: doc_1, 值: {doc_1}")
    else:
        print("- 找不到文档: doc_1")
    print()
    
    # 执行 ListNamespaces 操作,列出存储中的命名空间
    print("尝试列出命名空间:")
    list_namespaces_result = store.list_namespaces()
    for ns in list_namespaces_result:
        print(f"- 命名空间: {ns}") 
    print()
    
    # 执行 Search 操作,搜索存储中的项目
    print("尝试搜索用户 'user123' 的消息:")
    # 尝试直接传入namespace参数
    search_result = store.search(('users', 'user123', 'messages'))
    print(f"搜索结果: {search_result} size={len(search_result)}")
    print()
    
    # 更新一个项目
    updated_item1 = Item(
        value={"text": "这是更新后的第一条消息", "priority": "high", "status": "read"},
        key="message_1",
        namespace=("users", "user123", "messages"),
        created_at=item1.created_at,  # 保留原始创建时间
        updated_at=datetime.datetime.now()  # 更新时间戳
    )
    
    # 更新一个项目
    # 直接调用put方法更新项目
    store.put(key=updated_item1.key, value=updated_item1.value, namespace=updated_item1.namespace)
    
    # 验证更新是否成功
    updated_message_1 = store.get(key='message_1', namespace=('users', 'user123', 'messages'))
    
    print("更新后的项目:")
    print(f"- 键: message_1, 值: {updated_message_1.value}")
    print(f"- 创建时间: {updated_message_1.created_at}")
    print(f"- 更新时间: {updated_message_1.updated_at}")

if __name__ == "__main__":
    basic_store_operations()

代码解析

1. 导入必要的模块

from langgraph.store.memory import InMemoryStore
from langgraph.store.base import Item
import datetime

在这个示例中,我们导入了:

  • InMemoryStore:一个基于内存的存储实现
  • Item:表示存储中的一个项目
  • datetime:用于处理时间戳

2. 创建存储实例

# 创建一个内存存储实例
store = InMemoryStore()

这行代码创建了一个InMemoryStore实例,它将数据存储在内存中。在实际应用中,你也可以使用其他存储后端,如SQLite或PostgreSQL。

3. 准备存储项目

item1 = Item(
    value={"text": "这是第一条消息", "priority": "high"},
    key="message_1",
    namespace=("users", "user123", "messages"),
    created_at=datetime.datetime.now(),
    updated_at=datetime.datetime.now()
)

我们创建了一个Item对象,它包含以下关键字段:

  • value:要存储的数据,可以是任何可序列化的字典
  • key:在命名空间内唯一的标识符
  • namespace:层次化的命名空间,用于组织数据
  • created_atupdated_at:时间戳,记录项目的创建和更新时间

4. 添加项目到存储

store.put(key=item1.key, value=item1.value, namespace=item1.namespace)
store.put(key=item2.key, value=item2.value, namespace=item2.namespace)
store.put(key=item3.key, value=item3.value, namespace=item3.namespace)

我们使用put方法将项目添加到存储中,指定了键、值和命名空间。

5. 从存储中获取项目

message_1 = store.get(key='message_1', namespace=('users', 'user123', 'messages'))

我们使用get方法从存储中检索项目,需要提供键和命名空间。

6. 更新存储中的项目

# 创建更新后的项目
updated_item1 = Item(
    value={"text": "这是更新后的第一条消息", "priority": "high", "status": "read"},
    key="message_1",
    namespace=("users", "user123", "messages"),
    created_at=item1.created_at,  # 保留原始创建时间
    updated_at=datetime.datetime.now()  # 更新时间戳
)

# 执行更新操作
store.put(key=updated_item1.key, value=updated_item1.value, namespace=updated_item1.namespace)

更新操作与添加操作类似,只需使用相同的键和命名空间,但提供新的值。

执行结果分析

执行上述代码,你会看到类似以下的输出:

已成功添加 3 个项目到存储中

获取的项目:
- 键: message_1, 值: Item(namespace=['users', 'user123', 'messages'], key='message_1', value={'text': '这是第一条消息', 'priority': 'high'}, created_at='2025-09-08T06:55:32.824577+00:00', updated_at='2025-09-08T06:55:32.824577+00:00')

- 键: doc_1, 值: Item(namespace=['users', 'user123', 'documents'], key='doc_1', value={'title': '项目计划', 'content': '每周会议安排'}, created_at='2025-09-08T06:55:32.824577+00:00', updated_at='2025-09-08T06:55:32.824577+00:00')

尝试列出命名空间:
- 命名空间: ('users', 'user123', 'documents')
- 命名空间: ('users', 'user123', 'messages')

尝试搜索用户 'user123' 的消息:
搜索结果: [Item(namespace=['users', 'user123', 'messages'], key='message_1', value={'text': '这是第一条消息', 'priority': 'high'}, created_at='2025-09-08T06:55:32.824577+00:00', updated_at='2025-09-08T06:55:32.824577+00:00', score=None), Item(namespace=['users', 'user123', 'messages'], key='message_2', value={'text': '这是第二条消息', 'priority': 'medium'}, 
created_at='2025-09-08T06:55:32.824577+00:00', updated_at='2025-09-08T06:55:32.824577+00:00', score=None)] size=2

更新后的项目:
- 键: message_1, 值: {'text': '这是更新后的第一条消息', 'priority': 'high', 'status': 'read'}
- 创建时间: 2025-09-08 06:55:32.826575+00:00
- 更新时间: 2025-09-08 06:55:32.826575+00:00

这个输出展示了存储模块的核心特性:

  • 数据持久化:数据可以被成功添加到存储中
  • 数据检索:可以通过键和命名空间检索数据
  • 数据更新:可以更新已存储的数据
  • 命名空间组织:可以使用命名空间层次结构组织数据

存储模块的优势

通过上面的示例,我们可以看到LangGraph存储模块的几个主要优势:

  1. 统一的接口:提供了一致的API来管理不同类型的数据
  2. 层次化命名空间:通过命名空间提供了结构化的数据组织方式
  3. 灵活的数据模型:支持存储各种类型的数据
  4. 多后端支持:可以根据需要选择不同的存储后端
  5. 时间戳管理:自动记录项目的创建和更新时间

优化

使用持久化存储:对于生产环境,应该使用更持久的存储,如SQLite或PostgreSQL

from langgraph.store.sqlite import SqliteStore

# 使用SQLite存储
store = SqliteStore.from_conn_string(":memory:")  # 或使用文件路径

考虑数据序列化:对于复杂的数据结构,确保正确处理序列化和反序列化

import json

# 序列化复杂数据
complex_data = {"nested": {"array": [1, 2, 3]}}
serialized_data = json.dumps(complex_data)

# 存储序列化后的数据
store.put(key='complex_data', value={'serialized': serialized_data}, namespace=('data', 'complex'))

# 获取并反序列化数据
result = store.get(key='complex_data', namespace=('data', 'complex'))
if result is not None:
 deserialized_data = json.loads(result.value['serialized'])

实际应用场景

LangGraph存储模块在很多实际应用场景中都非常有用:

  1. 用户数据管理:存储和管理用户相关的各种数据
  2. 会话历史存储:保存聊天机器人的对话历史
  3. 文档管理系统:组织和检索各种类型的文档
  4. 向量数据库:存储和搜索嵌入向量
  5. 应用状态管理:保存应用的配置和状态信息

总结

LangGraph的存储模块提供了强大而灵活的数据持久化功能,可以帮助我们构建更复杂和可靠的AI应用。在本文中,我们通过一个简单的用户消息管理系统示例,学习了如何使用InMemoryStore进行基本的数据管理操作。

在接下来的文章中,我们将深入探讨更多存储模块的高级用法。

如果你觉得这篇文章对你有帮助,请点赞、收藏并关注我,以获取更多关于LangGraph的教程和技巧!