nanobot的session和workspace机制
nanobot Session、SessionManager 与 Workspace 机制
本文结合 nanobot.md 的执行流程梳理,说明 nanobot 中 Session、SessionManager 与 Workspace 的设计关系,以及一条会话从创建、使用、持久化、压缩到删除的生命周期。
1. 总体关系
nanobot 的会话系统可以按下面的层次理解:
1 | |
Workspace是 nanobot 的状态与上下文根目录。SessionManager绑定到某一个 workspace,管理该 workspace 下的所有 session 文件。Session表示一条具体聊天线程,通常对应一个channel:chat_id。
普通 CLI/gateway 模式中,workspace 同时承担这些职责:
1 | |
默认 workspace 是:
1 | |
启动时可以通过 --workspace 或 -w 覆盖:
1 | |
2. Workspace 与 Session 的绑定关系
Session 文件存放在 workspace 下:
1 | |
因此,同一个 session key 在不同 workspace 下是不同会话:
1 | |
它们都可以对应 cli:direct,但内容彼此独立。
所以 workspace 与 session 的关系不是 session 自己携带一个独立 workspace 字段,而是:
1 | |
3. Session 是什么
Session 定义在 session/manager.py,核心结构如下:
1 | |
字段含义:
key:会话唯一标识,通常是channel:chat_id。messages:会话中持久化保存的消息。created_at:创建时间。updated_at:最近一次更新或维护时间。metadata:扩展状态,例如 WebUI 标题、workspace scope、goal state、摘要、checkpoint 等。last_consolidated:已经被归档/摘要过的消息前缀长度。
未归档、仍可能进入近期上下文的消息是:
1 | |
常见 session key:
1 | |
nanobot agent 默认使用:
1 | |
也可以手动指定:
1 | |
4. SessionManager 是什么
SessionManager 是当前 workspace 下所有 session 的仓库和持久化层。
初始化时:
1 | |
内部会设置:
1 | |
它管理的是:
1 | |
注意:它不是全局管理所有 workspace。换一个 workspace,就会有另一个 SessionManager。
核心职责:
1 | |
典型使用方式:
1 | |
Session 负责一条会话自身的数据和局部逻辑,SessionManager 负责多条会话的生命周期和磁盘持久化。
5. Session 文件格式
Session 文件是 JSONL:
1 | |
第一行大致如下:
1 | |
后续消息行大致如下:
1 | |
保存时采用临时文件加原子替换:
1 | |
进程退出时还会通过 flush_all(fsync=True) 尽量保证缓存中的 session 稳定落盘。
6. Session 生命周期
一个 session 的生命周期可以概括为:
1 | |
6.1 创建
当某个 channel:chat_id 第一次被处理时,AgentLoop 会调用:
1 | |
如果内存缓存中已有,则直接返回。
如果磁盘存在对应 JSONL,则加载。
如果都不存在,则创建新的:
1 | |
6.2 使用
每次处理用户消息时,AgentLoop 会:
根据
InboundMessage得到 session key。使用
SessionManager.get_or_create()获取 session。通过
session.get_history()取近期历史。用
ContextBuilder构建模型上下文。调用
AgentRunner执行 LLM/tool 循环。将用户消息、assistant 回复、tool call/result 等写回 session。
调用
SessionManager.save()保存。
6.3 恢复
进程重启后,内存 cache 清空。
下一次访问同一个 key 时:
1 | |
会重新从 JSONL 文件恢复 session。
如果 JSONL 中有损坏行,SessionManager._repair() 会尝试跳过坏行,尽量恢复可用消息。
6.4 删除
普通运行不会自动删除整个 session。
显式删除主要来自 WebUI 删除聊天,对应:
1 | |
删除时会:
从内存 cache 移除。
删除
<workspace>/sessions/<safe_key>.jsonl。WebUI 还会清理自己的 thread/transcript 文件。
需要区分:
删除 session:整个会话文件消失。
压缩/归档 session:旧消息被摘要或迁移,但会话仍然存在。
7. AgentLoop 如何使用 Session
nanobot.md 中的一条请求主线是:
1 | |
AgentLoop 是产品层编排器,负责:
从
MessageBus接收消息。按 session 串行、跨 session 并发地调度任务。
维护 turn 状态机。
构建历史、记忆、技能和运行时上下文。
调用
AgentRunner。保存 session 并发布响应。
同一个 session 会用锁串行执行:
1 | |
这样可以避免同一条聊天同时跑多个互相覆盖的 turn。
8. Session 不等于最终 Prompt
Session 是上下文来源之一,但最终发给模型的 prompt 不是简单地把 session.messages 全量塞进去。
ContextBuilder 会组合:
1 | |
session.get_history() 也会对历史做处理:
只取
last_consolidated之后的未归档消息。按
max_messages截取尾部。尽量从 user turn 开始。
避免孤立 tool result。
可按 token 预算裁剪。
图片消息回放时加占位 breadcrumb。
因此可以理解为:
1 | |
9. 压缩、归档与 last_consolidated
nanobot 有多层上下文控制机制:
1 | |
9.1 AutoCompact
空闲时间达到配置阈值后,对 session 做后台压缩。
它可能会:
将旧消息通过 LLM 归档。
保留最近合法消息尾巴。
替换
session.messages。保存
_last_summary。
9.2 Token consolidation
在 BUILD 前同步检查,并在 SAVE 后后台检查。
当历史重放窗口或 prompt token 接近预算时,它会:
对旧消息生成摘要。
写入
memory/history.jsonl。推进
last_consolidated。让
get_history()忽略已归档前缀。
9.3 Runner snip
这是 AgentRunner 请求模型前的临时防线。
它只影响当前发送给模型的 messages_for_model:
不修改 Session。
不更新
last_consolidated。不写入摘要。
9.4 Session 文件硬上限
SAVE 阶段还会执行:
1 | |
它限制 session 文件中实际保存的消息数量。如果超过上限,会保留最近合法后缀,并把尚未归档的被删除消息 raw archive 到历史中。
10. WebUI 的 workspace_scope
普通 CLI 模式中,workspace 同时是:
1 | |
WebUI 多了一层 workspace_scope。
这表示:
1 | |
可以区分为:
1 | |
因此 WebUI 可以做到:
1 | |
而 CLI 模式目前没有完整的 data-dir/project-dir 分离。
11. 总结
核心关系如下:
1 | |
一句话概括:
1 | |