用FunctionCall实现文件解析(八):构建跨页面记忆模块

前言

既然我们在之前的文章中实现了调用大模型获取答案的过程,接下来我们也将多个页面整合起来,最终实现一个总览的效果。因此,本文通过每个页面维护一个历史记录字典,则可以实现跨页面记忆。

在全局字典上建立局部字典

streamlit里面,全局字典就是st.session_state。这个全局字典每个页面都可以灵活访问。也正因如此,局部字典的建立也就有了依托。

我们先建立一个页面:

1
2
3
4
5
6
7
8
9
import streamlit as st
st.session_state["dashscope"] = {
"messages": [
{
"role": "system",
"content": "You are a helpful assistant."
}
]
}

看上去不错。但考虑到用户在这个页面上多次对话的时候,循环又循环地执行的时候,往往会重新给dashscope这个字典赋值,导致每次对话都是第一次。我们可以再加一点鲁棒:

1
2
3
4
5
6
7
8
9
if "dashscope" not in st.session_state:
st.session_state["dashscope"] = {}
if "messages" not in st.session_state["dashscope"]:
st.session_state["dashscope"]["messages"] = [
{
"role": "system",
"content": "You are a helpful assistant."
}
]

看上去更不错了。

多页面兼容

当然,光这样是不行的,直接复制这段代码到另外一个页面也是不行的。因为这样的话,其他页面将直接访问到上一个页面中的历史记录。

一打开页面:这TM给我干哪儿来了?

如果你爱收拾,代码文件都分类整理好,那就更好办了。通过文件夹的名称与页面文件的名称,可以决定一个页面的代码文件。毕竟,操作系统中就不允许有两个重名(带后缀)的文件。所以呢,我们借助一点Python给我们留下来的魔法__file__

就好比说,我在tongyi文件加下建立了一个文件名叫test.py,那么我们就可以借助Path这样做:

1
2
3
4
5
6
7
8
9
10
11
12
from pathlib import Path
parent_path = Path(__file__).parent.name
current_path = Path(__file__).stem
if parent_path not in st.session_state:
st.session_state[parent_path] = {}
if current_path not in st.session_state[parent_path]:
st.session_state[parent_path] = {
current_path: [
"role": "system",
"content": "you are a helpful assistant."
]
}

P.S.:需要注意的是,如果你只使用Path(__file__).parent,那么你获取到的并不是文件夹的名字,而是文件夹的绝对路径

看上去完善了。

但是每个页面整这么大一段似乎也有点怪怪的。

但很可惜的是,streamlit本身并没有什么自动注入机制。所以,我们能做的就是尽可能保证文件存放位置的规范性,然后用统一的代码调度:

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
from pathlib import Path
from typing import Optional
def init_page_state(
path: str | Path = __file__,
default_welcome_message: Optional[str | None] = None
):
p = Path(path)
parent_path, current_path = (
p.parent.name, p.stem
)
PAGE_KEY = (parent_path, current_path)
if parent_path not in st.session_state:
st.session_state[parent_path] = {}
if current_path not in st.session_state[parent_path]:
st.session_state[parent_path] = {
current_path: [
"role": "system",
"content": (
default_welcome_message
or
"you are a helpful assistant."
)
]
}
return PAGE_KEY

P.S.:这里返回PAGE_KEY主要是为了之前定义的ChatRender类用于前端的对话历史渲染。

看上去没问题。

页面应用

到这一步基本就完成了。每一页都可以写上两句话就可以了:

1
2
from utils import init_page_state
init_page_state(path = __file__)

或者说你为了表现出不一样的内容,可以这么做:

1
2
3
4
5
from utils import init_page_state
init_page_state(
path = __file__,
default_welcome_message = "你是一个,一个,一个,智能体啊啊啊啊啊啊啊啊啊啊啊啊"
)

目前看来还没有更简单的方案。