mirror of
https://github.com/agentscope-ai/ReMe.git
synced 2026-10-09 03:20:54 +00:00
up
This commit is contained in:
parent
3b106b5d01
commit
4f1e5c9d5f
24 changed files with 3316 additions and 305 deletions
442
README.md
442
README.md
|
|
@ -1,273 +1,102 @@
|
|||
<div align="center">
|
||||
<img src="docs/figure/reme_logo.png" alt="ReMe Logo" width="420">
|
||||
<p align="center">
|
||||
<img src="docs/figure/reme_logo.png" alt="ReMe Logo" width="50%">
|
||||
</p>
|
||||
|
||||
<h3>Remember Me, Refine Me</h3>
|
||||
<p><strong>面向 Agent 的、文件优先的自进化记忆系统。</strong></p>
|
||||
<p align="center">
|
||||
<a href="https://pypi.org/project/reme-ai/"><img src="https://img.shields.io/badge/python-3.10+-blue" alt="Python Version"></a>
|
||||
<a href="https://pypi.org/project/reme-ai/"><img src="https://img.shields.io/pypi/v/reme-ai.svg?logo=pypi" alt="PyPI Version"></a>
|
||||
<a href="https://pepy.tech/project/reme-ai/"><img src="https://img.shields.io/pypi/dm/reme-ai" alt="PyPI Downloads"></a>
|
||||
<a href="https://github.com/agentscope-ai/ReMe"><img src="https://img.shields.io/github/commit-activity/m/agentscope-ai/ReMe?style=flat-square" alt="GitHub commit activity"></a>
|
||||
<a href="./LICENSE"><img src="https://img.shields.io/badge/license-Apache--2.0-black" alt="License"></a>
|
||||
<a href="./README.md"><img src="https://img.shields.io/badge/English-Click-yellow" alt="English"></a>
|
||||
<a href="./README_ZH.md"><img src="https://img.shields.io/badge/简体中文-点击查看-orange" alt="简体中文"></a>
|
||||
<a href="https://github.com/agentscope-ai/ReMe"><img src="https://img.shields.io/github/stars/agentscope-ai/ReMe?style=social" alt="GitHub Stars"></a>
|
||||
<a href="https://deepwiki.com/agentscope-ai/ReMe"><img src="https://img.shields.io/badge/DeepWiki-Ask_Devin-navy.svg" alt="DeepWiki"></a>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<a href="https://pypi.org/project/reme-ai/"><img src="https://img.shields.io/badge/python-3.11+-3776AB?logo=python&logoColor=white" alt="Python Version"></a>
|
||||
<a href="https://pypi.org/project/reme-ai/"><img src="https://img.shields.io/pypi/v/reme-ai.svg?logo=pypi&logoColor=white" alt="PyPI Version"></a>
|
||||
<a href="https://pepy.tech/project/reme-ai/"><img src="https://img.shields.io/pypi/dm/reme-ai?color=2ea44f" alt="PyPI Downloads"></a>
|
||||
<a href="https://github.com/agentscope-ai/ReMe"><img src="https://img.shields.io/github/commit-activity/m/agentscope-ai/ReMe?color=7c3aed" alt="GitHub commit activity"></a>
|
||||
<a href="./LICENSE"><img src="https://img.shields.io/badge/license-Apache--2.0-111827" alt="License"></a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://trendshift.io/repositories/20528" target="_blank"><img src="https://trendshift.io/api/badge/repositories/20528" alt="agentscope-ai%2FReMe | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<a href="https://github.com/agentscope-ai/ReMe">GitHub</a>
|
||||
·
|
||||
<a href="https://deepwiki.com/agentscope-ai/ReMe">DeepWiki</a>
|
||||
·
|
||||
<a href="./docs/reme_design.md">设计文档</a>
|
||||
·
|
||||
<a href="./README_old.md">历史 README</a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<strong>A memory management toolkit for AI agents — Remember Me, Refine Me.</strong><br>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<a href="https://github.com/agentscope-ai/ReMe"><img src="https://img.shields.io/github/stars/agentscope-ai/ReMe?style=social" alt="GitHub Stars"></a>
|
||||
<a href="https://trendshift.io/repositories/20528" target="_blank"><img src="https://trendshift.io/api/badge/repositories/20528" alt="agentscope-ai/ReMe | Trendshift" width="220" height="48"></a>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
历史版本:
|
||||
<a href="https://github.com/agentscope-ai/ReMe/tree/v0.3.1.10">0.3.x</a>
|
||||
·
|
||||
<a href="https://github.com/agentscope-ai/ReMe/tree/v0.2.0.6">0.2.x</a>
|
||||
·
|
||||
<a href="https://github.com/agentscope-ai/ReMe/tree/memoryscope_branch">memoryscope</a>
|
||||
</p>
|
||||
</div>
|
||||
> 历史版本 [0.3.x](https://github.com/agentscope-ai/ReMe/tree/v0.3.1.10)
|
||||
> [0.2.x](https://github.com/agentscope-ai/ReMe/tree/v0.2.0.6)
|
||||
> [MemoryScope](https://github.com/agentscope-ai/ReMe/tree/memoryscope_branch)
|
||||
|
||||
---
|
||||
|
||||
🧠 ReMe 是一个专为 **AI Agent** 打造的记忆管理框架。它把记忆保存为 vault 目录中的普通文件,并通过 Markdown、front matter、wikilink、BM25 索引、文件图谱和后台 Agent 管线,把对话与外部资料逐步沉淀为可读、可查、可追溯的长期记忆。
|
||||
🧠 ReMe 是一个专为 **AI 智能体** 打造的记忆管理工具,以 **Memory as File** 为核心,将对话、资料和长期知识沉淀为可读、可编辑、可检索的文件化记忆。
|
||||
|
||||
它解决 Agent 记忆中的两类核心问题:**会话无状态**(新会话无法自然继承历史)和 **长期记忆不可控**(记忆被锁在黑盒数据库里,难以审查、迁移和修正)。
|
||||
|
||||
ReMe 的当前实现以 `reme/config/default.yaml` 为中心装配:启动 `Application` 后,默认暴露 HTTP 服务,后台监听 `daily/`、`digest/`、`resource/` 的变化,并提供检索、文件读写、daily note、自进化记忆等 Job。
|
||||
ReMe 让智能体拥有可持续维护的记忆库:历史对话可归档,外部资料可索引,重要信息可沉淀为长期记忆,并通过关键词、语义检索和
|
||||
wikilink 图谱重新找到。
|
||||
|
||||
<details>
|
||||
<summary><b>你可以用 ReMe 做什么</b></summary>
|
||||
|
||||
<br>
|
||||
|
||||
- **个人助理**:把用户偏好、长期事实和历史上下文写入可读的 daily/digest 记忆。
|
||||
- **编程助手**:沉淀项目约定、排错经验、操作流程和长期决策。
|
||||
- **研究助理**:把报告、网页、日志、会议纪要等文本资源解读为 daily note。
|
||||
- **知识图谱**:通过 Markdown、front matter 和 `[[wikilink]]` 维护本地知识网络。
|
||||
- **主动记忆**:从 daily 输入中抽取兴趣主题,生成 `interests.yaml` 给上层 Agent 使用。
|
||||
- **服务化工具**:通过 HTTP、MCP 或 CLI 调用同一组 Job。
|
||||
- **个人助理**:为 [QwenPaw](https://github.com/agentscope-ai/QwenPaw) 等智能体提供长期记忆,记住用户偏好和历史对话。
|
||||
- **编程助手**:记录代码风格偏好、项目上下文,跨会话保持一致的开发体验。
|
||||
- **客服机器人**:记录用户问题历史、偏好设置,提供个性化服务。
|
||||
- **任务自动化**:从历史任务中学习成功/失败模式,持续优化执行策略。
|
||||
- **知识问答**:构建可检索的知识库,支持语义搜索和精确匹配。
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
## 📁 文件优先的记忆系统
|
||||
## 📁 基于文件的记忆系统
|
||||
|
||||
> 记忆即文件,文件即记忆。
|
||||
> Memory as files, files as memory.
|
||||
|
||||
ReMe 的默认运行目录来自 `ApplicationConfig`。应用启动时会自动创建 vault 根目录和主要子目录。
|
||||
将**记忆视为文件**:原始对话和外部资料先进入输入层,再加工成 daily note,最后沉淀为可长期复用的 digest 记忆。
|
||||
|
||||
| 层级 | 目录 | 内容 |
|
||||
|------|-----------------------------|-------------------------|
|
||||
| 原始输入 | `reme_session/`、`resource/` | 原始对话、Agent session、外部资料 |
|
||||
| 浅加工 | `daily/` | 当天事实、对话摘要、资源解读、兴趣主题 |
|
||||
| 深加工 | `digest/` | 用户画像、长期事实、流程经验、知识节点 |
|
||||
|
||||
```text
|
||||
<vault_dir>/
|
||||
├── reme_metadata/ # 索引、图谱、catalog 等持久状态
|
||||
├── reme_session/ # Agent session 与原始对话
|
||||
├── reme_metadata/ # 系统索引、图谱、catalog 等持久状态
|
||||
├── reme_session/ # 原始对话和 Agent session
|
||||
│ ├── dialog/
|
||||
│ │ └── <session_id>.jsonl # auto_memory 保存的对话消息
|
||||
│ │ └── <session_id>.jsonl
|
||||
│ ├── agentscope/
|
||||
│ └── claude_code/
|
||||
├── resource/ # 外部文本资源
|
||||
├── resource/ # 外部原始材料
|
||||
│ └── YYYY-MM-DD/
|
||||
│ └── <resource>.<ext>
|
||||
├── daily/ # 浅加工记忆
|
||||
│ ├── YYYY-MM-DD.md # 当天索引页
|
||||
├── daily/ # 浅加工记忆:当天事实、对话摘要、资源解读
|
||||
│ ├── YYYY-MM-DD.md
|
||||
│ └── YYYY-MM-DD/
|
||||
│ ├── <session_id>.md # 对话或资源加工后的 daily note
|
||||
│ └── interests.yaml # auto_dream 产出的主动兴趣主题
|
||||
└── digest/ # 深加工记忆
|
||||
│ ├── <session_id>.md
|
||||
│ ├── <resource_stem>.md
|
||||
│ └── interests.yaml
|
||||
└── digest/ # 长期记忆:个人事实、流程经验、知识节点
|
||||
├── personal/
|
||||
├── procedure/
|
||||
└── wiki/
|
||||
```
|
||||
|
||||
| 层级 | 内容 | 主要写入方 |
|
||||
| --- | --- | --- |
|
||||
| `resource/` | 外部文本材料,默认监听 `md/txt/json/jsonl/csv/yaml/html` | 手动同步、`resource_watch_loop` |
|
||||
| `reme_session/dialog/` | 原始对话 JSONL | `auto_memory` |
|
||||
| `daily/` | 当天索引页、对话笔记、资源解读、兴趣主题 | `daily_create`、`auto_memory`、`auto_resource`、`auto_dream` |
|
||||
| `digest/personal/` | 用户画像、偏好、长期个人事实 | `auto_dream` |
|
||||
| `digest/procedure/` | 方法论、流程、操作经验 | `auto_dream` |
|
||||
| `digest/wiki/` | 通用知识、概念、决策先例 | `auto_dream` |
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 📝 Markdown、Front Matter 与 Wikilink
|
||||
### 🚀 快速开始
|
||||
|
||||
ReMe 的核心读写对象是 Markdown 文件。写入类 Job 会处理 front matter;索引时,front matter 会进入 `FileNode.front_matter`,供检索、图谱和 Agent 判断使用。
|
||||
#### 安装
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: 光伏产业链研究
|
||||
description: 从硅料到组件的全链条梳理
|
||||
tags: [新能源, 光伏, 产业链]
|
||||
---
|
||||
```
|
||||
ReMe 要求 Python 3.11+。
|
||||
|
||||
`WikilinkHandler` 是统一的 wikilink 解析和改写入口:
|
||||
|
||||
| 写法 | 示例 | 含义 |
|
||||
| --- | --- | --- |
|
||||
| 标准链接 | `[[digest/wiki/光伏.md]]` | 指向 vault-relative 目标 |
|
||||
| 锚点链接 | `[[digest/wiki/钴.md#应用]]` | 指向目标章节 |
|
||||
| 别名链接 | `[[digest/wiki/宁德时代.md\|宁德]]` | 显示别名,目标不变 |
|
||||
| 嵌入引用 | `![[resource/2026-06-01/report.md]]` | 作为 wikilink 记录边 |
|
||||
| 行级属性 | `industry:: [[digest/wiki/新能源.md]]` | 提取 predicate |
|
||||
| 内联属性 | `[competitor:: [[digest/wiki/比亚迪.md]]]` | 提取 predicate |
|
||||
|
||||
当前实现采用**字面路径语义**:`[[X]]` 的 target 就是 `X`,不会自动补 `.md`,不会做 basename 搜索,也不会做 folder note 解析。推荐使用带扩展名的 vault-relative 路径。
|
||||
|
||||
---
|
||||
|
||||
## 🔍 索引、图谱与检索
|
||||
|
||||
默认 `file_store` 是 `local`,组合了:
|
||||
|
||||
| 子组件 | 默认后端 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `file_graph` | `local` | 维护文件节点、outlinks、inlinks 和 pending links |
|
||||
| `keyword_index` | `bm25` | 基于 tokenizer 的全文检索 |
|
||||
| `embedding_store` | 空字符串 | 代码支持向量检索,但默认配置未启用 |
|
||||
|
||||
因此默认行为是:**BM25 全文检索 + wikilink 图谱扩展**。如果配置了 `embedding_store`,`search_step` 会并行执行向量检索和 BM25,并用 RRF 融合结果。
|
||||
|
||||
### Markdown 分块
|
||||
|
||||
`MarkdownFileChunker` 使用 `mistletoe` 构建 Markdown AST,并按标题层级生成 chunk:
|
||||
|
||||
- 按 H1/H2/H3 等章节递归分块。
|
||||
- 每个 chunk 带完整标题骨架,便于理解命中位置。
|
||||
- 表格、代码块、列表会按结构拆分,过长片段会标记 `[Part X/N]`。
|
||||
- 默认 `chunk_chars=10000`,`embed_toc=true`。
|
||||
- front matter 和 wikilink 会在同一轮 chunk 中提取。
|
||||
|
||||
### Search
|
||||
|
||||
`search` Job 面向外部问答和检索:
|
||||
|
||||
1. 读取 `query`、`limit`、`min_score` 和可选 `search_filter`。
|
||||
2. 并行调用 `vector_search` 与 `keyword_search`。
|
||||
3. 默认未启用 embedding 时,实际返回 BM25 结果。
|
||||
4. 如果两路都有结果,用 RRF 融合。
|
||||
5. 对命中文件做 link expansion,返回 chunk 正文、行号、分数和出入链目录。
|
||||
|
||||
`node_search` 是 `auto_dream` 集成阶段使用的专用召回:只返回 `digest/` 下的节点,并附带 front matter 中的 `name` 和 `description`;它不返回正文,也不做 link expansion。
|
||||
|
||||
---
|
||||
|
||||
## 🧬 自进化记忆管线
|
||||
|
||||
ReMe 的自进化流程由默认 Job 组合完成:
|
||||
|
||||
```text
|
||||
对话 messages ── auto_memory ──> daily/YYYY-MM-DD/<session_id>.md
|
||||
外部 resource ── auto_resource ─> daily/YYYY-MM-DD/<resource_stem>.md
|
||||
daily notes ── auto_dream ────> digest/personal|procedure|wiki + interests.yaml
|
||||
```
|
||||
|
||||
### Auto Memory
|
||||
|
||||
`auto_memory` 接收 `messages` 和可选 `session_id`:
|
||||
|
||||
1. 把输入标准化为 AgentScope `Msg`。
|
||||
2. 如果有 `session_id`,保存到 `reme_session/dialog/<session_id>.jsonl`。
|
||||
3. 调用 `daily_create` 创建或复用 daily note。
|
||||
4. 通过 Agent 使用 `read`、`edit`、`frontmatter_update`、`write` 工具整理记忆。
|
||||
5. 写入 `source_conversation` front matter,关联原始对话 JSONL。
|
||||
6. 刷新当天索引页。
|
||||
|
||||
保存对话时会移除 base64 数据块,并把超长 tool result 截断到约 2KB。
|
||||
|
||||
### Auto Resource
|
||||
|
||||
`auto_resource` 处理 `resource/` 下的变更批次。默认后台 `resource_watch_loop` 监听:
|
||||
|
||||
```yaml
|
||||
watch_dirs: [resource_dir]
|
||||
watch_suffixes: [md, txt, json, jsonl, csv, yaml, html]
|
||||
```
|
||||
|
||||
资源路径约定为:
|
||||
|
||||
```text
|
||||
resource/YYYY-MM-DD/<filename>
|
||||
```
|
||||
|
||||
`added/modified` 会读取资源文本,创建或更新同名 daily note,并让 Agent 解读内容;`deleted` 会删除对应 daily note、更新 file_store,并刷新当天索引页。
|
||||
|
||||
### Auto Dream
|
||||
|
||||
`auto_dream` 扫描指定日期的 daily 输入,将值得长期保存的内容整合进 `digest/`,并写出主动兴趣主题。
|
||||
|
||||
默认步骤:
|
||||
|
||||
```yaml
|
||||
auto_dream:
|
||||
steps:
|
||||
- dream_extract_step
|
||||
- dream_integrate_step
|
||||
- dream_topics_step
|
||||
- dream_finish_step
|
||||
```
|
||||
|
||||
| 阶段 | 实际行为 |
|
||||
| --- | --- |
|
||||
| Extract | 刷新当天索引页,比较 dream catalog,只处理 changed daily 输入,抽取 memory units 和 topics |
|
||||
| Integrate | 对每个 unit 调用 Agent,使用 `node_search/read/frontmatter_read/write/edit/frontmatter_update` 整合进 digest |
|
||||
| Topics | 写入 `daily/<date>/interests.yaml`,默认最多 3 个 topic,并参考过去 7 天去重 |
|
||||
| Finish | checkpoint 成功处理的 changed paths,持久化 dream catalog,返回摘要 |
|
||||
|
||||
Extract 和 Integrate 需要可用 LLM;如果未配置 LLM,会返回失败。Topics 阶段在部分情况下可以退化为本地去重,但完整 `auto_dream` 仍依赖 LLM 完成抽取与整合。
|
||||
|
||||
### Proactive
|
||||
|
||||
`proactive` 读取:
|
||||
|
||||
```text
|
||||
daily/<date>/interests.yaml
|
||||
```
|
||||
|
||||
它返回 topics,并可按 `include_content` 返回 YAML 原文,供上层 Agent 获取当天值得主动关注的主题。
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ 默认 Job
|
||||
|
||||
默认 Job 由 `reme/config/default.yaml` 注册。后台 Job 会随应用启动自动运行;普通 Job 会通过 HTTP/MCP/CLI 暴露。
|
||||
|
||||
| 类别 | Job | 说明 |
|
||||
| --- | --- | --- |
|
||||
| 后台索引 | `index_update_loop` | 监听 `daily/`、`digest/` Markdown 变更,增量更新 file_store |
|
||||
| 后台资源 | `resource_watch_loop` | 监听 `resource/` 文本资源,更新 catalog 并触发 `auto_resource_step` |
|
||||
| 后台 catalog | `digest_watch_loop` | 监听 `daily/`、`digest/`,更新 digest catalog |
|
||||
| 系统 | `version`、`health_check`、`help` | 版本、健康检查、Job 列表 |
|
||||
| 检索 | `search`、`node_search` | chunk 级检索、digest 节点召回 |
|
||||
| 图谱 | `traverse` | 从指定 path 遍历 wikilink 图 |
|
||||
| 索引维护 | `reindex` | 清空 file_store 并重建索引 |
|
||||
| Daily | `daily_create`、`daily_list`、`daily_reindex` | 创建、列出、刷新 daily note |
|
||||
| 文件读写 | `read`、`read_image`、`write`、`edit`、`delete`、`move`、`list`、`stat` | 操作 vault 内文件 |
|
||||
| Front Matter | `frontmatter_read`、`frontmatter_update`、`frontmatter_delete` | 读取、合并更新、删除 front matter 字段 |
|
||||
| 自进化 | `auto_memory`、`auto_resource`、`auto_dream` | 对话、资源、daily 输入的自动加工 |
|
||||
| 主动记忆 | `proactive` | 读取 `interests.yaml` |
|
||||
|
||||
代码中还包含 `ingest`、`upload`、`download` 等 transfer step,但它们没有在默认配置中注册为 Job。
|
||||
|
||||
---
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 安装
|
||||
从 pip 安装:
|
||||
|
||||
```bash
|
||||
pip install reme-ai
|
||||
pip install "reme-ai[core]"
|
||||
```
|
||||
|
||||
从源码安装:
|
||||
|
|
@ -275,116 +104,127 @@ pip install reme-ai
|
|||
```bash
|
||||
git clone https://github.com/agentscope-ai/ReMe.git
|
||||
cd ReMe
|
||||
pip install -e ".[full]"
|
||||
pip install -e ".[core]"
|
||||
```
|
||||
|
||||
### 启动服务
|
||||
`core` extra 建议安装:当前代码会导入 AgentScope wrapper,自进化记忆也依赖它。
|
||||
|
||||
#### 环境变量
|
||||
|
||||
配置环境变量:
|
||||
|
||||
```bash
|
||||
cat > .env <<'EOF'
|
||||
EMBEDDING_API_KEY=sk-xxx
|
||||
EMBEDDING_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
|
||||
LLM_API_KEY=sk-xxx
|
||||
LLM_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
|
||||
EOF
|
||||
```
|
||||
|
||||
#### 启动
|
||||
|
||||
```bash
|
||||
reme start
|
||||
```
|
||||
|
||||
指定配置或覆盖参数:
|
||||
默认服务地址是 `127.0.0.1:2333`。如果端口被占用:
|
||||
|
||||
```bash
|
||||
reme start config=default service.port=8090
|
||||
reme start service.port=23333
|
||||
# reme start vault_dir=/tmp/reme-demo service.port=8181
|
||||
```
|
||||
|
||||
配置解析支持:
|
||||
|
||||
- 默认加载 `reme/config/default.yaml`。
|
||||
- `config=<name-or-path>` 指定配置文件。
|
||||
- dot notation 覆盖,如 `service.port=8090`。
|
||||
- `${ENV_VAR:-default}` 环境变量展开。
|
||||
|
||||
### CLI 调用
|
||||
|
||||
CLI 入口在 `reme/reme.py`:
|
||||
|
||||
```bash
|
||||
reme find_reme
|
||||
reme version
|
||||
reme health_check
|
||||
reme search query="用户偏好" limit=5
|
||||
reme daily_create date=2026-06-20
|
||||
reme proactive date=2026-06-20
|
||||
curl -s http://127.0.0.1:23333/version -H 'Content-Type: application/json' -d '{}'
|
||||
```
|
||||
|
||||
`reme start` 启动服务;`reme find_reme` 探测服务;其它 action 会作为 Job 名称,通过 client 调用已运行服务。
|
||||
更多细节见 [快速开始](docs/zh/quick_start.md)。
|
||||
|
||||
---
|
||||
|
||||
## 🌐 服务接口
|
||||
## 核心能力
|
||||
|
||||
### HTTP Service
|
||||
| 能力 | 说明 |
|
||||
|---------------------------------------------|-------------------------------------------------------------------------------|
|
||||
| [Memory as File](docs/zh/memory_as_file.md) | 用 vault 目录、Markdown、frontmatter 和 wikilink 表达记忆分层与文件关系。 |
|
||||
| [Memory Search](docs/zh/memory_search.md) | 持续索引 `daily/`、`digest/`、`resource/`,支持 BM25、可选向量召回和链接展开。 |
|
||||
| [Auto Memory](docs/zh/auto_memory.md) | 将对话按 `session_id` 保存为原始 JSONL,并整理成 daily 记忆卡片。 |
|
||||
| [Auto Resource](docs/zh/auto_resource.md) | 将 `resource/` 中的外部资料解读为 daily 资源卡片,保留原始资料出处。 |
|
||||
| [Auto Dream](docs/zh/auto_dream.md) | 从 daily 输入中抽取长期记忆单元,沉淀到 `digest/personal`、`digest/procedure` 和 `digest/wiki`。 |
|
||||
| [Auto Link](docs/zh/auto_link.md) | 在写入 digest 时召回相关节点,完成去重、来源链接和 digest 之间的 wikilink 织入。 |
|
||||
| [Proactive](docs/zh/proactive.md) | 读取 `auto_dream` 生成的 `interests.yaml`,向上层 Agent 暴露当天值得主动关注的主题。 |
|
||||
|
||||
默认服务后端是 FastAPI:
|
||||
### Memory as File
|
||||
|
||||
- 非 stream Job 注册为 `POST /<job.name>`。
|
||||
- 请求体是 `Request`,响应是 `Response`。
|
||||
- StreamJob 注册为 `POST /<job.name>`,返回 `text/event-stream`。
|
||||
- CORS 默认开放。
|
||||
- lifespan 中启动和关闭整个 `Application`。
|
||||
<p align="center">
|
||||
<img src="docs/figure/memory-as-file.svg" alt="Memory as File model" width="78%">
|
||||
</p>
|
||||
|
||||
### MCP Service
|
||||
### Auto Memory & Auto Resource(BETA)
|
||||
|
||||
`MCPService` 使用 FastMCP:
|
||||
<p align="center">
|
||||
<img src="docs/figure/auto-memory-resource.svg" alt="Auto Memory and Auto Resource flow" width="78%">
|
||||
</p>
|
||||
|
||||
- 非 stream Job 注册为 MCP tool。
|
||||
- 支持 `stdio`、`sse`、`streamable-http`。
|
||||
- StreamJob 当前不注册为 MCP tool。
|
||||
- MCP 服务内置 `claim_channel` 相关通道机制,用于把 vault 变更通知发送给最近 claim 的客户端会话。
|
||||
### Auto Dream & Auto Link & Proactive
|
||||
|
||||
<p align="center">
|
||||
<img src="docs/figure/auto-dream.svg" alt="Auto Dream flow" width="78%">
|
||||
</p>
|
||||
|
||||
### Memory Search
|
||||
|
||||
<p align="center">
|
||||
<img src="docs/figure/memory-search.svg" alt="Memory Search flow" width="78%">
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
## 💾 持久化状态
|
||||
## ⭐ 社区与支持
|
||||
|
||||
除 vault 正文文件外,ReMe 会在 `reme_metadata/` 下保存组件状态:
|
||||
- **问题反馈与需求**:请先查看 [Open Issues](https://github.com/agentscope-ai/ReMe/issues);如无相关讨论,可新建 Issue
|
||||
说明背景、目标行为和影响范围。
|
||||
- **代码贡献**:改动前建议阅读 [贡献指南](docs/zh/contributing.md) 和 [代码框架](docs/zh/framework.md),遵循 CLI /
|
||||
Service / Application / Job / Step / Component 的分层。
|
||||
- **文档贡献**:用户可见的安装、配置、调用或行为变化,请同步更新 `docs/zh/` 或 `README.md`。
|
||||
- **提交规范**:建议使用 Conventional Commits,例如 `feat(search): add link expansion option`、
|
||||
`docs(zh): update quick start`。
|
||||
- **提交前检查**:提交 PR 前请尽量运行 `pre-commit run --all-files` 和 `pytest`;如有依赖 LLM、embedding 或外部服务的测试无法运行,请在
|
||||
PR 中说明。
|
||||
- **获取帮助**:Bugs 和功能请求使用 [GitHub Issues](https://github.com/agentscope-ai/ReMe/issues)
|
||||
,项目文档见 [https://reme.agentscope.io/](https://reme.agentscope.io/)。
|
||||
|
||||
| 组件 | 持久化内容 |
|
||||
| --- | --- |
|
||||
| `file_store` | `file_chunks_<name>_v1.jsonl.zst`,保存 chunk 元数据 |
|
||||
| `file_graph` | `<name>.jsonl.zst`,保存 `FileNode` 和 links |
|
||||
| `keyword_index` | `bm25_*.pkl`,保存 vocab、posting list、doc meta |
|
||||
| `file_catalog` | `<catalog_name>.jsonl.zst`,保存已处理文件 mtime checkpoint |
|
||||
### 贡献者
|
||||
|
||||
应用关闭时会按启动顺序反向关闭组件,并触发 file_store、keyword index、file graph 和 catalog 的持久化。
|
||||
感谢所有为 ReMe 做出贡献的朋友们:
|
||||
|
||||
<a href="https://github.com/agentscope-ai/ReMe/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=agentscope-ai/ReMe" alt="贡献者" />
|
||||
</a>
|
||||
|
||||
---
|
||||
|
||||
## 🧩 关键数据模型
|
||||
## 📄 引用
|
||||
|
||||
```text
|
||||
Response
|
||||
success: bool
|
||||
answer: str
|
||||
metadata: dict
|
||||
|
||||
FileNode
|
||||
path: str
|
||||
st_mtime: float
|
||||
links: list[FileLink]
|
||||
chunk_ids: list[str]
|
||||
front_matter: FileFrontMatter
|
||||
|
||||
FileChunk
|
||||
id: str
|
||||
path: str
|
||||
start_line: int
|
||||
end_line: int
|
||||
text: str
|
||||
metadata: dict
|
||||
scores: dict[str, float]
|
||||
|
||||
FileLink
|
||||
source_path: str
|
||||
target_path: str
|
||||
target_anchor: str | None
|
||||
predicate: str | None
|
||||
```bibtex
|
||||
@software{AgentscopeReMe2026,
|
||||
title = {AgentscopeReMe: Memory Management Kit for Agents},
|
||||
author = {ReMe Team},
|
||||
url = {https://reme.agentscope.io},
|
||||
year = {2026}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📄 License
|
||||
## ⚖️ 许可证
|
||||
|
||||
This project is open-sourced under the Apache License 2.0. See [LICENSE](./LICENSE) for details.
|
||||
本项目基于 Apache License 2.0 开源,详情参见 [LICENSE](./LICENSE) 文件。
|
||||
|
||||
---
|
||||
|
||||
## 📈 Star 历史
|
||||
|
||||
[](https://www.star-history.com/#agentscope-ai/ReMe&Date)
|
||||
|
|
|
|||
110
docs/figure/auto-dream.svg
Normal file
110
docs/figure/auto-dream.svg
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="680" viewBox="0 0 1200 680" role="img" aria-labelledby="title desc">
|
||||
<title id="title">ReMe auto dream flow</title>
|
||||
<desc id="desc">A left-to-right auto dream flow from changed daily notes to digest integration, interest topic writing, catalog checkpointing, and proactive reads.</desc>
|
||||
<defs>
|
||||
<style>
|
||||
.bg { fill: #f7f8fb; }
|
||||
.title { font: 700 28px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #172033; }
|
||||
.subtitle { font: 14px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #607086; }
|
||||
.step-num { font: 700 12px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #ffffff; }
|
||||
.step-title { font: 700 18px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #172033; }
|
||||
.step-subtitle { font: 13px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #607086; }
|
||||
.chip-title { font: 700 13px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #253246; }
|
||||
.chip-text { font: 12px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #627087; }
|
||||
.note { font: 12px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #5d6a7d; }
|
||||
.panel { fill: #ffffff; stroke: #d7dde7; stroke-width: 1.2; rx: 10; ry: 10; }
|
||||
.chip { fill: #f9fafc; stroke: #dfe5ee; stroke-width: 1; rx: 8; ry: 8; }
|
||||
.badge { fill: #44546a; }
|
||||
.arrow { stroke: #8794a8; stroke-width: 2; fill: none; marker-end: url(#arrow); }
|
||||
.soft-arrow { stroke: #a3adbd; stroke-width: 1.6; stroke-dasharray: 5 5; fill: none; marker-end: url(#arrow-soft); }
|
||||
.line { stroke: #e1e6ee; stroke-width: 1; }
|
||||
</style>
|
||||
<marker id="arrow" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto" markerUnits="strokeWidth">
|
||||
<path d="M0,0 L0,6 L8,3 z" fill="#8794a8"/>
|
||||
</marker>
|
||||
<marker id="arrow-soft" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto" markerUnits="strokeWidth">
|
||||
<path d="M0,0 L0,6 L8,3 z" fill="#a3adbd"/>
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
<rect class="bg" x="0" y="0" width="1200" height="680"/>
|
||||
<text class="title" x="600" y="54" text-anchor="middle">Auto Dream</text>
|
||||
<text class="subtitle" x="600" y="80" text-anchor="middle">Scan changed daily memory, integrate reusable units into digest, then expose fresh proactive topics.</text>
|
||||
|
||||
<rect class="panel" x="54" y="132" width="224" height="300"/>
|
||||
<circle class="badge" cx="88" cy="170" r="15"/>
|
||||
<text class="step-num" x="88" y="174" text-anchor="middle">1</text>
|
||||
<text class="step-title" x="116" y="176">Extract</text>
|
||||
<text class="step-subtitle" x="82" y="206">dream_extract_step</text>
|
||||
<rect class="chip" x="82" y="232" width="168" height="44"/>
|
||||
<text class="chip-title" x="166" y="251" text-anchor="middle">refresh day index</text>
|
||||
<text class="chip-text" x="166" y="269" text-anchor="middle">daily/<date>.md</text>
|
||||
<rect class="chip" x="82" y="296" width="168" height="44"/>
|
||||
<text class="chip-title" x="166" y="315" text-anchor="middle">compare catalog</text>
|
||||
<text class="chip-text" x="166" y="333" text-anchor="middle">changed daily markdown</text>
|
||||
<rect class="chip" x="82" y="360" width="168" height="44"/>
|
||||
<text class="chip-title" x="166" y="379" text-anchor="middle">LLM extract</text>
|
||||
<text class="chip-text" x="166" y="397" text-anchor="middle">units + topic candidates</text>
|
||||
|
||||
<rect class="panel" x="326" y="132" width="224" height="300"/>
|
||||
<circle class="badge" cx="360" cy="170" r="15"/>
|
||||
<text class="step-num" x="360" y="174" text-anchor="middle">2</text>
|
||||
<text class="step-title" x="388" y="176">Integrate</text>
|
||||
<text class="step-subtitle" x="354" y="206">dream_integrate_step</text>
|
||||
<rect class="chip" x="354" y="232" width="168" height="44"/>
|
||||
<text class="chip-title" x="438" y="251" text-anchor="middle">node_search</text>
|
||||
<text class="chip-text" x="438" y="269" text-anchor="middle">recall digest nodes</text>
|
||||
<rect class="chip" x="354" y="296" width="168" height="44"/>
|
||||
<text class="chip-title" x="438" y="315" text-anchor="middle">auto link</text>
|
||||
<text class="chip-text" x="438" y="333" text-anchor="middle">dedup + wikilinks</text>
|
||||
<rect class="chip" x="354" y="360" width="168" height="44"/>
|
||||
<text class="chip-title" x="438" y="379" text-anchor="middle">write digest</text>
|
||||
<text class="chip-text" x="438" y="397" text-anchor="middle">create / update nodes</text>
|
||||
|
||||
<rect class="panel" x="598" y="132" width="224" height="300"/>
|
||||
<circle class="badge" cx="632" cy="170" r="15"/>
|
||||
<text class="step-num" x="632" y="174" text-anchor="middle">3</text>
|
||||
<text class="step-title" x="660" y="176">Topics</text>
|
||||
<text class="step-subtitle" x="626" y="206">dream_topics_step</text>
|
||||
<rect class="chip" x="626" y="232" width="168" height="44"/>
|
||||
<text class="chip-title" x="710" y="251" text-anchor="middle">merge candidates</text>
|
||||
<text class="chip-text" x="710" y="269" text-anchor="middle">same-day topics kept</text>
|
||||
<rect class="chip" x="626" y="296" width="168" height="44"/>
|
||||
<text class="chip-title" x="710" y="315" text-anchor="middle">avoid repeats</text>
|
||||
<text class="chip-text" x="710" y="333" text-anchor="middle">last 7 days by default</text>
|
||||
<rect class="chip" x="626" y="360" width="168" height="44"/>
|
||||
<text class="chip-title" x="710" y="379" text-anchor="middle">write interests.yaml</text>
|
||||
<text class="chip-text" x="710" y="397" text-anchor="middle">top 3 by default</text>
|
||||
|
||||
<rect class="panel" x="870" y="132" width="224" height="300"/>
|
||||
<circle class="badge" cx="904" cy="170" r="15"/>
|
||||
<text class="step-num" x="904" y="174" text-anchor="middle">4</text>
|
||||
<text class="step-title" x="932" y="176">Finish</text>
|
||||
<text class="step-subtitle" x="898" y="206">dream_finish_step</text>
|
||||
<rect class="chip" x="898" y="232" width="168" height="44"/>
|
||||
<text class="chip-title" x="982" y="251" text-anchor="middle">checkpoint paths</text>
|
||||
<text class="chip-text" x="982" y="269" text-anchor="middle">skip failed inputs</text>
|
||||
<rect class="chip" x="898" y="296" width="168" height="44"/>
|
||||
<text class="chip-title" x="982" y="315" text-anchor="middle">persist catalog</text>
|
||||
<text class="chip-text" x="982" y="333" text-anchor="middle">file_catalog: dream</text>
|
||||
<rect class="chip" x="898" y="360" width="168" height="44"/>
|
||||
<text class="chip-title" x="982" y="379" text-anchor="middle">return summary</text>
|
||||
<text class="chip-text" x="982" y="397" text-anchor="middle">counts + errors</text>
|
||||
|
||||
<path class="arrow" d="M278 282 H326"/>
|
||||
<path class="arrow" d="M550 282 H598"/>
|
||||
<path class="arrow" d="M822 282 H870"/>
|
||||
|
||||
<rect class="panel" x="126" y="502" width="948" height="82"/>
|
||||
<text class="note" x="176" y="532">Inputs</text>
|
||||
<text class="chip-text" x="176" y="554">daily/<date>.md and daily/<date>/**/*.md</text>
|
||||
<line class="line" x1="404" y1="518" x2="404" y2="566"/>
|
||||
<text class="chip-title" x="448" y="532">Long-term memory</text>
|
||||
<text class="chip-text" x="448" y="554">digest/procedure, digest/personal, digest/wiki</text>
|
||||
<line class="line" x1="744" y1="518" x2="744" y2="566"/>
|
||||
<text class="chip-title" x="786" y="532">Proactive output</text>
|
||||
<text class="chip-text" x="786" y="554">daily/<date>/interests.yaml</text>
|
||||
|
||||
<path class="soft-arrow" d="M982 432 C982 476 710 472 710 432"/>
|
||||
<text class="note" x="850" y="474" text-anchor="middle">proactive reads interests.yaml after auto_dream writes it</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.3 KiB |
106
docs/figure/auto-memory-resource.svg
Normal file
106
docs/figure/auto-memory-resource.svg
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="680" viewBox="0 0 1200 680" role="img" aria-labelledby="title desc">
|
||||
<title id="title">ReMe auto memory and auto resource flow</title>
|
||||
<desc id="desc">A concise flow where conversations and dated resources are turned into daily cards, indexed by the daily page, and then used by downstream dream and search workflows.</desc>
|
||||
<defs>
|
||||
<style>
|
||||
.bg { fill: #f7f8fb; }
|
||||
.title { font: 700 28px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #172033; }
|
||||
.subtitle { font: 14px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #607086; }
|
||||
.step-num { font: 700 12px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #ffffff; }
|
||||
.step-title { font: 700 18px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #172033; }
|
||||
.step-subtitle { font: 13px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #607086; }
|
||||
.chip-title { font: 700 13px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #253246; }
|
||||
.chip-text { font: 12px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #627087; }
|
||||
.note { font: 12px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #5d6a7d; }
|
||||
.panel { fill: #ffffff; stroke: #d7dde7; stroke-width: 1.2; rx: 10; ry: 10; }
|
||||
.chip { fill: #f9fafc; stroke: #dfe5ee; stroke-width: 1; rx: 8; ry: 8; }
|
||||
.badge { fill: #44546a; }
|
||||
.arrow { stroke: #8794a8; stroke-width: 2; fill: none; marker-end: url(#arrow); }
|
||||
.soft-arrow { stroke: #a3adbd; stroke-width: 1.6; stroke-dasharray: 5 5; fill: none; marker-end: url(#arrow-soft); }
|
||||
.line { stroke: #e1e6ee; stroke-width: 1; }
|
||||
</style>
|
||||
<marker id="arrow" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto" markerUnits="strokeWidth">
|
||||
<path d="M0,0 L0,6 L8,3 z" fill="#8794a8"/>
|
||||
</marker>
|
||||
<marker id="arrow-soft" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto" markerUnits="strokeWidth">
|
||||
<path d="M0,0 L0,6 L8,3 z" fill="#a3adbd"/>
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
<rect class="bg" x="0" y="0" width="1200" height="680"/>
|
||||
<text class="title" x="600" y="54" text-anchor="middle">Auto Memory & Auto Resource</text>
|
||||
<text class="subtitle" x="600" y="80" text-anchor="middle">Conversations and dated resources become readable daily cards, then share one daily index and downstream memory flow.</text>
|
||||
|
||||
<rect class="panel" x="60" y="132" width="225" height="300"/>
|
||||
<circle class="badge" cx="94" cy="170" r="15"/>
|
||||
<text class="step-num" x="94" y="174" text-anchor="middle">1</text>
|
||||
<text class="step-title" x="122" y="176">Conversations</text>
|
||||
<text class="step-subtitle" x="88" y="206">auto_memory</text>
|
||||
<rect class="chip" x="88" y="234" width="169" height="44"/>
|
||||
<text class="chip-title" x="172" y="253" text-anchor="middle">session input</text>
|
||||
<text class="chip-text" x="172" y="271" text-anchor="middle">conversation by id</text>
|
||||
<rect class="chip" x="88" y="302" width="169" height="44"/>
|
||||
<text class="chip-title" x="172" y="321" text-anchor="middle">memory card</text>
|
||||
<text class="chip-text" x="172" y="339" text-anchor="middle">daily/<date>/<id>.md</text>
|
||||
<rect class="chip" x="88" y="370" width="169" height="34"/>
|
||||
<text class="chip-text" x="172" y="392" text-anchor="middle">source: dialog jsonl</text>
|
||||
|
||||
<rect class="panel" x="335" y="132" width="225" height="300"/>
|
||||
<circle class="badge" cx="369" cy="170" r="15"/>
|
||||
<text class="step-num" x="369" y="174" text-anchor="middle">2</text>
|
||||
<text class="step-title" x="397" y="176">Resources</text>
|
||||
<text class="step-subtitle" x="363" y="206">auto_resource</text>
|
||||
<rect class="chip" x="363" y="234" width="169" height="44"/>
|
||||
<text class="chip-title" x="447" y="253" text-anchor="middle">dated files</text>
|
||||
<text class="chip-text" x="447" y="271" text-anchor="middle">md / txt / csv / json</text>
|
||||
<rect class="chip" x="363" y="302" width="169" height="44"/>
|
||||
<text class="chip-title" x="447" y="321" text-anchor="middle">resource card</text>
|
||||
<text class="chip-text" x="447" y="339" text-anchor="middle">daily/<date>/<name>.md</text>
|
||||
<rect class="chip" x="363" y="370" width="169" height="34"/>
|
||||
<text class="chip-text" x="447" y="392" text-anchor="middle">source file stays put</text>
|
||||
|
||||
<rect class="panel" x="610" y="132" width="225" height="300"/>
|
||||
<circle class="badge" cx="644" cy="170" r="15"/>
|
||||
<text class="step-num" x="644" y="174" text-anchor="middle">3</text>
|
||||
<text class="step-title" x="672" y="176">Daily Workbench</text>
|
||||
<text class="step-subtitle" x="638" y="206">daily/<date>/</text>
|
||||
<rect class="chip" x="638" y="234" width="169" height="44"/>
|
||||
<text class="chip-title" x="722" y="253" text-anchor="middle">shared index</text>
|
||||
<text class="chip-text" x="722" y="271" text-anchor="middle">daily/<date>.md</text>
|
||||
<rect class="chip" x="638" y="302" width="169" height="44"/>
|
||||
<text class="chip-title" x="722" y="321" text-anchor="middle">readable cards</text>
|
||||
<text class="chip-text" x="722" y="339" text-anchor="middle">facts, context, actions</text>
|
||||
<rect class="chip" x="638" y="370" width="169" height="34"/>
|
||||
<text class="chip-text" x="722" y="392" text-anchor="middle">one daily memory stream</text>
|
||||
|
||||
<rect class="panel" x="885" y="132" width="255" height="300"/>
|
||||
<circle class="badge" cx="919" cy="170" r="15"/>
|
||||
<text class="step-num" x="919" y="174" text-anchor="middle">4</text>
|
||||
<text class="step-title" x="947" y="176">Downstream</text>
|
||||
<text class="step-subtitle" x="913" y="206">dream + search</text>
|
||||
<rect class="chip" x="913" y="234" width="199" height="44"/>
|
||||
<text class="chip-title" x="1012" y="253" text-anchor="middle">auto_dream</text>
|
||||
<text class="chip-text" x="1012" y="271" text-anchor="middle">daily to digest</text>
|
||||
<rect class="chip" x="913" y="302" width="199" height="44"/>
|
||||
<text class="chip-title" x="1012" y="321" text-anchor="middle">memory search</text>
|
||||
<text class="chip-text" x="1012" y="339" text-anchor="middle">daily + digest retrieval</text>
|
||||
<rect class="chip" x="913" y="370" width="199" height="34"/>
|
||||
<text class="chip-text" x="1012" y="392" text-anchor="middle">long-term memory path</text>
|
||||
|
||||
<path class="arrow" d="M285 282 C316 282 318 456 448 456 C574 456 592 390 610 356"/>
|
||||
<path class="arrow" d="M560 316 C582 316 590 316 610 316"/>
|
||||
<path class="arrow" d="M835 282 H885"/>
|
||||
|
||||
<rect class="panel" x="126" y="502" width="948" height="82"/>
|
||||
<text class="note" x="176" y="532">Conversation source</text>
|
||||
<text class="chip-text" x="176" y="554">reme_session/dialog/<session_id>.jsonl</text>
|
||||
<line class="line" x1="454" y1="518" x2="454" y2="566"/>
|
||||
<text class="chip-title" x="498" y="532">Resource source</text>
|
||||
<text class="chip-text" x="498" y="554">resource/<date>/<resource_file></text>
|
||||
<line class="line" x1="796" y1="518" x2="796" y2="566"/>
|
||||
<text class="chip-title" x="838" y="532">Daily output</text>
|
||||
<text class="chip-text" x="838" y="554">daily cards plus daily/<date>.md</text>
|
||||
|
||||
<path class="soft-arrow" d="M172 432 C172 470 326 470 447 432"/>
|
||||
<text class="note" x="310" y="474" text-anchor="middle">both flows preserve original sources for verification</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.2 KiB |
158
docs/figure/framework-structure.svg
Normal file
158
docs/figure/framework-structure.svg
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="920" viewBox="0 0 1200 920" role="img" aria-labelledby="title desc">
|
||||
<title id="title">ReMe framework structure</title>
|
||||
<desc id="desc">An architectural map of ReMe from file-backed vault storage through knowledge kernel, workflows, application wiring, and external service surfaces.</desc>
|
||||
<defs>
|
||||
<linearGradient id="paper" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0" stop-color="#fbfaf6"/>
|
||||
<stop offset="0.55" stop-color="#f5f7fb"/>
|
||||
<stop offset="1" stop-color="#eef7f4"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="serviceGrad" x1="0" y1="0" x2="1" y2="0">
|
||||
<stop offset="0" stop-color="#d9f3ec"/>
|
||||
<stop offset="1" stop-color="#dcecf7"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="appGrad" x1="0" y1="0" x2="1" y2="0">
|
||||
<stop offset="0" stop-color="#e5e8fb"/>
|
||||
<stop offset="1" stop-color="#f1e4f5"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="jobGrad" x1="0" y1="0" x2="1" y2="0">
|
||||
<stop offset="0" stop-color="#fae4d8"/>
|
||||
<stop offset="1" stop-color="#f4eccd"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="kernelGrad" x1="0" y1="0" x2="1" y2="0">
|
||||
<stop offset="0" stop-color="#d9f1f4"/>
|
||||
<stop offset="1" stop-color="#e1e8f7"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="vaultGrad" x1="0" y1="0" x2="1" y2="0">
|
||||
<stop offset="0" stop-color="#f0e5d3"/>
|
||||
<stop offset="1" stop-color="#e3efd9"/>
|
||||
</linearGradient>
|
||||
<filter id="softShadow" x="-8%" y="-18%" width="116%" height="145%">
|
||||
<feDropShadow dx="0" dy="16" stdDeviation="16" flood-color="#1c2b3a" flood-opacity="0.13"/>
|
||||
</filter>
|
||||
<filter id="nodeShadow" x="-15%" y="-30%" width="130%" height="170%">
|
||||
<feDropShadow dx="0" dy="8" stdDeviation="8" flood-color="#1c2b3a" flood-opacity="0.12"/>
|
||||
</filter>
|
||||
<style>
|
||||
.bg { fill: url(#paper); }
|
||||
.grain { fill: none; stroke: #d9ded8; stroke-width: 1; opacity: 0.38; }
|
||||
text { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif; }
|
||||
.title { font-size: 30px; font-weight: 800; fill: #172033; }
|
||||
.subtitle { font-size: 14px; fill: #667489; }
|
||||
.layer-title { font-size: 18px; font-weight: 800; fill: #263548; }
|
||||
.layer-note { font-size: 13px; fill: rgba(38,53,72,0.72); }
|
||||
.section-label { font-size: 11px; font-weight: 800; fill: #647187; letter-spacing: 1.8px; }
|
||||
.node-title { font-size: 14px; font-weight: 700; fill: #1d2838; }
|
||||
.node-text { font-size: 12px; fill: #667489; }
|
||||
.micro { font-size: 11px; fill: #728096; }
|
||||
.plate { filter: url(#softShadow); }
|
||||
.plate-bg { fill: #ffffff; stroke: rgba(52, 65, 84, 0.12); stroke-width: 1; }
|
||||
.side { opacity: 0.18; }
|
||||
.node { fill: #ffffff; stroke: rgba(90, 105, 128, 0.22); stroke-width: 1; filter: url(#nodeShadow); }
|
||||
</style>
|
||||
</defs>
|
||||
|
||||
<rect class="bg" width="1200" height="920"/>
|
||||
<path class="grain" d="M98 138 C246 104 337 155 475 121 C616 86 752 124 890 96 C1019 70 1082 98 1136 126"/>
|
||||
<path class="grain" d="M64 768 C218 720 374 790 514 742 C660 692 812 740 966 706 C1066 684 1118 708 1146 732"/>
|
||||
|
||||
<text class="title" x="600" y="56" text-anchor="middle">ReMe Framework Structure</text>
|
||||
<text class="subtitle" x="600" y="83" text-anchor="middle">File-backed memory, searchable knowledge kernel, composable jobs, app wiring, and public service surfaces.</text>
|
||||
|
||||
<g class="plate" transform="translate(0,0)">
|
||||
<path class="side" d="M126 196 L1074 196 L1042 226 L158 226 Z" fill="#83cbbb"/>
|
||||
<rect class="plate-bg" x="126" y="128" width="948" height="96" rx="18"/>
|
||||
<rect x="126" y="128" width="280" height="96" rx="18" fill="url(#serviceGrad)"/>
|
||||
<text class="layer-title" x="178" y="166">Service</text>
|
||||
<text class="layer-note" x="178" y="188">Public interfaces</text>
|
||||
|
||||
<rect class="node" x="436" y="151" width="138" height="50" rx="13"/>
|
||||
<text class="node-title" x="505" y="172" text-anchor="middle">HTTP API</text>
|
||||
<text class="node-text" x="505" y="190" text-anchor="middle">server routes</text>
|
||||
<rect class="node" x="626" y="151" width="138" height="50" rx="13"/>
|
||||
<text class="node-title" x="695" y="172" text-anchor="middle">MCP Tools</text>
|
||||
<text class="node-text" x="695" y="190" text-anchor="middle">agent actions</text>
|
||||
<rect class="node" x="816" y="151" width="138" height="50" rx="13"/>
|
||||
<text class="node-title" x="885" y="172" text-anchor="middle">CLI Client</text>
|
||||
<text class="node-text" x="885" y="190" text-anchor="middle">local access</text>
|
||||
</g>
|
||||
|
||||
<g class="plate" transform="translate(0,0)">
|
||||
<path class="side" d="M94 336 L1106 336 L1074 366 L126 366 Z" fill="#aab5ec"/>
|
||||
<rect class="plate-bg" x="94" y="264" width="1012" height="100" rx="18"/>
|
||||
<rect x="94" y="264" width="280" height="100" rx="18" fill="url(#appGrad)"/>
|
||||
<text class="layer-title" x="146" y="303">Application</text>
|
||||
<text class="layer-note" x="146" y="325">Config, wiring, lifecycle</text>
|
||||
|
||||
<rect class="node" x="424" y="290" width="122" height="48" rx="12"/>
|
||||
<text class="node-title" x="485" y="319" text-anchor="middle">Context</text>
|
||||
<rect class="node" x="574" y="290" width="122" height="48" rx="12"/>
|
||||
<text class="node-title" x="635" y="319" text-anchor="middle">Wiring</text>
|
||||
<rect class="node" x="724" y="290" width="122" height="48" rx="12"/>
|
||||
<text class="node-title" x="785" y="319" text-anchor="middle">Lifecycle</text>
|
||||
<rect class="node" x="874" y="290" width="122" height="48" rx="12"/>
|
||||
<text class="node-title" x="935" y="319" text-anchor="middle">Job APIs</text>
|
||||
</g>
|
||||
|
||||
<g class="plate" transform="translate(0,0)">
|
||||
<path class="side" d="M126 500 L1074 500 L1042 530 L158 530 Z" fill="#e9ad8f"/>
|
||||
<rect class="plate-bg" x="126" y="404" width="948" height="124" rx="18"/>
|
||||
<rect x="126" y="404" width="280" height="124" rx="18" fill="url(#jobGrad)"/>
|
||||
<text class="layer-title" x="178" y="448">Steps / Jobs</text>
|
||||
<text class="layer-note" x="178" y="470">Composable workflows</text>
|
||||
|
||||
<rect class="node" x="424" y="428" width="232" height="76" rx="14"/>
|
||||
<text class="node-title" x="448" y="456">Jobs</text>
|
||||
<text class="node-text" x="448" y="479">base · stream · background · cron</text>
|
||||
<rect class="node" x="704" y="428" width="300" height="76" rx="14"/>
|
||||
<text class="node-title" x="728" y="456">Step Modules</text>
|
||||
<text class="node-text" x="728" y="479">file_io · index · evolve · transfer · channel · common</text>
|
||||
</g>
|
||||
|
||||
<g class="plate" transform="translate(0,0)">
|
||||
<path class="side" d="M94 674 L1106 674 L1074 704 L126 704 Z" fill="#8fcbd5"/>
|
||||
<rect class="plate-bg" x="94" y="568" width="1012" height="134" rx="18"/>
|
||||
<rect x="94" y="568" width="280" height="134" rx="18" fill="url(#kernelGrad)"/>
|
||||
<text class="layer-title" x="146" y="615">Knowledge Kernel</text>
|
||||
<text class="layer-note" x="146" y="637">Index, watch, schema</text>
|
||||
|
||||
<rect class="node" x="424" y="594" width="228" height="82" rx="14"/>
|
||||
<text class="node-title" x="448" y="623">Index Stores</text>
|
||||
<text class="node-text" x="448" y="646">file_store · keyword_index</text>
|
||||
<text class="node-text" x="448" y="664">embedding_store · file_graph</text>
|
||||
<rect class="node" x="698" y="594" width="172" height="82" rx="14"/>
|
||||
<text class="node-title" x="722" y="623">File Watcher</text>
|
||||
<text class="node-text" x="722" y="646">scanner · chunker</text>
|
||||
<text class="node-text" x="722" y="664">catalog</text>
|
||||
<rect class="node" x="916" y="594" width="142" height="82" rx="14"/>
|
||||
<text class="node-title" x="940" y="623">Memory</text>
|
||||
<text class="node-text" x="940" y="646">FileNode</text>
|
||||
<text class="node-text" x="940" y="664">FileChunk · FileLink</text>
|
||||
</g>
|
||||
|
||||
<g class="plate" transform="translate(0,0)">
|
||||
<path class="side" d="M126 818 L1074 818 L1042 848 L158 848 Z" fill="#d0b98f"/>
|
||||
<rect class="plate-bg" x="126" y="738" width="948" height="108" rx="18"/>
|
||||
<rect x="126" y="738" width="280" height="108" rx="18" fill="url(#vaultGrad)"/>
|
||||
<text class="layer-title" x="178" y="777">Vault Layout</text>
|
||||
<text class="layer-note" x="178" y="799">File-backed memory</text>
|
||||
|
||||
<rect class="node" x="424" y="767" width="126" height="50" rx="13"/>
|
||||
<text class="node-title" x="487" y="788" text-anchor="middle">daily/</text>
|
||||
<text class="node-text" x="487" y="806" text-anchor="middle">working notes</text>
|
||||
<rect class="node" x="584" y="767" width="126" height="50" rx="13"/>
|
||||
<text class="node-title" x="647" y="788" text-anchor="middle">digest/</text>
|
||||
<text class="node-text" x="647" y="806" text-anchor="middle">long-term</text>
|
||||
<rect class="node" x="744" y="767" width="126" height="50" rx="13"/>
|
||||
<text class="node-title" x="807" y="788" text-anchor="middle">resource/</text>
|
||||
<text class="node-text" x="807" y="806" text-anchor="middle">resources</text>
|
||||
<rect class="node" x="904" y="767" width="126" height="50" rx="13"/>
|
||||
<text class="node-title" x="967" y="788" text-anchor="middle">metadata/</text>
|
||||
<text class="node-text" x="967" y="806" text-anchor="middle">state</text>
|
||||
</g>
|
||||
|
||||
<text class="section-label" x="92" y="118">EXTERNAL SURFACES</text>
|
||||
<text class="section-label" x="92" y="734">PERSISTENT MEMORY BASE</text>
|
||||
<text class="micro" x="600" y="878" text-anchor="middle">Structure reads bottom-up during boot and top-down during use.</text>
|
||||
<text class="micro" x="600" y="896" text-anchor="middle">Vault files feed the kernel; workflows compose operations; application wiring exposes stable service entry points.</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.4 KiB |
110
docs/figure/memory-as-file.svg
Normal file
110
docs/figure/memory-as-file.svg
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="680" viewBox="0 0 1200 680" role="img" aria-labelledby="title desc">
|
||||
<title id="title">ReMe memory as file model</title>
|
||||
<desc id="desc">A diagram showing ReMe vault files as both a human readable memory interface and an agent operable memory graph, flowing from raw input to daily notes, digest nodes, and metadata indexes.</desc>
|
||||
<defs>
|
||||
<style>
|
||||
.bg { fill: #f7f8fb; }
|
||||
.title { font: 700 28px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #172033; }
|
||||
.subtitle { font: 14px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #607086; }
|
||||
.section-title { font: 700 18px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #172033; }
|
||||
.section-subtitle { font: 13px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #607086; }
|
||||
.chip-title { font: 700 13px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #253246; }
|
||||
.chip-text { font: 12px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #627087; }
|
||||
.note { font: 12px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #5d6a7d; }
|
||||
.label { font: 700 12px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #ffffff; }
|
||||
.panel { fill: #ffffff; stroke: #d7dde7; stroke-width: 1.2; rx: 10; ry: 10; }
|
||||
.chip { fill: #f9fafc; stroke: #dfe5ee; stroke-width: 1; rx: 8; ry: 8; }
|
||||
.badge { fill: #44546a; }
|
||||
.soft { fill: #eef2f7; stroke: #d7dde7; stroke-width: 1; rx: 8; ry: 8; }
|
||||
.arrow { stroke: #8794a8; stroke-width: 2; fill: none; marker-end: url(#arrow); }
|
||||
.soft-arrow { stroke: #a3adbd; stroke-width: 1.6; stroke-dasharray: 5 5; fill: none; marker-end: url(#arrow-soft); }
|
||||
.line { stroke: #e1e6ee; stroke-width: 1; }
|
||||
</style>
|
||||
<marker id="arrow" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto" markerUnits="strokeWidth">
|
||||
<path d="M0,0 L0,6 L8,3 z" fill="#8794a8"/>
|
||||
</marker>
|
||||
<marker id="arrow-soft" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto" markerUnits="strokeWidth">
|
||||
<path d="M0,0 L0,6 L8,3 z" fill="#a3adbd"/>
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
<rect class="bg" x="0" y="0" width="1200" height="680"/>
|
||||
<text class="title" x="600" y="54" text-anchor="middle">Memory as File</text>
|
||||
<text class="subtitle" x="600" y="80" text-anchor="middle">Vault files are the readable memory surface and the operable graph/index substrate.</text>
|
||||
|
||||
<rect class="panel" x="72" y="124" width="280" height="168"/>
|
||||
<rect class="badge" x="102" y="150" width="94" height="24" rx="12" ry="12"/>
|
||||
<text class="label" x="149" y="167" text-anchor="middle">Human</text>
|
||||
<text class="section-title" x="102" y="202">Read and edit files</text>
|
||||
<text class="section-subtitle" x="102" y="226">Markdown, YAML, JSONL, resources</text>
|
||||
<rect class="chip" x="102" y="248" width="220" height="28"/>
|
||||
<text class="chip-text" x="212" y="267" text-anchor="middle">open, revise, move, delete</text>
|
||||
|
||||
<rect class="panel" x="460" y="120" width="280" height="176"/>
|
||||
<text class="section-title" x="600" y="152" text-anchor="middle">Vault directory</text>
|
||||
<text class="section-subtitle" x="600" y="176" text-anchor="middle">the shared memory interface</text>
|
||||
<rect class="soft" x="504" y="204" width="192" height="30"/>
|
||||
<text class="chip-title" x="600" y="224" text-anchor="middle">Memory as File</text>
|
||||
<rect class="soft" x="504" y="248" width="192" height="30"/>
|
||||
<text class="chip-title" x="600" y="268" text-anchor="middle">File as Memory</text>
|
||||
|
||||
<rect class="panel" x="848" y="124" width="280" height="168"/>
|
||||
<rect class="badge" x="878" y="150" width="94" height="24" rx="12" ry="12"/>
|
||||
<text class="label" x="925" y="167" text-anchor="middle">Agent</text>
|
||||
<text class="section-title" x="878" y="202">Parse and operate graph</text>
|
||||
<text class="section-subtitle" x="878" y="226">frontmatter, chunks, wikilinks</text>
|
||||
<rect class="chip" x="878" y="248" width="220" height="28"/>
|
||||
<text class="chip-text" x="988" y="267" text-anchor="middle">search, link, rewrite, index</text>
|
||||
|
||||
<path class="arrow" d="M352 208 H460"/>
|
||||
<path class="arrow" d="M740 208 H848"/>
|
||||
<path class="soft-arrow" d="M848 250 C740 326 460 326 352 250"/>
|
||||
<text class="note" x="600" y="336" text-anchor="middle">people and agents see the same file tree, so edits and evidence links stay inspectable</text>
|
||||
|
||||
<rect class="panel" x="54" y="398" width="224" height="144"/>
|
||||
<circle class="badge" cx="88" cy="432" r="15"/>
|
||||
<text class="label" x="88" y="436" text-anchor="middle">1</text>
|
||||
<text class="section-title" x="116" y="438">Raw input</text>
|
||||
<text class="section-subtitle" x="82" y="468">keep the original scene</text>
|
||||
<rect class="chip" x="82" y="494" width="168" height="28"/>
|
||||
<text class="chip-text" x="166" y="513" text-anchor="middle">reme_session/ + resource/</text>
|
||||
|
||||
<rect class="panel" x="326" y="398" width="224" height="144"/>
|
||||
<circle class="badge" cx="360" cy="432" r="15"/>
|
||||
<text class="label" x="360" y="436" text-anchor="middle">2</text>
|
||||
<text class="section-title" x="388" y="438">Daily</text>
|
||||
<text class="section-subtitle" x="354" y="468">shallow working memory</text>
|
||||
<rect class="chip" x="354" y="494" width="168" height="28"/>
|
||||
<text class="chip-text" x="438" y="513" text-anchor="middle">daily/YYYY-MM-DD/*.md</text>
|
||||
|
||||
<rect class="panel" x="598" y="398" width="224" height="144"/>
|
||||
<circle class="badge" cx="632" cy="432" r="15"/>
|
||||
<text class="label" x="632" y="436" text-anchor="middle">3</text>
|
||||
<text class="section-title" x="660" y="438">Digest</text>
|
||||
<text class="section-subtitle" x="626" y="468">long-term reusable nodes</text>
|
||||
<rect class="chip" x="626" y="494" width="168" height="28"/>
|
||||
<text class="chip-text" x="710" y="513" text-anchor="middle">personal / procedure / wiki</text>
|
||||
|
||||
<rect class="panel" x="870" y="398" width="224" height="144"/>
|
||||
<circle class="badge" cx="904" cy="432" r="15"/>
|
||||
<text class="label" x="904" y="436" text-anchor="middle">4</text>
|
||||
<text class="section-title" x="932" y="438">Metadata</text>
|
||||
<text class="section-subtitle" x="898" y="468">system state and indexes</text>
|
||||
<rect class="chip" x="898" y="494" width="168" height="28"/>
|
||||
<text class="chip-text" x="982" y="513" text-anchor="middle">catalog + chunks + links</text>
|
||||
|
||||
<path class="arrow" d="M278 470 H326"/>
|
||||
<path class="arrow" d="M550 470 H598"/>
|
||||
<path class="arrow" d="M822 470 H870"/>
|
||||
<path class="soft-arrow" d="M982 398 C982 356 710 356 710 398"/>
|
||||
<path class="soft-arrow" d="M982 542 C982 592 438 592 438 542"/>
|
||||
|
||||
<rect class="panel" x="126" y="594" width="948" height="46"/>
|
||||
<text class="note" x="176" y="622">Stable paths</text>
|
||||
<line class="line" x1="308" y1="606" x2="308" y2="628"/>
|
||||
<text class="chip-text" x="360" y="622">vault-relative wikilinks</text>
|
||||
<line class="line" x1="548" y1="606" x2="548" y2="628"/>
|
||||
<text class="chip-text" x="600" y="622">derived_from evidence edges</text>
|
||||
<line class="line" x1="814" y1="606" x2="814" y2="628"/>
|
||||
<text class="chip-text" x="862" y="622">search expands structure and links</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7 KiB |
98
docs/figure/memory-search.svg
Normal file
98
docs/figure/memory-search.svg
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="640" viewBox="0 0 1200 640" role="img" aria-labelledby="title desc">
|
||||
<title id="title">ReMe memory search flow</title>
|
||||
<desc id="desc">A concise left-to-right memory search flow from watched vault files to progressive link expansion.</desc>
|
||||
<defs>
|
||||
<style>
|
||||
.bg { fill: #f7f8fb; }
|
||||
.title { font: 700 28px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #172033; }
|
||||
.subtitle { font: 14px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #607086; }
|
||||
.step-num { font: 700 12px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #ffffff; }
|
||||
.step-title { font: 700 18px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #172033; }
|
||||
.step-subtitle { font: 13px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #607086; }
|
||||
.chip-title { font: 700 13px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #253246; }
|
||||
.chip-text { font: 12px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #627087; }
|
||||
.note { font: 12px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #5d6a7d; }
|
||||
.panel { fill: #ffffff; stroke: #d7dde7; stroke-width: 1.2; rx: 10; ry: 10; }
|
||||
.chip { fill: #f9fafc; stroke: #dfe5ee; stroke-width: 1; rx: 8; ry: 8; }
|
||||
.badge { fill: #44546a; }
|
||||
.arrow { stroke: #8794a8; stroke-width: 2; fill: none; marker-end: url(#arrow); }
|
||||
.soft-arrow { stroke: #a3adbd; stroke-width: 1.6; stroke-dasharray: 5 5; fill: none; marker-end: url(#arrow-soft); }
|
||||
.line { stroke: #e1e6ee; stroke-width: 1; }
|
||||
</style>
|
||||
<marker id="arrow" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto" markerUnits="strokeWidth">
|
||||
<path d="M0,0 L0,6 L8,3 z" fill="#8794a8"/>
|
||||
</marker>
|
||||
<marker id="arrow-soft" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto" markerUnits="strokeWidth">
|
||||
<path d="M0,0 L0,6 L8,3 z" fill="#a3adbd"/>
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
<rect class="bg" x="0" y="0" width="1200" height="640"/>
|
||||
<text class="title" x="600" y="54" text-anchor="middle">Memory Search</text>
|
||||
<text class="subtitle" x="600" y="80" text-anchor="middle">Index file changes continuously, recall relevant chunks, then expand nearby wikilink context.</text>
|
||||
|
||||
<rect class="panel" x="64" y="150" width="232" height="260"/>
|
||||
<circle class="badge" cx="100" cy="188" r="15"/>
|
||||
<text class="step-num" x="100" y="192" text-anchor="middle">1</text>
|
||||
<text class="step-title" x="128" y="194">Watch memory</text>
|
||||
<text class="step-subtitle" x="92" y="224">index_update_loop</text>
|
||||
<rect class="chip" x="92" y="250" width="176" height="44"/>
|
||||
<text class="chip-title" x="180" y="269" text-anchor="middle">daily / digest / resource</text>
|
||||
<text class="chip-text" x="180" y="287" text-anchor="middle">md and jsonl files</text>
|
||||
<rect class="chip" x="92" y="314" width="176" height="44"/>
|
||||
<text class="chip-title" x="180" y="333" text-anchor="middle">init + watch changes</text>
|
||||
<text class="chip-text" x="180" y="351" text-anchor="middle">added / modified / deleted</text>
|
||||
|
||||
<rect class="panel" x="348" y="150" width="232" height="260"/>
|
||||
<circle class="badge" cx="384" cy="188" r="15"/>
|
||||
<text class="step-num" x="384" y="192" text-anchor="middle">2</text>
|
||||
<text class="step-title" x="412" y="194">Build index</text>
|
||||
<text class="step-subtitle" x="376" y="224">update_index_step</text>
|
||||
<rect class="chip" x="376" y="250" width="176" height="44"/>
|
||||
<text class="chip-title" x="464" y="269" text-anchor="middle">chunk file</text>
|
||||
<text class="chip-text" x="464" y="287" text-anchor="middle">FileNode + FileChunk[]</text>
|
||||
<rect class="chip" x="376" y="314" width="176" height="44"/>
|
||||
<text class="chip-title" x="464" y="333" text-anchor="middle">store structures</text>
|
||||
<text class="chip-text" x="464" y="351" text-anchor="middle">BM25 + graph + chunks</text>
|
||||
|
||||
<rect class="panel" x="632" y="150" width="232" height="260"/>
|
||||
<circle class="badge" cx="668" cy="188" r="15"/>
|
||||
<text class="step-num" x="668" y="192" text-anchor="middle">3</text>
|
||||
<text class="step-title" x="696" y="194">Recall chunks</text>
|
||||
<text class="step-subtitle" x="660" y="224">search_step</text>
|
||||
<rect class="chip" x="660" y="250" width="176" height="44"/>
|
||||
<text class="chip-title" x="748" y="269" text-anchor="middle">BM25 search</text>
|
||||
<text class="chip-text" x="748" y="287" text-anchor="middle">keyword-ranked chunks</text>
|
||||
<rect class="chip" x="660" y="314" width="176" height="44"/>
|
||||
<text class="chip-title" x="748" y="333" text-anchor="middle">optional vector search</text>
|
||||
<text class="chip-text" x="748" y="351" text-anchor="middle">RRF fusion when enabled</text>
|
||||
|
||||
<rect class="panel" x="916" y="150" width="220" height="260"/>
|
||||
<circle class="badge" cx="952" cy="188" r="15"/>
|
||||
<text class="step-num" x="952" y="192" text-anchor="middle">4</text>
|
||||
<text class="step-title" x="980" y="194">Expand context</text>
|
||||
<text class="step-subtitle" x="944" y="224">expand_links</text>
|
||||
<rect class="chip" x="944" y="250" width="164" height="44"/>
|
||||
<text class="chip-title" x="1026" y="269" text-anchor="middle">top chunks</text>
|
||||
<text class="chip-text" x="1026" y="287" text-anchor="middle">path + line range</text>
|
||||
<rect class="chip" x="944" y="314" width="164" height="44"/>
|
||||
<text class="chip-title" x="1026" y="333" text-anchor="middle">outlinks + inlinks</text>
|
||||
<text class="chip-text" x="1026" y="351" text-anchor="middle">name, description, via</text>
|
||||
|
||||
<path class="arrow" d="M296 280 H348"/>
|
||||
<path class="arrow" d="M580 280 H632"/>
|
||||
<path class="arrow" d="M864 280 H916"/>
|
||||
|
||||
<rect class="panel" x="142" y="484" width="916" height="76"/>
|
||||
<text class="note" x="190" y="514">Default path</text>
|
||||
<text class="chip-text" x="190" y="536">BM25 first, vector optional.</text>
|
||||
<line class="line" x1="410" y1="500" x2="410" y2="544"/>
|
||||
<text class="chip-title" x="454" y="514">BM25 is enabled by default</text>
|
||||
<text class="chip-text" x="454" y="536">embedding_store is empty unless configured.</text>
|
||||
<line class="line" x1="720" y1="500" x2="720" y2="544"/>
|
||||
<text class="chip-title" x="764" y="514">Results stay compact first</text>
|
||||
<text class="chip-text" x="764" y="536">Use read or traverse for deeper expansion.</text>
|
||||
|
||||
<path class="soft-arrow" d="M1026 410 C1026 456 748 452 748 410"/>
|
||||
<text class="note" x="888" y="454" text-anchor="middle">link expansion is contextual, not a full-vault dump</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.4 KiB |
186
docs/figure/reme-overview.svg
Normal file
186
docs/figure/reme-overview.svg
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="640" viewBox="0 0 1200 640" role="img" aria-labelledby="title desc">
|
||||
<title id="title">ReMe overview</title>
|
||||
<desc id="desc">A hand-drawn style overview of ReMe, showing Auto Memory, Auto Resource, Auto Dream, Memory Search, and Memory as File.</desc>
|
||||
<defs>
|
||||
<style>
|
||||
.bg { fill: #fffdf8; }
|
||||
.ink { stroke: #1f2430; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
|
||||
.thin { stroke-width: 1.6; }
|
||||
.dash { stroke-dasharray: 8 7; }
|
||||
.title { font: 700 30px "Comic Sans MS", "Bradley Hand", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #1f2430; }
|
||||
.subtitle { font: 14px "Comic Sans MS", "Bradley Hand", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #556276; }
|
||||
.head { font: 700 17px "Comic Sans MS", "Bradley Hand", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #1f2430; }
|
||||
.label { font: 700 13px "Comic Sans MS", "Bradley Hand", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #1f2430; }
|
||||
.text { font: 12px "Comic Sans MS", "Bradley Hand", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #4f5c6f; }
|
||||
.tiny { font: 11px "Comic Sans MS", "Bradley Hand", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #5e6a7c; }
|
||||
.box { fill: #ffffff; }
|
||||
.blue { fill: #eef7ff; }
|
||||
.green { fill: #f0fbf5; }
|
||||
.yellow { fill: #fff7e5; }
|
||||
.pink { fill: #fff2f6; }
|
||||
.violet { fill: #f4f1ff; }
|
||||
.mint { fill: #eefafa; }
|
||||
.peach { fill: #fff2ea; }
|
||||
.paper { fill: #f8fbff; }
|
||||
.tab { fill: #ffffff; }
|
||||
.step { stroke-dasharray: 6 5; }
|
||||
.arrow { fill: none; stroke: #7f8b9d; stroke-width: 1.45; stroke-linecap: round; stroke-linejoin: round; marker-end: url(#arrow); }
|
||||
.soft-arrow { fill: none; stroke: #a3adbd; stroke-width: 1.25; stroke-dasharray: 6 6; stroke-linecap: round; stroke-linejoin: round; marker-end: url(#arrow-soft); }
|
||||
</style>
|
||||
<marker id="arrow" markerWidth="6" markerHeight="6" refX="5" refY="2" orient="auto" markerUnits="strokeWidth">
|
||||
<path d="M0,0 L0,4 L5,2 z" fill="#7f8b9d"/>
|
||||
</marker>
|
||||
<marker id="arrow-soft" markerWidth="6" markerHeight="6" refX="5" refY="2" orient="auto" markerUnits="strokeWidth">
|
||||
<path d="M0,0 L0,4 L5,2 z" fill="#a3adbd"/>
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
<rect class="bg" x="0" y="0" width="1200" height="640"/>
|
||||
<text class="title" x="600" y="44" text-anchor="middle">ReMe</text>
|
||||
<text class="subtitle" x="600" y="68" text-anchor="middle">A file-native memory loop: capture, consolidate, link, search, and proactively surface what matters.</text>
|
||||
|
||||
<!-- Top workflow -->
|
||||
<g transform="translate(40 124)">
|
||||
<rect class="blue ink thin" x="0" y="0" width="300" height="110" rx="16"/>
|
||||
<text class="head" x="150" y="27" text-anchor="middle">Auto Memory</text>
|
||||
<rect class="box ink thin step" x="38" y="48" width="100" height="42" rx="10"/>
|
||||
<text class="label" x="88" y="67" text-anchor="middle">Capture</text>
|
||||
<text class="tiny" x="88" y="82" text-anchor="middle">conversation</text>
|
||||
<rect class="yellow ink thin step" x="162" y="48" width="100" height="42" rx="10"/>
|
||||
<text class="label" x="212" y="67" text-anchor="middle">Write</text>
|
||||
<text class="tiny" x="212" y="82" text-anchor="middle">daily card</text>
|
||||
<path class="arrow" d="M140 69 H160"/>
|
||||
</g>
|
||||
|
||||
<g transform="translate(40 260)">
|
||||
<rect class="green ink thin" x="0" y="0" width="300" height="110" rx="16"/>
|
||||
<text class="head" x="150" y="27" text-anchor="middle">Auto Resource</text>
|
||||
<rect class="box ink thin step" x="38" y="48" width="100" height="42" rx="10"/>
|
||||
<text class="label" x="88" y="67" text-anchor="middle">Read</text>
|
||||
<text class="tiny" x="88" y="82" text-anchor="middle">resource file</text>
|
||||
<rect class="yellow ink thin step" x="162" y="48" width="100" height="42" rx="10"/>
|
||||
<text class="label" x="212" y="67" text-anchor="middle">Write</text>
|
||||
<text class="tiny" x="212" y="82" text-anchor="middle">daily card</text>
|
||||
<path class="arrow" d="M140 69 H160"/>
|
||||
</g>
|
||||
|
||||
<g transform="translate(380 120)">
|
||||
<rect class="yellow ink thin" x="0" y="0" width="320" height="260" rx="18"/>
|
||||
<text class="head" x="160" y="32" text-anchor="middle">Auto Dream</text>
|
||||
<text class="text" x="160" y="58" text-anchor="middle">Daily notes -> durable digest</text>
|
||||
|
||||
<rect class="box ink thin step" x="38" y="84" width="112" height="56" rx="12"/>
|
||||
<text class="label" x="94" y="108" text-anchor="middle">Extract</text>
|
||||
<text class="tiny" x="94" y="126" text-anchor="middle">changed files</text>
|
||||
|
||||
<rect class="peach ink thin step" x="170" y="84" width="112" height="56" rx="12"/>
|
||||
<text class="label" x="226" y="108" text-anchor="middle">Auto Link</text>
|
||||
<text class="tiny" x="226" y="126" text-anchor="middle">dedupe + edges</text>
|
||||
|
||||
<rect class="pink ink thin step" x="38" y="168" width="112" height="56" rx="12"/>
|
||||
<text class="label" x="94" y="192" text-anchor="middle">Integrate</text>
|
||||
<text class="tiny" x="94" y="210" text-anchor="middle">write digest</text>
|
||||
|
||||
<rect class="mint ink thin step" x="170" y="168" width="112" height="56" rx="12"/>
|
||||
<text class="label" x="226" y="192" text-anchor="middle">Proactive</text>
|
||||
<text class="tiny" x="226" y="210" text-anchor="middle">interests.yaml</text>
|
||||
|
||||
<path class="arrow" d="M152 112 H168"/>
|
||||
<path class="arrow" d="M226 142 V166"/>
|
||||
<path class="arrow" d="M168 196 H152"/>
|
||||
<path class="arrow" d="M94 166 V142"/>
|
||||
</g>
|
||||
|
||||
<g transform="translate(730 120)">
|
||||
<rect class="violet ink thin" x="0" y="0" width="430" height="260" rx="18"/>
|
||||
<text class="head" x="230" y="31" text-anchor="middle">Memory Search</text>
|
||||
<text class="text" x="230" y="56" text-anchor="middle">Ask the vault, then follow the graph.</text>
|
||||
|
||||
<g transform="translate(44 96)">
|
||||
<rect class="blue ink thin step" x="0" y="0" width="96" height="92" rx="13"/>
|
||||
<text class="label" x="48" y="28" text-anchor="middle">Hybrid</text>
|
||||
<text class="label" x="48" y="46" text-anchor="middle">Index</text>
|
||||
<text class="tiny" x="48" y="67" text-anchor="middle">chunks</text>
|
||||
<text class="tiny" x="48" y="82" text-anchor="middle">+ wikilinks</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(166 96)">
|
||||
<rect class="green ink thin step" x="0" y="0" width="96" height="92" rx="13"/>
|
||||
<text class="label" x="48" y="28" text-anchor="middle">Hybrid</text>
|
||||
<text class="label" x="48" y="46" text-anchor="middle">Retrieval</text>
|
||||
<text class="tiny" x="48" y="67" text-anchor="middle">BM25</text>
|
||||
<text class="tiny" x="48" y="82" text-anchor="middle">+ vectors</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(288 96)">
|
||||
<rect class="mint ink thin step" x="0" y="0" width="96" height="92" rx="13"/>
|
||||
<text class="label" x="48" y="28" text-anchor="middle">Progressive</text>
|
||||
<text class="label" x="48" y="46" text-anchor="middle">Expansion</text>
|
||||
<text class="tiny" x="48" y="67" text-anchor="middle">outlinks</text>
|
||||
<text class="tiny" x="48" y="82" text-anchor="middle">+ inlinks</text>
|
||||
</g>
|
||||
|
||||
<path class="arrow" d="M140 142 H164"/>
|
||||
<path class="arrow" d="M262 142 H286"/>
|
||||
</g>
|
||||
|
||||
<path class="arrow" d="M340 179 H380"/>
|
||||
<path class="arrow" d="M340 315 H380"/>
|
||||
|
||||
<!-- Bottom foundation -->
|
||||
<rect class="box ink" x="40" y="408" width="1120" height="202" rx="24"/>
|
||||
<text class="head" x="600" y="442" text-anchor="middle">Memory as File</text>
|
||||
<text class="text" x="600" y="466" text-anchor="middle">Every memory is readable, editable, indexable, linkable, and auditable as files.</text>
|
||||
|
||||
<g transform="translate(94 500)">
|
||||
<rect class="paper ink thin" x="10" y="6" width="62" height="52"/>
|
||||
<rect class="paper ink thin" x="5" y="3" width="62" height="52"/>
|
||||
<rect class="paper ink thin" x="0" y="0" width="62" height="52"/>
|
||||
<rect class="tab ink thin" x="-12" y="14" width="44" height="22"/>
|
||||
<text class="label" x="10" y="30" text-anchor="middle">.json</text>
|
||||
<line class="ink thin" x1="26" y1="28" x2="50" y2="28"/>
|
||||
<line class="ink thin" x1="22" y1="42" x2="52" y2="42"/>
|
||||
<text class="label" x="106" y="19">reme_session/</text>
|
||||
<text class="tiny" x="106" y="39">raw session logs</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(344 500)">
|
||||
<rect class="paper ink thin" x="10" y="6" width="62" height="52"/>
|
||||
<rect class="paper ink thin" x="5" y="3" width="62" height="52"/>
|
||||
<rect class="paper ink thin" x="0" y="0" width="62" height="52"/>
|
||||
<rect class="tab ink thin" x="-8" y="14" width="34" height="22"/>
|
||||
<text class="label" x="9" y="30" text-anchor="middle">.md</text>
|
||||
<line class="ink thin" x1="26" y1="28" x2="50" y2="28"/>
|
||||
<line class="ink thin" x1="22" y1="42" x2="52" y2="42"/>
|
||||
<text class="label" x="106" y="19">resource/</text>
|
||||
<text class="tiny" x="106" y="39">raw material with source</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(604 500)">
|
||||
<rect class="paper ink thin" x="10" y="6" width="62" height="52"/>
|
||||
<rect class="paper ink thin" x="5" y="3" width="62" height="52"/>
|
||||
<rect class="paper ink thin" x="0" y="0" width="62" height="52"/>
|
||||
<rect class="tab ink thin" x="-8" y="14" width="34" height="22"/>
|
||||
<text class="label" x="9" y="30" text-anchor="middle">.md</text>
|
||||
<line class="ink thin" x1="26" y1="28" x2="50" y2="28"/>
|
||||
<line class="ink thin" x1="22" y1="42" x2="52" y2="42"/>
|
||||
<text class="label" x="106" y="19">daily/</text>
|
||||
<text class="tiny" x="106" y="39">working memory cards</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(850 500)">
|
||||
<rect class="paper ink thin" x="10" y="6" width="62" height="52"/>
|
||||
<rect class="paper ink thin" x="5" y="3" width="62" height="52"/>
|
||||
<rect class="paper ink thin" x="0" y="0" width="62" height="52"/>
|
||||
<rect class="tab ink thin" x="-8" y="14" width="34" height="22"/>
|
||||
<text class="label" x="9" y="30" text-anchor="middle">.md</text>
|
||||
<line class="ink thin" x1="26" y1="28" x2="50" y2="28"/>
|
||||
<line class="ink thin" x1="22" y1="42" x2="52" y2="42"/>
|
||||
<text class="label" x="106" y="19">digest/</text>
|
||||
<text class="tiny" x="106" y="39">long-term knowledge nodes</text>
|
||||
</g>
|
||||
|
||||
<path class="soft-arrow" d="M190 370 V408"/>
|
||||
<path class="soft-arrow" d="M540 380 V408"/>
|
||||
<path class="soft-arrow" d="M945 380 V408"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 10 KiB |
196
docs/zh/auto_dream.md
Normal file
196
docs/zh/auto_dream.md
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
# Auto Dream
|
||||
|
||||
`auto_dream` 是 ReMe 的 daily 到 digest 的长期记忆沉淀流程。它扫描指定日期的 daily 输入,只处理相对上次 dream
|
||||
发生变化的文件,把值得长期保留的内容抽取成 memory units,整合进 `digest/`,再生成当天可供主动提醒使用的 `interests.yaml`。
|
||||
|
||||

|
||||
|
||||
它消费的 daily 输入通常来自 [Auto Memory](./auto_memory.md) 和 [Auto Resource](./auto_resource.md)。`digest/`、`derived_from::`
|
||||
和 wikilink 的文件语义见 [Memory as File](./memory_as_file.md);Integrate 阶段的链接策略详见 [Auto Link](./auto_link.md)。
|
||||
`interests.yaml` 的读取接口见 [Proactive](./proactive.md)。
|
||||
|
||||
## 配置入口
|
||||
|
||||
默认配置在 `reme/config/default.yaml`:
|
||||
|
||||
```yaml
|
||||
auto_dream:
|
||||
backend: base
|
||||
parameters:
|
||||
date:
|
||||
type: string
|
||||
default: ""
|
||||
hint:
|
||||
type: string
|
||||
default: ""
|
||||
topic_count:
|
||||
type: integer
|
||||
default: 3
|
||||
topic_diversity_days:
|
||||
type: integer
|
||||
default: 7
|
||||
steps:
|
||||
- backend: dream_extract_step
|
||||
file_catalog: dream
|
||||
topic_session_id: interests
|
||||
- backend: dream_integrate_step
|
||||
- backend: dream_topics_step
|
||||
topic_count: 3
|
||||
topic_diversity_days: 7
|
||||
- backend: dream_finish_step
|
||||
file_catalog: dream
|
||||
```
|
||||
|
||||
参数含义:
|
||||
|
||||
| 参数 | 作用 |
|
||||
|------------------------|------------------------------------------------|
|
||||
| `date` | 要处理的日期,格式为 `YYYY-MM-DD`。为空时使用应用时区中的今天。 |
|
||||
| `hint` | 调用方给抽取和整合阶段的额外指导。 |
|
||||
| `topic_count` | 最终写入 `interests.yaml` 的 topic 上限,默认 3。 |
|
||||
| `topic_diversity_days` | 选择 topic 时参考过去多少天的 `interests.yaml` 避免重复,默认 7。 |
|
||||
|
||||
## 输入和输出
|
||||
|
||||
输入来自指定日期的 daily markdown:
|
||||
|
||||
```text
|
||||
daily/<date>.md
|
||||
daily/<date>/**/*.md
|
||||
```
|
||||
|
||||
`daily/<date>/interests.yaml` 不作为抽取输入,避免上一轮主动主题反过来污染下一轮抽取。
|
||||
|
||||
主要输出有三类:
|
||||
|
||||
| 输出 | 说明 |
|
||||
|-------------------------------------|-------------------------------------|
|
||||
| `digest/procedure/*.md` | 方法、流程、runbook、可执行经验。 |
|
||||
| `digest/personal/*.md` | 用户、团队、项目相关的偏好、事实、长期上下文。 |
|
||||
| `digest/wiki/*.md` | 通用知识、概念、观察、决策先例。 |
|
||||
| `daily/<date>/interests.yaml` | 当天值得上层 Agent 主动关注的兴趣主题。 |
|
||||
| `reme_metadata/file_catalog/dream*` | dream 专用 catalog,用于判断 daily 输入是否变化。 |
|
||||
|
||||
## 四个阶段
|
||||
|
||||
### 1. Extract
|
||||
|
||||
`dream_extract_step` 做三件事:
|
||||
|
||||
1. 刷新当天索引页 `daily/<date>.md`。
|
||||
2. 扫描 `daily/<date>.md` 和 `daily/<date>/**/*.md`,与 `file_catalog: dream` 中记录的 mtime 对比。
|
||||
3. 只把 changed files 交给 LLM,全局抽取两类结构化结果:`units` 和 `topics`。
|
||||
|
||||
`units` 是准备沉淀进 digest 的长期记忆单元,包含 `name`、`bucket`、`summary`、`paths`。`bucket` 只允许 `procedure`、
|
||||
`personal`、`wiki`;未知值会路由到 `wiki`。
|
||||
|
||||
`topics` 是当天主动兴趣候选,包含 `title`、`reason`、`evidence`、`keywords`、`paths`,后续由 Topics 阶段再筛选。
|
||||
|
||||
如果没有 changed files,流程会提前成功结束后续抽取工作;如果有变化但没有配置 LLM,Extract 会失败,因为抽取依赖 LLM。
|
||||
|
||||
### 2. Integrate
|
||||
|
||||
`dream_integrate_step` 对每个 unit 独立调用 Agent,将一个 unit 整合成一个 digest 节点。它会给 Agent 暴露这些工具:
|
||||
|
||||
```text
|
||||
node_search, read, frontmatter_read, write, edit, frontmatter_update
|
||||
```
|
||||
|
||||
这一阶段承担 `auto_link` 的核心职责:先用 `node_search` 在 digest 节点级召回相似或相关节点,再判断是新建还是更新,最后把来源和相关
|
||||
digest 节点写成 wikilink。具体召回、去重和写边规则见 [Auto Link](./auto_link.md)。
|
||||
|
||||
整合动作只有四种:
|
||||
|
||||
| 动作 | 含义 |
|
||||
|---------------|-------------------------|
|
||||
| `CREATE` | 没有相同抽象,创建新的 digest 节点。 |
|
||||
| `CORROBORATE` | 同一记忆再次出现,追加来源或强化表述。 |
|
||||
| `REFINE` | 新材料补充了边界、步骤、前提、适用范围或细节。 |
|
||||
| `CORRECT` | 新材料修正了旧节点的错误、遗漏或冲突。 |
|
||||
|
||||
Integrate 成功的 unit 会记录到 `integrate_results`;失败的 unit 会进入 `failed_units`,其来源路径会进入 `failed_paths`。
|
||||
Finish 阶段不会 checkpoint 失败路径,保证下次还能重试。
|
||||
|
||||
### 3. Topics
|
||||
|
||||
`dream_topics_step` 将 Extract 阶段产生的 topic candidates 变成当天最终的 `daily/<date>/interests.yaml`。
|
||||
|
||||
它会读取:
|
||||
|
||||
```text
|
||||
daily/<date>/interests.yaml
|
||||
daily/<previous-date>/interests.yaml
|
||||
```
|
||||
|
||||
同一天已有 topics 会被保留,最近 `topic_diversity_days` 天出现过的相似主题会被去重。默认最多写 3 个 topic。配置了 LLM 时会让
|
||||
LLM 选择更具体、可行动、非重复的主题;没有 LLM 时会退化成本地规范化去重。
|
||||
|
||||
写入格式示例。读取这个文件的接口见 [Proactive](./proactive.md):
|
||||
|
||||
```yaml
|
||||
date: 2026-06-20
|
||||
topic_count: 3
|
||||
diversity_days: 7
|
||||
topics:
|
||||
- title: 记忆检索链路的质量回归
|
||||
reason: 用户近期持续修改 search、node_search 和 dream 集成链路。
|
||||
evidence: daily/2026-06-20/session.md
|
||||
keywords:
|
||||
- memory search
|
||||
- auto dream
|
||||
paths:
|
||||
- daily/2026-06-20/session.md
|
||||
```
|
||||
|
||||
### 4. Finish
|
||||
|
||||
`dream_finish_step` 负责收尾:
|
||||
|
||||
1. 将成功处理的 changed paths 写入 `file_catalog: dream`。
|
||||
2. 将 `daily/<date>/interests.yaml` 和 `daily/<date>.md` 也写入 catalog。
|
||||
3. 如果有 upsert 或 delete,持久化 dream catalog。
|
||||
4. 返回包含 scanned、changed、integrated、topics、checkpoint 等计数的摘要。
|
||||
|
||||
失败路径不会被 checkpoint。这样下一次 `auto_dream` 仍会把它们视作 changed input,直到整合成功。
|
||||
|
||||
## 运行方式
|
||||
|
||||
CLI:
|
||||
|
||||
```bash
|
||||
reme auto_dream date=2026-06-20
|
||||
```
|
||||
|
||||
带调用提示:
|
||||
|
||||
```bash
|
||||
reme auto_dream date=2026-06-20 hint="优先沉淀工程决策和长期偏好"
|
||||
```
|
||||
|
||||
也可以在配置中把同一组 step 放进 `cron` job,例如每天凌晨运行:
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
daily_auto_dream:
|
||||
backend: cron
|
||||
cron: "30 3 * * *"
|
||||
steps:
|
||||
- backend: dream_extract_step
|
||||
file_catalog: dream
|
||||
- backend: dream_integrate_step
|
||||
- backend: dream_topics_step
|
||||
- backend: dream_finish_step
|
||||
file_catalog: dream
|
||||
```
|
||||
|
||||
## 关键边界
|
||||
|
||||
`auto_dream` 只消费 daily 输入,不改写 daily 正文。daily 是事实和现场记录,digest 才是抽象后的长期记忆层。
|
||||
|
||||
`digest` 不是原文复制。正文应保留可复用抽象,细节通过 `derived_from:: [[daily/<date>/...]]` 指回来源。链接写法遵循
|
||||
[Memory as File](./memory_as_file.md) 中的 vault-relative wikilink 语义。
|
||||
|
||||
`auto_dream` 不凭空生成总览。只有 daily 输入中确实出现、并被抽取为 unit 或 topic 的内容,才会进入 digest 或
|
||||
`interests.yaml`。
|
||||
|
||||
完整流程依赖 LLM 完成 Extract 和 Integrate。Topics 可以在没有 LLM 时做本地去重,但这不等于完整 dream 能离线运行。
|
||||
136
docs/zh/auto_link.md
Normal file
136
docs/zh/auto_link.md
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
# Auto Link
|
||||
|
||||
当前实现中,`auto_link` 不是一个单独注册的 Job,而是 `auto_dream` 的 Integrate 阶段能力:`dream_integrate_step` 在把 memory
|
||||
unit 写入 `digest/` 时,同时完成 digest 节点召回、去重判断、来源链接和相关节点 wikilink 织入。
|
||||
|
||||
完整 dream 流程见 [Auto Dream](./auto_dream.md)。通用 wikilink、frontmatter 和 vault-relative 路径语义见
|
||||
[Memory as File](./memory_as_file.md)。面向问答的检索能力见 [Memory Search](./memory_search.md)。
|
||||
|
||||
## 所在位置
|
||||
|
||||
`auto_dream` 的默认流程如下:
|
||||
|
||||
```yaml
|
||||
auto_dream:
|
||||
steps:
|
||||
- dream_extract_step
|
||||
- dream_integrate_step # auto_link 的实际发生位置
|
||||
- dream_topics_step
|
||||
- dream_finish_step
|
||||
```
|
||||
|
||||
Integrate 阶段对每个 unit 独立运行。一个 unit 只落到一个目标 digest 节点,但这个目标节点可以链接多个来源和多个相关 digest
|
||||
节点。
|
||||
|
||||
## 目标
|
||||
|
||||
`auto_link` 解决的是写入时的图谱质量问题:
|
||||
|
||||
| 问题 | 处理方式 |
|
||||
|--------------|----------------------------------------------------|
|
||||
| 已有相同记忆 | 召回后更新旧节点,而不是重复创建。 |
|
||||
| 新旧材料有关联 | 在正文中写入 vault-relative wikilink。 |
|
||||
| digest 与来源断开 | 用 `derived_from:: [[...]]` 指回 daily/resource 原始材料。 |
|
||||
| 节点只有孤立正文 | 在 CREATE 和 UPDATE 时都补充相关 digest 节点链接。 |
|
||||
|
||||
## 工具链
|
||||
|
||||
`dream_integrate_step` 暴露给 Agent 的工具是:
|
||||
|
||||
```text
|
||||
node_search
|
||||
read
|
||||
frontmatter_read
|
||||
write
|
||||
edit
|
||||
frontmatter_update
|
||||
```
|
||||
|
||||
其中 `node_search` 是为 dream 集成设计的 digest-only 节点召回。它返回 digest 节点的 `path`、front matter 中的 `name`、
|
||||
`description` 等节点级信号,不展开正文,也不做普通 search 的 link expansion。
|
||||
|
||||
`read` 和 `frontmatter_read` 只用于可能相关的候选节点,避免把召回结果全部展开成大上下文。
|
||||
|
||||
## 链接流程
|
||||
|
||||
### 1. 召回候选节点
|
||||
|
||||
Agent 先用 unit 的触发条件、动词、名词、同义词和可能的 failure modes 调用 `node_search`。默认建议用较宽召回,例如
|
||||
`limit=20-30`,因为这一步同时服务去重和链接发现。
|
||||
|
||||
召回结果会被内部分成三类:
|
||||
|
||||
| 分类 | 含义 | 后续动作 |
|
||||
|--------------------|-----------------------------|----------------|
|
||||
| `same_abstraction` | 触发条件或抽象本质相同,内容实质重叠。 | 作为 UPDATE 目标。 |
|
||||
| `related` | 相邻流程、前置条件、失败模式、概念、偏好或上下游知识。 | 写入正文 wikilink。 |
|
||||
| `unrelated` | 只是表面相似或无关。 | 忽略。 |
|
||||
|
||||
### 2. 选择写入动作
|
||||
|
||||
每个 unit 必须选择一个动作:
|
||||
|
||||
| 动作 | 链接含义 |
|
||||
|---------------|-----------------------------------------------------|
|
||||
| `CREATE` | 写入新的 `digest/<bucket>/<slug>.md`,并在新正文里加入来源和相关节点链接。 |
|
||||
| `CORROBORATE` | 同一抽象再次出现,追加新的 `derived_from:: [[...]]`,必要时强化描述。 |
|
||||
| `REFINE` | 新材料扩展了旧节点,把补充内容插入合适段落,并保留旧链接。 |
|
||||
| `CORRECT` | 新材料修正旧节点,用来源链接标出修正依据。 |
|
||||
|
||||
UPDATE 必须尽量只增不删:不要删除已有 wikilink 或 `derived_from`。这是为了让后续图谱索引和检索不会丢边。
|
||||
|
||||
### 3. 写来源边
|
||||
|
||||
来源边使用 markdown wikilink:
|
||||
|
||||
```markdown
|
||||
derived_from:: [[daily/2026-06-20/session.md]]
|
||||
derived_from:: [[resource/2026-06-20/paper.md]]
|
||||
```
|
||||
|
||||
这些边表示 digest 节点的证据来源。纯文本描述不算来源边,因为只有 wikilink 能被 file graph 稳定解析。更完整的 wikilink
|
||||
解析规则见 [Memory as File](./memory_as_file.md#wikilink)。
|
||||
|
||||
### 4. 写 digest 关联边
|
||||
|
||||
digest 之间的关联也使用完整 vault-relative 路径:
|
||||
|
||||
```markdown
|
||||
relates_to:: [[digest/wiki/hybrid-search.md]]
|
||||
depends_on:: [[digest/procedure/rebuild-index.md]]
|
||||
blocks_on:: [[digest/personal/team-review-preference.md]]
|
||||
```
|
||||
|
||||
谓词是开放的,常见写法包括 `relates_to::`、`depends_on::`、`blocks_on::`。谓词在括号外,目标路径在 `[[...]]` 内,并且应包含
|
||||
`.md` 后缀。
|
||||
|
||||
## Bucket 差异
|
||||
|
||||
`auto_link` 的规则会随 unit bucket 调整写入形态:
|
||||
|
||||
| Bucket | 写入重点 |
|
||||
|-------------|---------------------------------------------|
|
||||
| `procedure` | 写成 runbook:触发条件、步骤、输入、失败模式。链接前置流程、子步骤、相关偏好。 |
|
||||
| `personal` | 写用户、团队、项目特定事实或偏好。链接相关项目、习惯、决策背景。 |
|
||||
| `wiki` | 写通用知识、原则、观察、决策先例。链接概念、方法、相邻知识。 |
|
||||
|
||||
无论 bucket 是什么,都要保留来源边,并尽量把召回到的相关 digest 节点织入正文。
|
||||
|
||||
## 与 search 的关系
|
||||
|
||||
`auto_link` 使用的是 `node_search`,不是面向问答的 `search`。
|
||||
|
||||
| 能力 | 用途 |
|
||||
|---------------|-------------------------------------------|
|
||||
| `search` | 面向外部问答,返回 chunk,并可展开上下游 link context。 |
|
||||
| `node_search` | 面向 dream 集成,只召回 digest 节点级摘要,用来判断去重和相关链接。 |
|
||||
|
||||
这个边界很重要:Integrate 阶段需要的是“是否已有相同抽象,以及应该链接哪些节点”,而不是直接把大量正文片段塞进上下文。
|
||||
[Memory Search](./memory_search.md) 负责面向用户问题的 chunk 召回、RRF 融合和链接展开。
|
||||
|
||||
## 失败和重试
|
||||
|
||||
如果某个 unit 整合失败,`dream_integrate_step` 会记录 `failed_units` 和 `failed_paths`。`dream_finish_step` 不会
|
||||
checkpoint 这些来源路径,因此下一次 `auto_dream` 仍会重新处理它们。
|
||||
|
||||
这保证了 auto_link 的写入具有可重试性:失败不会把输入标成已完成,也不会静默丢失应该建立的 digest 边。
|
||||
70
docs/zh/auto_memory.md
Normal file
70
docs/zh/auto_memory.md
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
# Auto Memory
|
||||
|
||||
Auto Memory 是 ReMe 的对话记忆入口:每段对话先按 `session_id` 沉淀成一张 daily 记忆卡片,再由当天的 `YYYY-MM-DD.md`
|
||||
统一索引。它负责把“聊过”变成“记住”,并把原始对话留好出处。
|
||||
|
||||
关于 `daily/`、`reme_session/`、frontmatter 和 wikilink 的通用文件语义,见 [Memory as File](./memory_as_file.md)。
|
||||
|
||||
```text
|
||||
Conversation
|
||||
├─ step 1: daily/YYYY-MM-DD/<session_id>.md # 每段对话先成卡片
|
||||
├─ step 2: daily/YYYY-MM-DD.md # 当天索引再串起来
|
||||
└─ source: reme_session/dialog/<session_id>.jsonl # 原始对话
|
||||
```
|
||||
|
||||
## 它记录什么
|
||||
|
||||
它不记录聊天流水账,只记录以后可能还会用到的内容:
|
||||
|
||||
- 用户偏好:喜欢什么风格、习惯怎么协作、长期要求是什么。
|
||||
- 关键事实:项目背景、重要数字、明确结论、限制条件。
|
||||
- 过程决定:发生了什么,为什么这么选,哪些方案被放弃。
|
||||
- 当前状态:做到哪一步,卡在哪里,下一步是什么。
|
||||
- 可复用经验:命令、流程、排查方法、解决方案。
|
||||
|
||||
## 写入位置
|
||||
|
||||
Auto Memory 会把整理后的记忆放进 `daily/`。当天发生的对话会先被整理成一张张小卡片:
|
||||
|
||||
示例目录:
|
||||
|
||||
```text
|
||||
vault/
|
||||
daily/
|
||||
2026-06-20.md
|
||||
2026-06-20/
|
||||
session-a.md
|
||||
session-b.md
|
||||
```
|
||||
|
||||
其中 `daily/2026-06-20/session-a.md`、`daily/2026-06-20/session-b.md` 是不同对话整理出的记忆卡片,
|
||||
`daily/2026-06-20.md` 是当天索引页。资源文件也会进入同一个 daily 工作台,见 [Auto Resource](./auto_resource.md)。
|
||||
|
||||
当调用时带上 `session_id`,Auto Memory 会按这个 id 单独记录这段对话:
|
||||
|
||||
```text
|
||||
daily/2026-06-20/session-a.md
|
||||
```
|
||||
|
||||
这样不同对话不会混在一起。一次需求讨论、一次问题排查、一次文档修改,都可以拥有自己的记忆卡片。以后想知道这一天发生了什么,先看
|
||||
`YYYY-MM-DD.md`;想看某段对话沉淀了什么,再进入对应的 `<session_id>.md`。
|
||||
|
||||
## 同时保存原始信息
|
||||
|
||||
整理后的 daily note 负责“好读”,原始对话负责“可信”。
|
||||
|
||||
Auto Memory 在生成记忆卡片的同时,也会保存原始会话:
|
||||
|
||||
```text
|
||||
reme_session/
|
||||
dialog/
|
||||
session-a.jsonl
|
||||
session-b.jsonl
|
||||
```
|
||||
|
||||
daily note 会指向对应的原始对话。需要核对某条记忆时,可以顺着链接回到当时的完整上下文。
|
||||
|
||||
## 后续流向
|
||||
|
||||
Auto Memory 只生成 daily 层记忆。要把这些材料进一步沉淀为长期 `digest/` 节点,使用 [Auto Dream](./auto_dream.md);要搜索
|
||||
daily 和 digest,使用 [Memory Search](./memory_search.md)。
|
||||
79
docs/zh/auto_resource.md
Normal file
79
docs/zh/auto_resource.md
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
# Auto Resource `Beta`
|
||||
|
||||
Auto Resource 是 ReMe 的资源解读入口,目前处于 **Beta**。资源文件先按日期进入 `resource/`,再被解读成同名 daily
|
||||
资源卡片,最后由当天的 `YYYY-MM-DD.md` 统一索引。
|
||||
|
||||
关于 vault 分层、`resource/` 和 `daily/` 的通用文件语义,见 [Memory as File](./memory_as_file.md)。对话进入 daily 的流程见
|
||||
[Auto Memory](./auto_memory.md)。
|
||||
|
||||
```text
|
||||
resource/YYYY-MM-DD/<resource_file>
|
||||
├─ step 1: daily/YYYY-MM-DD/<resource_name>.md # 资源解读卡片
|
||||
├─ step 2: daily/YYYY-MM-DD.md # 当天索引再串起来
|
||||
└─ source: resource/YYYY-MM-DD/<resource_file> # 原始资源保留原位
|
||||
```
|
||||
|
||||
## 它记录什么
|
||||
|
||||
它不只是搬运文件内容,而是把资料里以后方便检索和理解的信息提炼出来:
|
||||
|
||||
- 核心内容:这份资料主要讲什么。
|
||||
- 结构脉络:章节、表格、字段、数据组织方式。
|
||||
- 关键细节:重要数字、名称、日期、结论。
|
||||
- 背景用途:这份资料为什么存在,和当前工作有什么关系。
|
||||
- 可行动项:任务、截止时间、后续跟进。
|
||||
|
||||
简单说,它负责把“文件存档”变成“资料可用”。
|
||||
|
||||
## 原始资料入口
|
||||
|
||||
Auto Resource 以 `resource/` 作为原始资料入口。资源需要按日期放置,这个日期会决定它进入哪一天的 daily 工作台。
|
||||
|
||||
示例目录:
|
||||
|
||||
```text
|
||||
vault/
|
||||
resource/
|
||||
2026-06-20/
|
||||
market-report.md
|
||||
meeting-notes.csv
|
||||
```
|
||||
|
||||
当前 Beta 版本更适合处理文本类资源,例如 `md`、`txt`、`json`、`jsonl`、`csv`、`yaml`、`html`。
|
||||
|
||||
## 资源卡片
|
||||
|
||||
每个资源文件会生成一张同名 daily 资源卡片。文件名 stem 会成为卡片名:
|
||||
|
||||
```text
|
||||
resource/2026-06-20/market-report.md
|
||||
↓
|
||||
daily/2026-06-20/market-report.md
|
||||
```
|
||||
|
||||
如果资源文件更新,Auto Resource 会重新解读并更新这张卡片;如果资源文件删除,对应的 daily note 也会被清理。
|
||||
|
||||
## 当天索引
|
||||
|
||||
资源卡片会进入和 Auto Memory 相同的 daily 工作台。当天的 `YYYY-MM-DD.md` 会作为索引页,把这些资源卡片组织起来:
|
||||
|
||||
```text
|
||||
daily/
|
||||
2026-06-20.md
|
||||
2026-06-20/
|
||||
market-report.md
|
||||
meeting-notes.md
|
||||
```
|
||||
|
||||
以后想回看这一天处理过哪些资料,先看 `YYYY-MM-DD.md`;想看某份资料沉淀了什么,再进入对应的资源卡片。
|
||||
|
||||
## 同时保留原始资料
|
||||
|
||||
解读后的 daily note 负责“好读”,原始资源负责“可信”。
|
||||
|
||||
Auto Resource 不会把原始文件挪走:它仍然留在 `resource/YYYY-MM-DD/`。这样,文本资料会进入 daily 记忆流,原始文件也始终保留在它来时的位置。
|
||||
|
||||
## 后续流向
|
||||
|
||||
Auto Resource 只生成 daily 层的资源解读。要把资源中的长期知识沉淀进 `digest/`,使用 [Auto Dream](./auto_dream.md);要检索原始资源、
|
||||
daily 卡片和 digest 节点,使用 [Memory Search](./memory_search.md)。
|
||||
206
docs/zh/contributing.md
Normal file
206
docs/zh/contributing.md
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
# 开源与贡献
|
||||
|
||||
ReMe 已开源,项目仓库托管于 GitHub:
|
||||
|
||||
**https://github.com/agentscope-ai/ReMe**
|
||||
|
||||
---
|
||||
|
||||
## 如何参与贡献
|
||||
|
||||
感谢你对 ReMe 的关注。ReMe 是一个面向 Agent 的、文件优先的自进化记忆系统,欢迎通过问题反馈、文档改进、测试补充、Bug
|
||||
修复和新能力开发参与贡献。
|
||||
|
||||
如果是第一次本地运行,先看 [快速开始](./quick_start.md)。如果改动涉及运行时分层、Job、Step 或组件,先看
|
||||
[ReMe 代码框架](./framework.md);如果改动涉及 vault 目录、frontmatter、wikilink 或 chunking,先看
|
||||
[Memory as File](./memory_as_file.md)。
|
||||
|
||||
### 1. 开始之前
|
||||
|
||||
在投入实现前,建议先完成以下检查:
|
||||
|
||||
- 查看 [Open Issues](https://github.com/agentscope-ai/ReMe/issues),确认是否已有相关问题或讨论。
|
||||
- 如果相关 Issue 已存在且仍开放,请在评论中说明你想处理它,避免重复工作。
|
||||
- 如果没有相关 Issue,请新建 Issue 描述背景、目标行为、可能的实现方向和影响范围。
|
||||
- 对较大的功能变更,建议先和维护者对齐接口、配置、兼容性和测试策略,再提交实现。
|
||||
|
||||
### 2. 本地开发环境
|
||||
|
||||
ReMe 的核心代码位于:
|
||||
|
||||
- `reme/`:Python 包源码,包括配置、组件、服务、Job、Step、schema 和工具函数。
|
||||
- `pyproject.toml`:项目元数据、依赖、可选依赖、命令入口和测试配置。
|
||||
- `tests/`:单元测试和集成测试。
|
||||
|
||||
项目要求 Python 3.11 及以上。建议使用虚拟环境开发:
|
||||
|
||||
```bash
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install -e ".[dev,full]"
|
||||
pre-commit install
|
||||
```
|
||||
|
||||
### 3. 代码开发范式
|
||||
|
||||
开发 ReMe 代码前,请先阅读 [ReMe 代码框架](./framework.md)。新增或修改核心能力时,应遵照其中描述的分层与调用链:
|
||||
|
||||
```text
|
||||
CLI / Client -> Service -> Application -> Job -> Step -> Component / Vault
|
||||
```
|
||||
|
||||
也就是说:
|
||||
|
||||
- 面向用户或外部系统暴露的能力,优先通过 Job 编排,再由 Service 暴露为 CLI、HTTP 或 MCP 可调用接口。
|
||||
- 可复用基础设施放在 `reme/components/`,通过 `BaseComponent.bind()` 声明组件依赖。
|
||||
- 业务原子操作放在 `reme/steps/`,通过 `BaseStep.Ref` 访问 file store、agent wrapper、catalog、LLM 等组件。
|
||||
- 请求、响应和持久化数据结构放在 `reme/schema/` 或 `reme/enumeration/`,不要把隐式结构散落在 Step 内部。
|
||||
- 配置驱动的默认行为写入 `reme/config/default.yaml`,并保持默认配置可启动、可测试。
|
||||
|
||||
新增 Step 或 Job 时,特别注意以下约定:
|
||||
|
||||
- 使用 `@R.register("<backend_name>")` 注册实现,注册名应稳定、清晰,并与配置中的 `backend` 对齐。
|
||||
- 新增 Step 文件后,确认所在包的 `__init__.py` 会 import 该模块,否则注册表不会加载它。
|
||||
- Step 只处理单个业务原子操作;跨步骤流程应放在 Job 配置或专门的编排 Step 中。
|
||||
- Job 负责组合 Step,并决定普通、流式、后台或定时执行方式;是否对外暴露由 `enable_serve` 控制。
|
||||
- Step 需要组件时优先使用 `BaseStep.Ref`,不要在 Step 内重新构造全局组件或绕过 `ApplicationContext`。
|
||||
- 涉及文件、索引、图谱、front matter、wikilink 的行为,应保持 vault-relative 路径语义一致。
|
||||
- 新能力应补充 `tests/unit/` 中的快速测试;跨组件、LLM、embedding 或服务行为再放入 `tests/integration/`。
|
||||
|
||||
### 4. 代码与文档修改建议
|
||||
|
||||
根据改动类型选择合适的入口:
|
||||
|
||||
| 改动类型 | 主要位置 | 建议 |
|
||||
|------------|-----------------------------------------------------|---------------------------------------------------------------------------|
|
||||
| 配置或启动行为 | `reme/config/`、`reme/application.py`、`reme/reme.py` | 保持默认配置可运行,避免破坏现有 CLI、HTTP 和 MCP 入口 |
|
||||
| 组件能力 | `reme/components/` | 优先复用 `BaseComponent`、registry 和上下文对象 |
|
||||
| Job 或 Step | `reme/components/job/`、`reme/steps/` | 遵照 [ReMe 代码框架](./framework.md) 的 Job -> Step 范式,保持请求、响应 schema 清晰,并补充对应测试 |
|
||||
| 数据结构 | `reme/schema/`、`reme/enumeration/` | 注意序列化兼容性和已有 front matter、wikilink 语义 |
|
||||
| 工具函数 | `reme/utils/` | 保持函数边界小,并用单元测试覆盖边界情况 |
|
||||
| 用户文档 | `docs/zh/`、`README.md` | 当用户可见行为变化时同步更新文档 |
|
||||
|
||||
如果改动涉及 LLM、embedding、外部服务、文件监听或后台任务,请同时说明依赖条件、失败行为和本地验证方式。
|
||||
|
||||
### 5. 提交信息格式
|
||||
|
||||
建议遵循 [Conventional Commits](https://www.conventionalcommits.org/) 规范,以保持历史记录清晰。
|
||||
|
||||
格式:
|
||||
|
||||
```text
|
||||
<type>(<scope>): <subject>
|
||||
```
|
||||
|
||||
常用类型:
|
||||
|
||||
- `feat`:新功能
|
||||
- `fix`:Bug 修复
|
||||
- `docs`:仅文档
|
||||
- `style`:代码风格调整,不改变行为
|
||||
- `refactor`:重构,不修复 Bug 也不添加功能
|
||||
- `perf`:性能改进
|
||||
- `test`:添加或更新测试
|
||||
- `chore`:构建、工具或维护工作
|
||||
|
||||
示例:
|
||||
|
||||
```bash
|
||||
feat(search): add link expansion option
|
||||
fix(file-graph): handle pending wikilinks after move
|
||||
docs(memory): update auto memory guide
|
||||
test(config): cover default yaml parsing
|
||||
chore(pre-commit): update lint hooks
|
||||
```
|
||||
|
||||
### 6. Pull Request 标题
|
||||
|
||||
PR 标题建议使用相同格式:
|
||||
|
||||
```text
|
||||
<type>(<scope>): <description>
|
||||
```
|
||||
|
||||
要求:
|
||||
|
||||
- 类型使用 `feat`、`fix`、`docs`、`test`、`refactor`、`chore`、`perf`、`style`、`build` 或 `revert`。
|
||||
- 作用域使用小写字母、数字、连字符或下划线。
|
||||
- 描述保持简短,说明这次 PR 的实际效果。
|
||||
|
||||
示例:
|
||||
|
||||
```text
|
||||
feat(auto-memory): persist source conversation metadata
|
||||
fix(markdown): keep wikilink aliases during edit
|
||||
docs(zh): add contribution guide
|
||||
```
|
||||
|
||||
### 7. 提交前检查
|
||||
|
||||
提交或发起 PR 前,请至少运行:
|
||||
|
||||
```bash
|
||||
pre-commit run --all-files
|
||||
pytest
|
||||
```
|
||||
|
||||
如果只改了局部代码,可以先运行更小范围的测试:
|
||||
|
||||
```bash
|
||||
pytest tests/unit/test_search_step.py
|
||||
pytest tests/unit/test_reme_cli.py
|
||||
```
|
||||
|
||||
如果 `pre-commit` 自动修改了文件,请提交这些修改后重新运行检查,直到全部通过。
|
||||
|
||||
当前 pre-commit 配置包括 YAML/TOML/JSON 检查、私钥检测、尾随空格检查、`black`、`flake8`、`pylint` 和 `pyroma`。代码格式主要遵循:
|
||||
|
||||
- `black --line-length=120`
|
||||
- `flake8 --max-line-length=120`
|
||||
- `pylint --max-line-length=120`
|
||||
|
||||
部分集成测试可能依赖 LLM、embedding 或外部服务配置。若无法在本地完整运行,请在 PR 描述中说明跳过原因和已完成的替代验证。
|
||||
|
||||
### 8. 测试要求
|
||||
|
||||
请根据改动风险补充测试:
|
||||
|
||||
- 修复 Bug 时,优先添加能复现问题的回归测试。
|
||||
- 新增 Step、Job 或组件时,至少补充核心路径和失败路径测试。
|
||||
- 修改索引、图谱、wikilink、front matter、文件读写等共享逻辑时,补充边界用例。
|
||||
- 修改 CLI、服务或配置解析时,覆盖用户可见入口。
|
||||
- 文档-only 改动通常不需要新增测试,但仍建议运行 `pre-commit run --all-files`。
|
||||
|
||||
测试文件按现有结构放置:
|
||||
|
||||
- `tests/unit/`:无需真实外部服务的快速测试。
|
||||
- `tests/integration/`:跨组件或依赖外部配置的集成测试。
|
||||
|
||||
### 9. 文档贡献
|
||||
|
||||
当你的修改会影响用户如何安装、配置、调用或理解 ReMe 时,请同步更新文档。
|
||||
|
||||
文档位于:
|
||||
|
||||
```text
|
||||
docs/
|
||||
```
|
||||
|
||||
建议文档保持:
|
||||
|
||||
- 标题明确,直接说明能力或流程。
|
||||
- 命令可以复制运行。
|
||||
- 涉及路径时使用仓库内真实路径,例如 `reme/config/default.yaml`、`reme/steps/`、`tests/unit/`。
|
||||
- 涉及默认行为时,以当前代码和 `pyproject.toml`、默认配置为准。
|
||||
|
||||
---
|
||||
|
||||
## 获取帮助
|
||||
|
||||
- Bugs 和功能请求:[GitHub Issues](https://github.com/agentscope-ai/ReMe/issues)
|
||||
- 项目主页:[GitHub Repository](https://github.com/agentscope-ai/ReMe)
|
||||
- 文档站点:[https://reme.agentscope.io/](https://reme.agentscope.io/)
|
||||
|
||||
---
|
||||
|
||||
感谢你为 ReMe 做出贡献。你的改进会帮助 Agent 的长期记忆更可读、可控、可维护。
|
||||
763
docs/zh/framework.md
Normal file
763
docs/zh/framework.md
Normal file
|
|
@ -0,0 +1,763 @@
|
|||
# ReMe 代码框架
|
||||
|
||||
## 1. 总览
|
||||
|
||||
ReMe 的运行时可以理解为:**配置驱动的 Application 把组件和 Job 装配起来,Service 把可服务的 Job 暴露给 CLI、HTTP 或 MCP,Job
|
||||
再按顺序执行 Step**。
|
||||
|
||||

|
||||
|
||||
如果只想先运行和使用 ReMe,见 [快速开始](./quick_start.md)。vault 文件语义见 [Memory as File](./memory_as_file.md);检索、
|
||||
自动记忆和主动读取的用户侧说明分别见 [Memory Search](./memory_search.md)、[Auto Memory](./auto_memory.md)、
|
||||
[Auto Resource](./auto_resource.md)、[Auto Dream](./auto_dream.md) 和 [Proactive](./proactive.md)。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
CLI["reme CLI<br/>reme/reme.py"] --> Client["Client<br/>http / mcp"]
|
||||
Client --> Service["Service<br/>HTTP / MCP"]
|
||||
Service --> App["Application<br/>reme/application.py"]
|
||||
App --> Jobs["Jobs<br/>base / stream / background / cron"]
|
||||
Jobs --> Steps["Steps<br/>reme/steps/**"]
|
||||
Steps --> Ctx["RuntimeContext<br/>data + Response + stream queue"]
|
||||
Steps --> Components["Components<br/>store / graph / index / llm / agent / catalog"]
|
||||
Components --> Vault["Vault<br/>daily / digest / resource / reme_metadata"]
|
||||
```
|
||||
|
||||
核心分层:
|
||||
|
||||
| 层 | 主要目录 | 职责 |
|
||||
|-------------|----------------------------|--------------------------------------------------------------|
|
||||
| CLI | `reme/reme.py` | 解析命令;`start` 启动服务;其他 action 通过 client 调用服务 |
|
||||
| Service | `reme/components/service/` | 把 Job 注册成 HTTP endpoint 或 MCP tool |
|
||||
| Application | `reme/application.py` | 读取配置后的对象装配、依赖拓扑启动、关闭、Job 调用 |
|
||||
| Job | `reme/components/job/` | 编排一组 Step;决定同步、流式、后台、定时运行方式 |
|
||||
| Step | `reme/steps/` | 业务原子操作,例如读写文件、检索、索引、自进化 |
|
||||
| Component | `reme/components/` | 可复用基础设施,例如 file_store、file_graph、keyword_index、agent_wrapper |
|
||||
| Schema | `reme/schema/` | `Request`、`Response`、`FileChunk`、`FileNode`、配置模型等数据结构 |
|
||||
| Config | `reme/config/` | 默认 YAML 配置和命令行覆盖解析 |
|
||||
|
||||
## 2. 目录结构
|
||||
|
||||
```text
|
||||
reme/
|
||||
reme.py # CLI 入口
|
||||
application.py # Application 装配与生命周期
|
||||
config/
|
||||
default.yaml # 默认 service / jobs / components
|
||||
config_parser.py # config=、dot notation、env 占位符解析
|
||||
components/
|
||||
component_registry.py # 全局注册表 R
|
||||
base_component.py # ComponentMixin / BaseComponent / bind 依赖声明
|
||||
runtime_context.py # 单次 Job 执行上下文
|
||||
job/ # BaseJob / StreamJob / BackgroundJob / CronJob
|
||||
service/ # HTTP / MCP 服务
|
||||
client/ # HTTP / MCP 客户端
|
||||
file_store/ # 文件索引协调层
|
||||
file_graph/ # wikilink 图谱
|
||||
keyword_index/ # BM25 等关键词索引
|
||||
file_chunker/ # Markdown / 默认文本分块
|
||||
file_catalog/ # 变更 checkpoint
|
||||
as_llm/, as_embedding/ # 模型封装
|
||||
agent_wrapper/ # AgentScope / Claude Code wrapper
|
||||
steps/
|
||||
base_step.py # BaseStep、Ref、dispatch_steps
|
||||
common/ # version、help、health_check、demo
|
||||
file_io/ # read/write/edit/delete/move/frontmatter/daily
|
||||
index/ # watch/init/update/search/traverse
|
||||
evolve/ # auto_memory、auto_resource、auto_dream、proactive
|
||||
transfer/ # upload/download/ingest
|
||||
channel/ # MCP channel 工具
|
||||
```
|
||||
|
||||
默认 vault 目录由 `ApplicationConfig` 定义:
|
||||
|
||||
```text
|
||||
<vault_dir>/
|
||||
reme_metadata/ # file_store、file_graph、keyword_index、file_catalog 等持久状态
|
||||
reme_session/ # Agent session 与原始对话
|
||||
resource/ # 外部资源
|
||||
daily/ # 浅加工记忆
|
||||
digest/ # 长期 digest 记忆
|
||||
```
|
||||
|
||||
`Application.__init__()` 会先确保这些目录存在,然后初始化 service、components、jobs。
|
||||
|
||||
## 3. 启动与调用链
|
||||
|
||||
### 3.1 CLI
|
||||
|
||||
入口是 `reme/reme.py::main()`:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["main()"] --> B["parse_args(*sys.argv[1:])"]
|
||||
B --> C{action}
|
||||
C -->|" start "| D["load_env()"]
|
||||
D --> E["resolve_app_config(**kwargs)"]
|
||||
E --> F["precheck_start(service)"]
|
||||
F --> G["ReMe(**config).run_app()"]
|
||||
C -->|" find_reme "| H["cli_find_reme()"]
|
||||
C -->|" 其他 action "| I["call_server(action, **kwargs)"]
|
||||
I --> J["R.get(ComponentEnum.CLIENT, backend)"]
|
||||
J --> K["client(action=action, **kwargs)"]
|
||||
```
|
||||
|
||||
常用命令:
|
||||
|
||||
```bash
|
||||
reme start
|
||||
reme start service.port=8181
|
||||
reme version
|
||||
reme search query="memory" limit=5
|
||||
reme search query="memory" backend=mcp
|
||||
```
|
||||
|
||||
配置解析支持:
|
||||
|
||||
| 能力 | 源码 | 说明 |
|
||||
|--------------|-------------------------|---------------------------------------------|
|
||||
| 默认配置 | `resolve_app_config()` | 未指定 `config` 时加载 `reme/config/default.yaml` |
|
||||
| 指定配置 | `config=<name-or-path>` | 可传内置配置名或 YAML/JSON 文件路径 |
|
||||
| dot notation | `parse_dot_notation()` | 例如 `service.port=8181` |
|
||||
| 环境变量 | `_expand_env_vars()` | 支持 `${VAR}` 和 `${VAR:-default}` |
|
||||
| 值转换 | `_convert_value()` | bool、int、float、JSON list/dict/null 会自动转换 |
|
||||
|
||||
### 3.2 Service
|
||||
|
||||
`BaseService.run_app()` 的顺序:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["Service.build_service(app)"] --> B["读取 app.context.jobs"]
|
||||
B --> C{"job.enable_serve == true?"}
|
||||
C -->|是| D["Service.add_job(job)"]
|
||||
C -->|否| E["跳过注册"]
|
||||
D --> F["Service.start_service(app)"]
|
||||
E --> F
|
||||
F --> G["lifespan 中 app.start()"]
|
||||
G --> H["Application 启动 jobs"]
|
||||
```
|
||||
|
||||
HTTP service 行为:
|
||||
|
||||
| Job 类型 | HTTP 暴露方式 |
|
||||
|--------------------------------------|-------------------------------------------|
|
||||
| 非 `StreamJob` 且 `enable_serve: true` | `POST /<job.name>`,返回 `Response` JSON |
|
||||
| `StreamJob` | `POST /<job.name>`,返回 `text/event-stream` |
|
||||
| `enable_serve: false` | 不注册 endpoint |
|
||||
|
||||
MCP service 行为:
|
||||
|
||||
| Job 类型 | MCP 暴露方式 |
|
||||
|--------------------------------------|---------------------------------|
|
||||
| 非 `StreamJob` 且 `enable_serve: true` | 注册为 MCP tool |
|
||||
| `StreamJob` | 当前跳过,不注册 |
|
||||
| `BackgroundJob` | 构造时强制 `enable_serve=False`,不会暴露 |
|
||||
|
||||
## 4. Registry 与依赖注入
|
||||
|
||||
### 4.1 全局注册表 R
|
||||
|
||||
ReMe 使用进程级单例 `R = ComponentRegistry()`。所有组件、Job、Step 都通过 `@R.register("name")` 注册。
|
||||
|
||||
```python
|
||||
from ...components import R
|
||||
|
||||
|
||||
@R.register("version_step")
|
||||
class VersionStep(BaseStep):
|
||||
...
|
||||
```
|
||||
|
||||
注册表 key 是:
|
||||
|
||||
```text
|
||||
(component_type, register_name) -> class
|
||||
```
|
||||
|
||||
其中 `component_type` 来自类属性,例如:
|
||||
|
||||
| 类型 | 类属性 |
|
||||
|-----------|-----------------------------------------------------------|
|
||||
| Step | `BaseStep.component_type = ComponentEnum.STEP` |
|
||||
| Job | `BaseJob.component_type = ComponentEnum.JOB` |
|
||||
| Service | `BaseService.component_type = ComponentEnum.SERVICE` |
|
||||
| FileStore | `BaseFileStore.component_type = ComponentEnum.FILE_STORE` |
|
||||
|
||||
所以同名 backend 在不同 component type 下可以共存。例如 `http` 同时可以是 service backend 和 client backend。
|
||||
|
||||
### 4.2 模块导入触发注册
|
||||
|
||||
注册发生在模块 import 时。`reme/components/__init__.py` 会 import 各组件包,`reme/steps/__init__.py` 会 import
|
||||
`channel/common/evolve/file_io/index/transfer`。这些包的 `__init__.py` 再 import 具体模块,从而执行 `@R.register(...)`。
|
||||
|
||||
新增 Step 文件后,必须保证它所在包的 `__init__.py` 会 import 该模块,否则注册表里找不到这个 backend。
|
||||
|
||||
### 4.3 Component.bind
|
||||
|
||||
组件之间的依赖用 `BaseComponent.bind()` 声明。启动时 `Application._topological_order()` 读取每个组件的 `dependencies`
|
||||
,按拓扑顺序启动。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["Component.__init__<br/>self.keyword_index = self.bind(...)"] --> B["Dependency placeholder"]
|
||||
B --> C["Application._topological_order()"]
|
||||
C --> D["component.start()"]
|
||||
D --> E["_resolve_bindings()"]
|
||||
E --> F["self.keyword_index = app_context.components[type][name]"]
|
||||
F --> G["component._start()"]
|
||||
```
|
||||
|
||||
`BaseComponent.bind(name, BaseClass, optional=True)` 的规则:
|
||||
|
||||
| 场景 | 行为 |
|
||||
|------------------------|--------------------------------------------|
|
||||
| `name` 为空 | 返回 `None`,跳过依赖 |
|
||||
| `app_context` 存在 | 从 `app_context.components[ctype][name]` 查找 |
|
||||
| 依赖缺失且 `optional=True` | 解析为 `None` |
|
||||
| 依赖缺失且 `optional=False` | 启动时报错 |
|
||||
| standalone 模式 | 可用 `default_factory` 创建自有组件 |
|
||||
|
||||
### 4.4 Step.Ref
|
||||
|
||||
Step 不参与组件拓扑启动,它每次 Job 调用时临时创建。Step 访问组件主要靠 `BaseStep.Ref`:
|
||||
|
||||
```python
|
||||
file_store: BaseFileStore = Ref(BaseFileStore, ComponentEnum.FILE_STORE)
|
||||
agent_wrapper: BaseAgentWrapper = Ref(BaseAgentWrapper, ComponentEnum.AGENT_WRAPPER, optional=True)
|
||||
```
|
||||
|
||||
解析优先级:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["访问 self.file_store"] --> B{"kwargs 里有同名对象?"}
|
||||
B -->|是| C["使用 kwargs 对象"]
|
||||
B -->|否| D{"context.data 里有同名对象?"}
|
||||
D -->|是| E["使用 context 对象"]
|
||||
D -->|否| F["读取 kwargs['file_store'] 名称,默认 default"]
|
||||
F --> G["app_context.components[FILE_STORE][name]"]
|
||||
```
|
||||
|
||||
因此在 step 配置里可以写:
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- backend: update_catalog_step
|
||||
file_catalog: resource
|
||||
```
|
||||
|
||||
这里 `file_catalog: resource` 表示解析名为 `resource` 的 `file_catalog` 组件。
|
||||
|
||||
## 5. Application 生命周期
|
||||
|
||||
`Application` 的职责是把配置转换成运行时对象,并按顺序启动和关闭。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["Application(**kwargs)"] --> B["ApplicationContext(**kwargs)<br/>解析 ApplicationConfig"]
|
||||
B --> C["_setup_vault_directories()"]
|
||||
C --> D["_init_service()"]
|
||||
D --> E["_init_components()"]
|
||||
E --> F["_init_jobs()"]
|
||||
F --> G["run_app()"]
|
||||
G --> H["service.run_app(app)"]
|
||||
```
|
||||
|
||||
启动顺序在 `Application._start()` 中:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["创建 thread_pool,可选"] --> B["components 拓扑排序"]
|
||||
B --> C["启动 components"]
|
||||
C --> D["启动 BaseJob"]
|
||||
D --> E["启动 StreamJob"]
|
||||
E --> F["启动 BackgroundJob"]
|
||||
F --> G["启动 CronJob"]
|
||||
```
|
||||
|
||||
关闭时按 `_started_components` 的反序关闭,保证依赖方先关闭,被依赖方后关闭。
|
||||
|
||||
## 6. Job 模型
|
||||
|
||||
Job 是外部可调用能力或后台任务的编排单元。配置位置是 `reme/config/default.yaml` 的 `jobs:`。
|
||||
|
||||
### 6.1 BaseJob
|
||||
|
||||
`BaseJob` 是最常见的请求型 Job:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Caller["Caller"] --> Job["BaseJob<br/>job(**kwargs)"]
|
||||
Job --> Ctx["RuntimeContext<br/>merged_kwargs"]
|
||||
Ctx --> S1["Step 1<br/>await step(context)"]
|
||||
S1 --> D1["读写 context.data / response"]
|
||||
D1 --> S2["Step 2<br/>await step(context)"]
|
||||
S2 --> D2["读写 context.data / response"]
|
||||
D2 --> Resp["context.response"]
|
||||
Resp --> Caller
|
||||
```
|
||||
|
||||
关键源码行为:
|
||||
|
||||
| 源码 | 行为 |
|
||||
|------------------|-------------------------------------------------|
|
||||
| `_start()` | 把 YAML 中每个 step config 解析成 `(step_cls, params)` |
|
||||
| `_build_steps()` | 每次调用都创建新的 Step 实例,避免跨请求共享状态 |
|
||||
| `__call__()` | 创建 `RuntimeContext`,按顺序执行 step |
|
||||
| 异常处理 | 捕获异常,`response.success=False`,`answer=str(e)` |
|
||||
|
||||
### 6.2 StreamJob
|
||||
|
||||
`StreamJob` 继承 `BaseJob`,但返回流式 chunk:
|
||||
|
||||
| 行为 | 说明 |
|
||||
|---------|---------------------------------------------------------|
|
||||
| context | 带 `stream_queue` |
|
||||
| Step 输出 | 调用 `context.add_stream_string(text, ChunkEnum.CONTENT)` |
|
||||
| 异常 | 写入 `ChunkEnum.ERROR` |
|
||||
| 结束 | 总是发送 `DONE` chunk |
|
||||
|
||||
### 6.3 BackgroundJob
|
||||
|
||||
`BackgroundJob` 用于长运行循环,例如文件监听。它在构造时强制 `enable_serve=False`。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["Application 启动 BackgroundJob"] --> B["_start() 创建 stop_event 和 task"]
|
||||
B --> C["_run_with_supervisor()"]
|
||||
C --> D["await self()"]
|
||||
D --> E{"异常?"}
|
||||
E -->|否,正常返回| F["结束"]
|
||||
E -->|是且 supervisor = True| G["指数退避 + jitter"]
|
||||
G --> C
|
||||
E -->|是且 supervisor = False| H["抛出异常"]
|
||||
I["close()"] --> J["stop_event.set()"]
|
||||
J --> K["等待 close_timeout,超时 cancel"]
|
||||
```
|
||||
|
||||
默认 `BackgroundJob.__call__()` 也会按顺序执行配置里的 steps,但异常不会被吞掉,便于 supervisor 重启。
|
||||
|
||||
### 6.4 CronJob
|
||||
|
||||
`CronJob` 继承 `BackgroundJob`,增加 `cron` 表达式:
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
nightly_dream:
|
||||
backend: cron
|
||||
cron: "0 3 * * *"
|
||||
steps:
|
||||
- backend: dream_extract_step
|
||||
- backend: dream_integrate_step
|
||||
- backend: dream_topics_step
|
||||
- backend: dream_finish_step
|
||||
```
|
||||
|
||||
当前实现使用 `croniter` 计算下一次触发时间,时区来自 `app_config.timezone`。
|
||||
|
||||
### 6.5 默认 Job 类型分布
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Jobs["default.yaml jobs"] --> BG["background<br/>index_update_loop<br/>resource_watch_loop<br/>digest_watch_loop"]
|
||||
Jobs --> Base["base<br/>version / help / health_check<br/>search / node_search / traverse / reindex<br/>read / write / edit / delete / move / list / stat<br/>daily_create / daily_list / daily_reindex<br/>auto_memory / auto_resource / auto_dream / proactive"]
|
||||
```
|
||||
|
||||
## 7. Step 模型
|
||||
|
||||
Step 是具体业务动作。所有 Step 都继承 `BaseStep` 并实现 `execute()`。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["Job._build_steps()"] --> B["Step.__init__()"]
|
||||
B --> C["加载 prompt<br/>类名对应 YAML + prompt_dict override"]
|
||||
C --> D["Step.__call__(context, **kwargs)"]
|
||||
D --> E["清理 Ref cache"]
|
||||
E --> F["RuntimeContext.from_context()"]
|
||||
F --> G["input_mapping"]
|
||||
G --> H["execute()"]
|
||||
H --> I["output_mapping"]
|
||||
I --> J["返回 result"]
|
||||
```
|
||||
|
||||
### 7.1 RuntimeContext
|
||||
|
||||
`RuntimeContext` 是一次 Job 调用内所有 Step 共享的上下文:
|
||||
|
||||
| 字段 | 说明 |
|
||||
|----------------|---------------------------------------------|
|
||||
| `response` | 最终返回的 `Response(answer, success, metadata)` |
|
||||
| `data` | 自由字典,保存输入参数和中间结果 |
|
||||
| `stream_queue` | 流式 Job 的输出队列 |
|
||||
| `stop_event` | 后台 Job 的停止信号 |
|
||||
|
||||
Step 里常见写法:
|
||||
|
||||
```python
|
||||
assert self.context is not None
|
||||
query = self.context.get("query", "")
|
||||
self.context["processed_query"] = query.strip().lower()
|
||||
self.context.response.answer = "..."
|
||||
self.context.response.metadata["key"] = "value"
|
||||
return self.context.response
|
||||
```
|
||||
|
||||
### 7.2 input_mapping / output_mapping
|
||||
|
||||
`BaseStep.__call__()` 会在执行前后调用 `RuntimeContext.apply_mapping()`:
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- backend: some_step
|
||||
input_mapping:
|
||||
user_query: query
|
||||
output_mapping:
|
||||
result: final_result
|
||||
```
|
||||
|
||||
语义是把 `context.data[source]` 复制到 `context.data[target]`。
|
||||
|
||||
### 7.3 dispatch_steps
|
||||
|
||||
部分 Step 会产生批量事件,然后把事件分发给其他 Step。`BaseStep.dispatch_steps()` 会按配置解析并执行子 Step。
|
||||
|
||||
默认配置中的例子:
|
||||
|
||||
```yaml
|
||||
index_update_loop:
|
||||
backend: background
|
||||
watch_dirs: [ daily_dir, digest_dir ]
|
||||
watch_suffixes: [ md ]
|
||||
steps:
|
||||
- backend: init_changes_step
|
||||
monitor_type: file_store
|
||||
monitor_name: default
|
||||
dispatch_steps: [ update_index_step ]
|
||||
- backend: watch_changes_step
|
||||
dispatch_steps: [ update_index_step ]
|
||||
```
|
||||
|
||||
流程图:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Init["init_changes_step"] --> Batch["changes batch"]
|
||||
Watch["watch_changes_step"] --> Batch
|
||||
Batch --> Dispatch["dispatch_steps(...)"]
|
||||
Dispatch --> Update["update_index_step"]
|
||||
Update --> Store["file_store"]
|
||||
```
|
||||
|
||||
## 8. 默认配置里的组件
|
||||
|
||||
`reme/config/default.yaml` 当前默认组件:
|
||||
|
||||
| ComponentEnum | 名称 | backend | 说明 |
|
||||
|-------------------|---------------------------------|--------------------------------|------------------------------------------------------|
|
||||
| `service` | 单例 | `http` | 默认 HTTP 服务 |
|
||||
| `tokenizer` | `default` | `regex` | BM25 分词器 |
|
||||
| `as_embedding` | `default` | `${EMBEDDING_BACKEND:-openai}` | embedding 模型封装 |
|
||||
| `embedding_store` | `default` | `local` | embedding 存储,依赖 `as_embedding: default` |
|
||||
| `as_llm` | `default` | `${LLM_BACKEND:-openai}` | LLM 模型封装 |
|
||||
| `agent_wrapper` | `default` | `agentscope` | AgentScope wrapper |
|
||||
| `agent_wrapper` | `claude_code` | `claude_code` | Claude Code wrapper |
|
||||
| `file_graph` | `default` | `local` | wikilink 图谱 |
|
||||
| `file_catalog` | `default/resource/digest/dream` | `local` | 文件变更 checkpoint |
|
||||
| `file_chunker` | `markdown` | `markdown` | Markdown AST 分块 |
|
||||
| `file_chunker` | `default` | `default` | 默认文本分块,当前支持 `jsonl` |
|
||||
| `keyword_index` | `default` | `bm25` | BM25 关键词索引 |
|
||||
| `file_store` | `default` | `local` | 组合 file_graph、keyword_index;默认 `embedding_store: ""` |
|
||||
|
||||
注意:`search` step 的配置含 `vector_weight`,但默认 `file_store.default.embedding_store` 为空,因此实际是否有向量检索取决于运行配置是否启用
|
||||
embedding store。
|
||||
|
||||
## 9. 新增 Step
|
||||
|
||||
### 9.1 最小 Step
|
||||
|
||||
假设要新增一个把输入文本转大写的 Step。
|
||||
|
||||
新建文件,例如 `reme/steps/common/uppercase.py`:
|
||||
|
||||
```python
|
||||
from ..base_step import BaseStep
|
||||
from ...components import R
|
||||
|
||||
|
||||
@R.register("uppercase_step")
|
||||
class UppercaseStep(BaseStep):
|
||||
async def execute(self):
|
||||
assert self.context is not None
|
||||
text = self.context.get("text", "")
|
||||
result = str(text).upper()
|
||||
|
||||
self.context["uppercase_text"] = result
|
||||
self.context.response.answer = result
|
||||
self.context.response.metadata["length"] = len(result)
|
||||
return self.context.response
|
||||
```
|
||||
|
||||
### 9.2 让 Step 被注册
|
||||
|
||||
确认 `reme/steps/common/__init__.py` import 了新模块。新增:
|
||||
|
||||
```python
|
||||
from . import uppercase
|
||||
```
|
||||
|
||||
原因:`@R.register("uppercase_step")` 只有在模块被 import 后才会执行。
|
||||
|
||||
### 9.3 访问组件
|
||||
|
||||
如果 Step 需要访问已有组件,优先使用 `BaseStep` 已提供的 Ref:
|
||||
|
||||
```python
|
||||
class MySearchStep(BaseStep):
|
||||
async def execute(self):
|
||||
assert self.context is not None
|
||||
results = await self.file_store.keyword_search(
|
||||
self.context.get("query", ""),
|
||||
limit=5,
|
||||
)
|
||||
...
|
||||
```
|
||||
|
||||
可直接用的常见属性:
|
||||
|
||||
| 属性 | 默认解析的组件 |
|
||||
|----------------------|------------------------------|
|
||||
| `self.as_llm` | `as_llm: default` 的 `.model` |
|
||||
| `self.agent_wrapper` | `agent_wrapper: default`,可选 |
|
||||
| `self.file_catalog` | `file_catalog: default`,可选 |
|
||||
| `self.file_store` | `file_store: default` |
|
||||
|
||||
如果希望 Job 配置指定非 default 组件:
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- backend: my_step
|
||||
file_catalog: dream
|
||||
```
|
||||
|
||||
### 9.4 Step 设计建议
|
||||
|
||||
| 建议 | 原因 |
|
||||
|--------------------------------------------|--------------------------------------------|
|
||||
| 从 `context` 读取输入,向 `context` 写中间结果 | 多 Step Job 依赖同一个上下文传递数据 |
|
||||
| 最终结果写到 `context.response` | Service 和 client 只关心标准 `Response` |
|
||||
| 不在 Step 实例上保存请求级状态 | 每次 Job 调用会重建 Step,但保持无状态更容易测试 |
|
||||
| 需要中断的后台循环检查 `context.stop_event` | `BackgroundJob.close()` 依赖 stop_event 优雅退出 |
|
||||
| 流式输出只在 StreamJob 中调用 `add_stream_string()` | 普通 Job 没有 stream queue |
|
||||
|
||||
### 9.5 单测示例
|
||||
|
||||
可以直接实例化 Step 并传入 `RuntimeContext`:
|
||||
|
||||
```python
|
||||
import pytest
|
||||
|
||||
from reme.components.runtime_context import RuntimeContext
|
||||
from reme.steps.common.uppercase import UppercaseStep
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_uppercase_step():
|
||||
ctx = RuntimeContext(text="hello")
|
||||
resp = await UppercaseStep()(ctx)
|
||||
assert resp.answer == "HELLO"
|
||||
assert ctx["uppercase_text"] == "HELLO"
|
||||
```
|
||||
|
||||
## 10. 新增 Job
|
||||
|
||||
Job 通常不需要写 Python 类,只需要在配置里编排已有 Step。只有需要新的运行方式时,才新增 Job backend。
|
||||
|
||||
### 10.1 新增普通请求型 Job
|
||||
|
||||
在 YAML 配置的 `jobs:` 下新增:
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
uppercase:
|
||||
backend: base
|
||||
description: "Convert text to uppercase."
|
||||
parameters:
|
||||
type: object
|
||||
properties:
|
||||
text:
|
||||
type: string
|
||||
description: "input text"
|
||||
required:
|
||||
- text
|
||||
steps:
|
||||
- backend: uppercase_step
|
||||
```
|
||||
|
||||
启动后调用:
|
||||
|
||||
```bash
|
||||
reme start
|
||||
reme uppercase text="hello"
|
||||
```
|
||||
|
||||
调用链:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
CLI["CLI<br/>reme uppercase text=hello"] --> HTTP["HTTP Client"]
|
||||
HTTP --> Req["POST /uppercase"]
|
||||
Req --> S["HttpService"]
|
||||
S --> J["uppercase BaseJob<br/>job(text='hello')"]
|
||||
J --> Step["uppercase_step<br/>await step(context)"]
|
||||
Step --> Resp["context.response.answer = HELLO"]
|
||||
Resp --> JSON["Response JSON"]
|
||||
JSON --> CLIOut["CLI print answer"]
|
||||
```
|
||||
|
||||
### 10.2 新增多 Step Job
|
||||
|
||||
一个 Job 可以串联多个 Step:
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
demo_echo:
|
||||
backend: base
|
||||
description: "Normalize query, then echo it."
|
||||
parameters:
|
||||
type: object
|
||||
properties:
|
||||
query:
|
||||
type: string
|
||||
default: ""
|
||||
min_score:
|
||||
type: number
|
||||
default: 0.5
|
||||
steps:
|
||||
- backend: demo_echo_step1
|
||||
- backend: demo_echo_step2
|
||||
```
|
||||
|
||||
第一个 Step 写入:
|
||||
|
||||
```text
|
||||
context["processed_query"]
|
||||
context["adjusted_min_score"]
|
||||
```
|
||||
|
||||
第二个 Step 再读取这些字段并写最终 `response`。
|
||||
|
||||
### 10.3 新增 Stream Job
|
||||
|
||||
配置使用 `backend: stream`:
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
stream_uppercase:
|
||||
backend: stream
|
||||
description: "Stream uppercase text."
|
||||
parameters:
|
||||
type: object
|
||||
properties:
|
||||
text:
|
||||
type: string
|
||||
required:
|
||||
- text
|
||||
steps:
|
||||
- backend: uppercase_prepare_step
|
||||
- backend: uppercase_stream_step
|
||||
```
|
||||
|
||||
流式 Step 示例:
|
||||
|
||||
```python
|
||||
from ..base_step import BaseStep
|
||||
from ...components import R
|
||||
from ...enumeration import ChunkEnum
|
||||
|
||||
|
||||
@R.register("uppercase_stream_step")
|
||||
class UppercaseStreamStep(BaseStep):
|
||||
async def execute(self):
|
||||
assert self.context is not None
|
||||
for ch in self.context.get("uppercase_text", ""):
|
||||
await self.context.add_stream_string(ch, ChunkEnum.CONTENT)
|
||||
return self.context.response
|
||||
```
|
||||
|
||||
### 10.4 新增后台 Job
|
||||
|
||||
配置使用 `backend: background`:
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
my_watch_loop:
|
||||
backend: background
|
||||
watch_dirs: [ daily_dir ]
|
||||
watch_suffixes: [ md ]
|
||||
steps:
|
||||
- backend: init_changes_step
|
||||
monitor_type: file_store
|
||||
monitor_name: default
|
||||
dispatch_steps: [ update_index_step ]
|
||||
- backend: watch_changes_step
|
||||
dispatch_steps: [ update_index_step ]
|
||||
```
|
||||
|
||||
后台 Job 的特点:
|
||||
|
||||
| 特点 | 说明 |
|
||||
|--------------|----------------------------------------------------|
|
||||
| 不对外暴露 | `BackgroundJob.__init__()` 强制 `enable_serve=False` |
|
||||
| 有 supervisor | 默认异常后指数退避重启 |
|
||||
| 有 stop_event | close 时通知循环退出 |
|
||||
| 适合监听/消费 | 文件监听、队列消费、周期性长循环 |
|
||||
|
||||
### 10.5 新增 Cron Job
|
||||
|
||||
配置使用 `backend: cron`:
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
daily_auto_dream:
|
||||
backend: cron
|
||||
cron: "30 3 * * *"
|
||||
steps:
|
||||
- backend: dream_extract_step
|
||||
file_catalog: dream
|
||||
- backend: dream_integrate_step
|
||||
- backend: dream_topics_step
|
||||
- backend: dream_finish_step
|
||||
file_catalog: dream
|
||||
```
|
||||
|
||||
`cron` 表达式无效时会在启动时报错。
|
||||
|
||||
### 10.6 什么时候需要新增 Job backend
|
||||
|
||||
大多数场景只需要新增 Step + YAML Job。只有这些情况才考虑新增 `reme/components/job/*.py`:
|
||||
|
||||
| 需求 | 是否需要新 Job 类 |
|
||||
|----------------|---------------------------|
|
||||
| 新增一个业务命令 | 否,用 `backend: base` |
|
||||
| 串联多个已有步骤 | 否,用 `steps:` |
|
||||
| 要 SSE/流式输出 | 否,用 `backend: stream` |
|
||||
| 要后台循环 | 否,用 `backend: background` |
|
||||
| 要 cron 定时 | 否,用 `backend: cron` |
|
||||
| 要全新的调度/并发/事务语义 | 是,新增 Job backend |
|
||||
|
||||
新增 Job backend 的最小形态:
|
||||
|
||||
```python
|
||||
from .base_job import BaseJob
|
||||
from ..component_registry import R
|
||||
|
||||
|
||||
@R.register("my_job_backend")
|
||||
class MyJob(BaseJob):
|
||||
async def __call__(self, **kwargs):
|
||||
# 自定义调度逻辑
|
||||
return await super().__call__(**kwargs)
|
||||
```
|
||||
|
||||
同样需要确保模块被 `reme/components/job/__init__.py` import。
|
||||
356
docs/zh/memory_as_file.md
Normal file
356
docs/zh/memory_as_file.md
Normal file
|
|
@ -0,0 +1,356 @@
|
|||
# Memory as File
|
||||
|
||||
ReMe 的核心思想是:**Memory as File, File as Memory**。
|
||||
|
||||

|
||||
|
||||
**Memory as File**:长期记忆不是藏在黑盒数据库里,而是落在 vault 目录中的 Markdown 文件、资源文件和索引快照里。用户和 Agent
|
||||
都可以直接读、写、移动、删除这些文件。
|
||||
|
||||
**File as Memory**:每个文件不只是普通文本,也是一个可索引、可链接、可演化的记忆节点。ReMe 会从文件中解析 frontmatter、正文
|
||||
chunk、wikilink 边,并把它们组织成检索和图谱。
|
||||
|
||||
换句话说,文件是人的可读界面,也是 Agent 的操作接口;目录结构负责承载记忆分层,Markdown 语法负责表达内容、元数据和关系。
|
||||
|
||||
## 设计目标
|
||||
|
||||
ReMe 把记忆设计成文件,不只是为了“方便存储”,而是为了让长期记忆具备几个基本性质:
|
||||
|
||||
| 目标 | 含义 |
|
||||
|----------|----------------------------------------------------------------------|
|
||||
| 可读 | 用户可以直接打开 vault,像读普通笔记一样读 daily、digest 和原始材料。 |
|
||||
| 可编辑 | 用户和 Agent 都能用文件操作修正、补充、移动或删除记忆,不必依赖专用数据库客户端。 |
|
||||
| 可追溯 | digest 中的长期结论可以通过 `derived_from:: [[...]]` 回到 daily、resource 或 session 原文。 |
|
||||
| 可迁移 | vault 是普通目录,Markdown、JSONL、YAML 和资源文件可以被备份、同步、版本管理或迁移到其他工具。 |
|
||||
| 可索引 | 文件虽然是普通文本,但 ReMe 会解析 frontmatter、chunk、wikilink,构建检索索引和文件图谱。 |
|
||||
| 可协作 | 人负责判断和修正,Agent 负责整理、链接和检索;二者看到和操作的是同一套文件。 |
|
||||
|
||||
因此,ReMe 的记忆不是“数据库里的一条隐藏记录”,也不是“只给 LLM 看的 prompt 片段”。它首先是用户拥有的文件,其次才被系统索引成可召回的记忆。
|
||||
|
||||
## 记忆分层
|
||||
|
||||
ReMe 的 vault 把记忆分成四层:
|
||||
|
||||
```text
|
||||
raw input -> reme_session/ + resource/
|
||||
working memory -> daily/
|
||||
long memory -> digest/
|
||||
system state -> reme_metadata/
|
||||
```
|
||||
|
||||
这四层解决的是不同问题。
|
||||
|
||||
`reme_session/` 和 `resource/` 保存原始输入。它们强调“不要丢现场”:对话、Agent session、上传资料、网页或报告先原样留下,作为以后核对的证据。
|
||||
|
||||
`daily/` 是浅加工层。它把当天发生的对话和资源整理成更适合阅读的 daily note:什么事情发生了、有哪些结论、留下了哪些后续任务、对应原文在哪里。
|
||||
daily 不追求最终抽象,它更像当天工作台。
|
||||
|
||||
`digest/` 是深加工层。这里保存的是可以长期复用的记忆节点,例如用户偏好、项目背景、流程经验、概念知识、决策先例。digest
|
||||
不应该只是复制 daily,而应该把多次出现的事实、方法和关系合并成更稳定的表述。
|
||||
|
||||
`reme_metadata/` 是系统索引层。它保存 file catalog、chunk 索引、图谱快照等运行状态。用户通常不需要手写这里的内容;真正的人工编辑入口是
|
||||
`daily/`、`digest/` 和必要时的 `resource/`。
|
||||
|
||||
这个分层让 ReMe 可以同时保留“现场”和“抽象”:daily 负责还原当时发生了什么,digest 负责回答以后还能复用什么。
|
||||
|
||||
## 目录结构
|
||||
|
||||
ReMe 用目录表达记忆组织和记忆分层。原始材料先进入 `resource/` 或 `reme_session/`,再沉淀到 `daily/`,最后由 `auto_dream`
|
||||
整合到 `digest/`。
|
||||
|
||||
对应的自动流程分别是 [Auto Memory](./auto_memory.md)、[Auto Resource](./auto_resource.md) 和 [Auto Dream](./auto_dream.md)。
|
||||
检索这些文件时使用 [Memory Search](./memory_search.md)。
|
||||
|
||||
```text
|
||||
<vault_dir>/
|
||||
├── reme_metadata/ # 系统索引层;ReMe 索引、图谱、catalog 等持久状态,不作为人工编辑入口
|
||||
├── reme_session/ # 原始输入层;原始对话和 Agent session
|
||||
│ ├── dialog/
|
||||
│ │ └── <session_id>.jsonl # auto_memory 保存的对话消息
|
||||
│ ├── agentscope/
|
||||
│ │ └── <session_id>.jsonl
|
||||
│ └── claude_code/
|
||||
│ └── <session_id>.jsonl
|
||||
├── resource/ # 原始输入层;外部原始材料
|
||||
│ └── YYYY-MM-DD/
|
||||
│ └── <resource>.<ext>
|
||||
├── daily/ # 浅加工层;按日期组织当天事实、对话摘要、资源解读
|
||||
│ ├── YYYY-MM-DD.md # 当天索引页
|
||||
│ └── YYYY-MM-DD/
|
||||
│ ├── <session_id>.md # 对话加工后的 daily note
|
||||
│ ├── <resource_stem>.md # 资源加工后的 daily note
|
||||
│ └── interests.yaml # auto_dream 产出的主动兴趣主题
|
||||
└── digest/ # 深加工层;可长期复用的个人事实、流程经验、知识节点
|
||||
├── personal/
|
||||
│ └── <memory>.md # 用户画像、偏好、长期个人事实
|
||||
├── procedure/
|
||||
│ └── <memory>.md # 流程、方法论、操作经验
|
||||
└── wiki/
|
||||
└── <memory>.md # 通用知识、概念、决策先例
|
||||
```
|
||||
|
||||
典型流转如下:
|
||||
|
||||
```text
|
||||
对话
|
||||
-> reme_session/dialog/<session_id>.jsonl
|
||||
-> daily/YYYY-MM-DD/<session_id>.md
|
||||
-> digest/personal | digest/procedure | digest/wiki
|
||||
|
||||
外部资料
|
||||
-> resource/YYYY-MM-DD/<resource>.<ext>
|
||||
-> daily/YYYY-MM-DD/<resource_stem>.md
|
||||
-> digest/wiki | digest/procedure
|
||||
```
|
||||
|
||||
前两步偏向记录和整理,最后一步偏向长期沉淀。`auto_memory` 和 `auto_resource` 负责从原始输入生成 daily,`auto_dream`
|
||||
负责从 daily 抽取并整合 digest。
|
||||
|
||||
## Markdown 格式
|
||||
|
||||
ReMe 优先使用 Markdown 表达记忆,因为它同时适合人读、Agent 编辑和程序解析。
|
||||
|
||||
一个典型记忆文件:
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: 光伏产业链研究
|
||||
description: 从硅料到组件的全链条梳理
|
||||
tags: [新能源, 光伏]
|
||||
---
|
||||
|
||||
# 结论
|
||||
|
||||
光伏产业链可以拆成 [[digest/wiki/硅料.md]]、硅片、电池片和组件。
|
||||
|
||||
upstream:: [[digest/wiki/硅料.md]]
|
||||
[company:: [[digest/wiki/隆基绿能.md|隆基]]]
|
||||
```
|
||||
|
||||
### Frontmatter
|
||||
|
||||
Frontmatter 是文件开头的 YAML 块,用 `---` 包住:
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: 文档名
|
||||
description: 文档描述
|
||||
source_conversation: [[reme_session/dialog/abc.jsonl]]
|
||||
---
|
||||
```
|
||||
|
||||
当前代码固定识别 `name` 和 `description`,其他字段会作为额外 metadata 保留。写入接口会把 `name`、`description` 和
|
||||
`metadata` 合并成 frontmatter。
|
||||
|
||||
推荐把 frontmatter 当作“节点级摘要”,把正文当作“证据、解释和关系”。例如:
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: 用户偏好:文档说明风格
|
||||
description: 用户偏好直接、工程化、有上下文但不冗长的中文技术说明。
|
||||
kind: preference
|
||||
confidence: observed
|
||||
---
|
||||
|
||||
用户多次要求文档补充动机、边界和例子,但避免营销式表述。
|
||||
|
||||
derived_from:: [[daily/2026-06-20/session-a.md]]
|
||||
related:: [[digest/procedure/技术文档写作.md]]
|
||||
```
|
||||
|
||||
这样做有三个好处:
|
||||
|
||||
1. `name` 和 `description` 可以在列表、召回结果和 Agent 判断中作为轻量摘要。
|
||||
2. 正文可以承载更完整的事实、条件、反例和来源。
|
||||
3. `derived_from::`、`related::` 这类 typed wikilink 可以被图谱解析,后续移动文件时也能被维护。
|
||||
|
||||
Frontmatter 适合放稳定、短小、结构化的字段;正文适合放需要人读的解释。不要把大段正文塞进 YAML 字段。
|
||||
|
||||
### Wikilink
|
||||
|
||||
Wikilink 用 `[[...]]` 表达文件之间的关系:
|
||||
|
||||
```text
|
||||
[[digest/wiki/光伏.md]]
|
||||
[[digest/wiki/光伏.md#产业链]]
|
||||
[[digest/wiki/光伏.md|光伏]]
|
||||
![[resource/2026-06-01/report.md]]
|
||||
```
|
||||
|
||||
ReMe 的 wikilink 是**字面路径语义**:
|
||||
|
||||
```text
|
||||
[[X]] -> target_path = "X"
|
||||
```
|
||||
|
||||
它不会自动补 `.md`,不会按文件名搜索,也不会自动解析 folder note。推荐写完整的 vault 相对路径,并带上扩展名。
|
||||
|
||||
Wikilink 的作用:
|
||||
|
||||
```text
|
||||
正文链接 -> 建立 FileLink
|
||||
predicate:: 链接 -> 建立带关系名的 FileLink
|
||||
move 文件 -> 默认改写入边中的 [[旧路径]]
|
||||
delete 文件 -> 返回仍存在的入边,提示清理引用
|
||||
search 命中 -> 可展开出入链,帮助理解上下文
|
||||
```
|
||||
|
||||
支持的关系写法:
|
||||
|
||||
```markdown
|
||||
industry:: [[digest/wiki/新能源.md]]
|
||||
[competitor:: [[digest/wiki/比亚迪.md]]]
|
||||
```
|
||||
|
||||
解析结果:
|
||||
|
||||
```text
|
||||
FileLink
|
||||
source_path = 当前文件
|
||||
target_path = digest/wiki/新能源.md
|
||||
predicate = industry
|
||||
```
|
||||
|
||||
### 来源和关系
|
||||
|
||||
ReMe 里最重要的两类链接是来源链接和概念关系链接。
|
||||
|
||||
来源链接说明“这条长期记忆从哪里来”:
|
||||
|
||||
```markdown
|
||||
derived_from:: [[daily/2026-06-20/session-a.md]]
|
||||
derived_from:: [[resource/2026-06-20/report.pdf]]
|
||||
```
|
||||
|
||||
概念关系链接说明“这个节点和哪些长期记忆有关”:
|
||||
|
||||
```markdown
|
||||
related:: [[digest/wiki/光伏产业链.md]]
|
||||
depends_on:: [[digest/procedure/调研报告拆解流程.md]]
|
||||
contrasts_with:: [[digest/wiki/集中式逆变器.md]]
|
||||
```
|
||||
|
||||
普通正文 wikilink 也会建立图边,但当关系本身有语义价值时,推荐使用 `predicate:: [[path]]`。这能让搜索、图遍历和后续 Agent
|
||||
整合更容易理解链接含义。
|
||||
|
||||
## 人工编辑和 Agent 编辑
|
||||
|
||||
因为记忆就是文件,用户可以直接在编辑器里改 vault;Agent 也可以通过 ReMe 的文件工具读写同一批文件。两者遵守同一套约定:
|
||||
|
||||
| 操作 | 建议 |
|
||||
|--------|--------------------------------------------------------------------|
|
||||
| 新增记忆 | 写入合适目录,Markdown 使用 frontmatter,并尽量写完整 vault-relative wikilink。 |
|
||||
| 修改正文 | 保留已有来源和关键 wikilink;如果是修正旧结论,在正文里说明新材料如何改变旧判断。 |
|
||||
| 移动文件 | 使用 ReMe 的 move 工具时会默认改写入边中的旧路径;手工移动后建议重新检查入链。 |
|
||||
| 删除文件 | 删除前检查入链;ReMe 的 delete 会返回仍然指向目标的来源文件,方便清理悬空引用。 |
|
||||
| 修改元数据 | 用 frontmatter 表达短字段;正文发生实质变化时同步更新 `description`。 |
|
||||
|
||||
一个实用规则是:**可以让 Agent 重写表达,但不要让它丢掉证据边**。尤其是 digest 节点中的 `derived_from:: [[...]]` 和已有
|
||||
digest-to-digest wikilink,是长期记忆可追溯和可扩展的基础。
|
||||
|
||||
## 路径语义
|
||||
|
||||
所有文件工具和 wikilink 都以 vault-relative path 为基本单位:
|
||||
|
||||
```text
|
||||
digest/wiki/光伏.md
|
||||
daily/2026-06-20/session-a.md
|
||||
resource/2026-06-20/report.pdf
|
||||
```
|
||||
|
||||
这带来一个明确边界:ReMe 不把 `[[光伏]]` 当作全库标题搜索,也不假设 Obsidian 式的同名解析。`[[digest/wiki/光伏.md]]`
|
||||
就是指向这个具体路径。
|
||||
|
||||
推荐习惯:
|
||||
|
||||
1. 链接 Markdown 文件时带上 `.md`。
|
||||
2. 从 digest 指向 daily 或 resource 时写完整来源路径。
|
||||
3. 文件重命名或移动尽量通过 ReMe 的 move 工具完成,避免留下旧路径。
|
||||
4. 对外部资源使用 `resource/YYYY-MM-DD/...`,对长期抽象使用 `digest/...`,不要把原始资料直接塞进 digest。
|
||||
|
||||
这种显式路径语义牺牲了一点手写便利性,但换来的是可预测、可迁移和可自动维护。
|
||||
|
||||
## Memory Chunking
|
||||
|
||||
Memory chunking 是把一个文件拆成可检索片段的过程。ReMe 不是直接按固定长度切 Markdown,而是尽量保持语义结构。
|
||||
|
||||
本节说明文件如何被切成检索 chunk;索引更新、BM25、向量召回和链接展开流程见 [Memory Search](./memory_search.md)。
|
||||
|
||||
传统 RAG 常见做法是固定窗口切分:
|
||||
|
||||
```text
|
||||
Document
|
||||
|
|
||||
| every N tokens + overlap
|
||||
v
|
||||
chunk 1 | chunk 2 | chunk 3 | ...
|
||||
```
|
||||
|
||||
这种方式简单,但容易把标题、表格、代码块、列表和 `[[wikilink]]` 从中间切开。检索命中后,Agent
|
||||
往往只看到一段孤立文本,不知道它属于哪个章节,也不清楚它和其他记忆节点的关系。
|
||||
|
||||
ReMe 的 chunking 更接近“按文件结构切记忆”:
|
||||
|
||||
```text
|
||||
Markdown file
|
||||
|
|
||||
| frontmatter + headings + blocks + wikilinks
|
||||
v
|
||||
semantic chunks with document skeleton
|
||||
```
|
||||
|
||||
对比:
|
||||
|
||||
```text
|
||||
传统 RAG chunk
|
||||
= 固定长度文本片段 + overlap
|
||||
|
||||
ReMe memory chunk
|
||||
= 章节结构 + 正文片段 + 行号范围 + wikilink 关系上下文
|
||||
```
|
||||
|
||||
Markdown 文件使用 `MarkdownFileChunker`:
|
||||
|
||||
```text
|
||||
Markdown
|
||||
|
|
||||
| mistletoe AST
|
||||
v
|
||||
Document
|
||||
└─ H1 section
|
||||
├─ paragraph / list / table / code
|
||||
└─ H2 section
|
||||
└─ ...
|
||||
|
|
||||
v
|
||||
FileChunk[]
|
||||
```
|
||||
|
||||
分块规则:
|
||||
|
||||
```text
|
||||
1. 先解析 frontmatter,正文单独进入 chunker。
|
||||
2. 按标题层级构建章节树。
|
||||
3. 优先让一个完整章节成为一个 chunk。
|
||||
4. 章节过长时,向下递归拆子章节和正文块。
|
||||
5. 表格拆分时重复表头。
|
||||
6. 代码块拆分时重复 fence。
|
||||
7. 列表按 item 打包。
|
||||
8. 最后才按行贪心拆分,并添加 [Part X/N]。
|
||||
```
|
||||
|
||||
每个 chunk 默认会带上标题骨架:
|
||||
|
||||
```text
|
||||
# 一级标题
|
||||
|
||||
## 当前章节
|
||||
|
||||
命中的正文片段
|
||||
|
||||
## 后续章节标题
|
||||
```
|
||||
|
||||
这样检索命中时,Agent 不只看到孤立段落,还能看到它在原文件中的结构位置。
|
||||
|
||||
非 Markdown 默认走 `DefaultFileChunker`:按字节大小切分,并保留少量 overlap;对 Markdown 则会避免把 `[[wikilink]]` 从中间切开。
|
||||
195
docs/zh/memory_search.md
Normal file
195
docs/zh/memory_search.md
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
# Memory Search
|
||||
|
||||
Memory Search 是 ReMe 的记忆检索入口。它先把 `daily/`、`digest/`、`resource/` 里的文件持续构建成可搜索的 chunk 索引和
|
||||
wikilink 图谱;查询时先召回最相关的片段,再沿着片段所在文件的双向链接展开上下文。
|
||||
|
||||

|
||||
|
||||
文件分层、frontmatter、wikilink 和 chunking 的通用语义见 [Memory as File](./memory_as_file.md)。这里重点说明索引维护和查询执行。
|
||||
|
||||
```text
|
||||
vault files
|
||||
├─ index_update_loop: 发现 added / modified / deleted
|
||||
├─ update_index_step: 文件 -> FileNode + FileChunk[]
|
||||
├─ file_store: 保存 chunk、BM25、可选 embedding、wikilink graph
|
||||
└─ search_step: BM25 / vector 召回 -> RRF 融合 -> link expansion
|
||||
```
|
||||
|
||||
## 它搜索什么
|
||||
|
||||
默认配置里的 `index_update_loop` 监听三类记忆目录:
|
||||
|
||||
- `daily_dir`:Auto Memory 生成的每日工作记忆和 session 记忆卡片。
|
||||
- `digest_dir`:长期沉淀后的 digest 节点。
|
||||
- `resource_dir`:外部资源或导入资料。
|
||||
|
||||
默认后缀是 `md` 和 `jsonl`。其中 Markdown 用 `markdown` chunker,能解析 frontmatter、标题结构和 `[[wikilink]]`;`jsonl` 用
|
||||
`default` chunker,按字节大小做重叠切块。
|
||||
|
||||
## 索引怎么构建
|
||||
|
||||
索引由后台 Job `index_update_loop` 维护,配置来自 `reme/config/default.yaml`:
|
||||
|
||||
```yaml
|
||||
index_update_loop:
|
||||
backend: background
|
||||
watch_dirs: [ daily_dir, digest_dir, resource_dir ]
|
||||
watch_suffixes: [ md, jsonl ]
|
||||
steps:
|
||||
- backend: init_changes_step
|
||||
monitor_type: file_store
|
||||
monitor_name: default
|
||||
dispatch_steps: [ update_index_step ]
|
||||
- backend: watch_changes_step
|
||||
dispatch_steps: [ update_index_step ]
|
||||
```
|
||||
|
||||
启动时先跑 `init_changes_step`。它扫描 watch 目录,把磁盘上的文件 mtime 和 `file_store` 里已有的 `FileNode.st_mtime`
|
||||
对比,算出新增、修改、删除三类变化,然后把 `context["changes"]` 交给 `update_index_step`。
|
||||
|
||||
服务运行期间由 `watch_changes_step` 接手。它用 `watchfiles.awatch()` 监听同一批目录,按 quiet window 聚合文件事件,再用
|
||||
`coalesce_changes()` 把同一路径上的重复事件压成一批稳定变化。
|
||||
|
||||
`update_index_step` 真正写索引:
|
||||
|
||||
1. 按后缀选择 file chunker。
|
||||
2. 把文件解析成一个 `FileNode` 和多个 `FileChunk`。
|
||||
3. 对新增或修改的文件,先删除旧 chunk,再 upsert 新 chunk。
|
||||
4. 对删除的文件,从 `file_store`、`keyword_index` 和 `file_graph` 清掉对应记录。
|
||||
5. 有变化时 dump 到 `reme_metadata/`,让下次启动可以恢复。
|
||||
|
||||
Markdown chunker 会解析 YAML frontmatter、标题结构和 `[[...]]`,产出 `FileNode`、`FileChunk` 和 `FileLink`。更细的分块规则见
|
||||
[Memory as File](./memory_as_file.md#memory-chunking)。
|
||||
|
||||
## file_store 里有什么
|
||||
|
||||
默认 `file_store.default` 是 `local`:
|
||||
|
||||
```yaml
|
||||
file_store:
|
||||
default:
|
||||
backend: local
|
||||
embedding_store: ""
|
||||
keyword_index: default
|
||||
file_graph: default
|
||||
```
|
||||
|
||||
它组合三类能力:
|
||||
|
||||
| 部件 | 默认状态 | 作用 |
|
||||
|-------------------------|------|--------------------------------------|
|
||||
| `file_chunks` | 启用 | 保存 `FileChunk` 文本、行号、分数、可选 embedding |
|
||||
| `keyword_index.default` | 启用 | BM25 倒排索引,chunk id 是 doc id |
|
||||
| `file_graph.default` | 启用 | 保存 `FileNode` 和 wikilink 边 |
|
||||
| `embedding_store` | 默认关闭 | 开启后为 chunk 生成 embedding,并支持向量召回 |
|
||||
|
||||
所以开箱搜索主要是 BM25 + 链接展开。把 `embedding_store: default` 打开后,`SearchStep` 会同时跑向量召回和关键词召回。
|
||||
|
||||
## 怎么搜索
|
||||
|
||||
`search` Job 也是在 `default.yaml` 中配置:
|
||||
|
||||
```yaml
|
||||
search:
|
||||
backend: base
|
||||
description: "Hybrid vault search (vector + BM25, RRF-fused)."
|
||||
parameters:
|
||||
query: string
|
||||
limit: integer
|
||||
min_score: number
|
||||
steps:
|
||||
- backend: search_step
|
||||
vector_weight: 0.7
|
||||
candidate_multiplier: 3.0
|
||||
expand_links: true
|
||||
max_links_per_direction: 10
|
||||
```
|
||||
|
||||
调用时:
|
||||
|
||||
```bash
|
||||
reme search query="最近关于索引的讨论" limit=5
|
||||
```
|
||||
|
||||
`search_step` 的执行顺序是:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["query + limit"] --> B["candidates = limit * candidate_multiplier"]
|
||||
B --> C["file_store.vector_search(...)"]
|
||||
B --> D["file_store.keyword_search(...)"]
|
||||
C --> E["RRF 融合"]
|
||||
D --> E
|
||||
E --> F["min_score 过滤"]
|
||||
F --> G["截断到 limit"]
|
||||
G --> H["expand_links(...)"]
|
||||
H --> I["Response.answer + metadata"]
|
||||
```
|
||||
|
||||
如果只有 BM25 有结果,就直接返回 BM25 排名;如果只有向量有结果,就直接返回向量排名;两边都有结果时,用 RRF 融合。RRF 不直接比较
|
||||
BM25 分数和 cosine 分数,而是比较两个列表里的名次:
|
||||
|
||||
```text
|
||||
fused_score = vector_weight / (60 + vector_rank)
|
||||
+ keyword_weight / (60 + keyword_rank)
|
||||
```
|
||||
|
||||
默认 `vector_weight=0.7`,所以启用 embedding 后语义召回权重更高;关键词仍能把精确词命中的 chunk 拉上来。
|
||||
|
||||
## BM25 怎么工作
|
||||
|
||||
`keyword_search()` 调用 `keyword_index.retrieve(query, limit)`。BM25 索引里每个 chunk 是一篇文档:
|
||||
|
||||
- `doc_id` 是 `FileChunk.id`。
|
||||
- `content` 是 `FileChunk.text`。
|
||||
- tokenizer 把文本切成 token。
|
||||
- 倒排表记录 token 出现在哪些 chunk 里、每个 chunk 的词频是多少。
|
||||
- 查询时只对 query token 命中的 posting list 打分,再返回分数最高的 chunk id。
|
||||
|
||||
当文件被修改时,`LocalFileStore.upsert()` 会先删除该文件旧 `chunk_ids` 对应的 BM25 doc,再添加新 chunk 文本。删除采用 lazy
|
||||
delete,后续可通过 optimize 压缩索引。
|
||||
|
||||
## 渐进式展开怎么看
|
||||
|
||||
Memory Search 的“渐进式”不是一次把全库内容塞进结果,而是分三层展开:
|
||||
|
||||
1. 第一层是 chunk 召回:只返回最相关的 `limit` 个文本片段。
|
||||
2. 第二层是文件定位:每个结果带 `path:start_line-end_line`,可以继续用 `read` 精读原文件。
|
||||
3. 第三层是链接邻居:对命中文件调用 `expand_links()`,展开最多 `max_links_per_direction` 个 outlinks 和 inlinks。
|
||||
|
||||
展开的数据来自 `file_graph`,不是重新扫文件:
|
||||
|
||||
```text
|
||||
命中 chunk
|
||||
-> chunk.path
|
||||
-> file_store.get_outlinks(path)
|
||||
-> file_store.get_inlinks(path)
|
||||
-> file_store.get_nodes(neighbor_paths)
|
||||
-> 渲染邻居的 path、name、description、predicate、anchor
|
||||
```
|
||||
|
||||
这让搜索结果既保持短,又能看到“这条记忆连接到哪些长期节点、资源或其他 daily note”。如果某条结果值得继续追,可以用
|
||||
`read path=...` 打开原文,或用 `traverse path=... depth=2` 沿 wikilink 图谱继续扩展。
|
||||
|
||||
## 返回结果长什么样
|
||||
|
||||
`SearchStep` 会把结果写到两个地方:
|
||||
|
||||
- `response.answer`:给人看的文本,每个命中块包含路径、行号、分数和 chunk 内容,后面跟 outlinks / inlinks。
|
||||
- `response.metadata`:给程序看的结构化结果,包括 `results`、`link_expansion`、`counts`。
|
||||
|
||||
典型文本结构:
|
||||
|
||||
```text
|
||||
========== daily/2026-06-20/session-a.md:12-28 [score=0.0317 keyword=4.8120] ==========
|
||||
...命中的记忆片段...
|
||||
outlinks (2):
|
||||
-> digest/indexing.md name="Indexing" description="..."
|
||||
via predicate=related
|
||||
inlinks (1):
|
||||
<- daily/2026-06-19.md name="..."
|
||||
via plain
|
||||
```
|
||||
|
||||
`counts` 会告诉你本次向量、关键词各召回了多少候选,以及最终返回多少条。默认 embedding 关闭时,`vector` 通常是 `0`,`hybrid` 是
|
||||
`false`。
|
||||
132
docs/zh/proactive.md
Normal file
132
docs/zh/proactive.md
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
# Proactive
|
||||
|
||||
`proactive` 是 ReMe 的主动记忆读取接口。它不重新分析 daily,也不调用 LLM,只读取 `auto_dream` 写出的当天兴趣主题:
|
||||
|
||||
```text
|
||||
daily/<date>/interests.yaml
|
||||
```
|
||||
|
||||
上层 Agent 可以用它获取“今天值得主动关注什么”,再决定是否提醒、追问、推荐下一步或生成主动洞察。
|
||||
|
||||
`interests.yaml` 由 [Auto Dream](./auto_dream.md) 的 Topics 阶段生成;`proactive` 只负责读取和暴露结果。
|
||||
|
||||
## 配置入口
|
||||
|
||||
默认配置在 `reme/config/default.yaml`:
|
||||
|
||||
```yaml
|
||||
proactive:
|
||||
backend: base
|
||||
description: "Proactive: read daily/<date>/interests.yaml and expose the latest user-interest topics."
|
||||
parameters:
|
||||
date:
|
||||
type: string
|
||||
default: ""
|
||||
include_content:
|
||||
type: boolean
|
||||
default: true
|
||||
steps:
|
||||
- backend: proactive_step
|
||||
```
|
||||
|
||||
参数含义:
|
||||
|
||||
| 参数 | 作用 |
|
||||
|-------------------|----------------------------------------|
|
||||
| `date` | 要读取的日期,格式为 `YYYY-MM-DD`。为空时使用应用时区中的今天。 |
|
||||
| `include_content` | 是否在 metadata 中返回 YAML 原文,默认 `true`。 |
|
||||
|
||||
## 输入契约
|
||||
|
||||
典型格式如下:
|
||||
|
||||
```yaml
|
||||
date: 2026-06-20
|
||||
topic_count: 3
|
||||
diversity_days: 7
|
||||
topics:
|
||||
- title: 记忆检索链路的质量回归
|
||||
reason: 用户近期持续修改 search、node_search 和 dream 集成链路。
|
||||
evidence: daily/2026-06-20/session.md
|
||||
keywords:
|
||||
- memory search
|
||||
- auto dream
|
||||
paths:
|
||||
- daily/2026-06-20/session.md
|
||||
```
|
||||
|
||||
只有 `topics` 列表会被解析成结构化结果。每个 topic 至少需要 `title` 和 `reason`;`evidence`、`keywords`、`paths` 是辅助字段。
|
||||
|
||||
## 返回结果
|
||||
|
||||
成功读取时,`proactive_step` 会把结果写入标准 response metadata:
|
||||
|
||||
| 字段 | 说明 |
|
||||
|-----------|----------------------------------------|
|
||||
| `date` | 实际读取的日期。 |
|
||||
| `path` | `daily/<date>/interests.yaml`。 |
|
||||
| `topics` | 解析后的 topic 列表。 |
|
||||
| `content` | YAML 原文;仅在 `include_content=true` 时返回。 |
|
||||
| `skipped` | 文件不存在时为 `true`。 |
|
||||
| `error` | 读取或解析异常。 |
|
||||
| `summary` | 简短摘要。 |
|
||||
|
||||
文件存在且解析成功时,answer 类似:
|
||||
|
||||
```text
|
||||
Read 3 proactive topic(s) from daily/2026-06-20/interests.yaml
|
||||
```
|
||||
|
||||
文件不存在时不会报错,而是成功返回 skipped:
|
||||
|
||||
```text
|
||||
Skipped: interests file not found at daily/2026-06-20/interests.yaml
|
||||
```
|
||||
|
||||
这让上层 Agent 可以把“今天还没有 dream 结果”当作正常空状态处理。
|
||||
|
||||
## 运行方式
|
||||
|
||||
CLI:
|
||||
|
||||
```bash
|
||||
reme proactive date=2026-06-20
|
||||
```
|
||||
|
||||
不返回 YAML 原文:
|
||||
|
||||
```bash
|
||||
reme proactive date=2026-06-20 include_content=false
|
||||
```
|
||||
|
||||
## 与 auto_dream 的关系
|
||||
|
||||
`proactive` 是 `auto_dream` 的下游读取步骤:
|
||||
|
||||
```text
|
||||
daily notes
|
||||
-> auto_dream
|
||||
-> daily/<date>/interests.yaml
|
||||
-> proactive
|
||||
-> upper-level agent
|
||||
```
|
||||
|
||||
职责边界如下。更完整的 Extract、Integrate、Topics、Finish 说明见 [Auto Dream](./auto_dream.md):
|
||||
|
||||
| 模块 | 职责 |
|
||||
|----------------------|----------------------------------------|
|
||||
| `dream_extract_step` | 从 changed daily 输入抽取 topic candidates。 |
|
||||
| `dream_topics_step` | 去重、筛选并写入 `interests.yaml`。 |
|
||||
| `proactive_step` | 读取 `interests.yaml`,暴露给上层 Agent。 |
|
||||
|
||||
`proactive` 不修改任何文件,不更新 catalog,也不负责判断是否应该主动打扰用户。它只提供当天主题材料;是否推送、何时推送、用什么语气推送,应由调用方根据产品策略决定。
|
||||
|
||||
## 失败模式
|
||||
|
||||
| 场景 | 行为 |
|
||||
|----------------------|--------------------------------------------|
|
||||
| `interests.yaml` 不存在 | `success=true`,`skipped=true`,`topics=[]`。 |
|
||||
| YAML 无法读取或解析异常 | `success=false`,answer 返回错误摘要。 |
|
||||
| YAML 存在但没有合法 topics | `success=true`,`topics=[]`。 |
|
||||
|
||||
因此推荐调用方先检查 `success`,再检查 `skipped`,最后检查 `topics` 是否为空。
|
||||
210
docs/zh/quick_start.md
Normal file
210
docs/zh/quick_start.md
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
# 快速开始
|
||||
|
||||
---
|
||||
|
||||
## 安装
|
||||
|
||||
ReMe 要求 Python 3.11+。
|
||||
|
||||
从 pip 安装:
|
||||
|
||||
```bash
|
||||
pip install "reme-ai[core]"
|
||||
```
|
||||
|
||||
从源码安装:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/agentscope-ai/ReMe.git
|
||||
cd ReMe
|
||||
pip install -e ".[core]"
|
||||
```
|
||||
|
||||
`core` extra 建议安装:当前代码会导入 AgentScope wrapper,自进化记忆也依赖它。
|
||||
|
||||
如果要使用 `auto_memory`、`auto_resource`、`auto_dream` 这类 Agent 流程,再配置 LLM:
|
||||
|
||||
```bash
|
||||
cat > .env <<'EOF'
|
||||
LLM_BACKEND=openai
|
||||
LLM_MODEL_NAME=qwen3.7-plus
|
||||
LLM_API_KEY=your_api_key
|
||||
LLM_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
|
||||
EOF
|
||||
```
|
||||
|
||||
只跑基础文件读写和 BM25 检索,可以先不配。
|
||||
|
||||
---
|
||||
|
||||
## 启动
|
||||
|
||||
```bash
|
||||
reme start
|
||||
```
|
||||
|
||||
默认服务地址是 `127.0.0.1:2333`。如果端口被占用:
|
||||
|
||||
```bash
|
||||
reme start service.port=8181
|
||||
```
|
||||
|
||||
```bash
|
||||
reme version
|
||||
reme health_check
|
||||
reme list
|
||||
```
|
||||
|
||||
`reme list` 会列出服务端 action。普通命令会通过 HTTP 调用服务端 Job。
|
||||
|
||||
---
|
||||
|
||||
## Vault 目录
|
||||
|
||||
默认 vault 是当前目录下的 `.reme/`,启动时会自动创建:
|
||||
|
||||
```text
|
||||
.reme/
|
||||
├── reme_metadata/ # 索引、图谱、catalog 等持久状态
|
||||
├── reme_session/ # Agent session 与原始对话
|
||||
├── resource/ # 外部资料
|
||||
├── daily/ # daily note
|
||||
└── digest/ # 长期记忆
|
||||
```
|
||||
|
||||
目录分层、Markdown frontmatter 和 wikilink 语义见 [Memory as File](./memory_as_file.md)。
|
||||
|
||||
也可以启动时指定:
|
||||
|
||||
```bash
|
||||
reme start vault_dir=/tmp/reme-demo service.port=8181
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 写入、索引、检索
|
||||
|
||||
```bash
|
||||
reme write \
|
||||
path=digest/wiki/quick-start-demo \
|
||||
name="Quick Start Demo" \
|
||||
description="快速开始示例记忆" \
|
||||
content="# Quick Start Demo
|
||||
|
||||
ReMe 会索引 daily、digest 和 resource 目录中的 Markdown。
|
||||
|
||||
相关链接:[[digest/wiki/search-demo.md]]"
|
||||
```
|
||||
|
||||
`path` 是 vault 内路径;没有后缀时会自动补 `.md`;Markdown 文件会写入 `name` 和 `description` front matter。
|
||||
|
||||
后台 watcher 会自动建索引;也可以手动重建:
|
||||
|
||||
```bash
|
||||
reme reindex
|
||||
```
|
||||
|
||||
搜索:
|
||||
|
||||
```bash
|
||||
reme search query="快速开始 示例 记忆" limit=5
|
||||
```
|
||||
|
||||
读取:
|
||||
|
||||
```bash
|
||||
reme read path=digest/wiki/quick-start-demo start_line=1 end_line=20
|
||||
```
|
||||
|
||||
默认配置下,检索主要是 BM25 + wikilink 图谱扩展;向量检索能力在代码中支持,但默认未启用 embedding store。完整检索流程见
|
||||
[Memory Search](./memory_search.md)。
|
||||
|
||||
---
|
||||
|
||||
## 文件与 Daily Note
|
||||
|
||||
```bash
|
||||
reme stat path=digest/wiki/quick-start-demo
|
||||
reme edit path=digest/wiki/quick-start-demo old="会索引" new="会持续索引"
|
||||
reme frontmatter_read path=digest/wiki/quick-start-demo
|
||||
reme frontmatter_update path=digest/wiki/quick-start-demo metadata='{"tags":["demo"]}'
|
||||
```
|
||||
|
||||
`list` 这个名字在 CLI 中用于 action 列表,所以文件列表 Job 需要用 HTTP 调:
|
||||
|
||||
```bash
|
||||
curl -s http://127.0.0.1:2333/list \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"path":"digest","recursive":true,"limit":50}'
|
||||
```
|
||||
|
||||
Daily note:
|
||||
|
||||
```bash
|
||||
reme daily_create session_id=demo-session
|
||||
reme daily_list
|
||||
reme daily_reindex
|
||||
```
|
||||
|
||||
`daily_create` 会创建 `daily/<date>/<session_id>.md`,并刷新 `daily/<date>.md`。
|
||||
|
||||
---
|
||||
|
||||
## 自动记忆
|
||||
|
||||
```bash
|
||||
reme auto_memory \
|
||||
session_id=chat-demo \
|
||||
messages='[{"role":"user","content":"我偏好把项目经验沉淀成 Markdown。"},{"role":"assistant","content":"已记录。"}]' \
|
||||
memory_hint="记录用户偏好"
|
||||
```
|
||||
|
||||
外部资料放入 `resource/YYYY-MM-DD/` 后,默认后台会监听 `md/txt/json/jsonl/csv/yaml/html`。也可以手动触发:
|
||||
|
||||
```bash
|
||||
reme auto_resource changes='[{"path":"resource/2026-06-20/report.md","change":"added"}]'
|
||||
```
|
||||
|
||||
把 daily 整理到长期 digest:
|
||||
|
||||
```bash
|
||||
reme auto_dream date=2026-06-20
|
||||
reme proactive date=2026-06-20
|
||||
```
|
||||
|
||||
这些流程需要可用 LLM;未配置 LLM 时请先使用 `write/read/search/daily_create` 这类基础能力。
|
||||
|
||||
更多细节见 [Auto Memory](./auto_memory.md)、[Auto Resource](./auto_resource.md)、[Auto Dream](./auto_dream.md) 和
|
||||
[Proactive](./proactive.md)。
|
||||
|
||||
---
|
||||
|
||||
## HTTP 与配置
|
||||
|
||||
每个可服务 Job 都暴露为 `POST /<job>`:
|
||||
|
||||
```bash
|
||||
curl -s http://127.0.0.1:2333/version \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{}'
|
||||
|
||||
curl -s http://127.0.0.1:2333/search \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"query":"快速开始","limit":5}'
|
||||
```
|
||||
|
||||
默认配置来自 `reme/config/default.yaml`。启动时可以用 dot notation 覆盖:
|
||||
|
||||
```bash
|
||||
reme start \
|
||||
vault_dir=/tmp/reme-demo \
|
||||
service.host=127.0.0.1 \
|
||||
service.port=8181 \
|
||||
enable_logo=false
|
||||
```
|
||||
|
||||
也可以指定 YAML/JSON 配置文件:
|
||||
|
||||
```bash
|
||||
reme start config=/path/to/custom.yaml
|
||||
```
|
||||
|
|
@ -29,6 +29,14 @@
|
|||
|
||||
---
|
||||
|
||||
## 📰 最新文章
|
||||
|
||||
| 日期 | 标题 |
|
||||
|------------|----------------------------------------------------|
|
||||
| 2026-03-30 | [CoPaw 上下文管理设计解析](docs/copaw_context_design_zh.md) |
|
||||
|
||||
---
|
||||
|
||||
🧠 ReMe 是一个专为 **AI 智能体** 打造的记忆管理框架,同时提供基于[文件系统](#-基于文件的记忆系统-remelight)
|
||||
和基于[向量库](#-基于向量库的记忆系统)的记忆系统。
|
||||
|
||||
|
|
@ -72,6 +72,7 @@ include-package-data = true
|
|||
|
||||
[tool.setuptools.package-data]
|
||||
"*" = ["py.typed", "**/*.yaml", "**/*.json"]
|
||||
"reme" = ["skills/**/*"]
|
||||
"reme.components.tokenizer" = ["stopwords"]
|
||||
|
||||
[tool.setuptools.dynamic]
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@ service:
|
|||
jobs:
|
||||
index_update_loop:
|
||||
backend: background
|
||||
# watch_dirs: [daily_dir, digest_dir, resource_dir]
|
||||
watch_dirs: [daily_dir, digest_dir]
|
||||
# watch_suffixes: [md, jsonl]
|
||||
watch_suffixes: [md]
|
||||
watch_dirs: [daily_dir, digest_dir, resource_dir]
|
||||
# watch_dirs: [daily_dir, digest_dir]
|
||||
watch_suffixes: [md, jsonl]
|
||||
# watch_suffixes: [md]
|
||||
steps:
|
||||
- backend: init_changes_step
|
||||
monitor_type: file_store
|
||||
|
|
|
|||
51
reme/skills/qwenpaw_memory/SKILL.md
Normal file
51
reme/skills/qwenpaw_memory/SKILL.md
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
---
|
||||
name: qwenpaw_memory
|
||||
description: 记录、检索、更新重要信息
|
||||
---
|
||||
|
||||
## 记忆
|
||||
|
||||
每次会话都是全新的。工作目录下的文件是你的记忆延续:
|
||||
|
||||
- **每日笔记:** `memory/YYYY-MM-DD.md`(按需创建 `memory/` 目录)— 发生事件的原始记录
|
||||
- **长期记忆:** `MEMORY.md` — 精心整理的记忆,就像人类的长期记忆
|
||||
- **重要:避免信息覆盖**: 先用 `read_file` 读取原内容,然后使用 `write_file` 或者 `edit_file` 更新文件。
|
||||
|
||||
用这些文件来记录重要的东西,包括决策、上下文、需要记住的事。除非用户明确要求,否则不要在记忆中记录敏感的信息。
|
||||
|
||||
### 🧠 MEMORY.md - 你的长期记忆
|
||||
|
||||
- 出于**安全考虑** — 不应泄露给陌生人的个人信息
|
||||
- 你可以在主会话中**自由读取、编辑和更新** MEMORY.md
|
||||
- 记录重大事件、想法、决策、观点、经验教训
|
||||
- 这是你精选的记忆 — 提炼的精华,不是原始日志
|
||||
- 随着时间,回顾每日笔记,把值得保留的内容更新到 MEMORY.md
|
||||
|
||||
### 📝 写下来 - 别只记在脑子里!
|
||||
|
||||
- **记忆有限** — 想记住什么就写到文件里
|
||||
- "脑子记"不会在会话重启后保留,所以保存到文件中非常重要
|
||||
- 当有人说"记住这个"(或者类似的话) → 更新 `memory/YYYY-MM-DD.md` 或相关文件
|
||||
- 当你学到教训 → 更新 AGENTS.md、MEMORY.md 或相关技能文档
|
||||
- 当你犯了错 → 记下来,让未来的你避免重蹈覆辙
|
||||
- **写下来 远比 用脑子记住 更好**
|
||||
|
||||
### 🎯 主动记录 - 别总是等人叫你记!
|
||||
|
||||
对话中发现有价值的信息时,**先记下来,再回答问题**:
|
||||
|
||||
- 用户提到的个人信息(名字、偏好、习惯、工作方式)→ 更新 `PROFILE.md` 的「用户资料」section
|
||||
- 对话中做出的重要决策或结论 → 记录到 `memory/YYYY-MM-DD.md`
|
||||
- 发现的项目上下文、技术细节、工作流程 → 写入相关文件
|
||||
- 用户表达的喜好或不满 → 更新 `PROFILE.md` 的「用户资料」section
|
||||
- 工具相关的本地配置(SSH、摄像头等)→ 更新 `MEMORY.md` 的「工具设置」section
|
||||
- 任何你觉得未来会话可能用到的信息 → 立刻记下来
|
||||
|
||||
**关键原则:** 不要总是等用户说"记住这个"。如果信息对未来有价值,主动记录。先记录,再回答 — 这样即使会话中断,信息也不会丢失。
|
||||
|
||||
### 🔍 检索工具
|
||||
|
||||
回答关于过往工作、决策、日期、人员、偏好或待办的问题前:
|
||||
|
||||
1. 对 MEMORY.md 和 memory/*.md 运行 `memory_search`
|
||||
2. 如需阅读每日笔记 `memory/YYYY-MM-DD.md`,直接用 `read_file`
|
||||
Loading…
Add table
Reference in a new issue