# 「小说写作提问式插件」对话纪要(精简版) > 把那段 19 轮、1198 步、近 4 小时的对话压缩成一份可读记录:你要什么 → 最终做成什么 → 一个 bug 一个 bug 怎么修 → 踩了哪些 DSH 插件框架的坑 → 最后为什么「压缩」没成功。 > 需要细节时对照包里的 `dsh-novel-companion/使用说明.md`(15 个 bug 的完整根因与修法)和 `代码清单.md`(逐文件定位)。 ## 一、基本信息 | 项目 | 内容 | | --- | --- | | 对话标题 | 小说写作提问式插件 | | 会话 | 本地 DSH 会话(略) | | 时间 | 2026-10-17 15:31 创建;15:49–19:41 对话,约 3 小时 52 分 | | 规模 | 19 轮 / 1198 步 / 解码 369,678 tokens / 工具调用约 1,198 次 | | 模型 | 第 1–7 轮 `a1/deepseek-v4.1-flash`(262k 窗口)→ 第 8–17 轮 `deepseek-account/deepseek-flash`(1M)→ 第 18 轮起切回 `a1/deepseek-v4.1-flash` → **超窗** | | 自动压缩 | 成功 4 次,共遮蔽约 44.3 万 tokens;最后 6 次尝试全部失败(原因见第七节) | | 最终结果 | 插件 **15 个真实 bug 全部修复**,运行时验证可用;文档 + 16 项自检齐备。最后两轮因上下文超窗未完成,但插件代码已在 19:39 定稿 | ## 二、你提了什么(19 条,原文) | 轮次 | 原话 | 意图 | | --- | --- | --- | | 1 | 能不能创造一个插件关于写小说的,不断向作者提问完善背景人物介绍等信息,剧情方面每章写大纲让作者进行修缮然后你继续完善 | 从零做插件 | | 2 | 你是把插件装进来了吗 | 确认安装 | | 3 | 做的插件有问题,先不要运行插件,然后排查问题并修复 | 别急着启用,先修 | | 4 | 你打开这个插件并重启测试一下 | 启用并重启验证 | | 5 | 你这一打开这个插件就无法正常启动必须得禁用才能打开 | 报致命启动故障 | | 6 | 你做的插件叫什么 | 问包名/名称 | | 7 | 但我再设置的内置插件里面并没有找到啊 | 找不到插件入口 | | 8 | 怎么回事 | 追问 | | 9 | 继续 | 继续排查 | | 10 | 查看并修复 | 修复 | | 11 | 查看并修复插件 | 修复 | | 12 | 查看并修复插件 | 修复 | | 13 | (粘贴了 WorkBuddy 对 `parameters` 格式问题的完整分析) | 让 AI 核对并采纳 | | 14 | 你重启测试一下 | 重启运行时验证 | | 15 | 好了吗 | 问重启结果 | | 16 | 根本没有对话框啊 | 面板看不见 | | 17 | 现在是这样了,重启刷新也没用 | 面板显示报错 | | 18 | 下面这个"新增问题"旁边的蓝色空白是啥 | 主按钮文字不可见 | | 19 | 压缩 | 想压缩上下文(撞上超窗) | ## 三、最终做成的东西 **插件形态**:`dsh-novel-companion` v1.0.0,一个 DSH Desktop 插件(Host 半 + Client 半),中文名「小说创作搭档」。 | 维度 | 设计 | | --- | --- | | 两个入口 | 左侧边栏**「小说」面板**(人改)+ 对话里的 **`novel` 工具**(模型读写),共用同一份数据 | | 创作流程 | 7 阶段:了解作者 → 世界观 → 人物 → 情节 → 分章大纲 → 写正文 → 收尾 | | 设定板块 | 6 个:`core` 内核、`world` 世界观、`characters` 人物、`plot` 情节、`rules` 设定规则、`style` 文风;每块 `draft/final` + 待问问题清单 | | 章节 | 三态 `outline → writing → final`,大纲必须先由作者修改定稿才写正文 | | 数据 | DSH 存储域 `novel_companion` → 表 `projects`(single JSON,`%DSH_HOME%\storages\novel_companion.json`) | | Agent 侧 | `novel` 工具 8 个 action(list/get/create/stage/block/outline/chapter/appendChapter)+ 一段系统提示词摘要,模型始终知道项目进度 | | 规模 | 37 个文件 / 约 41.6 万字节;Host 半 679 行、Client 生成后 1566 行、共享逻辑 394 行、14 个自检脚本 | | 质量保障 | `node scripts/verify.mjs` 一条命令 16 项检查:真实 Cordis Context 挂载、服务商规则校验 schema、极简 React 真实渲染面板、全链路模拟等 | ## 四、四段式时间线 ### 第 1 段:从零把插件写出来(第 1–2 轮,15:49–16:42) - 读 `cordis-plugin-development` 技能,定位 DSH 插件规范(bundle = `package.json` + `cordis.patch.yml`,Host/Client 双子)。 - 写完:`package.json`、`cordis.patch.yml`、`locale`、`icon.svg`、`lib/state.js`(共享逻辑)、`lib/index.js`(Host)、`lib/client.js.tpl`(面板)、`.selftest`。 - 期间两次成功自动压缩(16:05、16:21)。 ### 第 2 段:静态排查,修 10+ 个致命问题(第 3–5 轮,16:43–17:18) - 你要求「先别运行,排查修复」——于是只用静态分析与假 `ctx` 测试,不碰运行环境。 - 修掉:`ctx.Service` 误用(会整个 Host 崩溃)、Remote 方法描述符位置错误、板块正文串位、自动保存不触发、写正文改错章节状态、`ctx.get('input')` 不存在、「读取失败」无提示、4 个不存在的主题 token、项目 id 靠运气、无效 try/catch、`list` 文案撒谎等。 - 16:51、17:14 又两次自动压缩(累计 4 次,全程 v4.1-flash)。 ### 第 3 段:安装 → 启用 → 运行时验证 → 撞出 5 个盲区(第 6–17 轮,17:23–19:18) - 17:49 第一次启用:`fatal load failure: cannot get property "Service" without inject` → 改为 `ctx.provide(...)`。 - 17:16–18:31 陆续修:Remote 描述符要用**字符串键**且挂原型(否则 6 个方法全不可见);`novel` 工具 schema 非法 → **每轮对话必 400**,加 `parameterJsonSchema()` 编译 + `check-tool-schema.mjs`;`list` 文案 bug("小说项目已更新")。 - 18:36 重启后 Host 零告警零错误,`novel action=list` 真实调用成功 → **第一次运行时闭环**。 - 18:57–19:02:`novel action=list` 时又抓出渲染文案 bug,补渲染层断言;确认 Host 半不热重载。 - 19:03–19:06:用脱离进程树的后台脚本重启 DSH;19:05 报告「无致命错误、无 Host 退出」。 - 19:07–19:13(第 16 轮):**面板被皮肤立绘整块盖住** → 根因是 CSS 绘制顺序(皮肤插画 `position:absolute; z-index:0` 盖住未定位的 `.nv-root`)→ 加 `position:relative; z-index:1; isolation:isolate`,并加「返回对话」按钮、新增 `check-panel-render.mjs`。 - 19:15–19:18(第 17 轮):面板显示「读取失败:Cannot read properties of undefined (reading 'list')」→ 第三方命名空间不在 `ctx.remote` → 全部 9 处改为 `connection.rpc.call('/api', 'novels/', { args })`。 ### 第 4 段:收尾 UI 修复,然后撞上上下文上限(第 18–19 轮,19:21–19:41) - 19:27–19:34(第 18 轮):修「主按钮是块没字的蓝色方块」——`color` 用了背景 token `--dsw-alias-bg-base`,皮肤把它设成 `transparent` 导致文字隐形 → 改成品牌色描边;`audit-css.mjs` 新增「禁止背景 token 当文字色」守卫 + 坏样本自检;更新 `使用说明.md`。 - 19:39(第 19 轮):重新生成 `lib/client.js`、整理交付物。 - 19:38–19:41:你发「压缩」;每次唤醒都触发自动压缩,但**摘要请求本身超窗**,6 次 `compaction/start → end` 全部带 `pi-ai detected context overflow`,turn 19 以 `CONTEXT_WINDOW_EXCEEDED` 结束(详见第七节)。 ## 五、15 个真实 bug(症状 → 根因 → 修复) | # | 症状 | 根因 | 修复 / 回归 | | --- | --- | --- | --- | | 1 | 启用后整个 DSH 起不来 | Host 把 `ctx.Service` 当服务基类读取,cordis 注入代理抛错,Host 直接退出 | 改为 `ctx.provide(命名空间, 实例)`;新增真实 Cordis Context 挂载测试 | | 2 | 面板所有按钮报 `gateway/invocation-unavailable` | Remote 方法描述符用 `Symbol.for(...)` 挂在实例上;`remoteMethods()` 只读原型且键是字符串 | 用字符串键 + 定义在类原型;`check-remote-contract.mjs` 验证 6 方法可发现 | | 3 | 每轮对话开头就 400 | `ctx.tools.register` 把 `parameters` 原样下发;传的是「参数名→定义」映射,`title` 撞 JSON Schema 保留字 | 加 `parameterJsonSchema()` 编译成对象根 schema;`check-tool-schema.mjs` 按服务商规则校验 | | 4 | 板块正文串位(core 内容跑到 world) | `normalizeProject` 按**下标**回退匹配 | 只按板块 id 匹配;`.selftest/normalize.mjs` 14 条回归 | | 5 | 自动保存永远不触发 | 防抖依赖整个 project 对象,每敲一个字就重置;旧快照覆盖新输入 | ref 读最新文档 + 版本号防回写 + 切换/关闭补存 | | 6 | 写正文会把已定稿章节改回 `writing` | 正文 onChange 强制改状态 | 改正文只改正文 | | 7 | 「写入对话」按钮从没生效 | `ctx.get('input')` 是每会话 Hook 源,不是服务 | 改用 `conversation.input.shell(sessionId).actions.setDraft` | | 8 | 读取失败时面板像空的 | 没有错误态 | 错误卡片 + 刷新 + 「Host 半可能没挂载」提示 | | 9 | 悬停态变透明 | CSS 用了 4 个不存在的主题 token + 字面颜色 | 换成真实 token;`audit-css.mjs` 审计 | | 10 | 项目 id 靠 UUID 运气 | 依赖随机 UUID 形状 | 显式生成 `novel-` | | 11 | 无效 try/catch | 包住 `ctx.slots.inject(...)` 的 try/catch 永远捕不到注册错误 | 移除 | | 12 | `list` 说「小说项目已更新:projects=[]」 | 返回对象没有 `brief`,落进通用分支,把原始 JSON 丢给模型 | 专门 `list` 分支;`simulate-tool.mjs` 加渲染层断言 | | 13 | 点开面板只有背景插画 | 皮肤立绘层 `absolute z-index:0` 盖住未定位的 `.nv-root` | `.nv-root` 加定位 + z-index + isolation;加「返回对话」;`check-panel-render.mjs` | | 14 | 面板「读取失败:…reading 'list'」 | 第三方命名空间不在 `ctx.remote`(构建期生成,无法手写 codec) | 9 处改用 `connection.rpc.call('/api', 'novels/', { args })` | | 15 | 主按钮是没字的蓝色方块 | 文字色用了背景 token,皮肤把它设为 `transparent` | 改为品牌色描边 + 文字;`audit-css.mjs` 新增守卫 + 坏样本自检 | 外加一次工具链事故:用 PowerShell `Set-Content -Encoding utf8` 改写源文件,把全部中文双重编码成乱码,已逐行恢复(教训:**不要用 PowerShell 字符串改写含中文的文件,用编辑器/Node 脚本**)。 ## 六、DSH 插件框架踩坑清单(继续开发必读) | 坑 | 现象 | 正确做法 | | --- | --- | --- | | `ctx.Service` | 读取它会让整个 Host 退出,不是只停插件 | 用 `ctx.provide(ns, instance)` 发布服务 | | Remote 描述符 | 方法不可见 / 所有按钮失效 | 键是**字符串** `@deepseek-ai/dsh-typert-protocol/remote-methods`,定义在类**原型**上 | | `ctx.tools.register` 的 `parameters` | 服务商 400,整轮对话死 | 必须编译成正规 JSON Schema(对象根 + `required` 数组),不能传参数表 | | `ctx.remote.` | 第三方命名空间是 `undefined` | 用 `ctx.connection.rpc.call('/api', '/', { args })` | | `main` 槽位 | 没有 `sessionId` prop | 主会话 id 取 `ctx.get('uiSession').adapter.current`,或回退 props | | 面板被皮肤遮挡 | 渲染正常但完全看不见 | 根节点 `position:relative; z-index:1; isolation:isolate` | | 主题 token | 只有 14 个真实 token | 只用真实 token;别把背景 token 当文字色(皮肤会把它设成透明) | | Host 半更新 | 改了代码运行时仍是旧行为 | Host 半**不热重载**,必须完全重启 DSH Desktop;Client 半刷新即可 | | linked 包依赖 | `schemastery` / 部分 `@deepseek-ai/*` 解析失败 | 只能 import `node:*`、自身相对文件、`@deepseek-ai/*` 内置包 | | profile 手改 | BOM 导致 HMR `SyntaxError: Unexpected token '锘'` | 不要手改出 BOM;优先用 DSH 插件管理器安装 | ## 七、结尾「压缩」为什么没成功(重要) - 第 18 轮开始前,模型从 `deepseek-flash`(1M 窗口)**切回了 `a1/deepseek-v4.1-flash`(262,144 窗口)**。 - 此时那条对话的上下文压力约 **343,433 tokens**,已经超过 262k 窗口。 - 因此第 18、19 轮每次唤醒都会触发自动压缩,但**生成摘要的请求本身也带着超窗历史**: - 6 次 `compaction/start → end` 全部带错误 `pi-ai detected context overflow for model "deepseek-v4.1-flash"`; - turn 18、turn 19 均以 `CONTEXT_WINDOW_EXCEEDED` 结束。 - 结论:**在那条对话里点「压缩」/ 输入 `/compact` 也会失败**(同一后端、同一超窗摘要请求),不是按钮坏了。 - 解法(任选其一): 1. 打开那条对话 → 把模型切回 **`deepseek-flash`(1M 窗口)** → 先正常发一句话(让一次请求成功、确认路由)→ 再执行 `/压缩`; 2. 直接用新对话继续(这份纪要 + 代码包就是为此准备的)。 - 提醒:这是 **模型窗口配置问题,不是插件代码问题**;插件代码在 19:39 已定稿。 ## 八、下一步建议 1. **使用/继续开发**:读包里的 `README.md` 和 `dsh-novel-companion/使用说明.md`,按 `代码清单.md` 定位。 2. **运行时复验清单**(重启 DSH 后):左侧书本图标出现 → 面板「新建项目/载入示例」→ 对话里 `novel action=list` → 写一章大纲并定稿。 3. **数据备份**:`%DSH_HOME%\storages\novel_companion.json`(所有小说项目都在这一个文件里)。 4. **每次改完**:`node scripts/verify.mjs` 必须 16 项全 PASS,再重启验证运行时。 ## 附录:4 次成功的自动压缩 | # | 时间(北京) | 轮次 | 模型 | 遮蔽 token | 压缩了什么 | | --- | --- | --- | --- | --- | --- | | 1 | 16:05:22 | 第 1 轮 | a1/deepseek-v4.1-flash | 112,977 | 插件规范调研、初版代码设计 | | 2 | 16:21:49 | 第 1 轮 | a1/deepseek-v4.1-flash | 114,641 | Host 半/Client 半实现与首次自检 | | 3 | 16:51:31 | 第 3 轮 | a1/deepseek-v4.1-flash | 108,468 | 静态排查 10+ 问题、共享逻辑重写 | | 4 | 17:14:08 | 第 5 轮 | a1/deepseek-v4.1-flash | 106,510 | 启用失败排查、Remote 契约修正 | > 之后(第 18 轮起)模型窗口不足,自动压缩不再成功——这就是第七节的事故。