nas1112/self-evolve:任務結束後自動復盤,把經驗寫進記憶和技能

前言

DSH 裏跑一個長任務,經常是連着十幾次工具調用,方向早就偏了,但每一步單獨看都「合理」;同一條 shell 命令換着寫法反覆失敗,也沒換思路;好不容易走通的多步驟流程,下次同類任務又從頭摸一遍。self-evolve(維護者 NAS1112,社區目錄分類:記憶)就是爲這件事做的:它會在任務完整結束後復盤一次,把真正值得記的經驗寫進本地記憶或可複用技能,右下角的桌寵播報這一次進化。不是每次對話結束都寫總結,也不是中途插話。

這是什麼

一句話定位:DSH 插件,Host 半 + Client 半。Host 半負責在任務結束時做觸發判斷並落盤,Client 半負責會話頁右下角的桌寵動畫和卡片。

它的三條硬約束,README 裏寫得很明確:

  • 不中途打斷:復盤只在任務結束時發生,對話流裏一個字不多;
  • 默認不寫:只有「會讓未來的你做得不一樣」的經驗才落盤,一次最多一條;
  • 數據全在本地:記憶進 AGENTS.md,技能進 ~/.agents/skills/,你手寫的內容永遠不會被覆蓋。

維護者是 NAS1112。README 的 package.json 裏版本是 0.5.2,INSTALL.md 裏的壓縮包還寫着 self-evolve-0.3.0.zip,兩處不一致,裝的時候以實際下載的目錄爲準。

觸發條件:長度不等於價值

依據是 DESIGN.md。核心判斷是「一個正常的開發任務輕鬆就能跑過 15 步」,如果按步數觸發,結果就是每輪都來一次復盤、記憶裏堆滿噪聲。所以「跑了多少步」默認不參與觸發,只有三種情形在任務結束時合併評估一次:

信號 默認閾值
用戶明確要求記住 命中即觸發(記住 / 記一下 / 以後都 / 我習慣 / 我偏好 / remember that / from now on)
確實出過問題 任意工具失敗 2 次;bash / shell 連續失敗峯值達 3 次
走通了複雜流程 25 步 且 4 種工具

幾個細節:

  1. 失敗判定覆蓋 isError、[exit code: N](N≠0)、[timed out after ...]、[killed by signal: ...];edit 因文件變更失敗同樣算。
  2. 鏈路計數在用戶發新消息時清零,閾值衡量的是「這一輪」;同一個 turn 用 turn 號做冪等鍵,只觸發一次。
  3. 用戶說「記住 X」時偏好類會直接指向 scope: "global",倉庫約定仍留在項目級。
  4. 想恢復按步數觸發,把 triggerOnLengthOnly 設爲 true,滿 chainSteps 步也會觸發。
  5. ask_user_question、exit_plan_mode 之後的 turn 結束屬於中途停下來等用戶,插件會跳過觸發,等任務真正走完的 turn 再進化。

觸發信號本身不產生任何消息。每步在 agent/pre-step 評估一次,條件滿足就把該 agent 標成「本輪已就緒」,協議正文通過 ctx.systemPrompt.section() 寫進系統提示、就緒狀態通過 ctx.systemPrompt.context() 寫進按步刷新的動態上下文——只有模型能看到,用戶界面上不會出現多出來的話或消息。DESIGN.md 裏還記了這件事的來歷:早先用 agent.steer() 在 turn 結束時追加一步專門復盤,那一步的回覆成了「本輪最後一條助手消息」,真正的結論被擠進摺疊的「已調用工具」分組,等於把答案藏起來了。

寫什麼、寫多少

協議基調是默認什麼都不寫,並明確列出不要記錄的內容:這個任務做成了什麼、報錯原文、記憶裏已有道理的另一種說法。沒有合格經驗就直接結束本輪,不向用戶解釋協議或標記。

「一次最多一條」不只寫在協議裏,工具側也硬性限制(maxRecordsPerTurn,默認 1),超出的調用直接返回 skipped,不落盤。

去重和合並交給判定模型(可選)。給了 key 時,它除了判斷值不值得寫,還會回答 merge_v1(這條是不是在重說已有條目)、relation_v1(是不是在更新某條,跨檔也能命中並取代)、stale_v1(這次寫入讓哪條舊條目的前提不再成立,標記「前提已過期」)。判定時會把已有條目的標題 + 正文前 160 字 + 所在檔位交給模型(最多 12 條),所以換個說法不再堆一條。不配 key 時判定降級爲直接寫入(fail-open),插件照常工作。

數據落在哪

內容 路徑
項目記憶 <工作區>/AGENTS.md(<!-- self-evolve:begin --> 託管區間內)
全局記憶 $DSH_HOME/AGENTS.md
本插件寫的技能 ~/.agents/skills/<name>/SKILL.md
長經驗詳情 $DSH_HOME/self-evolve/details/
判定流水 $DSH_HOME/self-evolve/judgements.jsonl
最近一次沉澱 $DSH_HOME/self-evolve/last-record.json

插件只改自己那一段託管區間,不碰你手寫的內容;自己寫的 SKILL.md(沒有歸屬標記)不會被接管。自建技能寫入時會帶一行 <!-- self-evolve:skill -->,老文件按正文裏的託管標題兜底識別——這也是「累計自建技能」的口徑,目錄裏你自己寫的其他技能不參與計數。

安裝與啓用

官方沒有 dsh plugin add 這類命令,走 link: 方式掛到某個 profile。下面以 profile 目錄 $DSH_HOME/profiles/desktop 爲例,其它 profile 換路徑即可。

1、把插件目錄解壓到固定位置:

unzip self-evolve-0.3.0.zip -d ~/plugins
# 得到 ~/plugins/self-evolve/

目錄裏應當有 index.js(Host)、client.js(Client)、package.json、cordis.patch.yml、assets/pet/*.gif、locale/。

2、編輯 profiles/desktop/package.json,把 @local/self-evolve 以 link: 方式加進 dependencies,並放進 dsh.profile.bundles(這一行是必須的,插件才隨 profile 加載):

{
  "dependencies": {
    "@local/self-evolve": "link:/Users/you/plugins/self-evolve"
  },
  "dsh": {
    "profile": {
      "bundles": [
        "@local/self-evolve"
      ]
    }
  }
}

3、安裝依賴或直接建軟鏈:

cd "$DSH_HOME/profiles/desktop"
pnpm install          # 有 pnpm 走這條
# 沒有 pnpm 就直接建鏈:
ln -sfn ~/plugins/self-evolve node_modules/@local/self-evolve

4、重載:重啓 dsh web,然後刷新頁面。

可選配置。profiles/desktop/cordis.patch.yml 裏的覆蓋段:

- id: self-evolve
  name: "@local/self-evolve"
  config:
    shellFailureThreshold: 3
    toolFailureThreshold: 2
    triggerOnLengthOnly: false
    chainSteps: 15
    scope: auto
    workflowMinSteps: 25
    workflowMinDistinctTools: 4
    maxRecordsPerTurn: 1
    decision:
      enabled: true
      shadow: true
      model: doubao-seed-2-1-lite-260915
      baseURL: https://ark.cn-beijing.volces.com/api/v1
      apiKey: ''            # 留空 = 用界面裏配的 key,或讀環境變量
      apiKeyEnv: A_API_KEY
      timeoutMs: 900
      worthThreshold: 0.5
      scopeMinConfidence: 0.6
  disabled: false

不加這段也能跑,走內置默認值。判定模型(火山方舟 System One)不是必需的:不配 key 時判定返回 degraded,插件照常寫入。要開判定就在經驗庫 ⚙ 頁裏填 key,或寫進 apiKey,也可以放環境變量。

5、驗證:

設置 → 插件:列表裏出現 self-evolve
頁面右下角出現桌寵
點桌寵 → 打開經驗庫,四個頁面(記憶 / 技能 / 判斷日誌 / 設置)都能進

命令行探一下路由:

curl -s -o /dev/null -w '%{http_code}\n' \
  -H 'x-self-evolve: 1' \
  "http://127.0.0.1:62100/api/self-evolve/pet/idle.gif"
# 期望 200

6、迴歸測試(可選,插件需要正在運行):

cd self-evolve/test
pnpm install
pnpm test

test-routes.mjs 默認打 http://127.0.0.1:62100;端口變了用 SELF_EVOLVE_BASE=http://127.0.0.1:<端口> 指定。

日常用法

裝好後不需要做任何事。平時它完全隱身,只在任務結束時於右下角彈一次小卡:

  1. 點桌寵打開經驗庫:看記憶 / 技能、應用或丟棄技能提案、就地編輯刪除、用「孟婆湯」批量清空。
  2. 對模型說「記住……」會直接觸發一次沉澱,不必等失敗或長鏈路。
  3. 模型側提供三個工具:self_evolve_record、self_evolve_propose、self_evolve_status。self_evolve_propose 的目標從「只允許自建技能」放寬到技能目錄下的任何技能,包括你自己寫的;模型只能提交提案,落盤必須由人在經驗庫裏點「應用」,且改寫裏不許悄悄加歸屬標記。

界面形態上有幾點值得知道:右下角桌寵常駐,右上角紫色角標顯示本會話累計進化次數;同一會話第 2 次進化起只以小卡形式自動出現,不打斷閱讀,點小卡升級爲整屏臺賬;dock 卡片有三個階段——沉澱中(burst)→ 完成瞬間(flash)→ 已落定(settled);臺賬顯示最近一次沉澱的觸發原因、判定值、寫入落點與本會話歷史。第 1 次進化不會用整屏卡片打斷正在流式輸出的回答說答案被蓋住了——動畫時機由 running 門控,本輪還在跑時什麼都不顯示。

適用場景與注意

適合這類做法:任務完成後才復盤、默認不寫只寫有用的、經驗落在本地倉庫或家目錄而不是雲端、想要一個不打斷對話的提醒形態。如果你更傾向於每次都手動總結、或把經驗統一收在集中服務裏,這套東西的落點可能不合用。

幾個必須知道的邊界:

  • 以當前 dsh 進程權限運行。插件能讀寫工作區和家目錄,安裝前應當檢查源碼與許可證(README 的 package.json 標的是 MIT)再決定是否掛進 profile。
  • 全局寫入依賴審批通道。如果當前組合沒有可用的審批迴答者(approval answerer),越界寫入會 fail closed 並返回 FS_SANDBOX_DENIED;此時只能把 scope 切回 workspace,或顯式把文件策略設爲 danger-full-access。
  • 「新工作流」是啓發式判斷(步數 + 工具種類),不做語義理解。一次長鏈路但確實是同一件事的重複勞動,可能被判成工作流;提示文案裏已經要求模型在「一次性流程」時跳過並說明,所以誤判的代價是一次無害的自我說明。
  • 計數按 agent 存在內存裏(WeakMap),進程重啓或會話切換後從零開始。這是「本輪是否跑偏」的指標,設計上不需要持久化。
  • 復盤發生在 agent/turn-stopping,用戶中途取消本輪則這一輪不會復盤——符合「任務完成後才進化」的語義,但被取消的工作不會沉澱經驗。
  • 技能被寫壞(frontmatter 不合法)時會被 dsh-skill-filesystem 忽略並在日誌裏告警;本插件只追加合法內容,不會修改已有 frontmatter。
  • 以 link: 方式安裝時,改完 index.js 未必立即熱生效。驗證前先調 self_evolve_status,確認 environment 字段存在——沒有該字段說明跑的還是舊代碼,需要在插件頁把該插件關掉再打開。
  • Client 半(動畫)的加載時機與 Host 半不同:dsh.client 的變更要重新安裝 bundle 並刷新頁面才生效。只重載插件不足以讓新註冊的 slot 出現。

觸發閾值、進化動畫的實現細節、記憶兩檔與去重合並、經驗庫、自我優化提案、判定模型、配置全表、設計取捨與驗證方法,都在倉庫的 DESIGN.md 裏。

結尾

self-evolve 做的事情很窄:任務結束後復盤一次,把一條真正有用的經驗寫進本地記憶或技能,然後用桌寵告訴你這次進化是什麼。它不改寫對話流程,不覆蓋你手寫的內容,也不把經驗送到別處去。如果這正是你缺的那塊,可以從這裏看起:

  • 目錄頁:https://www.skillhub.cn/plugins/NAS1112/self-evolve
  • GitHub:https://github.com/NAS1112/self-evolve
羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜