山东大学软件学院项目实训-创新实训-大数据租房推荐智能体-前端部分(4)
上一阶段我们搞定了“富媒体卡片”,让 AI 的回复不再枯燥。但在测试过程中,我遇到了一个让人抓狂的问题:只要浏览器刷新,之前的几十条聊天记录和筛选过的房源就全部消失了。
为了解决这个问题,也是为了满足任务书 4.1 节“对话历史 ≥50 条” 的硬性指标,这一阶段我投入了大量精力重构了状态管理模块。目标很明确:实现“刷新不丢历史”,并且在架构上确立“后端为数据权威”的原则,前端仅作为展示层和快照提交者。
1. 架构抉择:前端存储还是后端存储?
在动手写代码前,我思考了两种方案:
-
纯前端方案(LocalStorage):实现简单,但数据仅限当前浏览器,换台电脑就没了,且不利于后端做多轮约束推理(RAG)。
-
后端权威方案(REST API):数据由服务端统一管理,支持跨设备,且方便未来做数据分析。
我最终选择了方案 2。但为了照顾开发体验(后端接口没好时也能跑),我设计了一个“双重模式”:如果配置了后端地址,就走 HTTP 协议;如果没有配置,就自动降级回写本机文件。
2. 核心实现:persistence.py 的双重模式设计
这是本阶段最复杂的部分。我编写了 frontend/app_lib/persistence.py,核心逻辑在于 load_session 和 save_session 函数。
设计亮点:
-
无感切换:通过
resolve_persistence_api_base()检查环境变量。如果存在PERSISTENCE_API_BASE,则调用_save_session_api发送 HTTP PUT 请求;否则调用_save_session_local写入本地.persist/目录。 -
原子写入(防崩溃):在写入本地文件时,我没有直接覆盖原文件,而是先写入临时文件(
mkstemp),写完后再原子性地替换(os.replace)。这防止了程序在写入中途崩溃导致 JSON 文件损坏。 -
懒加载与防抖:页面加载时通过
hydrate_session_state拉取一次历史;平时只在状态变更时打标(mark_session_dirty),仅在页面卸载前(flush_session_persistence)写入一次,减少 API 调用频率。
# 简化逻辑示意
def save_session(session_key: str, state_subset: Mapping[str, Any]) -> None:
base = resolve_persistence_api_base()
if base: # 走后端
_save_session_api(session_key, state_subset, base)
else: # 降级成本地文件
_save_session_local(session_key, state_subset)
3. 会话标识与生命周期管理
3.1 基于 URL 的会话 ID
我不想引入复杂的登录系统,所以采用了 URL 查询参数来管理会话。
-
逻辑:检查 URL 中是否有
sk=(Session Key)。 -
实现:
ensure_session_key()函数。如果 URL 没有 key,就生成一个 UUID 并写入 URL(触发重定向)。这样,用户分享链接给朋友,朋友打开就是一模一样的看房记录。
3.2 FIFO 策略与 50+ 条约束
任务书要求支持 50 条以上的历史。为了防止前端内存溢出,我在 append_message 中强制实施了 FIFO(先进先出) 策略。
# frontend/app_lib/state.py
def append_message(role: str, content: str, **meta: Any) -> None:
msg: dict[str, Any] = {"role": role, "content": content, **meta}
st.session_state.messages.append(msg)
# 超过上限时,丢弃最老的记录
if len(st.session_state.messages) > MAX_CHAT_HISTORY:
st.session_state.messages = st.session_state.messages[-MAX_CHAT_HISTORY:]
mark_session_dirty()
4. HTTP 契约与安全性
为了让后端同学能顺利对接,我严格定义了 RESTful 接口契约,并写在了代码注释中:
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/chat/sessions/{sk} | 获取会话快照(JSON),包含 messages, preferences_snapshot 等 |
| PUT | 同上 | 提交最新快照 |
| DELETE | 同上 | 清除会话 |
-
鉴权:支持
Authorization: Bearer <token>,Token 从secrets.toml或环境变量读取。 -
数据结构:前端发送的 JSON 包含
version(版本号)、saved_at(时间戳)和payload(实际数据)。这种封装方便后端未来做格式迁移。
5. UI 交互反馈
在侧边栏(Sidebar)中,我增加了一个状态提示,让用户知道当前数据是存在“云端”还是“本地”。
# frontend/app.py
def persistence_mode_caption() -> str:
base = resolve_persistence_api_base()
if base:
return "会话快照通过后端 API 持久化...(由服务端入库)"
return "未配置 API:会话暂存本机...(便于开发)"
同时,我增加了“清除会话存档”的按钮。点击时,不仅会清空前端的 session_state,还会同步调用 DELETE 接口或删除本地文件,确保数据彻底擦除,避免刷新后又“诈尸”恢复。
6. 总结与展望
现在,我们的智能体终于有了“长期记忆”。
-
架构上:确立了“后端为数据源(Source of Truth)”的架构,前端只是视图层。
-
体验上:用户刷新页面不再丢失几十条对话,且支持跨标签页共享状态(只要 URL 的 sk 相同)。
更多推荐
所有评论(0)