跳到主要内容

自进化:空闲时回看对话、悄悄成长

30 秒导读: 这一章讲 CowAgent 最有个性的子系统——Self-Evolution(自进化)。它像一位在你离开后才悄悄整理笔记的助理:会话空闲一段时间后,后台自动派一个隔离的审查 agent 回看这段新对话,判断有没有值得长期学下来的东西(补一个技能、完成一件没做完的事、极少数时候补条记忆),动手改完再简短告诉你一声;绝大多数时候,它什么都不做、什么都不说。

本章属于 CowAgent 系列的一章,和它并列的还有:核心循环系统提示词上下文健壮性记忆与知识技能与工具。自进化复用了这些子系统(尤其是记忆和技能),而不是另造一套,读的时候可以随时跳回去。


1. 这是什么(零基础也能懂)

一句话定义: 自进化 = 会话空闲时,助手在后台自动回看最近的对话,发现"下次能做得更好"的地方就悄悄改掉(改技能 / 补记忆 / 完成没做完的活),大多数时候保持沉默。

它想解决什么问题。 普通聊天 agent 的"学习"发生在对话进行中——你说什么、它记什么。但很多"该长期改进"的信号,要等一整段对话结束、回头看才看得出来:比如某个技能的步骤总是漏一环,或者你让它写的文件它答应了却忘了写。自进化就是补上这个"事后复盘"的环节。

它给谁用。 面向 CowAgent 的部署者/终端用户。它默认关闭(见 agent/evolution/config.py:15 DEFAULT_ENABLED = False),要在 config.json 里打开 self_evolution_enabled 才生效。

它能做的四类事(按重要性排序):

类别干什么频率
SKILL修补一个用过的技能,或把一段可复用工作流沉淀成新技能主力价值,信号清晰就动手
未完成任务把"答应了却没交付、且材料都齐了"的活现在补做出来主力价值
MEMORY补一条主 agent 漏掉的持久事实罕见,最后手段
KNOWLEDGE把可复用的参考知识存进 knowledge/罕见

一句话直觉/类比: 把它想成夜里帮你整理的实习生——白天(对话中)你和主 agent 一起干活;你走开后,它翻一遍今天新增的对话,只有确实发现该改的地方才动手,还留了草稿备份好让你随时说"撤销"。它的默认动作是"什么都不做"——这是设计上的正确结果,不是失职。

沉默是常态。 审查 agent 绝大多数时候只回一个 [SILENT](agent/evolution/prompts.py:16),整个流程静默结束,你完全无感。只有当它真的改动了工作区里的文件,你才会收到一条简短通知。


2. 顶层全景(它大概怎么转)

自进化天然分成触发侧执行侧两半:触发侧是一个每 60 秒醒一次的后台守护线程,负责"什么时候该复盘";执行侧负责"复盘时具体怎么做"。

怎么读下面这张图: 从上到下是时间顺序。左半边(触发)在主进程里常驻;满足条件后调用右半边(执行),执行侧临时拉起一个隔离 agent 干活,干完把记录注回主会话并通知用户。

┌───────────────── 触发侧(trigger.py) ─────────────────┐
每轮用户对话 ──▶ note_user_turn:记 _evo_turns / _evo_last_active
主 agent 运行 ──▶ mark_run_active:标记 mid-run(防并发复盘)

后台守护线程(单进程,每 60s)
│ _scan_loop ─▶ _scan_once:逐个扫活跃 session
│ 判据:非 mid-run 且 idle≥idle_minutes
│ 且 enough_signal(轮数够 或 上下文压力≥80%)

┌───────────────── 执行侧(executor.py) ──────────────────┐
│ run_evolution_for_session │
│ ① _build_transcript:只取"上次以来的新消息"(游标) │
│ ② create_backup:快照 MEMORY/每日/AGENT.md/非内置技能 │
│ ③ _workspace_snapshot:记录改动前的文件指纹 │
│ ④ 拉起【隔离审查 agent】(同模型 + 受限工具 + 进化提示)│
│ ⑤ 结果 == [SILENT] 或 没有文件真被改 ──▶ 静默收工 │
│ ⑥ 否则:记日志 → 注入 [EVOLUTION] 记录 → 通知用户 │
└────────────────────────────────────────────────────────┘

各部件一句话职责:

部件干什么在哪个文件
trigger.py后台守护线程,判定"何时复盘"agent/evolution/trigger.py
executor.py一次复盘的完整流程与工程守卫agent/evolution/executor.py
config.pyconfig.jsonself_evolution_* 配置agent/evolution/config.py
prompts.py审查 agent 的系统提示 + 沉默/记录标记agent/evolution/prompts.py
backup.py改动前快照、支持按 backup_id 回滚agent/evolution/backup.py
record.py把每次进化追加到 memory/evolution/YYYY-MM-DD.mdagent/evolution/record.py

一个关键设计取向:复用而非 fork。 执行侧不自己造 agent、造工具、造推送通道,而是复用主管线:AgentBridge.create_agent 造隔离 agent、ToolManager 出工具、remember_scheduled_output 注入记录、channel_factory 推送通知(agent/evolution/executor.py:16-18 的模块 docstring 明说了这一点)。§6 会拆开讲。


3. 触发侧:什么时候才该复盘

这一节讲"守护线程如何判定一个 session 该进化了"。核心在 agent/evolution/trigger.py

3.1 一个单进程守护线程

整个进程只有一个扫描线程,由 start_evolution_trigger(trigger.py:90)启动,并用一个 _evolution_trigger_started 标志保证幂等——多次调用只会真正起一次线程:

# agent/evolution/trigger.py:90 —— 真实源码(节选)
def start_evolution_trigger(agent_bridge) -> None:
"""Start the idle-scan thread once per process (idempotent)."""
if getattr(agent_bridge, "_evolution_trigger_started", False):
return
agent_bridge._evolution_trigger_started = True
t = threading.Thread(target=_scan_loop, args=(agent_bridge,), daemon=True, ...)
t.start()

它由 AgentBridge 在初始化时调用(bridge/agent_bridge.py:301-302),是个 daemon 线程——随主进程退出,不阻塞关停。

线程主体 _scan_loop(trigger.py:103)是个死循环:睡 _SCAN_INTERVAL_SECONDS(= 60 秒,trigger.py:28)→ 读一次配置 → 若 cfg.enabled 就调 _scan_once。任何异常都被吞掉并再睡 60 秒,保证复盘线程永远不拖垮主管线(这是整个子系统反复出现的原则)。

3.2 每轮对话留下的"活动痕迹"

扫描线程要判断,靠的是主管线在每轮真实用户对话后往 agent 实例上记的几个轻量属性。这activity由 note_user_turn(trigger.py:56)写:

属性含义
_evo_last_active最后一轮对话的 epoch 秒
_evo_turns距上次进化以来的用户轮数
_evo_channel_type来源渠道(供之后通知)
_evo_receiver推送目标

注意计数是按"用户轮"而非按"消息"(见 trigger.py 顶部 docstring 第 11 行),而且每跑完一次进化,基线 _evo_turns 会清零(trigger.py:141),这样一段长对话可以多次进化、且不会重复评判旧内容。

note_user_turn 从主管线两处被调用:bridge/agent_bridge.py:594-600agent/chat/service.py:336-341。群聊时 receiver 会传空(不向群里推送私人进化结果)。

3.3 防并发:别在 agent 正干活时去复盘

如果一次用户请求本身跑得比 idle_minutes 还久,扫描线程可能在 agent 正在生成回答时就去复盘同一个 session——那会读到半截 transcript、甚至和主运行抢工作区。mark_run_active(trigger.py:76)就是防这个:

# agent/evolution/trigger.py:76 —— 真实源码(节选)
def mark_run_active(agent, active: bool) -> None:
try:
agent._evo_run_active = bool(active)
if active:
agent._evo_last_active = time.time() # 运行中也算"活跃",顺延空闲计时

主管线在开始一次运行前置 True、结束后置 False(agent/chat/service.py:60,213),扫描时只要看到 _evo_run_active 为真就直接跳过(trigger.py:124)。

3.4 触发判据:idle + enough_signal

_scan_once(trigger.py:116)遍历所有活跃 session,对每个逐条过滤。判据是三个条件同时成立:

非 mid-run (_evo_run_active 为假) trigger.py:124

enough_signal (轮数够 OR 上下文压力够) trigger.py:129

idle 足够 (now - _evo_last_active ≥ idle_seconds) trigger.py:132-133

其中最有意思的是 enough_signal 的"或"逻辑(trigger.py:129):

# agent/evolution/trigger.py:129 —— 真实源码
enough_signal = turns >= cfg.min_turns or _context_pressure_reached(agent)
  • 路径 A:轮数够。 _evo_turns 达到 cfg.min_turns(默认 6,config.py:17)。
  • 路径 B:上下文压力够。 即使轮数不够,只要上下文已经涨过预算的 80%,也触发。

为什么要路径 B? 因为一段"轮数少但每轮都很长"的对话,眼看就要被上下文裁剪(见 上下文健壮性)挤掉旧内容——那些内容一旦被裁就再也复盘不到了。所以在"上下文压力"下提前抢救一次,思路和 OpenClacky / Claude Code 在上下文压力下做整合一致(trigger.py:5-8 docstring)。

_context_pressure_reached(trigger.py:38)复用 agent 自己的 token 估算,和现有裁剪路径口径一致,超预算 80%(_CONTEXT_RATIO = 0.8,trigger.py:34)即为真;任何异常一律返回 False(best-effort,宁可不触发)。

3.5 先清零、再执行

命中后有个容易被忽略但重要的细节——先把基线清零,再启动复盘:

# agent/evolution/trigger.py:139-149 —— 真实源码(节选)
# Reset baseline BEFORE running so a long pass / new messages during
# it don't double-trigger; turns accrue fresh from here.
agent._evo_turns = 0
run_evolution_for_session(agent_bridge, session_id=session_id, ...)

先清零,是为了让复盘期间新到的对话轮从头累计,不会因为一次慢复盘而被重复触发。


4. 执行侧:一次复盘具体怎么做

这一节走一遍 run_evolution_for_session(agent/evolution/executor.py:303)的完整流程。它可以从后台线程安全调用,所有失败都被吞并记日志——进化绝不能干扰主管线

4.1 只看"上次以来的新消息"(游标)

复盘不该每次都把整段历史重判一遍(既贵又会重复写记忆)。执行侧在 agent 实例上维护一个内存游标 _evo_done_msg_count,只取游标之后的新消息:

# agent/evolution/executor.py:344-348 —— 真实源码(节选)
done = int(getattr(agent, "_evo_done_msg_count", 0))
if done > total_msgs:
done = 0 # history was trimmed/reset; start fresh
new_messages = all_messages[done:]
transcript = _build_transcript(new_messages)

这个游标存在内存、重启即丢(executor.py:342-343 有明确注释):代价至多是重启后多做一次冗余复盘,而下游的"文件是否真变"检查会兜住它,不会重复写入相同记忆。

_build_transcript(executor.py:75)把新消息渲染成紧凑文本;若超过 12000 字符,保留最近的尾部(尾部最相关,executor.py:89-91)。若 transcript 为空,就悄悄把游标推到末尾并返回,不打日志(每分钟的扫描会碰到每个空闲 session,这类 no-op 打日志纯是噪音,executor.py:349-353)。

4.2 改动前的两层快照(为"撤销"和"anti-nag"服务)

在动手前,执行侧做两种快照,目的不同:

第一层——内容快照(为撤销)。 create_backup(backup.py:29)把这些文件复制进 memory/.evolution_backups/<backup_id>/,并写一份 manifest.json:

  • MEMORY.md(长期记忆索引)与今天的每日文件 memory/YYYY-MM-DD.md
  • AGENT.md(人设,极少改,但也纳入以便撤销,executor.py:388)
  • 每一个非内置技能的 SKILL.md(executor.py:390-400)

内置技能被刻意排除在备份之外——因为它们根本不该被改(见 §5.2),没必要备份。backup.py 还会用 _prune_old_backups 只保留最近 10 份(backup.py:92-100),控制磁盘占用。配套的 restore_backup(backup.py:67)按 manifest 逐个还原,支撑"撤销"。

第二层——指纹快照(为 anti-nag)。 _workspace_snapshot(executor.py:256)扫一遍受关注的子树,记录每个文件的 (mtime, size),不读内容,很便宜:

# agent/evolution/executor.py:247 —— 真实源码
_WATCH_SUBDIRS = ("MEMORY.md", "AGENT.md", "skills", "knowledge", "output")

它还特意排除进化自己的记账目录和夜间 Deep Dream 日记(_MEMORY_IGNORE,executor.py:250)、以及技能子系统自动维护的开关索引 skills_config.json(executor.py:253)——这些文件即使变了也不算"用户可见的改动信号"。这层快照是 §4.5"确实改了才通知"的判据。

4.3 拉起一个隔离的审查 agent

关键设计:复盘不用主 agent,而是临时造一个隔离 agent,三点隔离:

  1. 同模型。 review_agent.model = agent.model(executor.py:432),跟随用户配置的模型。
  2. 受限工具集。 只给 _ALLOWED_TOOLS(executor.py:44)里的 7 个工具:read / write / edit / ls / bash / memory_search / memory_get,其余一律不给。
  3. 进化专用提示,叠加而非覆盖。extra_system_suffix = EVOLUTION_SYSTEM_PROMPT(executor.py:438)把进化任务追加在完整系统提示之后——这样审查 agent 既拿到完整上下文(工具、工作区、用户偏好、记忆、时间),又拿到进化专用指令,两者不互相覆盖。
# agent/evolution/executor.py:416-438 —— 真实源码(节选)
review_agent = agent_bridge.create_agent(
system_prompt="", tools=review_tools,
description="Self-evolution review agent",
max_steps=cfg.max_steps, workspace_dir=str(workspace_dir),
skill_manager=..., memory_manager=..., enable_skills=True, ...)
review_agent._evolution_restricted = True # 关键防护标志,见 §5.3
review_agent.model = agent.model
review_agent.extra_system_suffix = EVOLUTION_SYSTEM_PROMPT

审查 agent 用 run_stream(user_msg, clear_history=True)(executor.py:444)跑一遍;max_steps 默认 12(config.py:20),刻意小——复盘要便宜、聚焦,不是一次长时自主运行。

进化系统提示(prompts.py:23)的第一原则就写死了"默认什么都不做":问候、寒暄、"好的/谢谢"这类不是信号,直接回 [SILENT],不许探索文件、不许写总结。而且提示里有一条硬约束:只有真用 write/edit 改了文件,才允许输出总结(prompts.py:39-41)——这从 prompt 侧就压住了"只说不做"。

4.4 沉默判定

审查 agent 跑完后,先推进游标(executor.py:449,无论沉默与否,这批消息都算"已复盘"),再判沉默:

# agent/evolution/executor.py:453 —— 真实源码
if not result or result.startswith(SILENT_TOKEN):
... # 记一条"No change"日志,返回 False

空结果、或以 [SILENT](prompts.py:16)开头,都当作"模型选择保持沉默",静默收工。

4.5 Anti-nag:确实改了文件,才通知

这是整个子系统最点睛的一道工程守卫。即使模型写了一段"我改了 XXX"的总结,执行侧也不轻信文字,而是用 §4.2 的指纹快照做前后对比:

# agent/evolution/executor.py:459 —— 真实源码(节选)
# Anti-nag backstop: if the model wrote a summary but actually changed no
# watched file, stay silent — never notify about work that didn't happen.
if not _workspace_changed(workspace_dir, pre_snapshot):
... # "text produced but no file changed — staying silent"
return False

_workspace_changed(executor.py:298)就是重新快照一次、和改动前的 pre_snapshot!=只要没有任何受关注文件真的变了,就闭嘴——绝不为一件没发生的工作去打扰用户。这道守卫是基于事实(文件指纹)而非基于 prompt 承诺,所以模型"幻觉出一次改动"也骗不过它。

4.6 真改了:记日志 → 注入记录 → 通知

只有走到这一步,才算一次真实进化。三件事依次做:

  1. 记进化日志。 append_session_evolution(record.py:27)把总结追加到 memory/evolution/YYYY-MM-DD.md,带时间戳和 backup_id。这个目录独立于夜间 Deep Dream 日记(memory/dreams/)和主对话记忆,便于记忆 UI 在一条时间线上呈现"今天 agent 学/改了什么"。

  2. 往主会话注入 [EVOLUTION] 记录。 _inject_evolution_record(executor.py:502)让主 agent 之后能识别并响应"撤销"(下节详述)。

  3. 通知用户。 若有渠道信息,_notify_user(executor.py:522)按渠道把总结推回去。

通知只有这一道节流(即 §4.5 的文件变更闸门)就够了——真实进化本就罕见,不需要额外的每日次数上限(executor.py:486-488 注释明说)。


5. 工程化守卫:靠代码硬约束,不靠 prompt 求它听话

这一节是本章的技术精华。自进化让一个后台 agent 无人值守地改文件、跑 bash,这天然危险。CowAgent 的态度是:不把安全寄托在"提示词里请它别乱来",而是用代码把能力硬性关进笼子。 提示词只是第二道防线。

5.1 写操作只能落在 workspace 内

_WorkspaceWriteGuard(executor.py:117)包住 write/edit 工具,在真正执行前解析目标路径,凡是解析到 workspace 之外的写,一律拒绝:

# agent/evolution/executor.py:146-159 —— 真实源码(节选)
def execute(self, args):
path = (args.get("path") or "").strip()
if path:
resolved = Path(self._inner._resolve_path(path)).resolve()
if self._ws not in resolved.parents and resolved != self._ws:
return ToolResult.fail(
"Error: evolution may only write inside the workspace; "
f"path '{path}' is outside and was blocked.")
return self._inner.execute(args)

这道守卫的直接目的,是保护产品自带的内置技能(它们住在 workspace 之外),无论模型试图用什么路径都改不到。

5.2 内置技能的"保护名单"

产品自带技能住在项目根的 skills/ 目录。运行时 workspace 里虽然存在同名副本,但内置技能绝不能被进化改动_builtin_skill_names(executor.py:53)以项目目录为权威来源列出这份名单,并在两处用它:

  • 备份阶段跳过:内置技能不纳入备份(executor.py:398),因为它们根本不该被改。
  • 提示阶段告知:build_review_user_message(prompts.py:141)把受保护技能名注入用户消息(PROTECTED skills (built-in — never edit these): ...),让模型也知道别碰。

代码守卫(§5.1)+ 保护名单 + prompt 提示,三层叠着来。

5.3 bash 也被钉在 workspace 里

进化需要 bash(因为 skill-creator 的初始化脚本要用),但后台无人值守跑裸 shell 太宽。_BashWorkspaceGuard(executor.py:165)做两件事:

  • 把 shell 的 cwd 钉死在 workspace(executor.py:182-185)。
  • 文本层面拒绝逃逸命令:_escapes_workspace(executor.py:201)用正则扫命令里的绝对路径.. 上跳,凡指向 workspace 之外就拒绝执行。

代码里也诚实标注了它的边界——这是粗粒度的文本检查,不是沙箱(executor.py:174-176 注释),配合"只跑 skill-creator 脚本"的模型指令,把写操作留在本地。

_guard_tools(executor.py:231)是这套守卫的装配点:write/edit_WorkspaceWriteGuard,bash_BashWorkspaceGuard,其余原样透传。

5.4 别让 MCP 工具偷偷溜回来

还有一个隐蔽的洞:运行时 ToolManager.sync_mcp_into_agent(agent/tools/tool_manager.py:556)会在每次 LLM 轮把新加载的 MCP 工具同步进活跃 agent。如果不管它,_select_tools/_guard_tools 好不容易收窄的工具边界,会在审查 agent 的第一轮就被重新打开。

解法是给审查 agent 打一个 _evolution_restricted = True 标志(executor.py:431),sync_mcp_into_agent 一看到这个标志(在 agent 自身或其包装流执行器上)就直接返回、不注入任何 MCP 工具:

# agent/tools/tool_manager.py:578-581 —— 真实源码(节选)
if getattr(agent, "_evolution_restricted", False) or getattr(
getattr(agent, "agent", None), "_evolution_restricted", False):
return ([], [])

把这四道守卫连起来看: 写(§5.1)、内置技能(§5.2)、bash(§5.3)、MCP 回灌(§5.4)——四个可能的越界口,全被代码堵死,而不是靠提示词恳求。


6. 沉默约定、撤销与通知

6.1 [SILENT] 沉默约定

[SILENT](prompts.py:16)是审查 agent 表示"没啥可进化"的哨兵。执行侧对它有多重容错:结果为空、以 [SILENT] 开头都算沉默(executor.py:453);即便模型在正文里夹了零星的 [SILENT],也会先 replace 掉再判断是否还剩内容(executor.py:468-471)。

6.2 [EVOLUTION] 记录 + "撤销"

每次真实进化,_inject_evolution_record(executor.py:502)会往主会话注入一条 [EVOLUTION](prompts.py:20)记录,带上 backup_id 和"要撤销就还原这个备份"的提示:

# agent/evolution/executor.py:507-517 —— 真实源码(节选)
note = f"{EVOLUTION_MARKER} {summary}"
if backup_id:
note += f"\n(backup_id: {backup_id}; to undo, restore this backup)"
agent_bridge.remember_scheduled_output(
session_id=session_id, content=note,
channel_type=channel_type, task_description="self-evolution")

复用了调度器的输出注入路径 remember_scheduled_output——隔离执行,只把一条紧凑记录落进主会话,这样主 agent 之后就能识别过去的进化、并在用户说"撤销"时用 backup_id 回滚(还原逻辑在 backup.py:67 restore_backup)。

注入后还要再把游标推过这几条记录(executor.py:480-484),免得下次扫描把进化自己的记账当成"新用户内容"又触发一轮。

6.3 按渠道回推通知

_notify_user(executor.py:522)复用 channel_factory 造通道并 send。不同渠道有各自的适配细节:飞书/钉钉/企微机器人/QQ 这类"回复某条原消息"的通道要把 msgNone 走全新推送(executor.py:534);Web 是请求-响应式,需要合成一个 request_id 并建立 request→session 映射,才能把后台推送路由到用户的轮询队列(executor.py:546-551,和调度器同款做法)。


7. 它刻意不做什么(边界与局限)

诚实列出这个子系统的边界:

边界说明
默认关闭DEFAULT_ENABLED = False(config.py:15),不开就完全不跑。
不主动催办未完成任务只在"材料齐了"时才补做;若在等用户答复/决策,一律 [SILENT],绝不 ping 用户(prompts.py:70-75)。
拒绝"自我投毒"提示里有一份 "Do NOT capture" 名单(prompts.py:105-114):环境失败、"某工具用不了"这类负面断言、一次性任务叙述、重试即好的瞬时错误——都不许沉淀,否则会硬化成 agent 日后拿来拒绝自己的"规则"。
记忆是最后手段主 agent 聊天中已写记忆、夜间 Deep Dream 又做整合,所以进化默认不碰记忆,除非确有主 agent 漏掉、且会明显改变未来回复的持久事实(prompts.py:77-97)。
bash 守卫是文本检查非沙箱_BashWorkspaceGuard 自认是粗粒度检查(executor.py:174-176),不是真正的隔离沙箱。
游标随重启丢失_evo_done_msg_count 存内存(executor.py:342-343),重启后至多多做一次冗余复盘,由文件变更闸门兜底。
并发上限 2_MAX_CONCURRENT = 2(executor.py:48),一批空闲 session 同时到点也最多并行两个复盘,其余等下一轮扫描。

8. 横向对比与延伸阅读

自进化和 CowAgent 其它子系统的关系,一句话各表:

  • 记忆与知识: 自进化把记忆当"最后手段",因为记忆的主力写入在对话中和夜间 Deep Dream;它俩共用 MEMORY.md / 每日文件 / knowledge/,但进化的产物单独落在 memory/evolution/,不与 dream 日记混。
  • 技能与工具: 自进化最主力的动作就是修补/新建技能;它遵循 skill-creator 的约定写 skills/<name>/SKILL.md,并用工具守卫保护内置技能。
  • 上下文健壮性: 触发路径 B(上下文压力≥80%)正是为了在裁剪吃掉旧对话前抢救一次。
  • 核心循环 / 系统提示词: 审查 agent 复用同一套 create_agent 循环和系统提示重建机制,只是叠了 extra_system_suffix 并收窄了工具。

9. 代码地图(导航索引)

主题文件路径符号名
启动守护线程(幂等)agent/evolution/trigger.pystart_evolution_trigger
扫描循环(每 60s)agent/evolution/trigger.py_scan_loop / _SCAN_INTERVAL_SECONDS
逐 session 判据agent/evolution/trigger.py_scan_once
上下文压力判定(≥80%)agent/evolution/trigger.py_context_pressure_reached / _CONTEXT_RATIO
记录每轮活动agent/evolution/trigger.pynote_user_turn
防并发(mid-run 标记)agent/evolution/trigger.pymark_run_active
一次复盘主流程agent/evolution/executor.pyrun_evolution_for_session
只看新消息(游标)agent/evolution/executor.py_build_transcript / _evo_done_msg_count
内容快照(撤销)agent/evolution/executor.py / backup.pycreate_backup
指纹快照(anti-nag)agent/evolution/executor.py_workspace_snapshot / _workspace_changed
写守卫(限 workspace)agent/evolution/executor.py_WorkspaceWriteGuard
bash 守卫(限 workspace)agent/evolution/executor.py_BashWorkspaceGuard / _escapes_workspace
工具装配agent/evolution/executor.py_guard_tools / _select_tools / _ALLOWED_TOOLS
内置技能保护名单agent/evolution/executor.py_builtin_skill_names
MCP 回灌防护agent/tools/tool_manager.pysync_mcp_into_agent(_evolution_restricted)
注入 [EVOLUTION] 记录agent/evolution/executor.py_inject_evolution_record
按渠道通知agent/evolution/executor.py_notify_user
进化日志agent/evolution/record.pyappend_session_evolution
备份/回滚agent/evolution/backup.pycreate_backup / restore_backup
审查 agent 系统提示agent/evolution/prompts.pyEVOLUTION_SYSTEM_PROMPT / SILENT_TOKEN / EVOLUTION_MARKER
配置(默认关闭)agent/evolution/config.pyEvolutionConfig / get_evolution_config
触发线程接入点bridge/agent_bridge.pystart_evolution_trigger(调用处)