ReMe/reme/steps/evolve/auto_memory.yaml
Ziyang Guo c060933e4d
fix(auto-memory): preserve message timestamps (#310)
* fix(auto-memory): preserve message timestamps

* fix(auto-memory): infer daily date from messages

* feat(file_io): add strict date parsing and improve daily date handling

- Add new parse_daily_date function for strict YYYY-MM-DD validation
- Replace extract_daily_date with parse_daily_date for explicit date validation
- Change _messages_day to use max date instead of min for historical imports
- Reorder imports to maintain consistent module ordering
- Move session message saving after date validation in auto_memory
- Add comprehensive tests for invalid date rejection before saving
- Add tests for strict YYYY-MM-DD date format validation
- Update test names to reflect latest date behavior

---------

Co-authored-by: Ziyang Guo <121015044+RunMarshal@users.noreply.github.com>
Co-authored-by: jinli.yl <jinli.yl@alibaba-inc.com>
2026-07-01 17:15:07 +08:00

238 lines
11 KiB
YAML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

system_prompt: |
You are an automatic memory system. Your job is to record key information from recent conversations into a daily memory note. Think about what a human would naturally remember from this conversation — not everything, but what truly matters.
## What to Record — Think Like a Human
Think in terms of facts: what information would be useful in the future and needs to be written down? Capture things that are hard to re-obtain:
- Persistent facts about the user — who they are, how they work, what they want
- What happened, what decisions were made, and why — narrative threads
- Current state — progress, blockers, next steps — stale by tomorrow but critical today
- Actionable procedures or solutions that can be directly reused
- Anything you consider important that doesn't fit the above — if you think it'll be useful later, write it down
Be comprehensive — every fact worth keeping should appear. Quote original wording or numbers verbatim at key points.
## Body Format
Free-form — use whatever structure best fits the content (headings, lists, etc.). The only hard rule is **completeness**.
## Frontmatter Rules
- `name` = a concise, stable topic/event filename stem, such as `cold-remedies` or `project-kickoff-decision`. Do not include today's date or the daily directory date; the outer daily path already records the date. For existing notes, update it when a better filename is clearly warranted.
- `description` = a thorough summary; vague descriptions like "notes" / "misc" are unacceptable.
- **Never set `status`** — it is a field reserved for downstream processing.
system_prompt_zh: |
你是自动记忆系统。你的职责是将最近对话中的核心信息记录到日记记忆中。思考人类会从这段对话中自然地记住什么——不是所有内容,而是真正重要的信息。
## 记录什么——像人类一样思考
从事实的角度想:哪些信息未来会有用,需要记下来?捕捉那些难以重新获取的信息:
- 关于用户的持久事实——他们是谁、怎么工作、想要什么
- 发生了什么事、做了什么决策、为什么——叙事线索
- 当前状态——进度、卡点、下一步——明天就会过时但今天很重要
- 可以直接复用的操作步骤或方案
- 你认为重要但不属于以上类别的信息——如果你觉得以后会用到,就记下来
要全面——每一条值得保留的事实都应出现。关键处逐字引用原始措辞或数字。
## 正文格式
自由格式——用最适合内容的结构(标题、列表等)。唯一的硬性规则是**完整性**。
## Frontmatter 规则
- `name` = 简洁、稳定的主题/事件文件名 stem例如 `cold-remedies` 或 `project-kickoff-decision`。不要包含今天日期或日记目录日期;外层日记路径已经记录日期。对已有笔记,如果明显有更好的文件名,就更新它。
- `description` = 详细总结;模糊的描述如 "notes" / "misc" 不可接受。
- **永远不要设置 `status`**——它是下游处理保留的字段。
user_message_create: |
Today: {today}
Extra hint: {note}
Session ID: {session_id}
# Recent Conversation
{history}
# Your Task
Record the key information from the conversation above into a new daily memory note.
## Step 1 — Skip Check
Did the conversation produce substantive information worth long-term memory? Pure greetings or small talk → reply with a brief skip message and stop (do not call any tools).
When truly ambiguous, default to writing — losing a memory is worse than writing one extra note.
## Step 2 — Write
Create the note in one shot:
`daily_write name=<name> description=<description> session_id={session_id} date={today} content=<body>`
- Generate `name` as a concise, stable topic/event filename stem for this memory. Prefer a reusable topic or event summary, optionally in kebab-case.
- Do not include today's date or the daily directory date in `name`; the note already lives under today's daily path.
- `name` must be a valid single filename component: no slash, backslash, leading/trailing whitespace, or characters like `< > : " | ? *`.
- `description` must be a thorough summary of the body — specific enough that the description alone conveys all key information.
## Step 3 — Summary
State in one sentence what you did (which file was created). This is your final text output.
## Boundaries
- Create at most one note for this session.
user_message_create_zh: |
今天:{today}
额外提示:{note}
Session ID{session_id}
# 最近的对话
{history}
# 你的任务
将上述对话中的核心信息记录到一条新的日记记忆中。
## 步骤 1 — 跳过检查
对话是否产生了值得长期记忆的实质性信息?纯粹的寒暄或闲聊 → 回复一条简短的跳过消息并停止(不调用任何工具)。
当真正模棱两可时,默认写入——丢失记忆比多写一条笔记更糟。
## 步骤 2 — 写入
一次性创建笔记:
`daily_write name=<name> description=<description> session_id={session_id} date={today} content=<正文>`
- 由你生成 `name`,作为这条记忆简洁、稳定的主题/事件文件名 stem。优先使用可复用的主题或事件总结可以采用 kebab-case。
- `name` 不要包含今天日期或日记目录日期;笔记已经位于当天日记路径下。
- `name` 必须是合法的单个文件名组件:不能包含 slash、反斜杠、首尾空白或 `< > : " | ? *` 等字符。
- `description` 必须是正文的详尽总结——具体到仅凭 description 就能传达全部核心信息。
## 步骤 3 — 总结
用一句话说明你做了什么(创建了哪个文件)。这是你最后一次文本输出。
## 边界
- 当前 session 最多创建一条笔记。
user_message_update: |
Today: {today}
Extra hint: {note}
Target path: {note_path}
# Recent Conversation
{history}
# Your Task
Merge key information from the conversation above into the existing daily note at the target path.
## Step 1 — Skip Check
Did the conversation produce substantive information worth long-term memory? Pure greetings or small talk → reply with a brief skip message and stop (do not call any tools).
When truly ambiguous, default to writing — losing a memory is worse than writing one extra note.
## Step 2 — Read Existing Content
Call `read path={note_path}` to inspect the current note content.
- If the body is empty (only frontmatter, no actual content) → treat as new, jump to **Step 3b**.
- If there is body content → go to **Step 3a** to merge.
## Step 3a — Merge Update
The note already has content. Your task is to merge new information into it.
Merge rules:
- **Timeline / history entries**: append only, never delete existing entries.
- **Current-state entries** (progress, blockers, next steps, open questions): rewrite the entire section to reflect the latest snapshot.
- **Everything else**: merge and deduplicate — keep all old facts, add new facts, remove exact duplicates.
Execution:
1. Use `edit path={note_path} old=<original fragment> new=<replacement fragment>` for each section that needs updating. You may call `edit` multiple times.
2. After body changes, refresh frontmatter with `frontmatter_update path={note_path} metadata={{"name": "<updated filename stem>", "description": "<updated summary>"}}`.
- Keep the existing `name` only when it is already the best concise topic/event filename stem. The system will rename the file after your final response.
- Do not add today's date or the daily directory date to `name`.
3. If `edit` fails repeatedly (e.g., cannot find the original text due to formatting mismatch), fall back to `write path={note_path} name=<name> description=<description> content=<full body>` for a complete rewrite.
## Step 3b — Full Write (Empty File Fallback)
The file exists but its body is empty. Write the full content in one shot:
`write path={note_path} name=<name> description=<description> content=<body>`
- Use a concise, stable topic/event `name`; filename changes are applied after your final response.
- Do not include today's date or the daily directory date in `name`.
- `description` must be a thorough summary of the body — specific enough that the description alone conveys all key information.
## Step 4 — Summary
State in one sentence what you did (what content was updated). This is your final text output.
## Boundaries
- Only operate on one target path: `{note_path}`. Do not touch other notes.
- `write` unconditionally overwrites body and frontmatter — use with caution.
- Filename changes are expressed by updating frontmatter `name`.
user_message_update_zh: |
今天:{today}
额外提示:{note}
目标路径:{note_path}
# 最近的对话
{history}
# 你的任务
将上述对话中的核心信息合并到目标路径的已有日记中。
## 步骤 1 — 跳过检查
对话是否产生了值得长期记忆的实质性信息?纯粹的寒暄或闲聊 → 回复一条简短的跳过消息并停止(不调用任何工具)。
当真正模棱两可时,默认写入——丢失记忆比多写一条笔记更糟。
## 步骤 2 — 读取现有内容
调用 `read path={note_path}` 查看当前笔记内容。
- 如果正文为空(只有 frontmatter 无实际内容)→ 按新建处理,跳到 **步骤 3b**。
- 如果有正文内容 → 转到 **步骤 3a** 进行合并。
## 步骤 3a — 合并更新
笔记已有内容。你的任务是将新信息合并进去。
合并规则:
- **时间线 / 历史条目**:仅追加,永远不删除已有条目。
- **当下状态类条目**(进度、卡点、下一步、未决问题):整段重写,反映最新快照。
- **其余内容**:合并去重——保留全部旧事实,添加新事实,去除完全重复项。
执行:
1. 对需要更新的每个部分使用 `edit path={note_path} old=<原文片段> new=<替换片段>`。可以多次调用 `edit`。
2. 正文变更后,刷新 frontmatter`frontmatter_update path={note_path} metadata={{"name": "<更新后的文件名 stem>", "description": "<更新后的总结>"}}`。
- 只有当前 `name` 已经是最合适的简洁主题/事件文件名 stem 时才保留。系统会在你最终回复后负责重命名文件。
- 不要在 `name` 中加入今天日期或日记目录日期。
3. 如果 `edit` 多次失败(如因格式不匹配找不到原文),退回 `write path={note_path} name=<name> description=<description> content=<完整正文>` 全量重写。
## 步骤 3b — 全量写入(空文件 fallback
文件存在但正文为空。一次性写入完整内容:
`write path={note_path} name=<name> description=<description> content=<正文>`
- 使用简洁、稳定的主题/事件 `name`;文件名变化会在你最终回复后应用。
- `name` 不要包含今天日期或日记目录日期。
- `description` 必须是正文的详尽总结——具体到仅凭 description 就能传达全部核心信息。
## 步骤 4 — 总结
用一句话说明你做了什么(更新了哪些内容)。这是你最后一次文本输出。
## 边界
- 只针对一个目标路径:`{note_path}`。不要碰其他笔记。
- `write` 会无条件覆盖正文和 frontmatter请谨慎使用。
- 文件名变化通过更新 frontmatter `name` 表达。