QwenPaw 记忆与对话管理架构

QwenPaw 记忆与对话管理架构

一、整体架构

QwenPaw 采用三层记忆设计:

层级 存储位置 内容 生命周期
活跃记忆 内存 + Session JSON 完整对话消息(用户/Agent/工具调用/工具结果/思考过程) 单次会话
压缩摘要 内存 + Session JSON 旧消息被 LLM 浓缩后的 Markdown 摘要 单次会话,累积更新
长期记忆 工作区文件(MEMORY.md + memory/*.md) 跨会话持久知识 永久

二、Session 管理

2.1 Session 概念

一个 Session 代表同一个用户在同一个渠道下与 Agent 的持续对话上下文:

erlang 复制代码
Session(会话)
├── 第1轮:用户提问 → Agent 回答
├── 第2轮:用户提问 → Agent 思考 → 调工具 → 拿结果 → 回答
├── 第3轮:用户提问 → Agent 回答
└── ...

2.2 Session 隔离

每个 (user_id + session_id + channel) 组合对应一个独立的 JSON 文件:

复制代码
{save_dir}/{channel}/{safe_uid}_{safe_sid}.json
  • 不同用户、不同会话、不同渠道完全隔离
  • 一个用户同一个 Agent 默认只有一个活跃 Session
  • 没有历史 Session 列表,不能回溯或恢复旧 Session

2.3 Session 文件内容

csharp 复制代码
{
  "agent": {
    "content": [
      [msg_dict_1, ["marks"]],
      [msg_dict_2, ["marks"]]
    ],
    "_compressed_summary": "压缩后的摘要文本..."
  },
  "plan": { ... }
}

content 包含完整的对话消息:用户消息、Agent 回复、工具调用、工具结果、思考块等。


三、对话保存机制

3.1 存储位置

文件类型 路径 作用
Session JSON {save_dir}/{channel}/{uid}_{sid}.json 当前活跃会话的完整快照
JSONL 归档 {dialog_path}/YYYY-MM-DD.jsonl 被压缩掉的旧消息归档
MEMORY.md {workspace}/MEMORY.md 长期记忆(精炼知识)
每日笔记 {workspace}/memory/YYYY-MM-DD.md 每天的原始记录

3.2 保存时机

时机 触发方法 操作
每轮对话结束 save_session_state() 内存完整消息列表 + 压缩摘要 → 写入 Session JSON
上下文压缩时 mark_messages_compressed() 旧消息写入 JSONL 归档,然后从内存删除
/new 命令时 clear_content() 所有消息写入 JSONL 归档,然后清空内存

3.3 对话生命周期

scss 复制代码
第 1 轮:
  1. load_session_state() → 文件不存在,内存为空 []
  2. 用户说 "你好"
  3. Agent 回复 "你好!"
  4. save_session_state() → JSON: [用户消息, Agent回复]

第 2 轮:
  1. load_session_state() → 从 JSON 恢复: [用户消息1, Agent回复1]
  2. 用户说 "我叫小明"
  3. Agent 回复 "好的小明!"
  4. save_session_state() → JSON: [消息1, 回复1, 消息2, 回复2]

... 持续积累 ...

第 N 轮(token 超限触发压缩):
  1. load_session_state() → 恢复 30 条消息
  2. 用户说 "帮我写个脚本"
  3. pre_reasoning 钩子检测 token 超限
  4. 压缩前 20 条 → 归档 JSONL + 生成摘要
  5. 内存只剩 [摘要, 第21~30条, 新用户消息]
  6. Agent 推理 → 调工具 → 回复
  7. save_session_state() → JSON: 当前内存快照

四、对话压缩机制

4.1 触发条件

pre_reasoning 钩子中检查,当 token 总量超过阈值时触发:

ini 复制代码
总 token = 系统提示词 token + 压缩摘要 token + 活跃消息 token
阈值 = max_input_length × compact_threshold_ratio(默认约 80%)

4.2 压缩流程

markdown 复制代码
1. 拆分消息
   活跃消息 → messages_to_compact(旧的)+ messages_to_keep(近期的)
   分界线由 reserve_threshold_ratio(默认约 10%)决定

2. 生成摘要
   创建独立的 ReActAgent,输入:
   - 旧消息原文
   - 之前的压缩摘要(如果有)
   输出:带 ## 标题的 Markdown 摘要

3. 归档旧消息
   messages_to_compact → 写入 dialog/YYYY-MM-DD.jsonl
   → 从内存中删除

4. 更新摘要
   _compressed_summary = 新生成的摘要

5. 触发后台记忆总结(可选)
   summarize_when_compact → 后台任务写入 MEMORY.md

4.3 摘要累积

压缩摘要是累积更新的,不是每次从零开始:

css 复制代码
第1次压缩:摘要A = LLM(msg1~msg20)
第2次压缩:摘要B = LLM(摘要A + msg21~msg40)    ← 摘要A 参与压缩
第3次压缩:摘要C = LLM(摘要B + msg41~msg60)    ← 摘要B 参与压缩

信息逐层浓缩,但核心内容通过 LLM 总结保留。

4.4 /new 和 /clear 的区别

命令 内存消息 JSONL 归档 MEMORY.md
/new 清空 旧消息写入归档 后台总结追加
/clear 清空 不写入 不变
/compact 压缩旧的 旧消息写入归档 不变

五、记忆读取与保存时机

5.1 一轮对话的完整生命周期

scss 复制代码
load_session_state()           ← 【读取】恢复上次的对话上下文(Session JSON → 内存)
      │
pre_reply 钩子                 ← 【读取】搜索 MEMORY.md + memory/*.md,注入相关知识
      │
ReAct 循环(推理 + 工具调用)
      │
pre_reasoning 钩子             ← 如果 token 超限:
  ├── 压缩旧消息 → 归档 JSONL
  └── summarize_when_compact   → 【保存】后台总结写入 MEMORY.md
      │
post_acting 钩子               ← 截断过大的工具输出
      │
Agent 返回回复
      │
post_reply 钩子                ← 【保存】每 N 条用户消息,自动提取记忆
      │
save_session_state()           ← 【保存】对话上下文写入 Session JSON

5.2 读取时机汇总

时机 方法 读取什么 目的
对话开始 load_session_state() Session JSON 恢复对话上下文
每轮回复前 pre_reply → auto_memory_search() MEMORY.md + memory/*.md 自动注入相关长期记忆

5.3 保存时机汇总

时机 方法 保存什么 写到哪里
对话结束 save_session_state() 完整消息列表 + 压缩摘要 Session JSON
上下文压缩 mark_messages_compressed() 被压缩的旧消息 JSONL 归档
上下文压缩 summarize_when_compact() 对话总结 MEMORY.md
每 N 条用户消息 auto_memory() 近期对话总结 MEMORY.md
/new 命令 add_summarize_task() 全部消息总结 MEMORY.md

六、长期记忆系统

6.1 文件结构

yaml 复制代码
工作区/
├── MEMORY.md                    ← 精炼后的长期记忆(核心知识)
└── memory/
    ├── 2026-06-05.md            ← 当天的原始记录(流水账)
    ├── 2026-06-06.md
    └── 2026-06-07.md

6.2 MEMORY.md vs memory/YYYY-MM-DD.md

对比项 MEMORY.md memory/YYYY-MM-DD.md
定位 长期记忆(精炼知识) 每日笔记(原始记录)
类比 大脑长期记忆 日记本
内容 用户偏好、重要决策、经验教训 当天讨论了什么、做了什么
谁写入 Agent 主动 + Dream 整理 + 后台总结 Agent 主动 + 后台总结
关系 由每日笔记提炼而来 MEMORY.md 的上游数据源

6.3 写入方式

方式一:Agent 主动写入

系统提示词中明确指示 Agent:

"每次会话都是全新的。工作目录下的文件是你的记忆延续。" "写下来远比用脑子记住更好。" "主动记录 --- 别总是等人叫你记!"

Agent 在对话过程中通过 write_file / edit_file 工具自己写入文件。

方式二:后台总结任务

系统自动触发,创建一个带文件读写工具的独立 Agent,分析对话后写入记忆文件。

触发条件:

  • 上下文压缩时 → summarize_when_compact()
  • 每 N 条用户消息 → auto_memory()(N 由 auto_memory_interval 配置)
  • /new 命令时 → add_summarize_task()

6.4 Dream 梦境优化

定期(cron 定时任务)运行一次"做梦",整理 MEMORY.md

markdown 复制代码
1. 备份 MEMORY.md → backup/
2. 创建专用 ReActAgent(DreamOptimizer)
3. 读取 MEMORY.md + 当天日志 memory/YYYY-MM-DD.md
4. 按原则整理:
   - 极简去冗:只保留核心决策、用户偏好、高价值经验
   - 状态覆写:新信息替换旧信息,不允许矛盾并存
   - 归纳整合:相似条目合并
   - 废弃剔除:删除过时内容
5. 覆写 MEMORY.md

七、记忆匹配规则

7.1 关键词提取

从用户最新消息中提取最多 100 个字符作为搜索查询,然后通过 tokenize_query() 分词:

  • CJK 字符(中/日/韩文)→ 每个字作为一个 token
  • 非 CJK 字符 → 按空格拆分,整个单词作为一个 token
  • 混合词 → 中文字逐个拆开,英文部分保留
  • 上限 50 个 token
arduino 复制代码
输入:"你好 我叫小明 用python写个脚本"
分词:["你", "好", "我", "叫", "小", "明", "用", "python", "写", "个", "脚", "本"]

7.2 搜索方案 --- 混合搜索(Hybrid Search)

两路并行搜索,加权合并:

路 1:向量搜索(语义匹配,默认权重 70%)
css 复制代码
查询文本 → Embedding 模型 → 向量 [0.12, -0.34, ...]
         → ChromaDB 余弦相似度搜索
         → 返回语义最相近的文档片段
  • MEMORY.md 和 memory/*.md 在写入时被分块(Chunk)
  • 每个块通过 Embedding 模型转成向量存入 ChromaDB
  • 搜索时把查询也转成向量,用余弦距离计算相似度
  • 距离转分数:score = 1.0 - distance / 2.0

需要配置 Embedding API(base_url + model_name),否则不启用。

路 2:关键词搜索(全文匹配,默认权重 30%)
bash 复制代码
查询分词 → ChromaDB $contains 子串匹配(大小写不敏感)
        → 计算匹配词占比得分

打分算法:

ini 复制代码
匹配词数 = 文档中包含多少个查询词
base_score = 匹配词数 / 总查询词数
完整短语匹配加分 = +0.2
final_score = min(1.0, base_score + phrase_bonus)
合并规则
ini 复制代码
vector_weight = 0.7
text_weight = 0.3

# 同一文档在两路都命中
final_score = vector_score × 0.7 + keyword_score × 0.3

# 只在一路命中
final_score = 单路 score × 对应权重

# 按 final_score 降序排列,取 top N(默认 5 条)
# 过滤掉 score < 0.1 的结果

7.3 存储后端

后端 条件 搜索能力
ChromaDB 非 Windows + chromadb 可导入 向量搜索 + 关键词搜索
Local ChromaDB 不可用 仅关键词搜索

7.4 注入方式

搜索结果不是直接拼接到系统提示词中,而是伪装成 Agent 自己的操作注入到对话历史:

arduino 复制代码
assistant 消息: "Searching memory for relevant context..."
tool_result 消息: [匹配到的记忆片段,含路径、行号、内容]

这样 Agent 在对话中看到的是"我之前查过记忆",而不是系统注入的内容。


八、核心设计理念

8.1 "每次会话都是全新的"

Agent 不依赖内存中的对话历史做长期记忆,而是把所有重要的东西写到文件里。好处:

  1. 不丢失 --- 对话上下文会被压缩、清空,但文件永远在
  2. 可搜索 --- 通过语义搜索精准找到相关记忆,比翻长对话高效
  3. 可维护 --- Dream 机制定期整理,避免 MEMORY.md 膨胀
  4. 可移植 --- 记忆就是 Markdown 文件,可以复制、备份、版本管理

8.2 单 Session 滚动

  • 一个用户 + 一个 Agent + 一个渠道 = 一个 Session
  • /new 不是新建 Session,而是清空当前 Session 重新开始
  • 没有历史 Session 列表,不能回溯旧会话
  • 通过 MEMORY.md 实现跨会话记忆

九、Skill 技能系统

9.1 什么是 Skill

Skill 是以 SKILL.md 文件形式定义的扩展能力,存放在工作区的子目录中。每个 Skill 目录包含一个 SKILL.md 文件,用 YAML frontmatter 描述元信息,正文描述使用方法。

9.2 SKILL.md 文件格式

yaml 复制代码
---
name: web_search
description: 搜索互联网获取最新信息
---

## 使用方法

1. 使用 search_web(query) 工具进行搜索
2. 读取返回的搜索结果
3. 总结关键信息回答用户

9.3 注册流程

markdown 复制代码
启动时:
1. 扫描工作区下的 Skill 目录
2. 读取每个 SKILL.md 的 YAML frontmatter(name, description)
3. toolkit.register_agent_skill() 注册到工具箱
4. 工具箱自动生成技能描述 prompt

9.4 注入方式 --- 系统提示词

Skill 不是 作为工具定义(function calling)提交给 LLM 的,而是作为系统提示词中的描述文本

csharp 复制代码
# Agent Skills

## web_search
搜索互联网获取最新信息
Check "/path/to/skills/web_search/SKILL.md" for how to use this skill.

Agent 看到的只是"你有这个技能,详细用法请读 SKILL.md"。需要使用时,Agent 通过 read_file 工具主动读取 SKILL.md 获取完整说明。

设计意图:节省系统提示词的 token 开销,只在需要时才加载完整技能说明。


十、MCP(Model Context Protocol)工具

10.1 什么是 MCP

MCP 是一种标准化的外部工具接入协议。通过 MCP,Agent 可以调用外部服务提供的工具(如网页搜索、数据库查询、API 调用等)。

10.2 注册流程

vbscript 复制代码
启动时:
1. 读取配置中的 mcp_servers 列表
2. 为每个 MCP server 创建 Client 连接
3. 获取该 server 提供的所有工具列表(名称、参数 Schema、描述)
4. toolkit.register_mcp_client(client) 注册到工具箱
5. MCP 工具变成标准的 tool function

10.3 注入方式 --- 工具定义(Function Calling)

MCP 工具以标准的 tools JSON Schema 形式提交给 LLM:

json 复制代码
{
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "mcp_web_search",
        "description": "搜索互联网获取信息",
        "parameters": {
          "type": "object",
          "properties": {
            "query": { "type": "string", "description": "搜索关键词" }
          },
          "required": ["query"]
        }
      }
    }
  ]
}

MCP 工具和内置工具(write_fileread_fileexecute_shell 等)享有同等地位,LLM 可以直接通过 function calling 调用,无需额外步骤。

10.4 Skill vs MCP 对比

对比项 Skill MCP
定义方式 SKILL.md 文件(YAML + Markdown) 外部 MCP Server 协议
提交方式 系统提示词文本 tools JSON Schema(Function Calling)
LLM 看到的 技能名称 + 描述 + SKILL.md 路径 完整的工具定义(名称、参数、描述)
调用方式 Agent 需先 read_file 读取 SKILL.md,再间接使用 直接 function calling
扩展性 本地文件,适合文档类技能 外部服务,适合 API 类工具
典型场景 "你会写代码"、"你会做数据分析" 网页搜索、数据库查询、发送消息

十一、提交给大模型的完整内容结构

11.1 请求结构总览

每次 Agent 推理时,提交给 LLM 的内容包含两部分:

ini 复制代码
提交给 LLM 的请求
├── messages: [消息列表]          ← 对话上下文
│   ├── [0] system message        ← 系统提示词
│   ├── [1] 压缩摘要(如有)       ← assistant 角色注入
│   ├── [2] 记忆搜索结果(如有)    ← assistant + tool_result 伪装
│   ├── [3..N] 活跃对话消息        ← 真实对话历史
│   └── [N+1] 最新用户消息        ← 当前输入
│
└── tools: [工具定义列表]          ← Function Calling 工具
    ├── 内置工具                   ← write_file, read_file, execute_shell 等
    └── MCP 工具                   ← 外部服务提供的工具

11.2 系统提示词(System Message)详细结构

系统提示词由多个部分拼接而成:

csharp 复制代码
# {AGENTS.md 文件内容}
(包含工作流、规则、指南;已过滤 heartbeat 和 memory 标记段)

# {SOUL.md 文件内容}
(核心身份和行为原则)

# {PROFILE.md 文件内容}
(Agent 身份和用户画像)

{记忆引导 prompt}
(告诉 Agent 要主动写入 MEMORY.md,"每次会话都是全新的")

# Agent Skills
## web_search
搜索互联网获取最新信息
Check "/path/to/SKILL.md" for how to use this skill.
## code_executor
执行代码片段
Check "/path/to/SKILL.md" for how to use this skill.

{多模态提示}
(如果模型不支持多模态,提示 Agent 告知用户)

11.3 消息列表(Messages)完整结构

swift 复制代码
[
  // ===== 1. 系统提示词 =====
  {
    "role": "system",
    "content": "# AGENTS.md\n...(完整系统提示词)..."
  },

  // ===== 2. 压缩摘要(如果有被压缩过的消息)=====
  {
    "role": "assistant",
    "content": "Searching memory for relevant context..."
  },
  {
    "role": "tool_result",  // 伪装成工具结果
    "content": "## 压缩摘要\n\n之前的对话要点总结...(Markdown 格式,带 ## 标题分段)"
  },

  // ===== 3. 记忆搜索结果(如果匹配到相关长期记忆)=====
  {
    "role": "assistant",
    "content": "Searching memory for relevant context..."
  },
  {
    "role": "tool_result",
    "content": "找到的记忆片段:\n- {文件路径}:{行号}\n  {内容}\n- ..."
  },

  // ===== 4. 活跃对话历史 =====
  {
    "role": "user",
    "content": "你好"
  },
  {
    "role": "assistant",
    "content": "你好!有什么可以帮你的?"
  },
  {
    "role": "user",
    "content": "帮我写个 Python 脚本"
  },
  // ... Agent 的 ReAct 循环中间步骤(思考、工具调用、工具结果)...
  {
    "role": "assistant",
    "content": "好的,我已经为你创建了脚本..."
  },

  // ===== 5. 最新用户消息 =====
  {
    "role": "user",
    "content": "帮我创建一个文件"
  }
]

11.4 工具定义(Tools)结构

css 复制代码
[  {    "type": "function",    "function": {      "name": "read_file",      "description": "Read the content of a file at the given path",      "parameters": {        "type": "object",        "properties": {          "path": { "type": "string", "description": "File path to read" }        },        "required": ["path"]
      }
    }
  },
  {
    "type": "function",
    "function": {
      "name": "write_file",
      "description": "Write content to a file at the given path",
      "parameters": {
        "type": "object",
        "properties": {
          "path": { "type": "string", "description": "File path to write" },
          "content": { "type": "string", "description": "Content to write" }
        },
        "required": ["path", "content"]
      }
    }
  },
  {
    "type": "function",
    "function": {
      "name": "execute_shell",
      "description": "Execute a shell command",
      "parameters": {
        "type": "object",
        "properties": {
          "command": { "type": "string", "description": "Shell command to execute" }
        },
        "required": ["command"]
      }
    }
  },
  {
    "type": "function",
    "function": {
      "name": "memory_search",
      "description": "Search long-term memory for relevant information",
      "parameters": {
        "type": "object",
        "properties": {
          "query": { "type": "string", "description": "Search query" }
        },
        "required": ["query"]
      }
    }
  },
  {
    "type": "function",
    "function": {
      "name": "mcp_web_search",
      "description": "Search the internet (from MCP server)",
      "parameters": { "..." : "..." }
    }
  }
]

11.5 ReAct 循环中的消息流转

css 复制代码
第1次 LLM 调用:
  输入 → [system + 摘要 + 记忆 + 对话历史 + 用户消息]
  输出 → thought: "需要先读取文件" + tool_call: read_file("config.json")

第2次 LLM 调用:
  输入 → [system + 摘要 + 记忆 + 对话历史 + 用户消息
          + assistant(思考+工具调用) + tool_result(文件内容)]
  输出 → thought: "文件内容是..." + tool_call: write_file("output.txt", ...)

第3次 LLM 调用:
  输入 → [... + 之前所有步骤 ...]
  输出 → 最终回复文本(不再调用工具)

最多循环 max_iters(默认 10)次。

11.6 内容结构汇总图

sql 复制代码
┌─────────────────────────────────────────────┐
│             LLM API 请求                     │
├─────────────────────────────────────────────┤
│                                              │
│  messages:                                   │
│  ┌─ system ─────────────────────────────┐   │
│  │  AGENTS.md(工作流、规则)              │   │
│  │  SOUL.md(身份、原则)                 │   │
│  │  PROFILE.md(画像)                    │   │
│  │  记忆引导 prompt                      │   │
│  │  Skill 描述列表                       │   │
│  │  多模态提示                           │   │
│  └──────────────────────────────────────┘   │
│  ┌─ assistant + tool_result ────────────┐   │
│  │  压缩摘要(累积更新)                  │   │
│  └──────────────────────────────────────┘   │
│  ┌─ assistant + tool_result ────────────┐   │
│  │  长期记忆搜索结果(MEMORY.md 匹配)    │   │
│  └──────────────────────────────────────┘   │
│  ┌─ user / assistant / tool* ───────────┐   │
│  │  活跃对话历史(近期消息)               │   │
│  └──────────────────────────────────────┘   │
│  ┌─ user ───────────────────────────────┐   │
│  │  当前用户消息                         │   │
│  └──────────────────────────────────────┘   │
│                                              │
│  tools:                                      │
│  ┌─ 内置工具 ──────────────────────────┐    │
│  │  read_file, write_file, edit_file    │    │
│  │  execute_shell, list_directory       │    │
│  │  memory_search, memory_get           │    │
│  └──────────────────────────────────────┘   │
│  ┌─ MCP 工具 ──────────────────────────┐    │
│  │  mcp_web_search, mcp_*               │    │
│  └──────────────────────────────────────┘   │
│                                              │
└─────────────────────────────────────────────┘
相关推荐
用户1035702536811 小时前
Word 转 Markdown,一行命令搞定——AI 技能「docx2md」使用指南
人工智能
一切皆是因缘际会1 小时前
AI智能新时代
数据结构·人工智能·ai·架构
roshy1 小时前
作文高考作文
人工智能
明豆1 小时前
AI Agent 多智能体编排生产实战
人工智能
niuniuyi~1 小时前
科研阶段记录2-上
人工智能·知识图谱
astragin2 小时前
Wolfram Mathematica汉化版试用版下载入口
人工智能
Omics Pro2 小时前
3种蛋白结构输入方式!已申报欧洲发明专利
数据库·人工智能·python·机器学习·plotly
声光界2 小时前
《声音与音乐中的情感理解及人机交互设计》
人工智能·人机交互·声学
voidmort2 小时前
13. 强化学习中的评估、奖励设计与 Reward Hacking
人工智能