diff --git a/docs/reme_v2_design.md b/docs/reme_v2_design.md index 18d14657..6fa78140 100644 --- a/docs/reme_v2_design.md +++ b/docs/reme_v2_design.md @@ -1,378 +1,735 @@ -# ReMeV2 - agent memory kit design +# ReMeV2 深度设计文档:渐进式 Agentic Memory 方案 -ReMeV2 是一个面向智能体(Agent)的分层记忆系统,用于从连续的对话与交互中,逐步沉淀出稳定、可检索、可更新的长期记忆。 -系统强调从**原始轨迹 → 事件索引 → 高维抽象记忆**的分层存储,并通过**检索器(Retriever)**与**总结器(Summarizer)**实现动态更新。 +## 一、 背景与现状分析 + +### 1.1 当前面临的挑战 + +* **外功修炼(接口易用性)**:现有的 `server-client` 模式对新手开发者不够友好,集成成本高,需要更直观、纯 Pythonic 的调用方式。 +* **内功修炼(架构深度)**:受 `skills` 和 `agentic memory` 启发,现有的存储检索较为机械。我们需要一种基于**渐进式检索(Progressive Retrieval)**与**渐进式总结(Progressive Summarization)**的智能体记忆方案。 + +### 1.2 核心目标 + +1. **极简开发体验**:开发者友好,全异步接口,支持本地直接运行与 CLI 体验。 +2. **认知架构升级**:引入 渐进式检索 & 渐进式总结 的 Agentic 模式,融合多种记忆,让记忆的存取具备“思考”过程。 +3. **生态融合**:原生支持 AgentScope、LangChain 等主流框架。 --- -## 设计目标 +## 二、 竞品调研与启示 -本设计旨在构建一个可嵌入任意智能体框架(如对话 Agent、任务 Agent 等)的记忆子系统,使其具备以下能力: +### 2.1 主流竞品深度对比 + +| 产品 | 设计哲学 | 核心优势 | 局限性 | +|-------------|----------|---------------------------------------------|-------------------| +| **mem0** | 智能便签本 | 原子事实提取,极高 Token 效率。 | 缺乏对复杂逻辑链条的支持。 | +| **Letta** | 带硬盘的 CPU | 模拟计算机三级存储(Core/Recall/Archival),Agent 自主控存。 | 状态机管理相对复杂。 | +| **MIRIX** | 认知架构图谱 | 实体-关系双引擎,支持记忆“进化”与“固化”。 | 侧重研究,落地集成门槛较高。 | +| **LangMem** | 用户档案系统 | 异步 Compaction(压缩),Schema 驱动,强一致性。 | 偏向 SaaS 应用,灵活性略逊。 | + +### 2.2 mem0 +- https://github.com/mem0ai/mem0 +- https://docs.mem0.ai/core-concepts/memory-operations/add +- https://docs.mem0.ai/core-concepts/memory-operations/search +- https://docs.mem0.ai/core-concepts/memory-operations/update +- https://docs.mem0.ai/core-concepts/memory-operations/delete + +#### 2.2.1 API Reference +| 接口名称 | 核心输入参数 (Inputs) | 核心输出 (Outputs) | 背后逻辑 (Internal Logic) | +| --- | --- | --- | --- | +| **Add** | `messages` (文本/对话), `user_id`, `metadata` | `id`, `event` (ADD/UPDATE), `data` | **提取与合并**:LLM 提取事实,自动去重并更新已有记忆,而非简单堆叠。 | +| **Search** | `query` (自然语言), `filters`, `limit` | `id`, `memory` (事实文本), `score`, `metadata` | **语义检索**:基于向量相似度查找最相关的“原子事实”,支持多维过滤。 | +| **Update** | `memory_id` (必填), `data` (新内容) | 操作状态 (Success/Fail) | **手动干预**:允许开发者对特定的事实进行精确修正。 | +| **Delete** | `memory_id` 或 `user_id` (清空) | 操作状态 (Success/Fail) | **遗忘机制**:物理删除或逻辑移除不再需要的信息。 | + +#### 2.2.2 Tech Strategy & Benefits +| 维度 | 技术方案 (Technical Solution) | 核心优势 (Key Advantages) | +| --- | --- | --- | +| **存储架构** | **混合存储**:向量数据库 (Vector) + 图数据库 (Graph) + 关系型元数据。 | **多维关联**:不仅能搜到相似内容,还能理解实体间的逻辑关系(如“父子”、“因果”)。 | +| **数据处理** | **原子化事实提取**:利用 LLM 将长篇对话压缩为简短的 Fact。 | **极高 Token 效率**:注入 Prompt 的内容更精炼,减少 90% 以上的冗余信息,大幅降本。 | +| **管理层级** | **多级联动**:User (长期) Agent (专业) Session (短期)。 | **个性化定制**:实现跨会话的“长效记忆”,AI 能记住用户一个月前说过的偏好。 | +| **冲突处理** | **自适应更新算法**:新信息进入时自动比对旧记忆。 | **数据一致性**:自动处理矛盾信息(如用户更换了住址),确保记忆库始终是“最新真理”。 | +| **兼容性** | **解耦设计**:支持多种 Embedding 模型与向量数据库后端。 | **快速集成**:几行代码即可为现有 LLM 应用增加记忆层,适配各种生产环境。 | -- **支持多天、多会话的长期记忆积累**,不丢失原始细节。 -- **在推理时按需检索**与当前问题高度相关的记忆片段。 -- **在后台持续做分层总结与压缩**,提炼出抽象的“世界观 / 自我认知 / 用户画像 / 任务知识 / 工具经验”。 -- **支持记忆的增删改**,能够对过时或冲突信息进行更新,保持长期记忆的时效性与一致性。 -- **支持渐进式检索**,从高层抽象记忆逐步回溯到中层事件索引与底层原始轨迹,按需展开细节、控制上下文长度与推理成本。 -- **支持渐进式总结**,从最新对话开始按块增量总结并更新索引与主题记忆,而非每次对全量历史做重复总结。 --- -## 架构概览 +### 2.3 Letta +- https://github.com/letta-ai/letta +- https://docs.letta.com/guides/agents/archival-memory/ +- https://docs.letta.com/guides/agents/archival-search/ -系统整体可以分为两个维度来看: +#### 2.3.1 存储架构层级 (Memory Tiering) -- **静态维度**:记忆如何被分层存储(底层消息块 → 中层索引 → 顶层主题记忆)。 -- **动态维度**:记忆如何被检索与更新(上行 Summarizer、下行 Retriever)。 +Letta 将记忆分为三个物理/逻辑层,模拟计算机的存储架构: -核心组件包括: +| 记忆层级 | 存储介质 | 访问方式 | 核心作用 | +| --- | --- | --- | --- | +| **Core Memory** | **上下文窗口 (Prompt)** | 直接读写 | **即时意识**:包含 `Persona`(AI 设定)和 `Human`(用户信息)。Agent 随时可见,响应最快。 | +| **Recall Memory** | **关系型数据库 (SQL)** | 分页检索 | **短期/历史回顾**:存储完整的对话流(Messages)。用于回答“你刚才说了什么”。 | +| **Archival Memory** | **向量数据库 (Vector)** | 语义搜索 | **长期知识库**:存储海量事实或文档。Agent 通过工具自主检索或存入。 | -- **底层记忆层(Raw Dialogue Store)**:多天对话轨迹切分成 `messages block`。 -- **中层索引层(Index & Summary Store)**:为每个 `messages block` 生成 `block_id: time + summary`。 -- **顶层主题记忆层(High-level Memories)**:自我认知、任务记忆、工具记忆、用户画像等高维抽象。 -- **Meta Agent(记忆中枢)**:持有高层记忆视图与索引,负责路由检索与更新请求。 -- **Sub Agents(记忆子智能体)**:面向具体主题(如某个用户、某类任务、某组工具)的记忆管理单元。 -- **Retriever(检索器)**:从高层到低层,渐进式查找相关记忆。 -- **Summarizer(总结器)**:从低层到高层,渐进式抽象和更新长期记忆。 +#### 2.3.2 核心操作接口 (API & Tool Reference) + +在 Letta 中,记忆的操作通常封装为 **Tools**,由 Agent 根据推理需求主动调用。 + +| 接口/工具名称 | 输入参数 (Inputs) | 核心输出 (Outputs) | 背后逻辑 (Internal Logic) | +| --- | --- | --- | --- | +| **`core_memory_update`** | `section`, `new_content` | 更新后的段落内容 | **原子替换**:直接修改 System Prompt 中的特定块(如:更新用户的职业或 AI 的性格偏好)。 | +| **`archival_memory_insert`** | `content` (字符串) | 写入状态/ID | **知识沉淀**:将当前对话中的重要信息或外部文件片段“持久化”到向量数据库。 | +| **`archival_memory_search`** | `query`, `page` | 匹配的文本块列表 | **主动 RAG**:Agent 意识到知识不足时,自主发起向量检索,并将结果拉入临时上下文。 | +| **`conversation_search`** | `query`, `start_date` | 历史消息记录 | **全文检索**:在 Recall Memory 中根据关键词或时间戳查找历史对话详情。 | +| **`send_message`** | `message`, `agent_id` | 响应流/状态更新 | **状态循环**:这是主入口,触发 Agent 的“思考-行动-观察”循环,自动处理内存同步。 | + +#### 2.3.3 技术策略与核心优势 (Tech Strategy & Benefits) + +| 维度 | 技术方案 (Technical Solution) | 核心优势 (Key Advantages) | +| --- | --- | --- | +| **状态持久化** | **Agent State Snapshot**:将 Agent 的所有内存、工具定义和历史记录打包存入数据库。 | **无限存续**:Agent 不再是无状态的 API 调用。重启服务器后,Agent 依然记得所有细节。 | +| **自主演进** | **Self-Editing Loop**:Agent 拥有修改自己 Core Memory 的权限(通过函数调用)。 | **认知闭环**:AI 能在交流中发现矛盾并自我更正,例如发现用户搬家后自动更新 `Human` 模块。 | +| **算力调度** | **OOC (Out-of-Context) 管理**:当对话过长,系统自动将旧消息从 Core 移入 Recall。 | **突破 Context 限制**:在 8k 窗口的模型上也能处理相当于 1M 窗口的逻辑量,且成本更低。 | +| **多代理协同** | **Letta Server 中控**:统一管理多个 Agent 的状态机与资源访问权限。 | **企业级扩展**:支持创建 Agent 团队,每个 Agent 拥有独立的记忆空间但可共享 Archival 库。 | +| **解耦灵活性** | **Provider Agnostic**:后端支持 Postgres/Chroma,前端支持 OpenAI/Anthropic/Local LLMs。 | **无缝迁移**:不绑定特定模型,开发者可以根据成本或能力随时更换底座。 | + +#### 2.3.4 与 mem0 的深度对比 + +* **设计哲学**: +* **mem0** 像是一个**“智能记事本”**,它在后台默默地帮你总结事实。 +* **Letta** 像是一个**“带硬盘的 CPU”**,它把记忆管理完全交给了 Agent 自己的逻辑推理。 + + +* **交互模式**: +* **mem0** 通常是外部干预(Add/Search)。 +* **Letta** 强调 **Agentic Control**(Agent 意识到需要搜索时才去搜索),这种模式更接近人类的思维过程。 --- -## 静态记忆三层架构 +### 2.4 MIRIX +- https://github.com/Mirix-AI/MIRIX +- https://docs.mirix.io/ -### 垂直层级划分 +#### 2.4.1 API Reference -系统将记忆分为自下而上的三个层级,抽象程度逐级升高: +| 接口名称 | 核心输入参数 (Inputs) | 核心输出 (Outputs) | 背后逻辑 (Internal Logic) | +| --- | --- | --- | --- | +| **Add** | `content` (观察/对话), `agent_id`, `context_type` (如任务/闲聊) | `memory_id`, `graph_nodes`, `status` | **实体建模**:不只是提取事实,而是将信息拆解为实体(Entities)与关系(Relations),并挂载到智能体的知识图谱中。 | +| **Query** | `query` (意图), `scope` (全局/局部), `top_k` | `retrieved_memories`, `relation_paths`, `score` | **混合检索**:结合向量(Vector)的语义相关性和图(Graph)的拓扑连接性,寻找具有逻辑深度背景的记忆。 | +| **Evolve** | `target_memories` (可选), `agent_id` | `optimized_structure`, `merged_nodes` | **记忆固化/压缩**:模仿人类大脑的“睡眠”机制,自动合并碎片化记忆,将短期经验转化为长期的结构化知识。 | +| **Observe** | `interaction_stream`, `feedback` | `insights`, `priority_update` | **实时学习**:根据用户反馈或环境变化,动态调整记忆的权重(Importance)和置信度。 | -- **底层:记忆碎片(历史对话 / 轨迹)** - - 存放原始的、未经压缩的对话与交互记录。 - - **规则**:不去重、不解冲突,只做追加与切片。 +#### 2.4.2 Tech Strategy & Benefits -- **中层:记忆索引(事件总结)** - - 对原始消息块做首轮总结,形成“时间 + 简要摘要”的事件索引。 - - 连接底层细节与顶层抽象。 - - **规则**:允许存在部分冗余与矛盾,主要目标是加速检索。 +| 维度 | 技术方案 (Technical Solution) | 核心优势 (Key Advantages) | +| --- | --- | --- | +| **存储架构** | **语义-关系双引擎**:向量索引(Vector Index)+ 属性图(Property Graph)。 | **深度上下文**:不仅知道“是什么”,还能通过图路径推理出“为什么”,有效解决 LLM 幻觉问题。 | +| **记忆层级** | **三层架构**:感知记忆 (Perception) -> 语义记忆 (Semantic) -> 经验记忆 (Episodic)。 | **任务适应性**:不同任务自动匹配不同的记忆深度,短期任务关注细节,长期任务关注模式。 | +| **演化机制** | **自主固化 (Self-Consolidation)**:通过 LLM 定期对冗余、矛盾信息进行清洗和逻辑抽象。 | **永久生命力**:解决随时间推移记忆库膨胀导致的检索噪声,确保记忆库“越用越聪明”。 | +| **推理增强** | **基于记忆的 RAG+**:在检索到的事实基础上,额外提供关联的逻辑链条(Logic Chains)。 | **辅助决策**:为 Agent 提供决策支撑,使其在处理复杂流程时具备类似“长期经验值”的直觉。 | +| **多代理协同** | **内存共享协议**:支持 Agent 之间的记忆交换与知识同步。 | **群体智能**:多个 Agent 可以共享同一套底层知识体系,同时保留各自的私有工作记忆。 | -- **顶层:主题记忆(高维抽象知识)** - - 聚合并抽象出关于“自我、任务、工具、用户”等长期知识。 - - **规则**:需要去重(Deduplication)与解冲突(Resolve Conflict),追求紧凑、一致、可解释。 +#### 2.4.3 与 mem0 的主要区别 -### 底层:原始对话与轨迹(Raw Dialogue) - -- 按时间顺序堆叠的对话与行为轨迹: - - `Day1 Messages → Day2 Messages → ... → DayN Messages` -- 通过时间窗口、业务逻辑、固定长度(可重叠)等策略切分为多个 `messages block`: - - `Day1 Messages Block1` - - `Day1 Messages Block2` - - `DayN Messages BlockN` -- 特性: - - 不丢失任何历史细节。 - - 支持后续回溯、审计与再训练。 - -### 中层:记忆索引与事件总结(Index & Summary) - -- 对每个 `messages block` 生成一个结构化索引: - - 格式:`block_id: time + summary` -- 示例: - - `block_id1: 2025-11-11 + 用户提到他非常喜欢吃西瓜。` - - `block_id2: 2025-11-12 + 用户表达自己很孤独。` -- 作用: - - 作为底层原始消息与顶层抽象记忆之间的“桥梁”与“目录”。 - - 为时间感知检索、语义检索提供最小粒度的事件单元。 - -### 顶层:主题记忆(High-Dimensional Abstraction) - -顶层记忆由多个“记忆气泡”(Memory Bubble)构成,每个气泡代表一类主题知识,并通过关联的 `block_id` 与中层索引相连。主要包括: - -- **自我认知记忆(Self-Cognition Memory)** - - 示例: - - “我叫 Remy” - - “我每天需要上班” - - “我是一名面向长期陪伴的智能助手” - - 位置:处于记忆拓扑中心,与其他记忆类别广泛连接。 - -- **金融任务记忆(Financial Task Memory)** - - 示例: - - “做财报分析前需要调研企业的经营数据” - - “估值时可以优先考虑 forward PE” - - 作用:支撑在特定领域(如金融)的任务推理能力。 - -- **工具记忆(Tool Memory)** - - 示例: - - “`bing_search` 很不稳定,经常失败” - - “`tongyi_search` 的结果质量较好,常需要配合 `web_extract` 使用” - - 作用:让智能体在工具选择、调用顺序和错误预期上具有“经验”。 - -- **用户画像记忆(Per-User Memory,如用户 A / 用户 B)** - - 对用户 A 的记忆示例: - - “A 很喜欢吃西瓜” - - “A 非常有礼貌” - - “A 计划下周出行” - - 与中层事件关联: - - 顶层的“喜欢吃西瓜”记忆与 `block_id1` 建立逻辑连接。 - - 每个用户独立维护一套长期画像,避免混淆。 +* **Mem0** 侧重于**个性化偏好存储**(Personalization),核心是记住“用户喜欢什么”。 +* **MIRIX** 侧重于**智能体认知架构**(Agent Cognition),核心是让 Agent 具备类似人类的“知识归纳”和“逻辑推理”记忆能力。 --- -## 动态流程一:ReMeV2 Retriever(检索流程) +### 2.5 LangMem +- https://github.com/langchain-ai/langmem +- https://langchain-ai.github.io/langmem/ -检索流程是一个**自上而下**的渐进式过程:从高维抽象出发,逐步定位到具体事件甚至原始对话块,为当前问答或决策提供证据。 +#### 2.5.1 API Reference -```mermaid -flowchart TD - op0[op: 当前查询 / 上下文输入] - op1[op: Meta Agent 读取自我认知与主题索引] - op2[op: Meta Agent 基于 topic/intent 选择记忆子空间] - op3[op: 调度对应 Sub Agent 实例] - op4[op: 在顶层/中层记忆空间检索相关 Memories / 索引条目] - op5[op: 根据需要回溯到底层 messages block(retrieve_block)] - op6[op: 汇总得到的 messages block 列表,构造检索上下文] - op7[op: 将上下文交给上层 Agent 做回答 / 决策] +| 接口名称 | 核心输入参数 (Inputs) | 核心输出 (Outputs) | 背后逻辑 (Internal Logic) | +| --- | --- | --- | --- | +| **Add Messages** | `thread_id`, `messages` (List), `user_id` | 操作确认 / 任务 ID | **流式注入**:将原始对话追加到指定的 Thread。LangMem 会自动关联用户上下文,准备进行后续的异步处理。 | +| **Query Memory** | `user_id`, `query` (语义描述), `namespace` | 结构化记忆对象 (JSON / Text) | **多维检索**:不仅支持向量相似度搜索,还能根据定义的 Schema 返回结构化的用户画像或知识状态。 | +| **Trigger Logic** | `thread_id`, `memory_type` | 更新后的 Memory State | **异步固化**:后台启动 LLM 任务,将长篇对话“压缩”并“提取”到长期存储中。支持自定义提取逻辑(如更新用户信息)。 | +| **Manage State** | `user_id`, `patch_data` (增量更新) | 成功/失败 状态 | **精确受控**:开发者可以直接修改持久化的状态(State),支持类似于 Git 的状态管理。 | - op0 --> op1 --> op2 --> op3 --> op4 --> op5 --> op6 --> op7 +#### 2.5.2 Tech Strategy & Benefits + +| 维度 | 技术方案 (Technical Solution) | 核心优势 (Key Advantages) | +| --- | --- | --- | +| **存储架构** | **Stateful Persistence**:基于关系型数据库 (Postgres) + 向量索引。 | **强一致性**:利用数据库事务确保记忆更新的可靠性,支持复杂的结构化查询与过滤。 | +| **数据处理** | **异步化 Compaction (压缩)**:在对话间隙通过后台 Worker 提取知识。 | **无感延迟**:核心对话流程不被记忆提取阻塞,通过定时或事件驱动完成“记忆固化”,优化用户体验。 | +| **管理层级** | **Thread -> User -> Organization**:三层级联记忆。 | **上下文隔离**:完美适配 SaaS 应用场景,既能记住单次对话(Thread),也能沉淀用户习惯(User)。 | +| **逻辑引擎** | **Schema-Driven (模式驱动)**:允许定义 JSON Schema 来规范记忆内容。 | **高度可预测**:输出不再是散乱的句子,而是结构化的字段,方便下游程序直接调用逻辑(如自动填充表单)。 | +| **集成生态** | **LangGraph 原生集成**:作为 Checkpointer 或存储节点直接接入。 | **生态协同**:如果你已经在用 LangChain,LangMem 可以无缝接管状态流转,无需重写底层存储逻辑。 | + +#### 2.5.3 与 mem0 的核心差异 + +* **mem0** 像是一个**“便签本”**:它擅长从每一句话里抠出零散的事实(如“我喜欢吃苹果”),然后把它们存成一条条语义片段。 +* **LangMem** 像是一个**“用户档案系统”**:它更擅长分析一整段对话,然后更新一个复杂的 JSON 档案(如更新用户的偏好模型、性格标签、历史任务状态)。 + +--- + +## 三、 ReMeV2 API 接口设计 + +### 3.1 Long-Term Memory (长期记忆) + +#### 3.1.1 Basic Usage (基础用法) + +The most straightforward way to use ReMe for long-term memory management. Supports basic summary and retrieval operations. + +```python +import os +from reme_ai import ReMe + +os.environ["REME_LLM_API_KEY"] = "sk-..." +os.environ["REME_LLM_BASE_URL"] = "https://dashscope.aliyuncs.com/compatible-mode/v1" +os.environ["REME_EMBEDDING_API_KEY"] = "sk-..." +os.environ["REME_EMBEDDING_BASE_URL"] = "https://dashscope.aliyuncs.com/compatible-mode/v1" + +memory = ReMe( + memory_space="remy", # workspace identifier + llm={"backend": "openai", "model": "qwen-plus", "temperature": 0.6}, + embedding={"backend": "openai", "model": "text-embedding-v4", "dimension": 1024}, + vector_store={"backend": "local_file"}, # supported: local_file, chromadb, qdrant, etc. +) + +# Summarize conversation into memory +result = await memory.summary( + messages=[ + {"role": "user", "content": "I'm travelling to SF"}, + {"role": "assistant", "content": "That's great to hear!"} + ], + user_id="Alice", + # memory_type="auto" # default: auto (auto, personal, procedural, tool) +) + +# Retrieve relevant memories +memories = await memory.retrieve( + query="what is your travel plan?", + limit=3, + user_id="Alice", + # memory_type="auto" # default: auto +) +memories_str = "\n".join(f"- {m['memory']}" for m in memories["results"]) +print(memories_str) ``` -### Meta Agent:记忆入口与路由 +#### 3.1.2 CLI Chat Application (命令行聊天应用) -- 持有: - - **常驻的自我认知记忆**(总是加载在工作内存中)。 - - 各类主题记忆的**名称 + 描述索引**,例如: - - `1. 金融任务记忆` - - `2. 工具记忆` - - `3. 用户A记忆` - - `4. 用户B记忆` -- 职责: - - 根据当前查询的 **topic / intent**,选择合适的记忆子空间。 - - 按需创建或调度具体的 `Sub_Agent` 来完成深入检索。 +A complete example demonstrating how to build a memory-enhanced chatbot with CLI interface. -### Sub Agent:按主题检索 +```python +import os +from reme_ai import ReMe +from openai import OpenAI -- **预定义子智能体**(如:工具记忆 Agent、某个用户画像 Agent 等): - - 支持接口: - - `retrieve_by_query(query)`:基于语义相似度的检索。 - - `time_aware_retrieve(time)`:基于时间的窗口检索(适合用户时间线类记忆)。 -- **自定义子智能体**: - - 可以实现更复杂的检索逻辑(例如结合时间衰减、多源融合等)。 +os.environ["REME_LLM_API_KEY"] = "sk-..." +os.environ["REME_LLM_BASE_URL"] = "https://dashscope.aliyuncs.com/compatible-mode/v1" +os.environ["REME_EMBEDDING_API_KEY"] = "sk-..." +os.environ["REME_EMBEDDING_BASE_URL"] = "https://dashscope.aliyuncs.com/compatible-mode/v1" -### 渐进式检索与回溯 +memory = ReMe( + memory_space="remy", + llm={"backend": "openai", "model": "qwen-plus", "temperature": 0.6}, + embedding={"backend": "openai", "model": "text-embedding-v4", "dimension": 1024}, + vector_store={"backend": "local_file"}, +) -- 子智能体首先在顶层 / 中层记忆空间中找到语义相关的 **Memories / 索引条目**: - - 每条记忆通常携带 `block_id` 与简要 summary。 -- 若需要更细节的证据,系统可以进一步调用: - - `retrieve_block(block_id)`:回溯到底层 `messages block`。 - - `time_match_memories(time_range)`:根据时间窗口获取原始对话块。 -- 最终输出: - - 一组与当前问题高度相关的 `messages block List`,作为上下文,供 Agent 生成回答或做决策。 +os.environ["OPENAI_API_KEY"] = "sk-..." +os.environ["OPENAI_BASE_URL"] = "https://dashscope.aliyuncs.com/compatible-mode/v1" +openai_client = OpenAI() ---- +def chat_with_memories( + query: str, + history_messages: list[dict], + user_name: str = "", + start_summary_size: int = 2, + keep_size: int = 0 +) -> str: + # Retrieve relevant memories for the query + memories = memory.retrieve(query=query, user_id=user_name, limit=3) -## 动态流程二:ReMeV2 Summarizer(总结流程) + # Build system prompt with memories + system_prompt = ( + "You are a helpful AI named `Remy`. Use the user memories to answer the question. " + "If you don't know the answer, just say you don't know. Don't try to make up an answer.\n" + ) + if memories: + memories_str = "\n".join(f"- {m['memory']}" for m in memories["results"]) + system_prompt += f"User Memories:\n{memories_str}\n" -总结流程是一个**自下而上**的学习与更新过程:将新发生的对话转化为结构化事件,再进一步纳入长期记忆。 + # Generate response + system_message = {"role": "system", "content": system_prompt} + history_messages.append({"role": "user", "content": query}) + response = openai_client.chat.completions.create( + model="qwen-plus", + messages=[system_message] + history_messages + ) + history_messages.append({"role": "assistant", "content": response.choices[0].message.content}) -```mermaid -flowchart TD - op0[op: 收集 DayN 的原始对话与行为轨迹] - op1[op: 按时间/业务逻辑/固定长度切片为 messages block] - op2[op: 对每个 block 做摘要,生成 block_id: time + summary] - op3[op: 将 block summary 流交给 Meta Agent] - op4[op: Meta Agent 判断主题并路由/创建对应 Sub Agent] - op5[op: Sub Agent 加载当前主题的长期记忆与新 block] - op6[op: 通过 recall_similar_memory 检索历史相似记忆] - op7[op: 对比新旧信息,决定 delete/new/update 记忆操作] - op8[op: 写回更新后的高层主题记忆,维护 block_id 链接] - op9[op: 将最新记忆视图回流给 Meta Agent,用于后续检索] + # Summarize history when it gets too long + if len(history_messages) >= start_summary_size: + memory.summary(history_messages[:-keep_size], user_id=user_name) + print("Current memories: " + memory.list_memories(user_id=user_name)) + history_messages = history_messages[-keep_size:] - op0 --> op1 --> op2 --> op3 --> op4 --> op5 --> op6 --> op7 --> op8 --> op9 + return history_messages[-1]["content"] + +def main(): + user_name = input("Enter your name: ").strip() + print("Chat with Remy (type 'exit' to quit)") + + messages = [] + while True: + user_input = input(f"{user_name}: ").strip() + if user_input.lower() == 'exit': + print("Goodbye!") + break + + print(f"Remy: {chat_with_memories(user_input, messages, user_name)}") + + # Cleanup + memory.delete_all_memories(user_id=user_name) + print("All memories deleted") + +if __name__ == "__main__": + main() ``` -### 输入与切片 +#### 3.1.3 Advanced Usage (高级用法) -- 输入源: - - `DayN Messages / Trajectory`(第 N 天的完整对话与行为轨迹)。 -- 切片策略: - - 按时间窗口切片(例如每 5 分钟一个 block)。 - - 按业务逻辑边界切片(如一个任务完成即成一个 block)。 - - 按固定长度(可带重叠)切片,兼顾局部上下文。 -- 结果: - - `messages block1, block2, ... blockN` +For advanced users who want to customize retriever and summarizer behavior with Agentic mode. -### 初步总结:生成索引条目 +```python +import os +from reme_ai import ReMe +from reme_ai.retriever import AgenticRetriever +from reme_ai.summarizer import AgenticSummarizer +from reme_ai.tools import ATool, BTool, CTool -- 对每个 `messages block` 进行摘要,形成: - - `block_id: time + summary` -- 示例: - - `id: 20251111_01 - 用户询问“什么是 AI”并表达对技术的好奇。` -- 这一阶段的目标: - - 为中层索引层提供结构化事件单元,尚不过度抽象。 +os.environ["REME_LLM_API_KEY"] = "sk-..." +os.environ["REME_LLM_BASE_URL"] = "https://dashscope.aliyuncs.com/compatible-mode/v1" +os.environ["REME_EMBEDDING_API_KEY"] = "sk-..." +os.environ["REME_EMBEDDING_BASE_URL"] = "https://dashscope.aliyuncs.com/compatible-mode/v1" -### Meta Agent 路由与子智能体更新 +memory = ReMe( + memory_space="remy", + llm={"backend": "openai", "model": "qwen-plus", "temperature": 0.6}, + embedding={"backend": "openai", "model": "text-embedding-v4", "dimension": 1024}, + vector_store={"backend": "local_file"}, + use_agentic_mode=True, +) -- Meta Agent 接收新的 `block summary` 流: - - 根据内容判断其归属主题(例如用户相关、任务相关、工具体验相关等)。 - - 调用 `create_sub_agent(topic)` 或路由到已有的 `Sub_Agent`。 -- 子智能体内的处理步骤: - - **Step 1:加载上下文记忆** - - 加载与当前主题相关的长期记忆(如“对用户 A 的记忆”)与当前 `messages block`。 - - **Step 2:回忆(Recall)** - - 通过 `recall_similar_memory(query)` 拉取历史相似记忆,用于对比新旧信息。 - - **Step 3:更新(Update)** - - 基于新旧信息的对比,决定对顶层记忆的操作: - - `delete_memory(mem_id)`:删除已过时或被否定的记忆。 - - `new_memory(mem_content)`:新增一条新的长期记忆。 - - `update_memory(mem_id, mem_content)`:对既有记忆做内容更新或细化。 +# Customize retriever and summarizer with custom tools and prompts +memory.set_retriever( + AgenticRetriever(tools=[ATool(), BTool(), CTool()]), + system_prompt="Custom retrieval instructions..." +) +memory.set_summarizer( + AgenticSummarizer(tools=[ATool(), BTool(), CTool()]) +) + +# Use the customized memory system +result = memory.summary( + messages=[ + {"role": "user", "content": "I'm travelling to SF"}, + {"role": "assistant", "content": "That's great to hear!"} + ], + user_id="Alice", + memory_type="auto", # auto, personal, procedural, tool +) + +memories = memory.retrieve( + query="what is your travel plan?", + limit=3, + user_id="Alice", + memory_type="auto", +) +memories_str = "\n".join(f"- {m['memory']}" for m in memories["results"]) +print(memories_str) +``` + +### 3.2 Short-Term Memory (短期记忆) + +#### 3.2.1 Basic Usage (基础用法) + +Context offload/reload API for managing short-term conversational memory within a session. + +```python +import os +from reme_ai import ReMe + +os.environ["REME_LLM_API_KEY"] = "sk-..." +os.environ["REME_LLM_BASE_URL"] = "https://dashscope.aliyuncs.com/compatible-mode/v1" +os.environ["REME_EMBEDDING_API_KEY"] = "sk-..." +os.environ["REME_EMBEDDING_BASE_URL"] = "https://dashscope.aliyuncs.com/compatible-mode/v1" + +memory = ReMe( + memory_space="remy", + llm={"backend": "openai", "model": "qwen-plus", "temperature": 0.6}, + embedding={"backend": "openai", "model": "text-embedding-v4", "dimension": 1024}, + vector_store={"backend": "local_file"}, +) + +# Offload context when conversation gets too long +result = memory.offload_context( + messages=[ + {"role": "user", "content": "I'm travelling to SF"}, + {"role": "assistant", "content": "That's great to hear!"} + ], +) + +# Reload relevant context when needed +memories = memory.reload_context( + query="what is your travel plan?", + limit=3, +) +memories_str = "\n".join(f"- {m['memory']}" for m in memories["results"]) +print(memories_str) +``` + +### 3.3 Framework Integration (框架集成) + +#### 3.3.1 Integration with AgentScope + +Integration example for AgentScope ReActAgent with long-term memory support. + +```python +# TODO: Provide AgentScope integration example +``` + +#### 3.3.2 Integration with LangChain + +Integration example for LangChain agents with ReMe memory layer. + +```python +# TODO: Provide LangChain integration example +``` + +### 3.4 OpenAI Compatible Interface + +OpenAI-compatible API interface for seamless integration with existing OpenAI-based applications. + +```python +# TODO: Research and implement OpenAI-compatible interface +# - Support for threads and assistants API +# - Compatible with OpenAI SDK +# - Support for streaming responses +``` -### 输出与闭环 -- Summarizer 输出一批新的或更新后的 **高层记忆项(New Memories)**,并附带 `block_id` 链接。 -- 这些记忆将: - - 被 Meta Agent 纳入其主题索引中。 - - 为后续 Retriever 流程提供可检索的抽象知识。 -- 至此,形成从“原始对话 → 索引总结 → 高维记忆 → 检索使用 → 再次总结更新”的完整闭环。 --- -## 总结 +## 四、核心方案设计 -- **分层存储**:底层保留原始细节,中层作为事件索引,顶层沉淀抽象认知;中层通过 `block_id` 将两端连通。 -- **按需调用**:检索时从高层主题记忆出发,由 Meta Agent 路由到 Sub Agent,再逐级回溯到底层对话。 -- **动态增删改**:新对话先被切片与总结,再与旧记忆对比,通过新增、更新、删除操作保持长期记忆“准确 + 精简 + 有时序感”。 -- **渐进式检索与总结**:在检索方向自上而下逐级展开,在学习方向自下而上逐级抽象,实现“用多少展开多少、学多少沉淀多少”的流式记忆管理。 +### 4.1 设计概述 +ReMeV2 采用简洁的架构设计,核心理念为:**ReMeV2 = Tool(s) + Agent(s)** -``` python code -base_memory中增加 -# block_id: str = Field(default=..., description="block_id") -block_ids: List[str] = Field(default=..., description="block_id") +- **Tool层**:提供原子化的记忆操作能力,包括增删改查、检索、元数据管理等基础操作 +- **Agent层**:基于Tool层构建的智能代理,负责复杂的记忆管理逻辑,如分类总结、渐进式检索等 +- **Runtime层**:内部调度机制,协调Tool和Agent的交互流程 +### 4.2 Tool层设计 -from typing import List, Tuple +Tool层提供装饰器形式的记忆操作工具,每个工具类通过 `@tool` 装饰器注册,明确定义初始化参数和调用参数。 -from flowllm.core.schema import Message -from pydantic import BaseModel, Field +#### 4.2.1 基类:BaseMemoryToolOp -class MessageModel(Message): +**初始化参数:** +- `enable_multiple` (bool): Enable multi-item operation mode. Default: `True` +- `enable_thinking_params` (bool): Include thinking parameter in tool schema for model reasoning. Default: `False` +- `memory_metadata_dir` (str): Directory path for storing memory metadata. Default: `"./memory_metadata"` + +#### 4.2.2 Tool操作列表 + +以下是所有Tool操作的完整定义,包括继承关系、初始化参数和调用参数: + +| Tool类 | 继承自 | 初始化参数(除基类外) | Tool Call参数(单项模式) | Tool Call参数(多项模式) | +|----------------------------|------------------|------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------| +| **AddMemoryOp** | BaseMemoryToolOp | `add_when_to_use` (bool, 默认: False)
`add_metadata` (bool, 默认: True) | `when_to_use` (str, 可选)
`memory_content` (str, 必需)
`metadata` (dict, 可选) | `memories` (array, 必需):
- `when_to_use` (str, 可选)
- `memory_content` (str, 必需)
- `metadata` (dict, 可选) | +| **UpdateMemoryOp** | BaseMemoryToolOp | 无 | `memory_id` (str, 必需)
`memory_content` (str, 必需)
`metadata` (dict, 可选) | `memories` (array, 必需):
- `memory_id` (str, 必需)
- `memory_content` (str, 必需)
- `metadata` (dict, 可选) | +| **DeleteMemoryOp** | BaseMemoryToolOp | 无 | `memory_id` (str, 必需) | `memory_ids` (array[str], 必需) | +| **VectorRetrieveMemoryOp** | BaseMemoryToolOp | `enable_summary_memory` (bool, 默认: False)
`add_memory_type_target` (bool, 默认: False)
`top_k` (int, 默认: 20) | `query` (str, 必需)
`memory_type` (str, 可选, 枚举: [identity, personal, procedural])
`memory_target` (str, 可选) | `query_items` (array, 必需):
- `query` (str, 必需)
- `memory_type` (str, 可选)
- `memory_target` (str, 可选) | +| **AddMetaMemoryOp** | BaseMemoryToolOp | 无 | `memory_type` (str, 必需, 枚举: [personal, procedural])
`memory_target` (str, 必需) | `meta_memories` (array, 必需):
- `memory_type` (str, 必需)
- `memory_target` (str, 必需) | +| **ReadMetaMemoryOp** | BaseMemoryToolOp | `enable_tool_memory` (bool, 默认: False)
`enable_identity_memory` (bool, 默认: False) | 无(无输入schema) | N/A (enable_multiple=False) | +| **AddHistoryMemoryOp** | BaseMemoryToolOp | 无 | `messages` (array[object], 必需) | N/A (enable_multiple=False) | +| **ReadHistoryMemoryOp** | BaseMemoryToolOp | 无 | `memory_id` (str, 必需) | `memory_ids` (array[str], 必需) | +| **AddSummaryMemoryOp** | AddMemoryOp | 无(继承自AddMemoryOp) | `summary_memory` (str, 必需)
`metadata` (dict, 可选) | N/A (enable_multiple=False) | +| **ReadIdentityMemoryOp** | BaseMemoryToolOp | 无 | 无(无输入schema) | N/A (enable_multiple=False) | +| **UpdateIdentityMemoryOp** | BaseMemoryToolOp | 无 | `identity_memory` (str, 必需) | N/A (enable_multiple=False) | +| **ThinkToolOp** | BaseAsyncToolOp | `add_output_reflection` (bool, 默认: False) | `reflection` (str, 必需) | N/A | +| **HandsOffOp** | BaseMemoryToolOp | 无 | `memory_type` (str, 必需, 枚举: [identity, personal, procedural, tool])
`memory_target` (str, 必需) | `memory_tasks` (array, 必需):
- `memory_type` (str, 必需)
- `memory_target` (str, 必需) | + +### 4.3 Agent层设计 + +#### 4.3.1 基类:BaseMemoryAgentOp + +Agent层构建在Tool层之上,封装复杂的记忆管理逻辑。每个Agent通过组合多个Tool实现特定的记忆管理任务。 + +#### 4.3.2 Agent操作列表 + +以下是所有Agent操作的完整定义,包括初始化参数、调用参数和可用工具: + +| Agent类 | 继承自 | 初始化参数(基类外) | Tool Call参数 | 可用工具 | +|--------------------------------|-------------------|------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------| +| **PersonalSummaryAgentV1Op** | BaseMemoryAgentOp | None | `workspace_id` (str, required)
`memory_target` (str, required)
`query` (str, optional)
`messages` (array, optional)
`ref_memory_id` (str, required) | add_memory
update_memory
delete_memory
vector_retrieve_memory | +| **ProceduralSummaryAgentV1Op** | BaseMemoryAgentOp | None | `workspace_id` (str, required)
`memory_target` (str, required)
`query` (str, optional)
`messages` (array, optional)
`ref_memory_id` (str, required) | add_memory
update_memory
delete_memory
vector_retrieve_memory | +| **ToolSummaryAgentV1Op** | BaseMemoryAgentOp | None | `workspace_id` (str, required)
`memory_target` (str, required)
`query` (str, optional)
`messages` (array, optional)
`ref_memory_id` (str, required) | add_memory
update_memory
vector_retrieve_memory | +| **IdentitySummaryAgentV1Op** | BaseMemoryAgentOp | None | `workspace_id` (str, required)
`query` (str, optional)
`messages` (array, optional) | read_identity_memory
update_identity_memory | +| **ReMeSummaryAgentV1Op** | BaseMemoryAgentOp | `enable_tool_memory` (bool, 默认: True)
`enable_identity_memory` (bool, 默认: True) | `workspace_id` (str, required)
`query` (str, optional)
`messages` (array, optional) | add_meta_memory
add_summary_memory
hands_off
(内部调用: add_history_memory, read_identity_memory, read_meta_memory) | +| **ReMeRetrieveAgentV1Op** | BaseMemoryAgentOp | `enable_tool_memory` (bool, 默认: True) | `workspace_id` (str, required)
`query` (str, optional)
`messages` (array, optional) | vector_retrieve_memory
read_history_memory
(内部调用: read_meta_memory) | +| **ReMyAgentV1Op** | BaseMemoryAgentOp | `enable_tool_memory` (bool, 默认: True)
`enable_identity_memory` (bool, 默认: True) | `workspace_id` (str, required)
`query` (str, optional)
`messages` (array, optional) | vector_retrieve_memory
read_history_memory
(内部调用: read_identity_memory, read_meta_memory) | + +### 4.4 Runtime层设计(内部实现) + +Runtime层负责协调Tool和Agent的调用流程,实现记忆的渐进式处理。 + +#### 4.4.1 渐进式总结流程(Summary) + +总结流程采用分层处理策略,首先保存历史对话,读取元信息,然后由主Agent协调多个专用Agent完成分类总结。 + +**流程结构:** + +```python +# Step 1: Save conversation history +AddHistoryMemoryOp() + +# Step 2: Load meta information (memory types and targets) +ReadMetaMemoryOp() + +# Step 3: Progressive summarization with delegation +ReMeSummaryAgentV1Op(tools=[ + # Add meta memory entries for new memory types/targets + AddMetaMemoryOp(list(memory_type, memory_target)), + + # Add general summary memory as fallback + AddSummaryMemoryOp(summary_memory), + + # Delegate to specialized summary agents + HandsOffOp(list(memory_type, memory_target), agents=[ + PersonalSummaryAgentV1Op, # Summarize personal memories + ProceduralSummaryAgentV1Op, # Summarize procedural memories + ToolSummaryAgentV1Op, # Summarize tool-related memories + IdentitySummaryAgentV1Op # Update identity memory + ]), +]) + +# Specialized agents and their available tools +PersonalSummaryAgentV1Op(tools=[AddMemoryOp, UpdateMemoryOp, DeleteMemoryOp, VectorRetrieveMemoryOp]) +ProceduralSummaryAgentV1Op(tools=[AddMemoryOp, UpdateMemoryOp, DeleteMemoryOp, VectorRetrieveMemoryOp]) +ToolSummaryAgentV1Op(tools=[AddMemoryOp, UpdateMemoryOp, VectorRetrieveMemoryOp]) +IdentitySummaryAgentV1Op(tools=[ReadIdentityMemoryOp, UpdateIdentityMemoryOp]) +``` + +#### 4.4.2 渐进式检索流程(Retrieve) + +检索流程采用三层检索策略,类似于技能系统的加载机制,逐层加载和过滤记忆。 + +**流程结构:** + +```python +# Progressive retrieval with three layers +ReMeRetrieveAgentV1Op(tools=[ + # Layer 0: Load meta memory (all available memory types and targets) + ReadMetaMemoryOp(), + # Output format example: + # - personal(jinli): Information about Jinli's personal life and preferences + # - personal(jiaji): Information about Jiaji's background and interests + # - personal(jinli&jiaji): Shared memories between Jinli and Jiaji + # - procedural(appworld): Procedural knowledge for AppWorld tasks + # - procedural(bfcl-v3): Procedural knowledge for BFCL-v3 benchmark + # - tool(tool_guidelines): Guidelines for tool usage + # - identity(self): Agent's self-identity information + + # Layer 1+2: Vector-based retrieval on structured memories + VectorRetrieveMemoryOp(list(memory_type, memory_target, query)), + + # Layer 3: Load full conversation history for specific memory + ReadHistoryMemoryOp(ref_memory_id), +]) +``` + +**与技能系统的类比:** + +```python +# Skill system hierarchy (for reference) +load_meta_skills # Load skill metadata +load_skills # Load skill implementations +load_reference_skills # Load detailed skill documentation +execute_shell # Execute actual commands +``` + +## 五、扩展设计与实验方向 + +### 5.1 Summary Memory机制 + +Summary Memory作为通用维度的记忆类型,提供兜底的原始对话索引能力。 + +**工作流程示例:** + +```txt +Step 1: Progressive summarization across sessions + session1: List[Message] -> session2: List[Message] -> session3: List[Message] -> ... +summary ✓ (always) ✓ (always) ✓ (always) +personal ✗ ✗ ✓ (when applicable) +procedural ✗ ✓ (when applicable) ✗ + +Step 2: Retrieval with fallback strategy +vector_retrieve_memory(query, memory_type="personal", memory_target="jinli") + -> Search in memory_type: ["personal", "summary"] # Fallback to summary if personal not found +``` + +**设计优势:** +1. Provides a universal dimension for memory extraction across all memory types +2. Ensures fallback indexing of original conversations when specific meta memory is not available +3. Maintains conversation context even when specialized memory extraction fails + +### 5.2 Thinking参数实验 + +探索不同的模型推理能力增强方案,受AgentScope和Claude启发。 + +#### 5.2.1 Thinking参数设计 + +```python +async def record_to_memory( + self, + thinking: str, + content: list[str], + **kwargs: Any, +) -> ToolResponse: + """Use this function to record important information that you may + need later. The target content should be specific and concise, e.g. + who, when, where, do what, why, how, etc. + + Args: + thinking (`str`): + Your thinking and reasoning about what to record + content (`list[str]`): + The content to remember, which is a list of strings. """ - 被存储到传统的db中的数据结构,一行一条message - def retrieve_message(key_word=None, time_start=None, limit: int=50): - 支持关键字检索和时间检索 - content like %{key_word}% - 暂时不支持向量检索,向量只做抽取的memory的 - """ - name: str = Field(default="", description="The name of the character who actually outputs this message") - message_id: str = Field(default=..., description="message_id, uuid,每一条message不重样") - block_id: str = Field(default=..., description="block_id,message分块的id,用户链接summary") +``` +#### 5.2.2 实验对比方案 -from reme_ai.schema.memory import BaseMemory +| 方案类型 | 说明 | 灵感来源 | +|-------------------------------|--------------------------------------------|----------------| +| Thinking Model | Native reasoning-capable models (e.g., o1) | OpenAI | +| Instruct Model | Standard instruction-following models | Baseline | +| Instruct Model + Thinking Params | Add thinking parameter to tool schema | AgentScope | +| Instruct Model + Thinking Tool | Dedicated thinking tool for explicit reasoning | Claude | -""" -基本和原来一致,增加block compress的memory +### 5.3 多项操作模式实验 -工具记忆放到传统db中 -BlockCompress放到向量库中 -Personal + Task + Self-Cognition 放到向量库中 -""" +对比单次调用和批量调用的性能与准确性差异。 +**两种模式对比:** +| 模式 | Tool调用方式 | Model调用次数 | 优势 | 劣势 | +|--------------|----------------------------|---------------|------------------------------|--------------------------| +| 单项模式 | Single-item per call | Multiple | Fine-grained control | Higher latency, more tokens | +| 多项模式 | Batch multiple items | Single | Lower latency, fewer tokens | Potential batch errors | +**实验目标:** +- Evaluate accuracy: single vs. batch operations +- Measure latency and token efficiency +- Identify optimal use cases for each mode -class BlockCompressMemory(BaseMemory): - start_time: str = Field(default=..., description="block的开始时间") - end_time: str = Field(default=..., description="block的结束时间") +### 5.4 多版本与扩展性 -class SelfCognitionMemory(BaseMemory): - target_name: str = Field(default="自我认知") - memory_type: str = Field(default="self") +支持从基类继承实现自定义Agent,便于团队协作和功能迭代。 -class ToolMemory(BaseMemory): - target_name: str = Field(default="", description="工具名称") - memory_type: str = Field(default="tool") +**扩展示例:** -class PersonalMemory(BaseMemory): - target_name: str = Field(default="", description="这是记录谁的记忆?比如TOM、Jerry?") - memory_type: str = Field(default="personal") +```python +# Version 2 implementations by different team members +PersonalSummaryAgentV2Op / PersonalRetrieveAgentV2Op # @weikang +ProceduralSummaryAgentV2Op / ProceduralRetrieveAgentV2Op # @zouyin -class TaskMemory(BaseMemory): - target_name: str = Field(default="", description="这是什么任务的记忆、比如bfcl、appworld、金融?") - memory_type: str = Field(default="task") +# Inherit from BaseMemoryAgentOp +class PersonalSummaryAgentV2Op(BaseMemoryAgentOp): + """Enhanced personal memory summarization with improved algorithms""" + pass +``` +### 5.5 文件系统集成(未来方向) +探索将文件操作能力集成到记忆系统中,支持基于文件的记忆管理。 -# Handoffs allow meta agent to delegate tasks to another agent. -""" -summarizer -""" +**挑战与考虑:** -def compress_message_block(message_block: List[Message]) -> BlockCompressMemory: - """ - 压缩message_block信息到BlockCompressMemory - """ +1. **操作适配性**:Current operations (retrieve/add/update/delete) need adaptation for file-based storage +2. **工具选择**:Consider file operation tools: `grep`, `glob`, `ls`, `read_file`, `write_file`, `edit_file` +3. **模型能力**:Base models have limited file operation capabilities; `qwen3-code` shows better performance -def handoff_to_summary_agent(agent_name: str) -> str: - """ - 如果agent_name是存在的,那直接使用现成的,否则可以创建新的agent(默认是不开启的) - """ +**潜在架构:** -def retrieve_memory_by_query(query: str, limit_size: int = 50, threshold = 0.5) -> List[BaseMemory]: - """ - 如果之前使用过handoff_to_summary_agent - 在自我认知 - """ +```python +# File-based memory operations +FileMemoryOp(tools=[ + grep, # Search within files + glob, # File pattern matching + ls, # List directory contents + read_file, # Read file contents + write_file, # Write new memory files + edit_file, # Update existing memory files +]) +``` -def datetime_tool(**kwargs) - """ - 时间工具 - Returns: +### 5.6 自我修改上下文 - """ +支持Agent动态修改自身的上下文状态,实现自适应记忆管理。 -def delete_memory(memory_id: str): - ... +**实现方式:** -# 做实验 -def update_memory(memory_id: str, content: str, time_stamp: str): - ... +1. **Summary Agent 主动修改**: + - `add_meta_memory` directly modifies agent context + - Updates available memory types and targets during execution +2. **ReMy Agent 被动修改**: + - Retrieves `identity_memory` at each interaction + - Dynamically updates self-state based on retrieved identity + - Enables adaptive behavior based on accumulated identity knowledge -def new_memory(content: str, time_stamp: str) -> BaseMemory: - ... +## ReMe V2 开发路线图与实施计划 +### 技术改造阶段 +1. **代码整合与兼容**:合并flowllm中reme必要的代码,保留现在server-client的依赖,兼容现在各个仓库的依赖代码 +2. **核心接口重构**:新的ReMe接口设计,支持summary,retrieve,context_offload, context_reload 4个核心接口 +3. **Agentic算法升级**:新的agentic算法方案开发 -""" -summary reviser / Reward Model -""" +### 评估验证阶段 +4. **Benchmark测试** + - halumem + - locomo + - longmemevel + - personal-v2 ? + - appworld/bfcl-v3 -def revise_memory(messages: List[Message], - origin_memory: List[BaseMemory], - output_memory: List[BaseMemory]) -> Tuple["", bool]: - """ - reasoning and result - 是否所有信息都加入了? - 当前结果是不是有冲突和重复。 - 如果有问题,就重新react或者重试 - """ +### 发布推广阶段 +5. **技术报告**撰写与发布 +6. **生态更新**:更新各个仓库的依赖代码 + - agentscope + - agentscope-runtime + - evotraders + - alias(tool-memory) + - agentscope-java + - AgentEvolver + - cookbook: reme procedural memory paper + - tool-memory-upgrade(将要合并) -""" -retriever -""" +**里程碑目标**:春节前完成小版本发布 -def retrieve_memory_by_query(query: str, limit_size: int = 50, threshold = 0.5) -> List[BaseMemory]: - """ - 和上面不一致的在于,这里可以搜索到compress的记忆 - """ +--- -def retrieve_message(key_word=None, time_offset=None, time_limit=None): - """ - 支持关键字检索和时间检索 - content like %{key_word}% - 暂时不支持向量检索,向量只做抽取的memory的 - """ +## ReMe V2 核心竞争优势 -def retrieve_block_id(block_id): - """ - 支持关键字检索和时间检索 - content like %{key_word}% - 暂时不支持向量检索,向量只做抽取的memory的 - """ +### 1. 渐进式 Agentic Memory 架构【核心创新】 +融合了多种记忆的渐进式agentic方案,实现从短期到长期记忆的智能化演进 -``` \ No newline at end of file +### 2. 全生命周期记忆管理 +同时支持长期记忆(Long-term Memory)和短期记忆(Working Memory),完整覆盖Agent认知周期 + +### 3. 模型 +提供开源小模型 + +### 4. 开发者友好生态 + 1. **简洁接口**:提供简洁的接口设计,全异步接口 + 2. **即开即用**:提供CLI工具,开箱即用的体验 + 3. **生态融合**:提供和AgentScope、LangChain无缝集成的方案 + 4. **高度可扩展**:支持Agentic算法的二次开发与定制 \ No newline at end of file diff --git a/reme_ai/core/flow/__init__.py b/reme_ai/core/flow/__init__.py new file mode 100644 index 00000000..6d5a053b --- /dev/null +++ b/reme_ai/core/flow/__init__.py @@ -0,0 +1,11 @@ +"""flow""" + +from .base_flow import BaseFlow +from .cmd_flow import CmdFlow +from .expression_flow import ExpressionFlow + +__all__ = [ + "BaseFlow", + "CmdFlow", + "ExpressionFlow", +] diff --git a/reme_ai/core/flow/base_flow.py b/reme_ai/core/flow/base_flow.py new file mode 100644 index 00000000..1802af78 --- /dev/null +++ b/reme_ai/core/flow/base_flow.py @@ -0,0 +1,214 @@ +"""Base flow module providing abstract flow execution with caching and operation orchestration.""" + +import asyncio +import hashlib +import json +from abc import ABC, abstractmethod + +from loguru import logger + +from ..context import C, RuntimeContext +from ..enumeration import ChunkEnum, RegistryEnum +from ..op import BaseOp, SequentialOp, ParallelOp +from ..schema import Response, ToolCall, ToolAttr +from ..utils import camel_to_snake, CacheHandler + + +class BaseFlow(ABC): + """Abstract base class for flow execution with caching, streaming, and operation tree management. + + BaseFlow provides a framework for building complex workflows by composing operations + into executable trees. It supports both synchronous and asynchronous execution modes, + response caching, streaming outputs, and automatic tool call schema generation. + """ + + def __init__( + self, + name: str = "", + stream: bool = False, + raise_exception: bool = True, + enable_cache: bool = False, + cache_path: str = "cache/flow", + cache_expire_hours: float = 0.1, + **kwargs, + ): + """Initialize flow configuration and execution state.""" + super().__init__() + + self.name: str = name or camel_to_snake(self.__class__.__name__) + self.stream: bool = stream + self.raise_exception: bool = raise_exception + self.enable_cache: bool = enable_cache + self.cache_path: str = cache_path + self.cache_expire_hours: float = cache_expire_hours + self.flow_params: dict = kwargs + + self._flow_op: BaseOp | None = None + self._cache: CacheHandler | None = None + self._flow_printed: bool = False + self._tool_call: ToolCall | None = None + + def _build_tool_call(self) -> ToolCall | None: + """Generate the tool call schema definition for this flow.""" + + @abstractmethod + def _build_flow(self) -> BaseOp: + """Construct the root operation tree for flow execution.""" + + def _compute_cache_key(self, params: dict) -> str | None: + """Generate a SHA256 hash from input parameters for caching.""" + try: + payload = json.dumps(params, sort_keys=True, ensure_ascii=False, default=str) + return hashlib.sha256(payload.encode("utf-8")).hexdigest() + except Exception as e: + logger.exception(f"{self.name} cache key serialization failed: {e}") + return None + + def _maybe_load_cached(self, params: dict) -> Response | None: + """Retrieve a cached response if caching is enabled and available.""" + if not self.enable_cache or self.stream: + return None + + if key := self._compute_cache_key(params): + if cached := self.cache.load(key): + logger.info(f"Loaded {self.name} response from cache.") + return Response(**cached) + return None + + def _maybe_save_cache(self, params: dict, response: Response): + """Persist the execution response to the cache.""" + if not self.enable_cache or self.stream: + return + + if key := self._compute_cache_key(params): + self.cache.save( + key, + response.model_dump(exclude_none=True), + expire_hours=self.cache_expire_hours, + ) + + def _print_operation_tree(self, name: str, op: BaseOp, indent: int): + """Recursively log the hierarchy of the flow's operation tree.""" + prefix = " " * indent + op_type = "sequential" if isinstance(op, SequentialOp) else "parallel" if isinstance(op, ParallelOp) else name + logger.info(f"{prefix}{op_type} execution") + + for sub_op in op.sub_ops or []: + self._print_operation_tree(sub_op.name, sub_op, indent + 2) + + @property + def tool_call(self) -> ToolCall | None: + """Lazily construct the ToolCall schema describing this flow.""" + if self.flow_op.tool_call: + return self.flow_op.tool_call + + if self._tool_call is None: + self._tool_call = self._build_tool_call() + if self._tool_call: + self._tool_call.name = self._tool_call.name or self.name + self._tool_call.output = self._tool_call.output or { + f"{self.name}_result": ToolAttr( + type="string", + description=f"The execution result of the {self.name}", + ), + } + return self._tool_call + + @property + def cache(self) -> CacheHandler: + """Provide access to the internal CacheHandler instance.""" + assert self.enable_cache, "Cache usage requested while disabled." + if self._cache is None: + self._cache = CacheHandler(f"{self.cache_path}/{self.name}") + return self._cache + + @property + def flow_op(self) -> BaseOp: + """Lazily build and retrieve the root operation of the flow.""" + if self._flow_op is None: + self._flow_op = self._build_flow() + return self._flow_op + + @property + def async_mode(self) -> bool: + """Check if the current flow operation tree is asynchronous.""" + return self.flow_op.async_mode + + @staticmethod + def parse_expression(expression: str) -> BaseOp: + """Parse a string expression into an executable BaseOp instance.""" + lines = [x.strip() for x in expression.strip().splitlines() if x.strip()] + if not lines: + raise ValueError("Expression is empty") + + env: dict = C.registry_dict[RegistryEnum.OP] + if len(lines) > 1: + exec("\n".join(lines[:-1]), {"__builtins__": {}}, env) + + result = eval(lines[-1], {"__builtins__": {}}, env) + if not isinstance(result, BaseOp): + raise TypeError(f"Expression evaluated to {type(result)}, expected BaseOp") + return result + + def print_flow(self): + """Log the visual structure of the flow once.""" + if not self._flow_printed: + logger.info(f"---------- [Flow Structure] {self.name} ----------") + self._print_operation_tree(self.name, self.flow_op, 0) + logger.info("-" * 50) + self._flow_printed = True + + async def call(self, **kwargs) -> Response | asyncio.Queue: + """Execute the flow asynchronously with parameter caching.""" + kwargs["stream"] = self.stream + logger.info(f"{self.name} incoming params: {kwargs}") + if cached := self._maybe_load_cached(kwargs): + return cached + + context = RuntimeContext(**kwargs) + try: + self.print_flow() + flow_op: BaseOp = self._build_flow() + assert self.flow_op.async_mode, "Async call requires an async flow operation." + + await flow_op.call(context=context) + result = context.stream_queue if self.stream else context.response + + if self.stream: + await context.add_stream_done() + + self._maybe_save_cache(kwargs, result) + return result + except Exception as e: + logger.exception(f"{self.name} async call failed: {e}") + if self.raise_exception: + raise e + if self.stream: + await context.add_stream_chunk_and_type(str(e), ChunkEnum.ERROR) + await context.add_stream_done() + return context.stream_queue + context.add_response_error(e) + return context.response + + def call_sync(self, **kwargs) -> Response: + """Execute the flow synchronously with parameter caching.""" + logger.info(f"{self.name} incoming sync params: {kwargs}") + assert not self.stream, "Synchronous call cannot be used in stream mode." + if cached := self._maybe_load_cached(kwargs): + return cached + + context = RuntimeContext(**kwargs) + try: + self.print_flow() + flow_op: BaseOp = self._build_flow() + assert not self.flow_op.async_mode, "Sync call requires a sync flow operation." + + flow_op.call_sync(context=context) + self._maybe_save_cache(kwargs, context.response) + return context.response + except Exception as e: + logger.exception(f"{self.name} sync call failed: {e}") + if self.raise_exception: + raise e + context.add_response_error(e) + return context.response diff --git a/reme_ai/core/flow/cmd_flow.py b/reme_ai/core/flow/cmd_flow.py new file mode 100644 index 00000000..2fe3b97a --- /dev/null +++ b/reme_ai/core/flow/cmd_flow.py @@ -0,0 +1,18 @@ +"""Command-based flow implementation for parsing and executing operation sequences.""" + +from .base_flow import BaseFlow +from ..op import BaseOp + + +class CmdFlow(BaseFlow): + """A flow class that builds an operation chain from a string expression.""" + + def __init__(self, flow: str = "", **kwargs): + """Initialize the command flow with a string-based operation definition.""" + super().__init__(**kwargs) + self.flow = flow + assert flow, "add `flow=` in cmd!" + + def _build_flow(self) -> BaseOp: + """Parse the stored flow expression into a functional operation object.""" + return self.parse_expression(self.flow) diff --git a/reme_ai/core/flow/expression_flow.py b/reme_ai/core/flow/expression_flow.py new file mode 100644 index 00000000..5ac8f0d8 --- /dev/null +++ b/reme_ai/core/flow/expression_flow.py @@ -0,0 +1,30 @@ +"""Expression-based flow implementation driven by configuration objects.""" + +from .base_flow import BaseFlow +from ..op import BaseOp +from ..schema import FlowConfig, ToolCall + + +class ExpressionFlow(BaseFlow): + """A flow implementation that constructs operations from a FlowConfig definition.""" + + def __init__(self, flow_config: FlowConfig): + """Initialize the flow using settings and metadata from a FlowConfig instance.""" + self.flow_config: FlowConfig = flow_config + super().__init__( + name=flow_config.name, + stream=self.flow_config.stream, + raise_exception=self.flow_config.raise_exception, + enable_cache=self.flow_config.enable_cache, + cache_path=self.flow_config.cache_path, + cache_expire_hours=self.flow_config.cache_expire_hours, + **flow_config.model_extra, + ) + + def _build_flow(self) -> BaseOp: + """Generate the operation chain by parsing the flow content string.""" + return self.parse_expression(self.flow_config.flow_content) + + def _build_tool_call(self) -> ToolCall: + """Construct a tool call representation based on configuration parameters.""" + return ToolCall(**{"description": self.flow_config.description, "parameters": self.flow_config.parameters}) diff --git a/reme_ai/core/op/__init__.py b/reme_ai/core/op/__init__.py index a8ba9a12..c009e89f 100644 --- a/reme_ai/core/op/__init__.py +++ b/reme_ai/core/op/__init__.py @@ -1,11 +1,13 @@ """op""" from .base_op import BaseOp +from .base_ray_op import BaseRayOp from .parallel_op import ParallelOp from .sequential_op import SequentialOp __all__ = [ "BaseOp", + "BaseRayOp", "ParallelOp", "SequentialOp", ]