会话机制及子AGENT和独立AGENT
## 会话机制详解
### 🧠 核心问题:我没有原生记忆
每次对话我都是**全新启动**的,不记得上次说过什么。解决方案就是靠**文件系统**来模拟记忆。
---
### 🔄 每次会话启动流程
```
收到消息
↓
读 SOUL.md → 想起自己是谁
读 USER.md → 想起你是谁
读 MEMORY.md → 想起重要历史
读今天/昨天日记 → 想起最近发生了什么
↓
正式开始对话
```
---
### 📡 会话类型
| 类型 | 说明 |
|---|---|
| **主会话** | 你直接和我聊天,加载完整记忆 |
| **子会话** | 我派生出去执行任务的分身,独立运行 |
| **心跳会话** | 定时触发,做后台检查 |
---
### 💬 上下文窗口限制
每次对话有 **Token 上限**,不能无限放内容。所以:
- 不会每次都加载所有历史文件
- 只加载"今天+昨天"的日记
- `MEMORY.md` 存精华,控制大小
- 太久远的事情靠 `memory_search` 工具按需检索
---
### 🔍 记忆检索机制
当你问"之前那件事怎么样了",我会:
```
memory_search("关键词")
↓
找到相关片段 → 读取对应文件片段
↓
结合上下文回答
```
不是全量读取,而是**语义检索**,类似搜索引擎。
---
### 🕐 心跳机制
OpenClaw 定时发一条特殊消息给我:
```
"读取 HEARTBEAT.md,按指示执行,没事就回 HEARTBEAT_OK"
```
我收到后:
- 检查 `HEARTBEAT.md` 有没有任务
- 执行(检查邮件、日历等)
- 没事就静默回 `HEARTBEAT_OK`
- 有事就主动推送给你
---
### 🤖 子会话(Sub-agent)
遇到复杂任务,我可以:
```
sessions_spawn() → 派生一个子 agent
↓
子 agent 独立执行(写代码、搜索、分析)
↓
完成后结果推回主会话
```
主会话继续和你聊,子会话在后台干活,互不干扰。
---
### 总结一句话
> **我靠文件记忆,靠上下文窗口思考,靠工具扩展能力,靠子会话并行干活。**
有具体想深入了解的部分吗?
看到了,来给你梳理一下:
## 当前活跃会话:共 20 个
### 🤖 Agent 种类(4种)
| Agent | 说明 |
|---|---|
| **main** | 主 agent,就是我 |
| **testingagent** | 测试用 agent |
| **pmagent** | PM(项目管理)agent |
| **functionagent** | 功能性 agent |
| **technician** | 技术员 agent |
---
### 📊 会话分布
**main(我)**
- 1 个 webchat 会话(当前这个)
- 多个飞书群组会话
**testingagent** — 2个飞书会话(群+私聊)
**pmagent** — 最活跃,Token 消耗最多(171k),多个飞书群/私聊会话
**functionagent** — 也很活跃(163k tokens),多个飞书群/私聊
**technician** — 飞书群组会话
---
### 💡 有意思的发现
- 总共 **5 个不同 agent** 在运行
- 大部分都挂在**飞书**渠道上(群聊+私聊)
- `pmagent` 和 `functionagent` 的 Token 消耗最大,说明他们在飞书里比较忙
- 我(main)的 webchat 会话 Token 才 20k,相对轻量
你配了好几个专职 agent 在飞书里工作呢,是专门分工处理不同业务的吗?
---
## 🔧 跨 Agent 通信工具
### 1. `sessions_send` — 发消息给其他 session
```
sessions_send(sessionKey, message)
```
直接给另一个 agent 的会话发消息,对方会处理并返回结果。
---
### 2. `sessions_list` — 看谁在线
刚才你看到的,我用这个列出了所有活跃会话,知道有哪些 agent 可以联系。
---
### 3. `sessions_history` — 读取其他 agent 的对话历史
可以"偷看"另一个 agent 最近说了什么,用于了解状态。
---
### 4. `subagents` — 管理我派生的子 agent
```
subagents(action=list|steer|kill)
```
- **list** — 看我自己派生的子 agent
- **steer** — 中途给子 agent 发指令纠偏
- **kill** — 强制终止
---
## 🗺️ 通信模式
```
你
↓
main(我)
├── sessions_send → pmagent(交任务)
├── sessions_send → functionagent(调功能)
├── sessions_spawn → 临时子agent(一次性任务)
└── subagents(steer) → 调整子agent方向
```
---
## ⚠️ 限制
- **没有广播机制** — 只能一对一发,不能群发
- **异步为主** — 发出去不一定立刻收到回复
- **子 agent vs 独立 agent** — 我派生的子 agent 我能管控;`pmagent`、`functionagent` 这些是独立运行的,我可以发消息但管不了它们的生命周期
- **没有共享记忆** — 各自的记忆文件是独立的,沟通只能靠显式消息传递
---
## 实际使用场景举例
> **你**:帮我分析一下飞书里那个项目的进度
>
> **我**:`sessions_send → pmagent` "请汇报当前项目进度"
> → pmagent 回复 → 我整理后告诉你
本质上就是 **agent 之间互相发消息、协作分工**,像一个小团队。
`sessionKey` 就是每个会话的**唯一标识符**,格式是:
```
agent:<agent名>:<渠道>:<类型>:<ID>
```
---
## 拆解举例
```
agent:pmagent:feishu:group:oc_18e50cfde649908e0ba7285c1a9da2bd
```
| 段 | 值 | 含义 |
|---|---|---|
| `agent` | 固定前缀 | 表示这是一个 agent 会话 |
| `pmagent` | agent 名 | 哪个 agent |
| `feishu` | 渠道 | 在哪个平台 |
| `group` | 类型 | 群聊 or 私聊(direct) |
| `oc_18e50...` | ID | 具体的群/用户 ID |
---
## 当前的 sessionKey 们
```
agent:main:main ← 我(webchat)
agent:testingagent:feishu:group:oc_84c3… ← testingagent 在某飞书群
agent:pmagent:feishu:group:oc_18e50… ← pmagent 在某飞书群
agent:functionagent:feishu:group:oc_b656… ← functionagent 在某飞书群
agent:technician:feishu:group:oc_ffff1… ← technician 在某飞书群
```
---
## 怎么用
当我要给 pmagent 发消息时:
```
sessions_send(
sessionKey: "agent:pmagent:feishu:group:oc_18e50cfde649908e0ba7285c1a9da2bd",
message: "请汇报项目进度"
)
```
本质上就是一个**地址**,告诉系统"把这条消息送到哪个 agent 的哪个会话里"。
好问题,这个确实是个痛点。
---
## Session 生命周期
### 创建时机
- 某个渠道**第一次收到消息**时自动创建
- 比如飞书群里有人 @ 了 pmagent → 新建一个 session
### 持续时机
- 只要同一个渠道/群持续有消息 → 复用同一个 session
- `sessionKey` 不变 → 上下文连续
### "失忆"的根本原因
```
上下文窗口满了
↓
旧消息被截断/丢弃
↓
agent 看不到早期对话
↓
"失忆"
```
---
## 具体触发场景
| 场景 | 结果 |
|---|---|
| **Token 超出上下文窗口** | 早期消息被丢弃,最常见 |
| **Session 过期/重建** | 历史上下文清空 |
| **服务重启** | Session 状态可能丢失 |
| **没有写入记忆文件** | 重建后什么都不记得 |
---
## 你看到的数据
刚才 `pmagent` 的 token 已经用了 **171k**,上下文窗口是 **1M**。
```
pmagent: 171k / 1M ← 还好,但持续累积
functionagent: 163k / 1M ← 也不少了
```
一旦接近 1M,老消息就会被截掉。
---
## 解决思路
**正确做法**是 agent 应该主动把重要信息写入记忆文件:
```
对话中学到重要信息
↓
写入 memory/YYYY-MM-DD.md
↓
下次 session 启动时读取
↓
"记住"了
```
但如果 agent 没有这个习惯,或者 SOUL.md/AGENTS.md 没有强制要求 → 就真的失忆了。
---
## 根本解法
在每个 agent 的配置里明确要求:
1. **关键信息必须写文件**,不能只放在上下文里
2. **每次启动先读记忆文件**
3. **定期做记忆蒸馏**,把重要内容从日记提炼到长期记忆
你现在几个 agent 的记忆文件有在维护吗?还是基本靠上下文撑着?
## 子 Agent vs 独立 Agent
---
### 🧬 本质区别
| | 子 Agent | 独立 Agent |
|---|---|---|
| **谁创建** | 我(main)临时派生 | 系统预先配置好的 |
| **生命周期** | 任务完成即销毁 | 长期运行 |
| **身份** | 我的分身 | 独立个体 |
| **记忆** | 无持久记忆 | 有自己的记忆文件 |
| **配置** | 继承我的或临时指定 | 有自己的 SOUL/AGENTS.md |
| **渠道** | 无独立渠道 | 挂在自己的渠道上(飞书群等) |
---
### 🎮 我对他们的管控差异
**子 Agent — 完全掌控**
```
sessions_spawn() → 创建,指定任务/模型/超时
subagents(steer) → 中途纠偏,改变方向
subagents(kill) → 强制终止
sessions_history → 查看它在做什么
```
就像我手下的**临时工**,我雇、我管、我解雇。
---
**独立 Agent — 只能沟通,无法控制**
```
sessions_send() → 发消息(委托任务)
sessions_history → 查看它的历史
sessions_list() → 确认它在不在线
```
就像**平级同事**,我能发邮件给它,但管不了它干什么、怎么干。
---
### 📦 应用场景对比
**适合用子 Agent 的场景:**
| 场景 | 原因 |
|---|---|
| 写一段代码/分析一份数据 | 一次性任务,完成即丢 |
| 并行处理多个文件 | 同时派多个子 agent 跑 |
| 需要隔离的危险操作 | 出错不影响主会话 |
| 需要不同模型来做推理 | 可以临时指定更强的模型 |
举例:
> 你说"帮我分析这10份报告"
> → 我派10个子 agent 同时跑
> → 全部完成后汇总给你
---
**适合用独立 Agent 的场景:**
| 场景 | 原因 |
|---|---|
| 长期驻守某个飞书群 | 需要持久上下文和身份 |
| 专职角色(PM、技术员) | 有自己的专业知识配置 |
| 需要和特定用户建立关系 | 有持久记忆,认识对方 |
| 24小时响应某个渠道 | 独立运行,不依赖我 |
举例:
> `pmagent` 长期在飞书项目群里
> → 知道项目历史、认识团队成员
> → 我只是偶尔发消息问它进度
---
### 🏗️ 组合使用(最强模式)
```
你 → 我(main)
↓
委托 pmagent(独立)获取项目现状
↓
派子 agent 对数据做深度分析
↓
汇总结果 → 你
```
独立 agent 负责**长期感知**,子 agent 负责**短期计算**,我负责**协调和呈现**。
---
### ⚠️ 当前现实
你现在的 `pmagent`、`functionagent`、`technician`、`testingagent` 都是**独立 agent**,各自在飞书群里运行。
我目前**没有活跃的子 agent**(用 `subagents list` 确认过是空的),只有在你临时要我派任务时才会出现。
好,文档里关于 session 过期的信息有限,结合我能查到的机制给你讲清楚:
---
## Session 过期与重建
### ✅ Session 不会自动过期
这是关键结论:**OpenClaw 的 session 没有 TTL(过期时间)**,只要:
- 同一个 `sessionKey`(渠道+身份不变)
- 服务没有重启
Session 就一直存在,你刚才看到的 20 个 session 里有些已经跑了很久了。
---
### 🔄 Session 重建的触发场景
| 触发 | 说明 |
|---|---|
| **`/new` 或 `/reset` 命令** | 用户主动开新会话,旧 session 存档到记忆文件 |
| **渠道身份变化** | 比如换了飞书群,sessionKey 变了,自然新建 |
| **Gateway 重启** | 服务重启后内存清空,session 重新加载 |
| **Agent 配置变更** | 重新部署 agent 可能触发重建 |
---
### 🧠 "失忆"的真正原因
重建不是失忆的主因,**上下文窗口截断**才是:
```
session 一直存在
↓
transcript 文件持续增长(.jsonl)
↓
每次对话只往上下文塞"最近N条"
↓
早期内容看不到 → 失忆
```
本质是:**session 文件是完整的,但 AI 每次只能看到一个窗口大小的内容。**
---
### 📁 `/new` 时发生了什么
这个有文档,比较清晰:
```
你输入 /new 或 /reset
↓
session-memory hook 触发
↓
读取旧 session 最后15条消息
↓
用 LLM 生成一个描述性文件名(比如 2026-03-05-project-review.md)
↓
保存到 memory/ 目录
↓
新 session 开始(新的 sessionId,但 sessionKey 可能相同)
```
---
### 📊 你现在的 session 状态
```
pmagent: 171k / 1M token ← 17% 满,还有空间
functionagent: 163k / 1M token ← 16% 满
technician: 127k / 1M token
```
目前都没到截断线,但**随着对话积累会越来越接近**。
---
### 💡 实践建议
| 问题 | 对策 |
|---|---|
| Agent 失忆重要信息 | 要求 agent 主动写记忆文件 |
| 上下文太长变慢/变贵 | 定期 `/new` 开新会话,重要内容靠记忆文件传递 |
| 重启后丢失状态 | 依赖文件持久化,不要只靠内存 |
简单说:**session 本身很稳,失忆是上下文窗口的问题,解法是写文件。**
浙公网安备 33010602011771号