LlamaIndex学习2:会话状态管理与持久化

LlamaIndex会话状态管理与持久化

在上一篇文章中介绍了LlamaIndex与不同API的基本调用方式,这篇文章将探讨LlamaIndex的会话状态管理功能,这是构建具有记忆能力的AI应用的关键部分。

1. 状态管理的重要性

在与LLM交互过程中,保持对话上下文至关重要。如果没有状态管理:

  • 用户每次提问都变成独立对话
  • 模型无法记住之前的信息
  • 无法构建连贯的交互体验

LlamaIndex提供了优雅的状态管理和持久化方案,使模型能够”记住”之前的对话内容。

2. 基本状态管理示例

下面是一个基本的状态管理示例,展示了如何使用LlamaIndex的ContextAgentWorkflow实现对话记忆:

from dotenv import load_dotenv
load_dotenv()
import os
from llama_index.llms.openrouter import OpenRouter
from llama_index.core.agent.workflow import AgentWorkflow
from llama_index.core.workflow import Context
from llama_index.core.workflow import JsonSerializer


# 初始化LLM
llm = OpenRouter(
    model="google/gemma-3-27b-it:free",
    api_key=os.getenv("OPEN_ROUTER_API_KEY"),
)

# 创建工作流
workflow = AgentWorkflow.from_tools_or_functions(
    tools_or_functions=[],
    llm=llm,
    system_prompt="你是一个智能助手,请热情地回答用户的问题"
)

# 创建上下文对象
ctx = Context(workflow)

3. 状态持久化的实现

LlamaIndex的状态管理核心是Context对象,它可以:

  1. 跟踪对话历史
  2. 保存用户信息
  3. 维护工具调用记录

以下示例展示如何实现状态的序列化和反序列化:

async def main():
    # 第一轮对话
    response = await workflow.run(user_msg="Hi, my name is 张三!", ctx=ctx)
    print("*****************第一次对话**************************")
    print('用户:Hi, my name is 张三!')
    print('AI:',response)
    
    # 第二轮对话 - 使用同一上下文
    response2 = await workflow.run(user_msg="What's my name?", ctx=ctx)
    print("*****************第二次对话**************************")
    print('用户:What\'s my name?')
    print('AI:',response2)
    
    # 状态序列化 - 将上下文转换为字典
    ctx_dict = ctx.to_dict(serializer=JsonSerializer())
    
    # 状态反序列化 - 从字典恢复上下文
    restored_ctx = Context.from_dict(
        workflow, ctx_dict, serializer=JsonSerializer()
    )
    
    # 使用恢复的上下文继续对话
    response3 = await workflow.run(user_msg="What's my name?", ctx=restored_ctx)
    print("*****************第三次对话**************************")
    print('用户:What\'s my name?')
    print('AI:',response3)

运行结果:

*****************第一次对话**************************
用户:Hi, my name is 张三!
AI: 你好,张三!很高兴认识你!(Hello, Zhang San! Nice to meet you!)
*****************第二次对话**************************
用户:What's my name?
AI: 你的名字是张三。(Your name is Zhang San.)
*****************第三次对话**************************
用户:What's my name?
AI: 你的名字是张三。(Your name is Zhang San.)

代码解释:

 # 状态序列化 - 将上下文转换为字典
ctx_dict = ctx.to_dict(serializer=JsonSerializer())
    
# 状态反序列化 - 从字典恢复上下文
restored_ctx = Context.from_dict(
    workflow, ctx_dict, serializer=JsonSerializer()
)

这一步的作用是实现对话状态的序列化和反序列化,用于保存和恢复会话上下文。具体来说:

  • 首先将Context对象(ctx)序列化成字典格式(ctx_dict),使用JsonSerializer()作为序列化器
  • 然后再从这个字典重新创建一个新的Context对象(restored_ctx)

这种机制允许将对话的状态(包括历史会话、用户信息等)转换为可持久化的格式,以便可以:

  • 将状态存储到数据库或文件系统
  • 在服务重启后恢复上下文
  • 在分布式系统中传递状态
  • 跨会话维持用户对话连续性

在这个示例中,通过response3的调用可以验证状态是否成功保存和恢复,因为如果恢复成功,LLM应该能记住用户名是”张三”。

4. 状态持久化的实际应用

这种状态序列化机制可以应用于多种场景:

4.1 数据库存储

# 保存状态到数据库
def save_context_to_db(user_id, ctx):
    ctx_dict = ctx.to_dict(serializer=JsonSerializer())
    db.save_context(user_id, ctx_dict)

# 从数据库恢复状态
def load_context_from_db(user_id, workflow):
    ctx_dict = db.load_context(user_id)
    if ctx_dict:
        return Context.from_dict(workflow, ctx_dict, serializer=JsonSerializer())
    return Context(workflow)

4.2 文件系统存储

# 保存状态到文件
def save_context_to_file(file_path, ctx):
    """
    将会话上下文保存到文件
    
    Args:
        file_path: 保存的文件路径
        ctx: Context对象
    """
    ctx_dict = ctx.to_dict(serializer=JsonSerializer())
    with open(file_path, 'w', encoding='utf-8') as f:
        json.dump(ctx_dict, f, ensure_ascii=False, indent=2)

# 从文件恢复状态
def load_context_from_file(file_path, workflow):
    """
    从文件加载会话上下文
    
    Args:
        file_path: 文件路径
        workflow: 工作流对象
        
    Returns:
        恢复的Context对象,如果文件不存在则返回新的Context
    """
    try:
        with open(file_path, 'r', encoding='utf-8') as f:
            ctx_dict = json.load(f)
        return Context.from_dict(workflow, ctx_dict, serializer=JsonSerializer())
    except FileNotFoundError:
        return Context(workflow)

这里进行本地文件保存上下文进行测试,修改上面main函数:

# 第一轮对话
response = await workflow.run(user_msg="Hi, my name is 张三!", ctx=ctx)
print("*****************第一次对话**************************")
print('用户:Hi, my name is 张三!')
print('AI:',response)

# 状态序列化 - 将上下文转换为字典
ctx_dict = ctx.to_dict(serializer=JsonSerializer())
# 保存会话状态到文件
save_context_to_file("conversation_state.json", ctx)
print("会话状态已保存到 conversation_state.json")

先运行第一遍,这时候会把序列化之后的ctx保存到conversation_state.json中,以下是执行log:

*****************第一次对话**************************
用户:Hi, my name is 张三!
AI:
Thought: The current language of the user is: Chinese. I need to acknowledge the user's greeting and introduction.
Action: None
Answer: 你好,张三!很高兴认识你!(Hello, Zhang San! Nice to meet you!)
会话状态已保存到 conversation_state.json

然后再将main函数改为:

# 先直接从缓存文件加载上下文
restored_ctx = load_context_from_file("conversation_state.json", workflow)
print("已从文件加载会话状态")

response = await workflow.run(user_msg="What's my name?",ctx=restored_ctx)
print("*****************第一次对话**************************")
print('用户:What\'s my name?')
print('AI:',response)

打印log

已从文件加载会话状态
*****************第一次对话**************************
用户:What's my name?
AI:
Thought: The current language of the user is: Chinese. The user is asking me what their name is. I should recall the information they provided earlier.    
Action: None
Answer: 你的名字是张三。(Your name is Zhang San.)

5.总结

LlamaIndex的状态管理机制为构建具有记忆能力的AI应用提供了强大支持:

  1. Context对象管理对话历史和状态
  2. 序列化/反序列化功能支持状态持久化
  3. 可以轻松集成到各种存储系统

通过这些功能,我们可以开发出能够记住用户偏好、历史交互和上下文信息的智能应用,大大提升用户体验和应用智能程度。