diff --git a/README.md b/README.md index 046a807d..7cfbc067 100644 --- a/README.md +++ b/README.md @@ -1,273 +1,102 @@ -
- ReMe Logo +

+ ReMe Logo +

-

Remember Me, Refine Me

-

面向 Agent 的、文件优先的自进化记忆系统。

+

+ Python Version + PyPI Version + PyPI Downloads + GitHub commit activity + License + English + 简体中文 + GitHub Stars + DeepWiki +

-

- Python Version - PyPI Version - PyPI Downloads - GitHub commit activity - License -

+

+agentscope-ai%2FReMe | Trendshift +

-

- GitHub - · - DeepWiki - · - 设计文档 - · - 历史 README -

+

+ A memory management toolkit for AI agents — Remember Me, Refine Me.
+

-

- GitHub Stars - agentscope-ai/ReMe | Trendshift -

- -

- 历史版本: - 0.3.x - · - 0.2.x - · - memoryscope -

-
+> 历史版本 [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 图谱重新找到。
你可以用 ReMe 做什么
-- **个人助理**:把用户偏好、长期事实和历史上下文写入可读的 daily/digest 记忆。 -- **编程助手**:沉淀项目约定、排错经验、操作流程和长期决策。 -- **研究助理**:把报告、网页、日志、会议纪要等文本资源解读为 daily note。 -- **知识图谱**:通过 Markdown、front matter 和 `[[wikilink]]` 维护本地知识网络。 -- **主动记忆**:从 daily 输入中抽取兴趣主题,生成 `interests.yaml` 给上层 Agent 使用。 -- **服务化工具**:通过 HTTP、MCP 或 CLI 调用同一组 Job。 +- **个人助理**:为 [QwenPaw](https://github.com/agentscope-ai/QwenPaw) 等智能体提供长期记忆,记住用户偏好和历史对话。 +- **编程助手**:记录代码风格偏好、项目上下文,跨会话保持一致的开发体验。 +- **客服机器人**:记录用户问题历史、偏好设置,提供个性化服务。 +- **任务自动化**:从历史任务中学习成功/失败模式,持续优化执行策略。 +- **知识问答**:构建可检索的知识库,支持语义搜索和精确匹配。
--- -## 📁 文件优先的记忆系统 +## 📁 基于文件的记忆系统 -> 记忆即文件,文件即记忆。 +> Memory as files, files as memory. -ReMe 的默认运行目录来自 `ApplicationConfig`。应用启动时会自动创建 vault 根目录和主要子目录。 +将**记忆视为文件**:原始对话和外部资料先进入输入层,再加工成 daily note,最后沉淀为可长期复用的 digest 记忆。 + +| 层级 | 目录 | 内容 | +|------|-----------------------------|-------------------------| +| 原始输入 | `reme_session/`、`resource/` | 原始对话、Agent session、外部资料 | +| 浅加工 | `daily/` | 当天事实、对话摘要、资源解读、兴趣主题 | +| 深加工 | `digest/` | 用户画像、长期事实、流程经验、知识节点 | ```text / -├── reme_metadata/ # 索引、图谱、catalog 等持久状态 -├── reme_session/ # Agent session 与原始对话 +├── reme_metadata/ # 系统索引、图谱、catalog 等持久状态 +├── reme_session/ # 原始对话和 Agent session │ ├── dialog/ -│ │ └── .jsonl # auto_memory 保存的对话消息 +│ │ └── .jsonl │ ├── agentscope/ │ └── claude_code/ -├── resource/ # 外部文本资源 +├── resource/ # 外部原始材料 │ └── YYYY-MM-DD/ │ └── . -├── daily/ # 浅加工记忆 -│ ├── YYYY-MM-DD.md # 当天索引页 +├── daily/ # 浅加工记忆:当天事实、对话摘要、资源解读 +│ ├── YYYY-MM-DD.md │ └── YYYY-MM-DD/ -│ ├── .md # 对话或资源加工后的 daily note -│ └── interests.yaml # auto_dream 产出的主动兴趣主题 -└── digest/ # 深加工记忆 +│ ├── .md +│ ├── .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` | +![ReMe overview](docs/figure/reme-overview.svg) --- -## 📝 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/.md -外部 resource ── auto_resource ─> daily/YYYY-MM-DD/.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/.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/ -``` - -`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//interests.yaml`,默认最多 3 个 topic,并参考过去 7 天去重 | -| Finish | checkpoint 成功处理的 changed paths,持久化 dream catalog,返回摘要 | - -Extract 和 Integrate 需要可用 LLM;如果未配置 LLM,会返回失败。Topics 阶段在部分情况下可以退化为本地去重,但完整 `auto_dream` 仍依赖 LLM 完成抽取与整合。 - -### Proactive - -`proactive` 读取: - -```text -daily//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=` 指定配置文件。 -- 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 /`。 -- 请求体是 `Request`,响应是 `Response`。 -- StreamJob 注册为 `POST /`,返回 `text/event-stream`。 -- CORS 默认开放。 -- lifespan 中启动和关闭整个 `Application`。 +

+ Memory as File model +

-### MCP Service +### Auto Memory & Auto Resource(BETA) -`MCPService` 使用 FastMCP: +

+ Auto Memory and Auto Resource flow +

-- 非 stream Job 注册为 MCP tool。 -- 支持 `stdio`、`sse`、`streamable-http`。 -- StreamJob 当前不注册为 MCP tool。 -- MCP 服务内置 `claim_channel` 相关通道机制,用于把 vault 变更通知发送给最近 claim 的客户端会话。 +### Auto Dream & Auto Link & Proactive + +

+ Auto Dream flow +

+ +### Memory Search + +

+ Memory Search flow +

--- -## 💾 持久化状态 +## ⭐ 社区与支持 -除 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__v1.jsonl.zst`,保存 chunk 元数据 | -| `file_graph` | `.jsonl.zst`,保存 `FileNode` 和 links | -| `keyword_index` | `bm25_*.pkl`,保存 vocab、posting list、doc meta | -| `file_catalog` | `.jsonl.zst`,保存已处理文件 mtime checkpoint | +### 贡献者 -应用关闭时会按启动顺序反向关闭组件,并触发 file_store、keyword index、file graph 和 catalog 的持久化。 +感谢所有为 ReMe 做出贡献的朋友们: + + + 贡献者 + --- -## 🧩 关键数据模型 +## 📄 引用 -```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 历史 + +[![Star History Chart](https://api.star-history.com/svg?repos=agentscope-ai/ReMe&type=Date)](https://www.star-history.com/#agentscope-ai/ReMe&Date) diff --git a/docs/figure/auto-dream.svg b/docs/figure/auto-dream.svg new file mode 100644 index 00000000..43da1b27 --- /dev/null +++ b/docs/figure/auto-dream.svg @@ -0,0 +1,110 @@ + + ReMe auto dream flow + A left-to-right auto dream flow from changed daily notes to digest integration, interest topic writing, catalog checkpointing, and proactive reads. + + + + + + + + + + + + Auto Dream + Scan changed daily memory, integrate reusable units into digest, then expose fresh proactive topics. + + + + 1 + Extract + dream_extract_step + + refresh day index + daily/<date>.md + + compare catalog + changed daily markdown + + LLM extract + units + topic candidates + + + + 2 + Integrate + dream_integrate_step + + node_search + recall digest nodes + + auto link + dedup + wikilinks + + write digest + create / update nodes + + + + 3 + Topics + dream_topics_step + + merge candidates + same-day topics kept + + avoid repeats + last 7 days by default + + write interests.yaml + top 3 by default + + + + 4 + Finish + dream_finish_step + + checkpoint paths + skip failed inputs + + persist catalog + file_catalog: dream + + return summary + counts + errors + + + + + + + Inputs + daily/<date>.md and daily/<date>/**/*.md + + Long-term memory + digest/procedure, digest/personal, digest/wiki + + Proactive output + daily/<date>/interests.yaml + + + proactive reads interests.yaml after auto_dream writes it + diff --git a/docs/figure/auto-memory-resource.svg b/docs/figure/auto-memory-resource.svg new file mode 100644 index 00000000..38c35063 --- /dev/null +++ b/docs/figure/auto-memory-resource.svg @@ -0,0 +1,106 @@ + + ReMe auto memory and auto resource flow + 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. + + + + + + + + + + + + Auto Memory & Auto Resource + Conversations and dated resources become readable daily cards, then share one daily index and downstream memory flow. + + + + 1 + Conversations + auto_memory + + session input + conversation by id + + memory card + daily/<date>/<id>.md + + source: dialog jsonl + + + + 2 + Resources + auto_resource + + dated files + md / txt / csv / json + + resource card + daily/<date>/<name>.md + + source file stays put + + + + 3 + Daily Workbench + daily/<date>/ + + shared index + daily/<date>.md + + readable cards + facts, context, actions + + one daily memory stream + + + + 4 + Downstream + dream + search + + auto_dream + daily to digest + + memory search + daily + digest retrieval + + long-term memory path + + + + + + + Conversation source + reme_session/dialog/<session_id>.jsonl + + Resource source + resource/<date>/<resource_file> + + Daily output + daily cards plus daily/<date>.md + + + both flows preserve original sources for verification + diff --git a/docs/figure/framework-structure.svg b/docs/figure/framework-structure.svg new file mode 100644 index 00000000..279f2775 --- /dev/null +++ b/docs/figure/framework-structure.svg @@ -0,0 +1,158 @@ + + ReMe framework structure + An architectural map of ReMe from file-backed vault storage through knowledge kernel, workflows, application wiring, and external service surfaces. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ReMe Framework Structure + File-backed memory, searchable knowledge kernel, composable jobs, app wiring, and public service surfaces. + + + + + + Service + Public interfaces + + + HTTP API + server routes + + MCP Tools + agent actions + + CLI Client + local access + + + + + + + Application + Config, wiring, lifecycle + + + Context + + Wiring + + Lifecycle + + Job APIs + + + + + + + Steps / Jobs + Composable workflows + + + Jobs + base · stream · background · cron + + Step Modules + file_io · index · evolve · transfer · channel · common + + + + + + + Knowledge Kernel + Index, watch, schema + + + Index Stores + file_store · keyword_index + embedding_store · file_graph + + File Watcher + scanner · chunker + catalog + + Memory + FileNode + FileChunk · FileLink + + + + + + + Vault Layout + File-backed memory + + + daily/ + working notes + + digest/ + long-term + + resource/ + resources + + metadata/ + state + + + + + Structure reads bottom-up during boot and top-down during use. + Vault files feed the kernel; workflows compose operations; application wiring exposes stable service entry points. + diff --git a/docs/figure/memory-as-file.svg b/docs/figure/memory-as-file.svg new file mode 100644 index 00000000..50a4d574 --- /dev/null +++ b/docs/figure/memory-as-file.svg @@ -0,0 +1,110 @@ + + ReMe memory as file model + 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. + + + + + + + + + + + + Memory as File + Vault files are the readable memory surface and the operable graph/index substrate. + + + + Human + Read and edit files + Markdown, YAML, JSONL, resources + + open, revise, move, delete + + + Vault directory + the shared memory interface + + Memory as File + + File as Memory + + + + Agent + Parse and operate graph + frontmatter, chunks, wikilinks + + search, link, rewrite, index + + + + + people and agents see the same file tree, so edits and evidence links stay inspectable + + + + 1 + Raw input + keep the original scene + + reme_session/ + resource/ + + + + 2 + Daily + shallow working memory + + daily/YYYY-MM-DD/*.md + + + + 3 + Digest + long-term reusable nodes + + personal / procedure / wiki + + + + 4 + Metadata + system state and indexes + + catalog + chunks + links + + + + + + + + + Stable paths + + vault-relative wikilinks + + derived_from evidence edges + + search expands structure and links + diff --git a/docs/figure/memory-search.svg b/docs/figure/memory-search.svg new file mode 100644 index 00000000..45a5acbb --- /dev/null +++ b/docs/figure/memory-search.svg @@ -0,0 +1,98 @@ + + ReMe memory search flow + A concise left-to-right memory search flow from watched vault files to progressive link expansion. + + + + + + + + + + + + Memory Search + Index file changes continuously, recall relevant chunks, then expand nearby wikilink context. + + + + 1 + Watch memory + index_update_loop + + daily / digest / resource + md and jsonl files + + init + watch changes + added / modified / deleted + + + + 2 + Build index + update_index_step + + chunk file + FileNode + FileChunk[] + + store structures + BM25 + graph + chunks + + + + 3 + Recall chunks + search_step + + BM25 search + keyword-ranked chunks + + optional vector search + RRF fusion when enabled + + + + 4 + Expand context + expand_links + + top chunks + path + line range + + outlinks + inlinks + name, description, via + + + + + + + Default path + BM25 first, vector optional. + + BM25 is enabled by default + embedding_store is empty unless configured. + + Results stay compact first + Use read or traverse for deeper expansion. + + + link expansion is contextual, not a full-vault dump + diff --git a/docs/figure/reme-overview.svg b/docs/figure/reme-overview.svg new file mode 100644 index 00000000..a828f6ba --- /dev/null +++ b/docs/figure/reme-overview.svg @@ -0,0 +1,186 @@ + + ReMe overview + A hand-drawn style overview of ReMe, showing Auto Memory, Auto Resource, Auto Dream, Memory Search, and Memory as File. + + + + + + + + + + + + ReMe + A file-native memory loop: capture, consolidate, link, search, and proactively surface what matters. + + + + + Auto Memory + + Capture + conversation + + Write + daily card + + + + + + Auto Resource + + Read + resource file + + Write + daily card + + + + + + Auto Dream + Daily notes -> durable digest + + + Extract + changed files + + + Auto Link + dedupe + edges + + + Integrate + write digest + + + Proactive + interests.yaml + + + + + + + + + + Memory Search + Ask the vault, then follow the graph. + + + + Hybrid + Index + chunks + + wikilinks + + + + + Hybrid + Retrieval + BM25 + + vectors + + + + + Progressive + Expansion + outlinks + + inlinks + + + + + + + + + + + + Memory as File + Every memory is readable, editable, indexable, linkable, and auditable as files. + + + + + + + .json + + + reme_session/ + raw session logs + + + + + + + + .md + + + resource/ + raw material with source + + + + + + + + .md + + + daily/ + working memory cards + + + + + + + + .md + + + digest/ + long-term knowledge nodes + + + + + + diff --git a/docs/reme_design.md b/docs/old/reme_design_v2.md similarity index 100% rename from docs/reme_design.md rename to docs/old/reme_design_v2.md diff --git a/docs/reme_scene.md b/docs/old/reme_scene.md similarity index 100% rename from docs/reme_scene.md rename to docs/old/reme_scene.md diff --git a/docs/todo.md b/docs/old/todo.md similarity index 100% rename from docs/todo.md rename to docs/old/todo.md diff --git a/docs/zh/auto_dream.md b/docs/zh/auto_dream.md new file mode 100644 index 00000000..c6f95b58 --- /dev/null +++ b/docs/zh/auto_dream.md @@ -0,0 +1,196 @@ +# Auto Dream + +`auto_dream` 是 ReMe 的 daily 到 digest 的长期记忆沉淀流程。它扫描指定日期的 daily 输入,只处理相对上次 dream +发生变化的文件,把值得长期保留的内容抽取成 memory units,整合进 `digest/`,再生成当天可供主动提醒使用的 `interests.yaml`。 + +![Auto Dream flow](../figure/auto-dream.svg) + +它消费的 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/.md +daily//**/*.md +``` + +`daily//interests.yaml` 不作为抽取输入,避免上一轮主动主题反过来污染下一轮抽取。 + +主要输出有三类: + +| 输出 | 说明 | +|-------------------------------------|-------------------------------------| +| `digest/procedure/*.md` | 方法、流程、runbook、可执行经验。 | +| `digest/personal/*.md` | 用户、团队、项目相关的偏好、事实、长期上下文。 | +| `digest/wiki/*.md` | 通用知识、概念、观察、决策先例。 | +| `daily//interests.yaml` | 当天值得上层 Agent 主动关注的兴趣主题。 | +| `reme_metadata/file_catalog/dream*` | dream 专用 catalog,用于判断 daily 输入是否变化。 | + +## 四个阶段 + +### 1. Extract + +`dream_extract_step` 做三件事: + +1. 刷新当天索引页 `daily/.md`。 +2. 扫描 `daily/.md` 和 `daily//**/*.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//interests.yaml`。 + +它会读取: + +```text +daily//interests.yaml +daily//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//interests.yaml` 和 `daily/.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//...]]` 指回来源。链接写法遵循 +[Memory as File](./memory_as_file.md) 中的 vault-relative wikilink 语义。 + +`auto_dream` 不凭空生成总览。只有 daily 输入中确实出现、并被抽取为 unit 或 topic 的内容,才会进入 digest 或 +`interests.yaml`。 + +完整流程依赖 LLM 完成 Extract 和 Integrate。Topics 可以在没有 LLM 时做本地去重,但这不等于完整 dream 能离线运行。 diff --git a/docs/zh/auto_link.md b/docs/zh/auto_link.md new file mode 100644 index 00000000..44675c3f --- /dev/null +++ b/docs/zh/auto_link.md @@ -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//.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 边。 diff --git a/docs/zh/auto_memory.md b/docs/zh/auto_memory.md new file mode 100644 index 00000000..5229f0e3 --- /dev/null +++ b/docs/zh/auto_memory.md @@ -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/.md # 每段对话先成卡片 + ├─ step 2: daily/YYYY-MM-DD.md # 当天索引再串起来 + └─ source: reme_session/dialog/.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`;想看某段对话沉淀了什么,再进入对应的 `.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)。 diff --git a/docs/zh/auto_resource.md b/docs/zh/auto_resource.md new file mode 100644 index 00000000..02e49c80 --- /dev/null +++ b/docs/zh/auto_resource.md @@ -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/ + ├─ step 1: daily/YYYY-MM-DD/.md # 资源解读卡片 + ├─ step 2: daily/YYYY-MM-DD.md # 当天索引再串起来 + └─ source: resource/YYYY-MM-DD/ # 原始资源保留原位 +``` + +## 它记录什么 + +它不只是搬运文件内容,而是把资料里以后方便检索和理解的信息提炼出来: + +- 核心内容:这份资料主要讲什么。 +- 结构脉络:章节、表格、字段、数据组织方式。 +- 关键细节:重要数字、名称、日期、结论。 +- 背景用途:这份资料为什么存在,和当前工作有什么关系。 +- 可行动项:任务、截止时间、后续跟进。 + +简单说,它负责把“文件存档”变成“资料可用”。 + +## 原始资料入口 + +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)。 diff --git a/docs/zh/contributing.md b/docs/zh/contributing.md new file mode 100644 index 00000000..87396ff2 --- /dev/null +++ b/docs/zh/contributing.md @@ -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` 对齐。 +- 新增 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 +(): +``` + +常用类型: + +- `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 +(): +``` + +要求: + +- 类型使用 `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 的长期记忆更可读、可控、可维护。 diff --git a/docs/zh/framework.md b/docs/zh/framework.md new file mode 100644 index 00000000..395e91e6 --- /dev/null +++ b/docs/zh/framework.md @@ -0,0 +1,763 @@ +# ReMe 代码框架 + +## 1. 总览 + +ReMe 的运行时可以理解为:**配置驱动的 Application 把组件和 Job 装配起来,Service 把可服务的 Job 暴露给 CLI、HTTP 或 MCP,Job +再按顺序执行 Step**。 + +![ReMe 代码框架结构](../figure/framework-structure.svg) + +如果只想先运行和使用 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
reme/reme.py"] --> Client["Client
http / mcp"] + Client --> Service["Service
HTTP / MCP"] + Service --> App["Application
reme/application.py"] + App --> Jobs["Jobs
base / stream / background / cron"] + Jobs --> Steps["Steps
reme/steps/**"] + Steps --> Ctx["RuntimeContext
data + Response + stream queue"] + Steps --> Components["Components
store / graph / index / llm / agent / catalog"] + Components --> Vault["Vault
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 +/ + 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=` | 可传内置配置名或 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 /`,返回 `Response` JSON | +| `StreamJob` | `POST /`,返回 `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__
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)
解析 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
job(**kwargs)"] + Job --> Ctx["RuntimeContext
merged_kwargs"] + Ctx --> S1["Step 1
await step(context)"] + S1 --> D1["读写 context.data / response"] + D1 --> S2["Step 2
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
index_update_loop
resource_watch_loop
digest_watch_loop"] + Jobs --> Base["base
version / help / health_check
search / node_search / traverse / reindex
read / write / edit / delete / move / list / stat
daily_create / daily_list / daily_reindex
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
类名对应 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
reme uppercase text=hello"] --> HTTP["HTTP Client"] + HTTP --> Req["POST /uppercase"] + Req --> S["HttpService"] + S --> J["uppercase BaseJob
job(text='hello')"] + J --> Step["uppercase_step
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。 diff --git a/docs/zh/memory_as_file.md b/docs/zh/memory_as_file.md new file mode 100644 index 00000000..589b5ab8 --- /dev/null +++ b/docs/zh/memory_as_file.md @@ -0,0 +1,356 @@ +# Memory as File + +ReMe 的核心思想是:**Memory as File, File as Memory**。 + +![Memory as File model](../figure/memory-as-file.svg) + +**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 +/ +├── reme_metadata/ # 系统索引层;ReMe 索引、图谱、catalog 等持久状态,不作为人工编辑入口 +├── reme_session/ # 原始输入层;原始对话和 Agent session +│ ├── dialog/ +│ │ └── .jsonl # auto_memory 保存的对话消息 +│ ├── agentscope/ +│ │ └── .jsonl +│ └── claude_code/ +│ └── .jsonl +├── resource/ # 原始输入层;外部原始材料 +│ └── YYYY-MM-DD/ +│ └── . +├── daily/ # 浅加工层;按日期组织当天事实、对话摘要、资源解读 +│ ├── YYYY-MM-DD.md # 当天索引页 +│ └── YYYY-MM-DD/ +│ ├── .md # 对话加工后的 daily note +│ ├── .md # 资源加工后的 daily note +│ └── interests.yaml # auto_dream 产出的主动兴趣主题 +└── digest/ # 深加工层;可长期复用的个人事实、流程经验、知识节点 + ├── personal/ + │ └── .md # 用户画像、偏好、长期个人事实 + ├── procedure/ + │ └── .md # 流程、方法论、操作经验 + └── wiki/ + └── .md # 通用知识、概念、决策先例 +``` + +典型流转如下: + +```text +对话 + -> reme_session/dialog/.jsonl + -> daily/YYYY-MM-DD/.md + -> digest/personal | digest/procedure | digest/wiki + +外部资料 + -> resource/YYYY-MM-DD/. + -> daily/YYYY-MM-DD/.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]]` 从中间切开。 diff --git a/docs/zh/memory_search.md b/docs/zh/memory_search.md new file mode 100644 index 00000000..94fa972e --- /dev/null +++ b/docs/zh/memory_search.md @@ -0,0 +1,195 @@ +# Memory Search + +Memory Search 是 ReMe 的记忆检索入口。它先把 `daily/`、`digest/`、`resource/` 里的文件持续构建成可搜索的 chunk 索引和 +wikilink 图谱;查询时先召回最相关的片段,再沿着片段所在文件的双向链接展开上下文。 + +![Memory Search 索引与检索流程](../figure/memory-search.svg) + +文件分层、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`。 diff --git a/docs/zh/proactive.md b/docs/zh/proactive.md new file mode 100644 index 00000000..adf2b23b --- /dev/null +++ b/docs/zh/proactive.md @@ -0,0 +1,132 @@ +# Proactive + +`proactive` 是 ReMe 的主动记忆读取接口。它不重新分析 daily,也不调用 LLM,只读取 `auto_dream` 写出的当天兴趣主题: + +```text +daily//interests.yaml +``` + +上层 Agent 可以用它获取“今天值得主动关注什么”,再决定是否提醒、追问、推荐下一步或生成主动洞察。 + +`interests.yaml` 由 [Auto Dream](./auto_dream.md) 的 Topics 阶段生成;`proactive` 只负责读取和暴露结果。 + +## 配置入口 + +默认配置在 `reme/config/default.yaml`: + +```yaml +proactive: + backend: base + description: "Proactive: read daily//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//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//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` 是否为空。 diff --git a/docs/zh/quick_start.md b/docs/zh/quick_start.md new file mode 100644 index 00000000..90ba99cc --- /dev/null +++ b/docs/zh/quick_start.md @@ -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//.md`,并刷新 `daily/.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 /`: + +```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 +``` diff --git a/README_old.md b/old.md similarity index 99% rename from README_old.md rename to old.md index 8d4c947d..210a11ce 100644 --- a/README_old.md +++ b/old.md @@ -29,6 +29,14 @@ --- +## 📰 最新文章 + +| 日期 | 标题 | +|------------|----------------------------------------------------| +| 2026-03-30 | [CoPaw 上下文管理设计解析](docs/copaw_context_design_zh.md) | + +--- + 🧠 ReMe 是一个专为 **AI 智能体** 打造的记忆管理框架,同时提供基于[文件系统](#-基于文件的记忆系统-remelight) 和基于[向量库](#-基于向量库的记忆系统)的记忆系统。 diff --git a/pyproject.toml b/pyproject.toml index bd5cbb8f..8023c378 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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] diff --git a/reme/config/default.yaml b/reme/config/default.yaml index 158acc99..5f486064 100644 --- a/reme/config/default.yaml +++ b/reme/config/default.yaml @@ -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 diff --git a/reme/skills/qwenpaw_memory/SKILL.md b/reme/skills/qwenpaw_memory/SKILL.md new file mode 100644 index 00000000..b5a84c11 --- /dev/null +++ b/reme/skills/qwenpaw_memory/SKILL.md @@ -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` \ No newline at end of file