第 14 章《会话管理 SessionManager》· 会话管理怎么做?SessionManager 原子写 + fsync 持久化实战(nanobot)

本文回答什么问题:nanobot 的会话怎么存?为什么用 JSONL 而不是 SQLite?原子写 + fsync 怎么保证不丢消息?session_key 重命名怎么用?

目标读者 :LLM Agent 开发者 / 系统架构师

预计阅读时间 :10 分钟

源码版本 :GitHub HKUDS/nanobot main 分支主线代码(仓库相对路径)

SessionManager(nanobot/session/manager.py)负责把所有回合的消息原子追加到 JSONL 文件。本章聚焦"为什么 JSONL"+"原子写怎么实现"+"重启后怎么恢复"。

1. 整体定位:为什么需要 SessionManager

如果不持久化,每次重启 nanobot 用户都会"失忆"------上轮的对话全部丢失。SessionManager 解决:

  • 每条消息原子写(不丢)
  • JSONL 格式(易读 / 易回放 / 易压缩)
  • 重启后 _load_recent() 自动恢复最近 50 条

核心要点速查(建议收藏)

  • 核心文件 :nanobot/session/manager.py(约 300 行)
  • 存储位置 :~/.nanobot/sessions/<session_key>.jsonl
  • 格式:每行一个 JSON 对象(Message)
  • 原子写 :写临时文件 + os.replace() + fsync 防丢
  • 3 个核心方法 :append(key, msg) / get_recent(key, n) / get_summary(key)

2. JSONL 格式

jsonl 复制代码
{"role": "user", "content": "你好", "timestamp": "2026-08-07T10:00:00Z"}
{"role": "assistant", "content": "你好!有什么可以帮你的?", "timestamp": "2026-08-07T10:00:05Z"}
{"role": "user", "content": "Python 怎么读取 JSON?", "timestamp": "2026-08-07T10:00:30Z"}
{"role": "assistant", "content": "用 json 模块...", "timestamp": "2026-08-07T10:00:35Z"}

JSONL vs SQLite 优势:

  • 易读(cat 即可看)
  • 易回放(head -n 100 取最近 100 条)
  • 易压缩(gzip 压 80%)
  • 简单实现(无需 SQLAlchemy / 迁移)

JSONL 劣势:

  • 查询慢(全文扫描)
  • 但 nanobot 只查"最近 N 条",无需全文搜索

3. 3 个核心方法

3.1 append(session_key, message)

python 复制代码
def append(self, session_key: str, message: Message) -> None:
    path = self._session_path(session_key)
    line = json.dumps(message.to_dict(), ensure_ascii=False) + "\n"

    # 原子写
    tmp_path = path.with_suffix(".tmp")
    with open(tmp_path, "a", encoding="utf-8") as f:
        f.write(line)
        f.flush()
        os.fsync(f.fileno())
    os.replace(tmp_path, path)  # 原子替换

3 步保证不丢:

  1. .tmp 临时文件
  2. flush() + os.fsync() 强制刷盘
  3. os.replace() 原子替换(同一文件系统内 rename 是原子的)

3.2 get_recent(session_key, n=50)

python 复制代码
def get_recent(self, session_key: str, n: int = 50) -> list[Message]:
    path = self._session_path(session_key)
    if not path.exists():
        return []
    # 从文件末尾读 N 行(避免加载全部)
    with open(path, "rb") as f:
        f.seek(0, 2)  # 跳到末尾
        size = f.tell()
        # 反向扫描,直到读够 N 行
        ...

优化:不读完整文件,反向扫描最近 N 行------避免长会话读取慢。

3.3 get_summary(session_key)

python 复制代码
def get_summary(self, session_key: str) -> str | None:
    summary_path = self._session_path(session_key).with_suffix(".summary.md")
    if not summary_path.exists():
        return None
    return summary_path.read_text(encoding="utf-8")

summary 文件 由 AutoCompact(第 16 章)生成,与消息文件分开存。

4. 4 个核心决策

决策 1 · 为什么用 JSONL?

简单 / 易读 / 易回放------SQLite 优势在 nanobot 用不到,劣势(迁移 / 依赖)反倒是负担。

决策 2 · 为什么原子写而不是直接 append?

直接 open(path, "a") 在断电 / kill -9 时可能丢消息;原子写保证要么完整要么完全没写。

决策 3 · 为什么每条消息一行而不是整个会话一个 JSON?

JSONL 支持追加(整个 JSON 必须 reparse-rewrite 整文件);JSONL 也支持反向扫描(每行独立)。

决策 4 · 为什么 session_key 默认 f"{channel}:{chat_id}"?

简单 + 隔离(详见第 05 章)。重命名机制见 §5。

5. 实战:session_key 重命名

python 复制代码
# WebUI 提供"重命名会话"功能
async def rename_session(old_key: str, new_key: str):
    old_path = Path.home() / ".nanobot" / "sessions" / f"{old_key}.jsonl"
    new_path = Path.home() / ".nanobot" / "sessions" / f"{new_key}.jsonl"
    old_path.rename(new_path)  # 原子重命名

6. 实战:跨会话引用

yaml 复制代码
# ~/.nanobot/config.yaml
sessionAlias:
  work: "telegram:12345"        # 别名 work -> 真实 session
  home: "discord:67890"

work 可在 WebUI / CLI 当 session_key 用,实际指向 telegram:12345

7. 常见问题 / 避坑

Q:session 文件太大怎么办?

A :du -sh ~/.nanobot/sessions/ 看大小;gzip ~/.nanobot/sessions/*.jsonl 压缩 80%;AutoCompact(第 16 章)会自动摘要旧消息。

Q:断电 / kill -9 后消息会丢吗?

A :------os.fsync() 强制刷盘。但当前正在写入的消息可能截断(因为是行级别,JSONL 解析会跳过截断行)。

Q:怎么导出 / 备份会话?

A :tar czf backup.tgz ~/.nanobot/sessions/;或单文件 cp <key>.jsonl ~/Desktop/

Q:删一个 session?

A :rm ~/.nanobot/sessions/<key>.jsonl;或 WebUI "delete session" 按钮。

8. 小结

  • JSONL 格式 每行一个 Message
  • 关键模块:原子写 临时文件 + fsync + os.replace
  • 设计要点:3 个核心方法 append / get_recent / get_summary
  • 常见坑**:存储位置 ~/.nanobot/sessions/<key>.jsonl

本文要点速查

  1. JSONL + 原子写 见 §2 + §3.1
  2. get_recent 反向扫描 见 §3.2
  3. 重命名 + 别名 见 §5 + §6
  4. 下一步:第 15 章《记忆与 Dream 整合》------ MEMORY.md 怎么从 messages 提取

按角色推荐

  • LLM Agent 开发者:必读(自定义 session 必读)
  • 系统架构师:必读(持久化机制必读)
  • LLM Provider 适配者:选读
  • 聊天通道开发者:选读
  • Tool / MCP 工具开发者:选读

下一步

  • 第 15 章《记忆与 Dream 整合》 ------ MEMORY.md 怎么生成(主题群"Agent 核心",第 3 周)
  • 第 16 章《自动压缩 AutoCompact》 ------ 长会话怎么摘要(主题群"Agent 核心",第 3 周)

tags :#nanobot #AI Agent #LLM #Python #源码解析 #会话管理 #持久化

相关推荐
凡尘——雨落凡尘11 天前
PHP 线上十大隐形故障复盘:90% 的网站卡顿、502、雪崩,都是这些细节导致的
redis·nginx·php·session
ShineWinsu17 天前
对于Linux:HTTP中cookie、session的解析
linux·网络·c++·网络协议·http·cookie·session
花生了什么事o20 天前
Session 和 Cookie 的区别、原理与应用场景
cookie·session
深蓝电商API2 个月前
浏览器自动化中的Cookie和Session管理最佳实践
数据采集·cookie·session
遇事不決洛必達2 个月前
【爬虫随笔】深入理解 HTTP/HTTPS 协议、接口交互与会话机制
爬虫·网络协议·http·https·session
Irissgwe2 个月前
5-1、HTTP cookie与session
linux·http·cookie·session
tryqaaa_2 个月前
学习日志(五)【php反序列化全加例题】【pop链,字符逃逸,session,伪协议】
android·学习·php·web·pop·session
rising start3 个月前
Web认证机制演进
架构·jwt·session
坐吃山猪3 个月前
【Nanobot】README04_LEVEL2 提供商系统设计
python·源码·agent·nanobot