Skip to content

chat: let the Agent redo the lower level of the selected card (reExpand) - #28

Merged
Yaphet2015 merged 7 commits into
mainfrom
chat-reexpand
Oct 25, 2026
Merged

Yaphet2015 merged 7 commits into
mainfrom
chat-reexpand

Conversation

@Yaphet2015

@Yaphet2015 Yaphet2015 commented Oct 25, 2026 •

Copy link
Copy Markdown
Owner

问题

选中第 1 站、让 Agent 把已展开的 4 张 L2 卡重做成「一个 Skill 一张卡」时,Agent 看不到子卡,也只有 editRoute / startStop 两种提案,于是去改了第 2、3 站,最后说做不到。

改动(每步一个提交)

  1. 上下文:askNote 把画布上选中的卡传给 chatPrompt,选中卡优先于 note.attachedTo 作为「当前站」;每轮多带一段「选中卡 + 它下一层的标题和 40 字摘要」(有数量上限)。SYSTEM_TOKENS 450→800(新规则的估算)。
  2. 提案:CHAT_INSTRUCTIONS 新增 {"proposal":{"type":"reExpand","stop":站号或卡标题,"instruction":"…"}},规则是学习者谈到选中卡的下一层/子卡/展开层时发;拿不准就先问;明确说 Agent 能重做下层。不做意图分类器。类型、zod 校验、checkProposal(站号或唯一卡标题 → 真实卡)一并加上。
  3. 生成与落地:reExpand 的 gate 打开 regenAny(被拒的卡一律重生成一次);展开上限 8→12(仅 reExpand 的 schema);landReExpand 先把旧下层(含孙卡)归档到 cardArchives(可恢复)再过 gate、落新卡;没有新卡落地就原样返回旧板。expand() 在被别的生成占用时会 toast 而不是静默返回。
  4. 应用流:聊天里出 RouteDiff 风格卡「将重新生成「X」的下层(替换 N 张子卡,含其下孙卡)」,应用 / 取消;画布上旧子卡显示「待替换」;应用时先快照(nodes、relations、route、cardArchives、批注与卡的关联),生成成功的同一次改动里变「已应用」;撤销 恢复快照。失败 / 空结果保持待处理,可重试。提案仍只在点「应用」(或「好」)后生效,自由聊天不会直接写卡。
  5. 测试与采样脚本。

测试

  • tsc --noEmit、tsc -p tsconfig.electron.json --noEmit、vite build:通过
  • vitest:44 个文件 / 412 个用例全绿(新增 tests/reexpand.test.ts、tests/chat.test.ts 里 reExpand 解析/校验、选中卡上下文、withProposal / restoreLinks;包含「旧子卡归档 + 新卡落地」「失败不动旧卡」)
  • Playwright e2e:101 passed / 49 skipped(截图类默认跳过);新增 chat.spec 第 8 条:选中卡 → 聊天(prompt 含选中卡和旧子卡)→ reExpand diff → 应用 → 子卡被 5 张新卡替换、旧卡进 cardArchives → 撤销恢复

真实模型采样(zenmux · deepseek/deepseek-v4.1-flash,thinking 关闭,与 app 一致)

① 选中卡 chat(Skills 板,选中 s1 + 4 张子卡,9 句,共跑 3 遍)

  • 该出 reExpand 的 4 句:12/12 命中,0 次被判成 editRoute(回复里会如实复述「把下面的子卡换成每个 Skill 一张」)。editRoute 2 句、3 句里的 2 句 editRoute 每遍都对。
  • 不该出提议的 3 句(问子卡含义、问 Skill 与 prompt 区别、「谢谢」):没有误出 reExpand / editRoute;但每遍有 2–3 句被误出 startStop(问题 / 感谢后建议进入下一站)。这是原有行为,不是本 PR 引入:同模型、同 thinking 关闭下,main 的旧 15 句采样两遍分别 9/15 和 14/15,误出的也都是 startStop,所以没改。
  • token:每轮 prompt ≈ 862、completion ≈ 120、合计 ≈ 980(选中卡 + 子卡摘要那段上下文的开销在内)。旧 15 句采样 prompt 均值约 1045–1067,含 6 条历史消息。

② reExpand 展开(旧下层先归档,prompt 含「每个 Skill 一张」,用 landReExpand + runGate 真跑)

阶段 6 个 Skill(×2 组) 10 个 Skill
初版(无任何改动,1 遍) 生成 6 / 保留 4;生成 6 / 保留 2 生成 10 / 保留 8
仅在 prompt 里提醒两条卡片检查(2 遍) 3–6 张保留 8–9 张保留
+ regenAny(3 遍,最终版) 保留 17/18、17/18 保留 28/30
  • 初版 14/22 保留;最终版 62/66(94%),被丢的 4 张都是单张卡片的检查题复述正文 / 硬货没写进正文 / 示例数字没标「比如」。
  • 原因:gate 在「被拒不超过一半」时直接丢弃被拒的卡,不再重生成,所以「每个 Skill 一张」会掉卡。修法:reExpand 的 gate 打开 regenAny,被拒的卡走一次现有的精简重生成;并在 reExpandNote 里提醒 substance 要写进 body、check.q 要换新情境。
  • token(单次展开):6 张 completion ≈ 1.7–2.0k、10 张 ≈ 2.8–3.2k(约 300 token/张),prompt ≈ 2.3k;重生成一次 completion 约 0.4–2.1k,judge 约 35–110。finish_reason 全是 stop,没有截断。
  • 12 张上限够用:10 张约 3k completion,12 张约 3.6k,远低于非推理模型的 8192 输出上限。

已知限制

  • 一次最多 12 张;Skill 超过 12 个时只会生成前 12 张。
  • 未在 macOS 真机 / DMG 上验证。

@Yaphet2015
Yaphet2015 merged commit 2e78a73 into main Oct 25, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant