diff --git a/docs/docs.json b/docs/docs.json
index 460727a7..0d18e321 100644
--- a/docs/docs.json
+++ b/docs/docs.json
@@ -179,7 +179,8 @@
"pages": [
"memory/index",
"memory/context",
- "memory/deep-dream"
+ "memory/deep-dream",
+ "memory/self-evolution"
]
}
]
@@ -393,7 +394,8 @@
"pages": [
"zh/memory/index",
"zh/memory/context",
- "zh/memory/deep-dream"
+ "zh/memory/deep-dream",
+ "zh/memory/self-evolution"
]
}
]
@@ -607,7 +609,8 @@
"pages": [
"ja/memory/index",
"ja/memory/context",
- "ja/memory/deep-dream"
+ "ja/memory/deep-dream",
+ "ja/memory/self-evolution"
]
}
]
diff --git a/docs/ja/memory/self-evolution.mdx b/docs/ja/memory/self-evolution.mdx
new file mode 100644
index 00000000..c25fb704
--- /dev/null
+++ b/docs/ja/memory/self-evolution.mdx
@@ -0,0 +1,78 @@
+---
+title: 自律進化
+description: Self-Evolution — 会話がアイドル状態になった後に振り返り、記憶を蓄積し、スキルを改善し、未完了のタスクに対応する
+---
+
+## 機能概要
+
+### はじめに
+
+自律進化(Self-Evolution)は、Agent が単発のタスクをこなすだけでなく、あなたとのやり取りを通じて成長し続けられるようにする仕組みです。会話が一段落すると、Agent は静かに振り返りを行います。覚えておくべきことを長期記憶に保存し、スキルで見つかった問題を修正し、やり残したタスクを引き継いで進めます。使い込むほど、Agent はあなたの好みを理解し、同じ失敗を繰り返さなくなり、自分から物事を仕上げるようになります。これらはすべてバックグラウンドで静かに行われ、実際に何かを行ったときだけ簡潔に知らせます。
+
+> 自律進化は[夢境蒸留](/ja/memory/deep-dream)と補完し合います。夢境蒸留が記憶そのものを整理するのに対し、自律進化はさらに一歩進んでスキルを改善し、未完了のタスクを前に進め、日々の利用を通じて Agent の能力を磨きます。
+
+### 3 つの目標
+
+自律進化は次の 3 つを軸に動きます:
+
+| 目標 | 説明 |
+| --- | --- |
+| **記憶の蓄積** | 会話中の重要な好み、決定、事実を記憶に補い、メインの会話の取りこぼしを補完します |
+| **スキルの改善** | スキルの利用中に問題(設定の誤りや手順の欠落など)が見つかったら、メモを残すだけでなくスキルファイルを直接修正します。必要に応じて新しいスキルも作成します |
+| **未完了タスクへの対応** | 会話に残ったやるべきことを見つけ、可能なときにその場で完了させます |
+
+振り返りが終わり、実際に変更を加えた場合は、Agent が「何を学び、どこを調整したか」を会話の中で一言で伝えるので、元に戻すかどうかを判断できます。
+
+## 使い方
+
+### トリガーのタイミング
+
+自律進化は定時実行ではなく、**会話が自然に終わってアイドル状態になった後**にのみ起動するため、進行中のやり取りを妨げることはありません。次の 2 つの条件を同時に満たす必要があります:
+
+- **会話がアイドル状態**:最後のやり取りから、設定したアイドル時間(デフォルトは 15 分)以上が経過している
+- **振り返るだけの内容がある**:前回の進化から十分なターン数が蓄積されている、またはコンテキストが容量の上限に近づいている
+
+両方の条件を満たしたときにのみ振り返りが始まります。これにより、振り返る価値のある内容を確保しつつ、会話の途中で邪魔をしないようにしています。
+
+### 関連設定
+
+自律進化はデフォルトでは無効です。Web コンソールの「設定 → Agent 設定」(「ディープシンキング」の下)にあるスイッチで有効にできるほか、設定ファイルで調整することもできます:
+
+| パラメータ | 説明 | デフォルト値 |
+| --- | --- | --- |
+| `self_evolution_enabled` | 自律進化を有効にするかどうか | `false` |
+| `self_evolution_idle_minutes` | 会話がアイドル状態になってからトリガーするまでの時間(分) | `15` |
+| `self_evolution_min_turns` | トリガーに必要な最小会話ターン数 | `6` |
+
+
+ Web コンソールでは有効・無効のスイッチのみを提供しています。アイドル時間やターン数のしきい値を変更したい場合は、設定ファイルを編集してください。変更は即時に反映され、再起動は不要です。
+
+
+### 進化の記録
+
+各振り返りは日付ごとに `memory/evolution/YYYY-MM-DD.md` に記録され、Web コンソールの「メモリ管理 → 自律進化」タブで確認できます。このタブには自律進化の記録と夢日記の両方がまとめられており、Agent の成長の軌跡を一箇所で振り返ることができます。
+
+### 元に戻す方法
+
+ある振り返りの変更に納得できない場合は、会話の中で Agent に「直前の変更を取り消して」と伝えるだけで、振り返り前のバックアップから該当ファイルを復元します。各振り返りはそれぞれ独立したバックアップを持つため、互いに干渉することはありません。
+
+## 設計
+
+自律進化はシステムの既存の機能を再利用しており、軽量に保たれています:
+
+- **隔離実行**:各振り返りは独立した短命のタスクとして実行されます。メインの会話と同じモデルを使いますが、ツールは制限されています(コンテキストの読み取りと、記憶およびスキルファイルの編集のみ可能)。メインの会話のコンテキストを汚さず、その動作にも影響しません。
+- **バックアップによる取り消し**:振り返り前に該当ファイルのスナップショットを取り、取り消し時にそのスナップショットから復元するため、すべての変更が追跡可能で元に戻せます。
+- **変更検知**:振り返り後にファイルのスナップショットを比較して実際に変更があったかを確認し、それをもとに通知するかどうかを判断します。これにより「何もしなければ通知しない」ことを仕組みとして保証します。
+
+### 抑制と安全性
+
+自律進化は、必要なときに動き、それ以外のときは邪魔をしないように設計されています:
+
+| 仕組み | 説明 |
+| --- | --- |
+| **何もしなければ通知しない** | 振り返りで実際の変更がなければ、静かなままで何も送りません |
+| **アイドル時のみトリガー** | 会話がアイドル状態になったときだけ実行し、進行中の会話を妨げません |
+| **変更を元に戻せる** | 振り返りごとに事前にバックアップを取るため、納得できない結果は取り消せます |
+| **組み込みスキルの保護** | 製品に付属する組み込みスキルは保護され、変更されません |
+| **ワークスペースに限定** | すべての読み書きはワークスペース内に限定され、他のシステムファイルには触れません |
+| **バックグラウンド実行** | 振り返りはバックグラウンドで実行され、通常の返信を妨げません |
diff --git a/docs/memory/self-evolution.mdx b/docs/memory/self-evolution.mdx
new file mode 100644
index 00000000..d06855f9
--- /dev/null
+++ b/docs/memory/self-evolution.mdx
@@ -0,0 +1,78 @@
+---
+title: Self-Evolution
+description: Self-Evolution — review a conversation after it goes idle to consolidate memory, improve skills, and follow up on unfinished tasks
+---
+
+## Overview
+
+### Introduction
+
+Self-Evolution lets the Agent do more than finish one task at a time; it keeps improving as it works with you. After a conversation winds down, it quietly reviews what just happened: it saves anything worth remembering into long-term memory, fixes problems that surfaced in a skill, and picks up tasks that were left unfinished. Over time the Agent learns your preferences, repeats fewer mistakes, and gets better at wrapping things up on its own. All of this runs in the background, and it only tells you when it actually did something.
+
+> Self-Evolution complements [Deep Dream](/memory/deep-dream). Deep Dream organizes memory itself, while Self-Evolution goes a step further to improve skills and push unfinished tasks forward, sharpening the Agent's abilities through everyday use.
+
+### Three Goals
+
+Self-Evolution focuses on three things:
+
+| Goal | Description |
+| --- | --- |
+| **Consolidate memory** | Record important preferences, decisions, and facts from the conversation, filling in what the main chat may have missed |
+| **Improve skills** | When a skill shows a problem in use (such as a wrong setting or a missing step), fix the skill file directly instead of just noting it; create a new skill when one is genuinely needed |
+| **Follow up on unfinished tasks** | Spot the to-dos left in a conversation and finish them when possible |
+
+Once a review is done, if it actually changed something, the Agent tells you in a single line what it just learned and what it adjusted, so you can decide whether to roll it back.
+
+## Usage
+
+### When It Triggers
+
+Self-Evolution does not run on a fixed schedule. It only kicks in **after a conversation naturally ends and goes idle**, so it never interrupts an ongoing exchange. Two conditions must both hold:
+
+- **The conversation is idle**: more time has passed since the last interaction than the configured idle window (15 minutes by default)
+- **There is enough to review**: enough turns have accumulated since the last evolution, or the context is close to its capacity
+
+Only when both are met does a review begin. This makes sure there is something worth reviewing while keeping it from bothering you mid-conversation.
+
+### Configuration
+
+Self-Evolution is off by default. You can turn it on with the toggle in the Web console under **Settings → Agent Config** (below "Deep Thinking"), or adjust it in the config file:
+
+| Parameter | Description | Default |
+| --- | --- | --- |
+| `self_evolution_enabled` | Whether Self-Evolution is enabled | `false` |
+| `self_evolution_idle_minutes` | How long the conversation must be idle before it triggers (minutes) | `15` |
+| `self_evolution_min_turns` | Minimum conversation turns required to trigger | `6` |
+
+
+ The Web console only exposes the on/off toggle. To change the idle window or the turn threshold, edit the config file. Changes take effect immediately, with no restart needed.
+
+
+### Evolution Records
+
+Each review is recorded by date in `memory/evolution/YYYY-MM-DD.md`, viewable in the Web console under the **Memory → Self-Evolution** tab. That tab gathers both self-evolution records and dream diaries in one place, so you can look back on how the Agent has grown.
+
+### Rolling Back
+
+If you disagree with a change from a review, just tell the Agent in chat to undo the last change. It restores the affected files from the backup taken before the review. Every review keeps its own backup, so they never interfere with each other.
+
+## Design
+
+Self-Evolution reuses what the system already has, which keeps it lightweight:
+
+- **Isolated execution**: each review runs as a separate, short-lived task. It uses the same model as the main chat but with a restricted toolset (it can only read context and edit memory and skill files). It does not pollute the main chat's context or affect its performance.
+- **Backup-based undo**: the relevant files are snapshotted before a review and restored from that snapshot on undo, so every change is traceable and reversible.
+- **Change detection**: after a review, the system compares file snapshots to see whether anything actually changed, and uses that to decide whether to notify you. This is how it guarantees, at the engineering level, that no work means no message.
+
+### Restraint and Safety
+
+Self-Evolution is built to act when needed and stay out of the way otherwise:
+
+| Mechanism | Description |
+| --- | --- |
+| **No work, no notification** | If a review produces no real change, it stays silent and sends nothing |
+| **Triggers only when idle** | It runs only after the conversation is idle, never interrupting an active one |
+| **Reversible changes** | A backup is taken before every review, so you can undo a result you do not like |
+| **Built-in skills protected** | The skills shipped with the product are protected and never modified |
+| **Workspace-scoped** | All reads and writes stay inside the workspace and never touch other system files |
+| **Runs in the background** | Reviews run in the background and do not block normal replies |
diff --git a/docs/zh/memory/self-evolution.mdx b/docs/zh/memory/self-evolution.mdx
new file mode 100644
index 00000000..1d04b28f
--- /dev/null
+++ b/docs/zh/memory/self-evolution.mdx
@@ -0,0 +1,78 @@
+---
+title: 自主进化
+description: Self-Evolution — 会话空闲后自动复盘,沉淀记忆、优化技能、处理未完成事项
+---
+
+## 功能介绍
+
+### 简介
+
+自主进化(Self-Evolution)让 Agent 不止于"完成单次任务",而是能在与你的相处中持续成长。每段对话告一段落后,它会自动"回头复盘"一次:把值得记住的沉淀为长期记忆、把使用中暴露的问题修进技能、把没做完的事情接着推进。久而久之,Agent 会越来越懂你的偏好、越来越少重复犯错、越来越主动地把事情收尾,而这一切都在后台静默完成,只有真正做了事情时才会简短地告诉你。
+
+> 它与[梦境蒸馏](/zh/memory/deep-dream)互补:梦境蒸馏负责整理记忆本身,自主进化则在记忆之外,进一步优化技能、推进未完成的任务,让 Agent 的能力随使用不断打磨。
+
+### 三个目标
+
+自主进化围绕三件事工作:
+
+| 目标 | 说明 |
+| --- | --- |
+| **沉淀记忆** | 把对话中重要的偏好、决策、事实补记到记忆中,作为主对话的查缺补漏 |
+| **优化技能** | 当某个技能在使用中暴露出问题(如配置错误、步骤缺失),直接修正技能文件,而不只是记一笔;也可在需要时创建新技能 |
+| **处理未完成事项** | 识别对话中遗留的待办,在能完成时直接完成 |
+
+复盘完成后,如果确实做了改动,Agent 会在对话中用一句话告诉你"刚刚自主学习了什么、调整了哪里",方便你判断是否需要回滚。
+
+## 如何使用
+
+### 触发时机
+
+自主进化不是定时执行,而是在**一段对话自然结束、进入空闲后**才触发,避免打断正在进行的交流。需要同时满足:
+
+- **对话已空闲** — 距离最后一次互动超过设定的空闲时长(默认 15 分钟)
+- **对话有足够内容** — 自上次进化以来累积了足够轮次,或上下文已接近容量上限
+
+只有两个条件都满足,才会启动一次复盘。这样既保证有足够的内容值得复盘,又不会在你还在对话时打扰你。
+
+### 相关配置
+
+自主进化默认关闭,可在 Web 控制台「配置 → Agent 配置」中通过开关启用(位于"深度思考"下方),也可在配置文件中调整:
+
+| 参数 | 说明 | 默认值 |
+| --- | --- | --- |
+| `self_evolution_enabled` | 是否启用自主进化 | `false` |
+| `self_evolution_idle_minutes` | 对话空闲多久后触发(分钟) | `15` |
+| `self_evolution_min_turns` | 触发所需的最少对话轮次 | `6` |
+
+
+ Web 控制台只提供启用开关,若需调整空闲时长或轮次阈值,请编辑配置文件。修改后即时生效,无需重启。
+
+
+### 进化记录
+
+每次进化的过程和结果会按日期记录在 `memory/evolution/YYYY-MM-DD.md` 中,可在 Web 控制台的「记忆管理 → 自主进化」tab 中查看。该 tab 同时汇总了自主进化记录与梦境日记,方便统一回顾 Agent 的成长轨迹。
+
+### 如何回滚
+
+如果你不认同某次进化的改动,直接在对话中告诉 Agent "把刚才的改动撤销"即可,它会根据进化前的备份还原相关文件。每次进化的改动都有独立备份,互不影响。
+
+## 实现设计
+
+自主进化复用了系统已有的能力,保持轻量:
+
+- **隔离执行**:每次复盘都启动一个独立的、临时的复盘任务,使用与主对话相同的模型,但拥有受限的工具集(只能读上下文、改记忆与技能文件)。它不会污染主对话的上下文,也不会影响主对话的性能。
+- **基于备份的撤销**:进化前对相关文件做快照备份,撤销时按备份还原,因此每一次改动都可追溯、可逆。
+- **改动检测**:复盘结束后通过对比文件快照判断是否真的有改动,以此决定要不要通知你,从工程上保证"没做事就不打扰"。
+
+### 克制与安全
+
+自主进化的设计原则是"必要时执行,减少打扰":
+
+| 机制 | 说明 |
+| --- | --- |
+| **没做事不通知** | 如果复盘后没有任何实际改动,全程静默,不产生任何通知 |
+| **空闲才触发** | 仅在对话空闲后运行,绝不打断正在进行的对话 |
+| **改动可回滚** | 每次进化前自动备份,若对结果不满意,可一键撤销本次改动 |
+| **保护内置技能** | 产品自带的内置技能受保护,进化过程不会改动 |
+| **限定工作空间** | 所有读写都限定在工作空间内,不会触碰系统其他文件 |
+| **后台异步** | 复盘在后台进行,不阻塞正常对话回复 |
diff --git a/tests/test_evolution.py b/tests/test_evolution.py
index 7efe770e..4ebc47e9 100644
--- a/tests/test_evolution.py
+++ b/tests/test_evolution.py
@@ -338,6 +338,114 @@ def scenario_skill_wrong_config():
}
+def scenario_skill_create():
+ """A reusable, repeatable workflow emerges that no skill covers, and the
+ user explicitly asks to make it permanent -> CREATE a new skill.
+ """
+ return {
+ "name": "技能-新增 (should create a new skill)",
+ "goal": "skill",
+ "turns": [
+ ("每周一帮我把客户反馈整理成晨会简报:先按「严重/一般/建议」给反馈分类,再统计每类数量,最后写一段 3 句话的总结,发我",
+ "好的,本周客户反馈晨会简报:\n\n## 严重 (2)\n- 支付失败\n- 数据丢失\n## 一般 (3)\n- 加载慢…\n## 建议 (1)\n- 增加暗色模式\n\n总结:本周以稳定性问题为主,建议优先排查支付与数据链路,其余可排期跟进。"),
+ ("很好,就是这个格式。上周我也是这么让你弄的", "嗯,分类+计数+三句总结这套流程挺顺手。"),
+ ("对,这个我每周一都要用,你能不能把这套流程固化下来,以后我直接说『生成客户反馈晨会简报』你就照这个来",
+ "可以,我把这套「客户反馈晨会简报」流程固化成一个可复用的技能。"),
+ ],
+ "scripted": (
+ "我刚做了一次自我学习。\n"
+ "- 学到:你每周一都要按「分类+计数+三句总结」生成客户反馈晨会简报\n"
+ "- 调整:新建了「客户反馈晨会简报」技能,固化这套流程\n"
+ "如果不对,回复「撤销上次学习」即可。"
+ ),
+ "on_edit": _create_briefing_skill,
+ "expect_evolved": True,
+ "expect_new_skill": True,
+ }
+
+
+def scenario_skill_create_implicit():
+ """A complex, clearly-structured multi-step workflow is executed once. The
+ user NEVER says "I do this weekly" or "make this a skill" — the agent must
+ 判断 on its own that this is a reusable procedure worth capturing.
+
+ This is the realistic, harder case: no explicit instruction to create a
+ skill, only an obviously repeatable workflow.
+ """
+ return {
+ "name": "技能-隐式新增 (complex workflow, user never asks to save)",
+ "goal": "skill",
+ "turns": [
+ ("帮我做一份竞品调研:对比一下 Notion、飞书文档、语雀这三个产品",
+ "好的,我按一套固定方法来做竞品调研:\n1. 先确定对比维度:定价、协作能力、模板生态、API 开放度、本地化;\n2. 逐个产品按维度收集信息;\n3. 做成对比表;\n4. 给出结论和选型建议。\n\n(随后产出了完整的五维度对比表 + 结论)"),
+ ("不错。再帮我用同样的方法调研一下 Slack、企业微信、钉钉",
+ "好的,沿用刚才那套方法(定价/协作/模板/API/本地化 五维度 → 收集 → 对比表 → 结论):\n\n(产出了第二份五维度对比表 + 选型建议)"),
+ ("可以,结论挺清楚的", "嗯,这套五维度对比的方法做下来结构很清楚。"),
+ ],
+ # In real mode the model decides on its own. The scripted side effect
+ # only wires stub mode; it emulates capturing the procedure as a skill.
+ "scripted": (
+ "我刚做了一次自我学习。\n"
+ "- 学到:你做竞品调研有一套固定方法(五维度对比 → 收集 → 对比表 → 结论)\n"
+ "- 调整:把这套竞品调研流程固化成了一个可复用技能\n"
+ "如果不对,回复「撤销上次学习」即可。"
+ ),
+ "on_edit": _create_competitor_skill,
+ "expect_evolved": True,
+ "expect_new_skill": True,
+ }
+
+
+def _create_competitor_skill(ws):
+ """Stub side effect: emulate capturing the competitor-research procedure."""
+ d = ws / "skills" / "competitor-research"
+ d.mkdir(parents=True, exist_ok=True)
+ (d / "SKILL.md").write_text(
+ "# Competitor Research\n\n"
+ "Compare a set of products with a fixed methodology.\n\n"
+ "## Steps\n"
+ "1. Fix the comparison dimensions (pricing, collaboration, templates, API, localization).\n"
+ "2. Collect info per product across each dimension.\n"
+ "3. Build a comparison table.\n"
+ "4. Give a conclusion and recommendation.\n",
+ encoding="utf-8",
+ )
+
+
+def scenario_skill_no_create():
+ """A one-off, novel task with no sign of recurrence -> must NOT create a
+ skill (and ideally stay silent). Guards against over-eager skill creation.
+ """
+ return {
+ "name": "技能-不应新增 (one-off task, must NOT create skill)",
+ "goal": "none",
+ "turns": [
+ ("帮我把这段话翻译成英文:今晚的庆功宴改到 8 点", "翻译:The celebration dinner tonight is moved to 8 PM."),
+ ("谢谢", "不客气。"),
+ ("嗯没事了", "好的,随时找我。"),
+ ],
+ "scripted": "[SILENT]",
+ "on_edit": None,
+ "expect_evolved": False,
+ "expect_no_new_skill": True,
+ }
+
+
+def _create_briefing_skill(ws):
+ """Stub side effect: emulate creating a new skill under workspace skills/."""
+ d = ws / "skills" / "customer-feedback-briefing"
+ d.mkdir(parents=True, exist_ok=True)
+ (d / "SKILL.md").write_text(
+ "# Customer Feedback Briefing\n\n"
+ "Turn raw customer feedback into a standup briefing.\n\n"
+ "## Steps\n"
+ "1. Classify each item as 严重/一般/建议.\n"
+ "2. Count items per category.\n"
+ "3. Write a 3-sentence summary.\n",
+ encoding="utf-8",
+ )
+
+
def scenario_unfinished_task():
"""A promised deliverable was not produced -> finish it now via tools."""
def edit(ws):
@@ -372,9 +480,24 @@ SCENARIOS = [
scenario_skill_gap,
scenario_skill_error,
scenario_skill_wrong_config,
+ scenario_skill_create,
+ scenario_skill_create_implicit,
+ scenario_skill_no_create,
scenario_unfinished_task,
]
+# Skill directories present in a fresh workspace; anything beyond these that
+# appears after a pass is a newly-created skill.
+_SEED_SKILLS = {"weekly-report", "expense-tracker", "data-fetch", "image-generation"}
+
+
+def _new_skill_dirs(ws: Path) -> set:
+ """Skill directories created beyond the seeded set."""
+ skills_dir = ws / "skills"
+ if not skills_dir.exists():
+ return set()
+ return {p.name for p in skills_dir.iterdir() if p.is_dir()} - _SEED_SKILLS
+
# ---------------------------------------------------------------------------
# Runner (stub mode)
@@ -431,6 +554,9 @@ def run_stub():
if txt not in content:
ok = False
errs.append("skill missing expected content")
+ if sc.get("expect_new_skill") and not _new_skill_dirs(ws):
+ ok = False
+ errs.append("expected a new skill to be created")
# notify happened
if not channel.sent:
ok = False
@@ -457,6 +583,9 @@ def run_stub():
if bridge.injected:
ok = False
errs.append("injected record on SILENT")
+ if sc.get("expect_no_new_skill") and _new_skill_dirs(ws):
+ ok = False
+ errs.append(f"unexpected new skill created: {_new_skill_dirs(ws)}")
ex._notify_user = orig_notify
@@ -626,6 +755,15 @@ def run_real():
else:
print(" (静默,未做任何改动)")
+ new_skills = _new_skill_dirs(ws)
+ if new_skills:
+ print(f" 新建技能: {', '.join(sorted(new_skills))}")
+ # Surface mismatches against the scenario's skill expectation.
+ if sc.get("expect_new_skill") and not new_skills:
+ print(" ⚠ 预期新建技能,但未创建")
+ if sc.get("expect_no_new_skill") and new_skills:
+ print(" ⚠ 不应新建技能,但创建了")
+
print("\n【给用户的回复】")
if captured["reply"]:
for line in captured["reply"].splitlines():