dsh-mcmp 3.0.0 → 3.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -11,13 +11,14 @@ DeepSeek Harness **持久化 Cordis 插件**:面向全国大学生数学建模
11
11
  **无论流水线正常完成、出错还是被中止,最终都会生成一版完整、干净的 Markdown 论文(`Final_Paper.md`)。**
12
12
 
13
13
  > 完整设计见仓库内两份设计文档:
14
- > **`架构.md`**(v3.17,流程架构设计书:中控台、五阶段、评级/回滚/教训机制)与
15
- > **`提示词.md`**(v3.4,全部子智能体 Prompt 模板,主循环提示词由其逐段生成)。
14
+ > **`ARCHITECTURE.md`**(v3.18,流程架构设计书:中控台、五阶段、评级/回滚/教训机制)与
15
+ > **`PROMPTS.md`**(v3.5,全部子智能体 Prompt 模板,主循环提示词由其逐段生成)。
16
16
 
17
17
  ## 功能特性
18
18
 
19
19
  - **消息触发**:输入文本以 `/loopbegin` 开头即自动运行(兼容斜杠命令路径)
20
- - **五阶段 × 22 子阶段**:阶段零(初始化 4)→ 阶段一(EDA 3)→ 阶段二(建模 5)→ 阶段三(撰写 7)→ 阶段四(排版 3),每遍 22 个子阶段,`--round=N` 控制外循环遍数(默认 1,上限 10)
20
+ - **五阶段 × 22 子阶段**:阶段零(初始化 4)→ 阶段一(EDA 3)→ 阶段二(建模 5)→ 阶段三(撰写 7)→ 阶段四(排版 3),共 22 个子阶段,线性推进 + 阶段级回滚
21
+ - **回滚上限可配置**:同一阶段回滚 ≥`--rollback-limit=N` 次(默认 10,1~99)强制锁定(架构 3.7)
21
22
  - **中控台双重质疑**:执行Agent自检与中控台扫描验证执行**同一套六维度质疑标准**(逻辑自洽性/模型合理性/数据敏感性/视觉合理性/规范符合性/教训对照);扫描通过→索引 `VALIDATED`,发现问题→转交评审决策模块
22
23
  - **唯一评级机构**:中控台评审决策模块按 **P0-P3** 评级——P0/P1/P2 经 DEPENDENCIES 链追溯根源阶段,阶段级回滚(物理移入 `deprecated/`、索引 `OBSOLETE`、写入教训文件、重跑);P3 精确定点修改(≤3 行代码/≤1 个句子/≤3 个参数,STAGING→VALIDATED)
23
24
  - **教训持久化**:每次回滚(<10 次)写入 `ROLLBACK_LESSONS.yaml`(查重去重),所有执行Agent启动时必须完整读取(ACTIVE+RESOLVED)
@@ -30,7 +31,7 @@ DeepSeek Harness **持久化 Cordis 插件**:面向全国大学生数学建模
30
31
  - **断点续跑与重置**:中断/关闭窗口不丢进度,再次 `/loopbegin` 自动续跑;`/loopreset` 或面板「重置(全新开始)」清空续跑记录
31
32
  - **原生工作流卡片**:聊天区实时显示每个子智能体的运行状态(执行/扫描/评审/兜底)
32
33
 
33
- ## 五阶段流水线(每遍 22 个子阶段)
34
+ ## 五阶段流水线( 22 个子阶段)
34
35
 
35
36
  | 阶段 | 子阶段 | 核心产出 |
36
37
  |------|--------|----------|
@@ -47,8 +48,8 @@ DeepSeek Harness **持久化 Cordis 插件**:面向全国大学生数学建模
47
48
 
48
49
  1. **发送赛题**:把数学建模竞赛题目完整粘贴到对话框并发送;
49
50
  2. **触发插件**:发送一条以 `/loopbegin` 开头的消息:
50
- - `/loopbegin` —— 默认 1 遍(22 个子阶段)
51
- - `/loopbegin --round=3` —— 3 遍外循环(上限 10)
51
+ - `/loopbegin` —— 默认启动(22 个子阶段)
52
+ - `/loopbegin --rollback-limit=5` —— 同一阶段回滚上限改为 5 次(默认 10,1~99)
52
53
  - `/loopbegin --from 题目.txt` —— 从文件读取赛题(支持含空格/中文的文件名,也可用 `--from=文件` 形式)
53
54
  - `/loopbegin --model glm-vision/glm-4.7-Flash` —— 可选显式覆盖(也可用 `--provider 提供商 --model 模型` 分拆形式)。**默认不需要此参数**:流水线子任务的模型**强制跟随你在对话右下角选择的模型**(每次启动时读取当前选择);若无法读取,兜底使用 `deepseek-official/deepseek-v4-flash`
54
55
  3. **查看进度**:右下角浮动面板(阶段打点、中控台、回滚、日志、文件)+ 聊天区工作流卡片。
@@ -59,7 +60,7 @@ DeepSeek Harness **持久化 Cordis 插件**:面向全国大学生数学建模
59
60
 
60
61
  | 消息内容 | 是否触发 |
61
62
  |---|---|
62
- | `/loopbegin --round=3`(开头) | ✅ |
63
+ | `/loopbegin --rollback-limit=5`(开头) | ✅ |
63
64
  | ` /loopbegin`(前导空格) | ✅ |
64
65
  | `题目正文… /loopbegin`(不在开头) | ❌ |
65
66
  | `/loopstatus`、`/loopabort`、`/loopreset` | 辅助命令,不触发 |
@@ -114,7 +115,7 @@ pnpm dsh plugin --profile web remove dsh-mcmp
114
115
 
115
116
  ## 架构
116
117
 
117
- > 插件按职责拆分为三个模块,主循环按《架构.md》v3 实现。
118
+ > 插件按职责拆分为三个模块,主循环按《ARCHITECTURE.md》v3 实现。
118
119
 
119
120
  ```
120
121
  用户消息(/loopbegin 开头)
@@ -125,12 +126,12 @@ pnpm dsh plugin --profile web remove dsh-mcmp
125
126
  ├─ 模型选择:父对话当前选择 / --model 显式覆盖 / 兜底 DeepSeek V4 Flash
126
127
  ├─ 题目检测:提取赛题(对话文本或 --from 文件)
127
128
  ├─ 识图能力探测(宿主服务/工具注册表扫描;子智能体以自身工具列表为准)
128
- └─ 启动解析与校验(--round/--fresh/--model/--from)→ 委托主循环模块启动
129
+ └─ 启动解析与校验(--rollback-limit/--fresh/--model/--from)→ 委托主循环模块启动
129
130
 
130
131
  论文写作主循环模块(lib/pipeline.js,createPipeline 实例)
131
- ├─ 五阶段 × 22 子阶段:每个子阶段启动执行Agent(提示词由 提示词.md 2.1/2.2 + 三 生成)
132
- ├─ 中控台·扫描验证模块:执行Agent上报 SUCCESS 后按 3.3 六维度扫描(提示词.md 4.1)
133
- ├─ 中控台·评审决策模块:P0-P3 评级、根源定位、回滚裁定与 P3 定点修改(提示词.md 5.1)
132
+ ├─ 五阶段 × 22 子阶段:每个子阶段启动执行Agent(提示词由 PROMPTS.md 2.1/2.2 + 三 生成)
133
+ ├─ 中控台·扫描验证模块:执行Agent上报 SUCCESS 后按 3.3 六维度扫描(PROMPTS.md 4.1)
134
+ ├─ 中控台·评审决策模块:P0-P3 评级、根源定位、回滚裁定与 P3 定点修改(PROMPTS.md 5.1)
134
135
  ├─ 代码逻辑:回滚计数器、阶段级删除调度、强制锁定(FORCED_FINAL)、重跑、断点续跑
135
136
  ├─ 执行失败重试(3 次指数退避)+ 事务日志 transactions.log
136
137
  └─ 兜底定稿:不论成败补一版 Final_Paper.md
@@ -149,8 +150,8 @@ pnpm dsh plugin --profile web remove dsh-mcmp
149
150
  | --- | --- |
150
151
  | `package.json` | 插件包清单:`dsh.bundle` 补丁声明、`dsh.client` 声明、`exports` 入口(`.` → Host,`./client` → 面板 bundle) |
151
152
  | `cordis.patch.yml` | **bundle 补丁**:安装时自动注册插件行(`insert: mcmp`),无需手动编辑配置 |
152
- | `架构.md` | **流程架构设计书 v3.17**:中控台双模块、五阶段、评级/回滚/教训/索引机制与约束 |
153
- | `提示词.md` | **Prompt 设计 v3.4**:执行Agent/扫描验证模块/评审决策模块的全部提示词模板(主循环按此生成) |
153
+ | `ARCHITECTURE.md` | **流程架构设计书 v3.18**:中控台双模块、五阶段、评级/回滚/教训/索引机制与约束 |
154
+ | `PROMPTS.md` | **Prompt 设计 v3.5**:执行Agent/扫描验证模块/评审决策模块的全部提示词模板(主循环按此生成) |
154
155
  | `lib/index.js` | **程序入口模块**:输入框内检测、模型选择、题目检测、识图能力探测、启动参数解析与校验、`/mcmp-api` 面板路由注册 |
155
156
  | `lib/pipeline.js` | **论文写作主循环模块**:五阶段元数据与提示词构造、中控台扫描/评审调度、回滚与锁定、断点续跑、运行状态与进度快照、中止/重置 |
156
157
  | `lib/client.js` | **显示界面模块**:浮动进度面板(标准 `__ModuleLoader__` bundle,`fetch` 轮询、显示与交互) |
package/lib/client.js CHANGED
@@ -2,7 +2,7 @@
2
2
  * dsh-mcmp · 显示界面模块(Client 半,标准 __ModuleLoader__ 客户端 bundle)
3
3
  * 小浮窗的显示与交互:每 1.2s 通过 fetch('/mcmp-api/state') 轮询 Host 状态,
4
4
  * 中止/重置通过 POST /mcmp-api/abort|reset。拖拽仅绑定标题文字。
5
- * 按《架构.md》v3 目标架构渲染:五阶段(阶段零~四)子阶段进度、中控台模块、
5
+ * 按《ARCHITECTURE.md》v3 目标架构渲染:五阶段(阶段零~四)子阶段进度、中控台模块、
6
6
  * 回滚计数与强制锁定、P0-P3 问题评级;主循环重写前,快照无 stages 字段时
7
7
  * 自动回退到旧 S1-S8 步骤渲染(见 index.js 中的 v3 快照契约)。
8
8
  * (程序入口模块见 lib/index.js,论文写作主循环模块见 lib/pipeline.js)
@@ -82,7 +82,7 @@ window.__ModuleLoader__.load({
82
82
  if (!snap || snap.status === 'idle' || dismissedRun === snap.runId) return null
83
83
  const st = snap.status
84
84
  const pct = snap.pct
85
- // v3 架构(架构.md):快照含 stages(五阶段)时走新渲染;当前主循环(旧 S1-S8)缺失该字段,回退旧渲染
85
+ // v3 架构(ARCHITECTURE.md):快照含 stages(五阶段)时走新渲染;当前主循环(旧 S1-S8)缺失该字段,回退旧渲染
86
86
  const isV3 = Array.isArray(snap.stages) && snap.stages.length > 0
87
87
 
88
88
  // 拖拽只绑定在标题文字上,避免指针捕获吞掉同栏按钮的点击
package/lib/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * dsh-mcmp · 数学建模竞赛自动化论文撰写系统 —— 程序入口模块(Host 侧)
3
3
  *
4
- * 按《架构.md》v3 目标架构对齐:中控台(扫描验证模块 + 评审决策模块)+ 五阶段流水线
4
+ * 按《ARCHITECTURE.md》v3 目标架构对齐:中控台(扫描验证模块 + 评审决策模块)+ 五阶段流水线
5
5
  * (阶段零初始化 → 阶段一EDA → 阶段二建模 → 阶段三撰写 → 阶段四排版)。
6
6
  * 论文写作主循环模块(lib/pipeline.js)将在后续版本重写为该架构;本模块与显示界面
7
7
  * 模块先行适配——命令文案、启动提示、模型侧提示、/loopstatus 与面板快照契约均按
@@ -16,7 +16,7 @@
16
16
  * 右下角模型选择器 → agent-default-model)、兜底 DeepSeek V4 Flash;
17
17
  * - 本地题目检测:从对话历史提取赛题(extractProblem)或 --from 指定本地文件;
18
18
  * - 识图能力探测(detectVision,宿主侧,仅供参考;以子智能体自检为准);
19
- * - 启动参数解析与校验(startPipeline:--round/--fresh/--model/--from),
19
+ * - 启动参数解析与校验(startPipeline:--rollback-limit/--fresh/--model/--from),
20
20
  * 校验通过后委托论文写作主循环模块(lib/pipeline.js)启动运行;
21
21
  * - 面板 API 路由(/mcmp-api/*)注册——显示界面模块(lib/client.js 浮窗)的
22
22
  * 宿主侧接口,转发到主循环模块的 snapshot/abort/reset。
@@ -26,7 +26,7 @@
26
26
  import { createPipeline, PER_ROUND } from './pipeline.js'
27
27
 
28
28
  // ---------------------------------------------------------------------------
29
- // 未来主循环模块(v3,参照《架构.md》)的快照契约(snapshot 输出):
29
+ // 未来主循环模块(v3,参照《ARCHITECTURE.md》)的快照契约(snapshot 输出):
30
30
  // 显示界面模块与本模块的 /loopstatus 按此渲染。当前主循环(旧 S1-S8)不提供
31
31
  // stages/supervisor 字段,面板与状态命令自动回退到旧渲染;主循环重写为 v3 后,
32
32
  // 移除旧渲染回退即可。
@@ -47,7 +47,7 @@ import { createPipeline, PER_ROUND } from './pipeline.js'
47
47
  // cur: { stageKey, stageIdx, stageName, subIdx, subName, subTotal }, // 当前子阶段
48
48
  // supervisor: { // 中控台状态
49
49
  // module: '扫描验证模块' | '评审决策模块' | null, // 当前值班模块
50
- // rollbackCount: 0, rollbackLimit: 10, forcedFinal: false, // 回滚与强制锁定(R-021)
50
+ // rollbackCount: 0, rollbackLimit: 10, forcedFinal: false, // 回滚与强制锁定(R-021;上限可由 --rollback-limit 配置,默认 10)
51
51
  // issues: { P0: 0, P1: 0, P2: 0, P3: 0 }, // 累计问题评级(3.4)
52
52
  // },
53
53
  // total: 22, done: 7, pct: 31, // 子阶段总数进度(4+3+5+7+3=22)
@@ -163,12 +163,19 @@ export function apply(ctx) {
163
163
  }
164
164
  pipeline.trackSession(session)
165
165
  const raw = rawInput || ''
166
- const mRound = /(?:^|\s)--round\s*[=:]?\s*(\d+)/i.exec(raw)
167
- let rounds = 1
168
- if (mRound) rounds = parseInt(mRound[1], 10)
169
- if (!Number.isSafeInteger(rounds) || rounds < 1) rounds = 1
170
- if (rounds > 10) return { kind: 'error', text: '--round 最大为 10。每遍 22 个子阶段、每个子阶段由专家子智能体执行并经过中控台双重验证,遍数过大会非常耗时。' }
171
166
  const fresh = /(?:^|\s)--fresh\b/i.test(raw)
167
+ // 同一阶段回滚上限(架构 3.7):默认 10 次,可用 --rollback-limit=N 参数化配置(1~99)
168
+ let rollbackLimit = 10
169
+ {
170
+ const mLimit = /(?:^|\s)--rollback-limit\s*[=:]?\s*(\d+)/i.exec(raw)
171
+ if (mLimit) {
172
+ const v = parseInt(mLimit[1], 10)
173
+ if (!Number.isSafeInteger(v) || v < 1 || v > 99) {
174
+ return { kind: 'error', text: '--rollback-limit 需为 1~99 的整数(同一阶段回滚上限,默认 10,架构 3.7)。' }
175
+ }
176
+ rollbackLimit = v
177
+ }
178
+ }
172
179
  // 子任务模型:默认强制跟随父对话当前选择的模型(右下角模型选择器);
173
180
  // 可选 --model 提供商/模型 显式覆盖;无法读取时兜底 DeepSeek V4 Flash。
174
181
  let explicitAgentOptions = undefined
@@ -190,7 +197,7 @@ export function apply(ctx) {
190
197
  }
191
198
  }
192
199
  const agentOptions = explicitAgentOptions || resolveForcedModel()
193
- // --from 路径:允许含空格,遇到下一个 -- 标志或行尾即结束,避免吞掉后面的 --round/--fresh
200
+ // --from 路径:允许含空格,遇到下一个 -- 标志或行尾即结束,避免吞掉后面的 --rollback-limit/--fresh
194
201
  const mFrom = /(?:^|\s)--from(?:=)?\s*([^\r\n]+?)(?=\s+--|\r?$)/i.exec(raw)
195
202
  let problem = null
196
203
  if (mFrom) {
@@ -211,18 +218,18 @@ export function apply(ctx) {
211
218
  const problemPath = problem.kind === 'file' ? problem.path : wsDir + '/00_赛题原文.md'
212
219
  const title = problem.kind === 'text' ? problem.text.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0)[0] : problem.path
213
220
  const vision = detectVision()
214
- const args = { rounds, wsDir, problemPath, problem, title: title ? title.slice(0, 40) : '赛题', vision, agentOptions }
215
- // 断点续跑:扫描本会话落盘的 v2 工作流记录,跳过已完成的迭代
221
+ const args = { rollbackLimit, wsDir, problemPath, problem, title: title ? title.slice(0, 40) : '赛题', vision, agentOptions }
222
+ // 断点续跑:扫描本会话落盘的 v3 工作流记录,跳过已完成的子阶段
216
223
  const resumeCount = fresh ? 0 : pipeline.completedIterations(session)
217
- if (resumeCount >= rounds * PER_ROUND) {
218
- return { kind: 'error', text: '检测到本会话已完成 ' + resumeCount + ' 个子阶段( 本次请求的 ' + (rounds * PER_ROUND) + ' 个),无需重复。\n- 如需继续优化,请增大遍数,例如 /loopbegin --round=' + Math.min(rounds + 1, 10) + '\n- 如需从头重跑,请使用 /loopbegin --fresh' }
224
+ if (resumeCount >= PER_ROUND) {
225
+ return { kind: 'error', text: '检测到本会话已完成 ' + resumeCount + ' 个子阶段( ' + PER_ROUND + ' 个),无需重复。\n- 如需从头重跑,请使用 /loopbegin --fresh' }
219
226
  }
220
227
  pipeline.start({ session, agent, args, resumeCount, explicitModel: !!explicitAgentOptions })
221
228
  return {
222
229
  kind: 'success',
223
230
  text: '数学建模竞赛自动化论文撰写系统(v3)已启动。\n输出目录: ' + wsDir
224
231
  + (resumeCount > 0 ? '\n断点续跑:已跳过此前完成的 ' + resumeCount + ' 个子阶段,从第 ' + (resumeCount + 1) + ' 个继续。' : '\n全新运行(若输出目录有旧成果,流水线会先按架构规则归档)。')
225
- + '\n流程:阶段零(初始化)→ 阶段一(EDA)→ 阶段二(建模)→ 阶段三(撰写)→ 阶段四(排版)。每个子阶段完成后:执行智能体按统一质疑标准自检,再由中控台双重质疑(扫描验证模块 + 评审决策模块);问题按 P0-P3 评级——P0/P1/P2 触发阶段级回滚(根源阶段及之后全部产出移入 deprecated/,写入教训文件 ROLLBACK_LESSONS.yaml,同一阶段回滚≥10 次强制锁定并通知人工),P3 由中控台精确定点修改;全部产出登记到索引 FILE_INDEX.yaml。'
232
+ + '\n流程:阶段零(初始化)→ 阶段一(EDA)→ 阶段二(建模)→ 阶段三(撰写)→ 阶段四(排版),共 22 个子阶段。每个子阶段完成后:执行智能体按统一质疑标准自检,再由中控台双重质疑(扫描验证模块 + 评审决策模块);问题按 P0-P3 评级——P0/P1/P2 触发阶段级回滚(根源阶段及之后全部产出移入 deprecated/,写入教训文件 ROLLBACK_LESSONS.yaml,同一阶段回滚≥' + rollbackLimit + ' 次强制锁定并通知人工,上限可用 --rollback-limit 调整),P3 由中控台精确定点修改;全部产出登记到索引 FILE_INDEX.yaml。'
226
233
  + '\n无论成败,最终都会生成完整论文 Final_Paper.md。\n进度请在右下角浮动面板实时查看(含阶段、中控台、回滚与评级),聊天区也会出现流水线运行卡片。\n提示:中断或关闭聊天窗口都不会丢进度,再次发送 /loopbegin 即从断点续跑;如要重头开始,点面板「重置(全新开始)」、运行 /loopreset 或加 --fresh。',
227
234
  }
228
235
  } catch (err) {
@@ -236,7 +243,7 @@ export function apply(ctx) {
236
243
  commands.register({
237
244
  name: 'loopbegin',
238
245
  description: '启动数学建模竞赛自动化论文撰写系统(中控台双重质疑 + 五阶段流水线:阶段零初始化/阶段一EDA/阶段二建模/阶段三撰写/阶段四排版;P0-P3 评级、阶段级回滚与教训持久化,成果全部落盘)',
239
- input: { hint: '可选 --from 题目文件 指定赛题文件;--model 提供商/模型 强制子任务模型(如 --model glm-vision/glm-4.7-Flash);--round=N 外循环轮数(默认 1);--fresh 从头重跑' },
246
+ input: { hint: '可选 --from 题目文件 指定赛题文件;--model 提供商/模型 强制子任务模型(如 --model glm-vision/glm-4.7-Flash);--rollback-limit=N 同一阶段回滚上限(默认 10,1~99);--fresh 从头重跑' },
240
247
  handler: (invocation) => startPipeline(invocation.agent, invocation.rawInput),
241
248
  })
242
249
  } catch (err) { console.warn('dsh-mcmp: loopbegin 注册失败(可能被其他实例占用): ' + String((err && err.message) || err)) }
@@ -268,7 +275,7 @@ export function apply(ctx) {
268
275
  description: '查看数学建模论文流水线的当前进度',
269
276
  handler: () => {
270
277
  const s = pipeline.snapshot()
271
- if (s.status === 'idle') return { kind: 'success', text: '流水线空闲。粘贴赛题后发送以 /loopbegin 开头的消息(可带 --round=N)即可启动。' }
278
+ if (s.status === 'idle') return { kind: 'success', text: '流水线空闲。粘贴赛题后发送以 /loopbegin 开头的消息(可带 --from 文件或 --rollback-limit=N)即可启动。' }
272
279
  // v3 架构快照(stages/supervisor 字段)优先;当前主循环(旧 S1-S8)缺失时回退旧渲染
273
280
  const isV3 = Array.isArray(s.stages) && s.stages.length > 0
274
281
  let text = '状态: ' + ({ running: '运行中', completed: '已完成', error: '出错', cancelled: '已中止' }[s.status] || s.status)
@@ -320,7 +327,7 @@ export function apply(ctx) {
320
327
  systemPrompt.section({
321
328
  name: 'mcmp-pipeline',
322
329
  order: 130,
323
- text: '数学建模流水线说明:当用户消息以 /loopbegin 开头(可带 --round=N、--from 文件或 --fresh 参数)时,插件已自动在后台启动「数学建模竞赛自动化论文撰写系统」(中控台 + 五阶段流水线:阶段零初始化→阶段一EDA→阶段二建模→阶段三撰写→阶段四排版,由工作流子智能体执行;每个子阶段完成后先由执行智能体按统一质疑标准自检,再由中控台扫描验证模块与评审决策模块双重质疑,问题按 P0-P3 评级:P0/P1/P2 触发阶段级回滚并写入教训文件 ROLLBACK_LESSONS.yaml,同一阶段回滚≥10 次强制锁定;P3 由中控台精确定点修改;全部产出登记到索引 FILE_INDEX.yaml)。此时你只需用一两句话确认已启动,并提示用户查看右下角进度面板与聊天区的工作流运行卡片;绝对不要自己去编写或执行该流水线的任何步骤,不要重复启动。系统无论成败都会产出完整论文 Final_Paper.md。若用户消息包含赛题但未以 /loopbegin 开头,说明尚未启动流水线,不要擅自运行。辅助命令 /loopstatus、/loopabort、/loopreset(清空续跑记录,下次从头开始)由系统处理,你无需执行。',
330
+ text: '数学建模流水线说明:当用户消息以 /loopbegin 开头(可带 --from 文件、--fresh --rollback-limit=N 参数)时,插件已自动在后台启动「数学建模竞赛自动化论文撰写系统」(中控台 + 五阶段流水线:阶段零初始化→阶段一EDA→阶段二建模→阶段三撰写→阶段四排版,共 22 个子阶段,由工作流子智能体执行;每个子阶段完成后先由执行智能体按统一质疑标准自检,再由中控台扫描验证模块与评审决策模块双重质疑,问题按 P0-P3 评级:P0/P1/P2 触发阶段级回滚并写入教训文件 ROLLBACK_LESSONS.yaml,同一阶段回滚≥10 次强制锁定,上限可用 --rollback-limit 调整;P3 由中控台精确定点修改;全部产出登记到索引 FILE_INDEX.yaml)。此时你只需用一两句话确认已启动,并提示用户查看右下角进度面板与聊天区的工作流运行卡片;绝对不要自己去编写或执行该流水线的任何步骤,不要重复启动。系统无论成败都会产出完整论文 Final_Paper.md。若用户消息包含赛题但未以 /loopbegin 开头,说明尚未启动流水线,不要擅自运行。辅助命令 /loopstatus、/loopabort、/loopreset(清空续跑记录,下次从头开始)由系统处理,你无需执行。',
324
331
  })
325
332
  } catch (err) { console.warn('dsh-mcmp: systemPrompt 注册失败: ' + String((err && err.message) || err)) }
326
333
  }
package/lib/pipeline.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * dsh-mcmp · 数学建模竞赛自动化论文撰写系统 —— 论文写作主循环模块(Host 侧)
3
3
  *
4
- * 按《架构.md》v3.17 与《提示词.md》v3.4 实现正式流水线:
4
+ * 按《ARCHITECTURE.md》v3.18 与《PROMPTS.md》v3.5 实现正式流水线:
5
5
  * - 五阶段流水线(执行层):阶段零(初始化)→ 阶段一(EDA)→ 阶段二(建模)→
6
6
  * 阶段三(撰写)→ 阶段四(排版),共 22 个子阶段,每个子阶段由专家子智能体执行;
7
7
  * - 中控台(Supervisor)两个职能模块,均为独立子智能体:
@@ -23,7 +23,7 @@
23
23
  */
24
24
 
25
25
  export const FLOW_NAME = '数学建模竞赛自动化论文撰写系统v3'
26
- // 每遍流水线的子阶段总数(4+3+5+7+3);入口模块用它与 --round 计算总量
26
+ // 流水线子阶段总数(4+3+5+7+3);入口模块用它做「已完成无需重复」校验
27
27
  export const PER_ROUND = 22
28
28
 
29
29
  // 架构核心文件名(附录A)
@@ -32,8 +32,8 @@ const LESSONS_FILE = 'ROLLBACK_LESSONS.yaml'
32
32
  const TX_LOG = 'transactions.log'
33
33
  const FINAL_PAPER = 'Final_Paper.md'
34
34
  const DEPRECATED_DIR = 'deprecated/'
35
- // 防崩溃:同一阶段回滚上限(架构 3.7)
36
- const ROLLBACK_LIMIT = 10
35
+ // 防崩溃:同一阶段回滚上限(架构 3.7);默认 10,可由 --rollback-limit 参数化配置
36
+ const DEFAULT_ROLLBACK_LIMIT = 10
37
37
 
38
38
  // ---------- 统一质疑标准(架构 3.3,全部六个维度,Agent自检与中控台扫描同标准) ----------
39
39
  const DIM_STANDARD = [
@@ -54,7 +54,7 @@ const VISION_FLOW = [
54
54
  '视觉模块不可用时,必须在产出与 _report.yaml 中标记"待人工确认"。',
55
55
  ].join('\n')
56
56
 
57
- // ---------- 五阶段 × 22 子阶段(提示词.md 三) ----------
57
+ // ---------- 五阶段 × 22 子阶段(PROMPTS.md 三) ----------
58
58
  const STAGES = [
59
59
  { key: '阶段零', name: '初始化', dir: 'stage_00_outputs', subs: [
60
60
  { id: '0.1', name: '创建目录', task: '创建标准化目录结构', outputs: 'config/、raw_data/、stage_XX_outputs/、deprecated/、code/、figures/', focusDims: '规范符合性', focusItems: '目录结构完整?权限正确?', vision: false },
@@ -122,7 +122,7 @@ export function createPipeline({ fs, subagents, agents, retryBackoffMs }) {
122
122
  let lastSession = null // 最近一次运行所属会话,供「清空状态」追加重置标记
123
123
  const recording = new Map()
124
124
  // v3 运行期状态(供 snapshot 输出 stages/supervisor 契约字段)
125
- let stageDone = new Array(STAGES.length).fill(0) // 当前遍每阶段已完成子阶段数
125
+ let stageDone = new Array(STAGES.length).fill(0) // 每阶段已完成子阶段数
126
126
  let supervisor = null // { module, rollbackCount, rollbackLimit, forcedFinal, issues:{P0..P3} }
127
127
 
128
128
  function fmtTime(ts) {
@@ -338,7 +338,7 @@ export function createPipeline({ fs, subagents, agents, retryBackoffMs }) {
338
338
  return p
339
339
  }
340
340
 
341
- // 流水线执行Agent提示词(提示词.md 2.1 系统提示词 + 2.2 用户模板 + 三、各阶段配置)
341
+ // 流水线执行Agent提示词(PROMPTS.md 2.1 系统提示词 + 2.2 用户模板 + 三、各阶段配置)
342
342
  function buildExecPrompt(args, stage, sub, si, ii, reportPath) {
343
343
  const p = []
344
344
  p.push('你正在参与「全国大学生数学建模竞赛论文」自动化撰写系统,担任本子阶段的执行Agent。')
@@ -409,7 +409,7 @@ export function createPipeline({ fs, subagents, agents, retryBackoffMs }) {
409
409
  return p.join('\n')
410
410
  }
411
411
 
412
- // 中控台·扫描验证模块提示词(提示词.md 4.1)
412
+ // 中控台·扫描验证模块提示词(PROMPTS.md 4.1)
413
413
  function buildScanPrompt(args, stage, sub, reportPath, outputFiles) {
414
414
  const p = []
415
415
  p.push('你正在参与「全国大学生数学建模竞赛论文」自动化撰写系统,担任中控台的【扫描验证模块】。')
@@ -456,8 +456,8 @@ export function createPipeline({ fs, subagents, agents, retryBackoffMs }) {
456
456
  return p.join('\n')
457
457
  }
458
458
 
459
- // 中控台·评审决策模块提示词(提示词.md 5.1)
460
- function buildReviewPrompt(args, stage, source, issueText, countsText, pass) {
459
+ // 中控台·评审决策模块提示词(PROMPTS.md 5.1)
460
+ function buildReviewPrompt(args, stage, source, issueText, countsText, limit) {
461
461
  const p = []
462
462
  p.push('你正在参与「全国大学生数学建模竞赛论文」自动化撰写系统,担任中控台的【评审决策模块】。')
463
463
  p.push('')
@@ -487,13 +487,13 @@ export function createPipeline({ fs, subagents, agents, retryBackoffMs }) {
487
487
  p.push(issueText || '(问题报告缺失,请读取索引与教训文件自行核对当前阶段产出)')
488
488
  p.push('')
489
489
  p.push('【当前各阶段回滚计数器】' + countsText)
490
- p.push('【当前阶段】' + stage.key + ' ' + stage.name + '(第 ' + pass + ' 遍)')
490
+ p.push('【当前阶段】' + stage.key + ' ' + stage.name)
491
491
  p.push('')
492
492
  p.push('【执行】')
493
493
  p.push('1. 按P0-P3评级')
494
494
  p.push('2. P3:执行精确定点修改(边界:≤3行代码/≤1个句子/≤3个参数);修改前为STAGING则直接升级为VALIDATED,修改前为VALIDATED则状态不变')
495
495
  p.push('3. P0/P1/P2:通过DEPENDENCIES链追溯根源阶段,裁定回滚目标')
496
- p.push('4. 回滚时检查目标阶段计数器:<10次:物理删除根源阶段及之后所有阶段的全部产出→索引OBSOLETE→计数+1→写入教训(重跑前,先查重去重)→重跑;≥10次:FORCED_FINAL')
496
+ p.push('4. 回滚时检查目标阶段计数器:<' + limit + '次:物理删除根源阶段及之后所有阶段的全部产出→索引OBSOLETE→计数+1→写入教训(重跑前,先查重去重)→重跑;≥' + limit + '次:FORCED_FINAL')
497
497
  p.push('5. 若最终判定无需处置(NO_ACTION),同时将本次产出在索引中的状态更新为 VALIDATED')
498
498
  p.push('6. 处理完成后,将本次 _report.yaml 移入 ' + args.wsDir + '/' + DEPRECATED_DIR + '(重命名加时间戳保留),避免下一子阶段误读旧报告(架构 3.10.2-4)')
499
499
  p.push('')
@@ -623,10 +623,11 @@ export function createPipeline({ fs, subagents, agents, retryBackoffMs }) {
623
623
  if (active && active.runId === runId) active = null
624
624
  return
625
625
  }
626
- const total = args.rounds * PER_ROUND
626
+ const total = PER_ROUND // 单遍五阶段 22 个子阶段(线性推进 + 阶段级回滚,无外循环轮次)
627
+ const limit = Number.isSafeInteger(args.rollbackLimit) && args.rollbackLimit >= 1 ? args.rollbackLimit : DEFAULT_ROLLBACK_LIMIT
627
628
  const stageCounts = {} // 阶段级独立回滚计数器(架构 3.7:递增被回滚到的阶段)
628
629
  const issues = { P0: 0, P1: 0, P2: 0, P3: 0 } // 累计评级(仅展示)
629
- supervisor = { module: null, rollbackCount: 0, rollbackLimit: ROLLBACK_LIMIT, forcedFinal: false, issues }
630
+ supervisor = { module: null, rollbackCount: 0, rollbackLimit: limit, forcedFinal: false, issues }
630
631
  let totalRollbacks = 0
631
632
  let attemptSeq = 0
632
633
 
@@ -668,16 +669,11 @@ export function createPipeline({ fs, subagents, agents, retryBackoffMs }) {
668
669
  let g = args.startIndex || 0 // 全局平铺游标(已完成的子阶段数)
669
670
  while (g < total) {
670
671
  if (controller.signal.aborted) { cancelled = true; break }
671
- const r = Math.floor(g / PER_ROUND) + 1 // 当前遍数(--round 外循环)
672
672
  const f = FLAT[g % PER_ROUND]
673
673
  const si = f.s
674
674
  const ii = f.i
675
675
  const stage = STAGES[si]
676
676
  const sub = stage.subs[ii]
677
- if (g % PER_ROUND === 0) {
678
- stageDone = new Array(STAGES.length).fill(0)
679
- pushLog('【第 ' + r + '/' + args.rounds + ' 遍开始】')
680
- }
681
677
  const execLabel = stage.key + '·' + sub.id + ' ' + sub.name
682
678
  const reportPath = args.wsDir + '/' + stage.dir + '/_report.yaml'
683
679
  pushLog('开始: ' + execLabel)
@@ -731,7 +727,7 @@ export function createPipeline({ fs, subagents, agents, retryBackoffMs }) {
731
727
  supervisor.module = '评审决策模块'
732
728
  const countsText = STAGES.map((st, idx) => st.key + ': ' + (stageCounts[idx] || 0) + ' 次').join(', ')
733
729
  const source = issueText.indexOf('扫描验证模块发现问题') === 0 ? '扫描验证模块转交' : 'Agent上报'
734
- const review = await runAgent('评审决策模块', '中控台·评审决策·' + sub.id, '中控台·评审决策', buildReviewPrompt(args, stage, source, issueText, countsText, r))
730
+ const review = await runAgent('评审决策模块', '中控台·评审决策·' + sub.id, '中控台·评审决策', buildReviewPrompt(args, stage, source, issueText, countsText, limit))
735
731
  if (review.outcome === 'cancelled') { cancelled = true; break }
736
732
  if (review.outcome !== 'completed') {
737
733
  stopReason = 'error'
@@ -751,7 +747,7 @@ export function createPipeline({ fs, subagents, agents, retryBackoffMs }) {
751
747
  const targetKey = (/回滚目标[::]\s*(阶段[零一二三四])/.exec(txt) || /"rollback_target"\s*:\s*"(阶段[零一二三四])"/.exec(txt) || [])[1]
752
748
  const idx = targetKey ? STAGES.findIndex((st) => st.key === targetKey) : -1
753
749
  const targetIdx = idx >= 0 ? idx : si // 无法解析时保守回滚到当前阶段
754
- if ((stageCounts[targetIdx] || 0) >= ROLLBACK_LIMIT) {
750
+ if ((stageCounts[targetIdx] || 0) >= limit) {
755
751
  // 已 FORCED_FINAL 锁定:回滚请求降级为 P3 处理(架构 3.7),不再计数/不再回滚,继续推进
756
752
  pushLog('评审决策: 回滚目标 ' + STAGES[targetIdx].key + ' 已 FORCED_FINAL 锁定,本次降级为 P3 处理,继续推进')
757
753
  } else {
@@ -759,15 +755,15 @@ export function createPipeline({ fs, subagents, agents, retryBackoffMs }) {
759
755
  totalRollbacks += 1
760
756
  supervisor.rollbackCount = totalRollbacks
761
757
  appendRec(runId, session, 'tool-workflow/rollback', { runId, targetIdx, count: stageCounts[targetIdx], rating: rating || '', subKey: sub.id })
762
- if (stageCounts[targetIdx] >= ROLLBACK_LIMIT) {
763
- // 防崩溃(架构 3.7):同一阶段回滚≥10 → FORCED_FINAL 强制锁定,通知人工,问题降级为 P3 处理,继续推进
758
+ if (stageCounts[targetIdx] >= limit) {
759
+ // 防崩溃(架构 3.7):同一阶段回滚≥上限 → FORCED_FINAL 强制锁定,通知人工,问题降级为 P3 处理,继续推进
764
760
  supervisor.forcedFinal = true
765
- pushLog('强制锁定: ' + STAGES[targetIdx].key + ' 回滚已达 ' + ROLLBACK_LIMIT + ' 次,状态 FORCED_FINAL,请人工介入(问题降级为 P3 处理,流水线继续推进)')
766
- await txLog(args, fmtTime(Date.now()) + ' [强制锁定] ' + STAGES[targetIdx].key + ' 回滚≥' + ROLLBACK_LIMIT + ' 次,FORCED_FINAL,通知人工')
761
+ pushLog('强制锁定: ' + STAGES[targetIdx].key + ' 回滚已达 ' + limit + ' 次,状态 FORCED_FINAL,请人工介入(问题降级为 P3 处理,流水线继续推进)')
762
+ await txLog(args, fmtTime(Date.now()) + ' [强制锁定] ' + STAGES[targetIdx].key + ' 回滚≥' + limit + ' 次,FORCED_FINAL,通知人工')
767
763
  } else {
768
764
  const rollbackTo = STAGE_OFFSETS[targetIdx]
769
765
  pushLog('阶段级回滚: 根源定位 ' + STAGES[targetIdx].key + ',删除该阶段及之后全部产出(评审决策模块已执行物理删除),从该阶段重跑(第 ' + stageCounts[targetIdx] + ' 次回滚)')
770
- g = (r - 1) * PER_ROUND + rollbackTo
766
+ g = rollbackTo
771
767
  // 游标回退后同步进度:已完成的子阶段 = 根源阶段之前的全部子阶段(架构 R-002 只删根源及之后)
772
768
  state.done = g
773
769
  for (let k = targetIdx; k < stageDone.length; k++) stageDone[k] = 0
@@ -779,14 +775,13 @@ export function createPipeline({ fs, subagents, agents, retryBackoffMs }) {
779
775
  }
780
776
  supervisor.module = null
781
777
  // ---- 子阶段通过:登记完成、推进游标 ----
782
- appendRec(runId, session, 'tool-workflow/substage-done', { runId, subKey: sub.id, pass: r })
778
+ appendRec(runId, session, 'tool-workflow/substage-done', { runId, subKey: sub.id, pass: 1 })
783
779
  stageDone[si] = (stageDone[si] || 0) + 1
784
780
  if (sub.id === '4.3') finalDone = true
785
781
  g += 1
786
782
  state.done = g
787
783
  refreshFiles()
788
784
  pushLog('完成: ' + execLabel)
789
- if (g % PER_ROUND === 0 && !controller.signal.aborted) pushLog('【第 ' + r + '/' + args.rounds + ' 遍完成】')
790
785
  }
791
786
 
792
787
  if (cancelled) { stopReason = 'cancelled'; errorMsg = '已被用户中止' }
@@ -833,20 +828,21 @@ export function createPipeline({ fs, subagents, agents, retryBackoffMs }) {
833
828
  state = freshState()
834
829
  state.status = 'running'
835
830
  state.runId = runId
836
- state.rounds = args.rounds
837
- state.total = args.rounds * PER_ROUND
831
+ state.rounds = 1
832
+ state.total = PER_ROUND
838
833
  state.done = resumeCount
839
- state.round = Math.min(Math.floor(resumeCount / PER_ROUND) + 1, args.rounds)
834
+ state.round = 1
840
835
  state.startedAt = Date.now()
841
836
  state.wsDir = args.wsDir
842
837
  state.problemPath = args.problemPath
843
838
  state.title = args.title
844
839
  state.vision = args.vision
845
840
  stageDone = new Array(STAGES.length).fill(0)
846
- supervisor = { module: null, rollbackCount: 0, rollbackLimit: ROLLBACK_LIMIT, forcedFinal: false, issues: { P0: 0, P1: 0, P2: 0, P3: 0 } }
841
+ const limit = Number.isSafeInteger(args.rollbackLimit) && args.rollbackLimit >= 1 ? args.rollbackLimit : DEFAULT_ROLLBACK_LIMIT
842
+ supervisor = { module: null, rollbackCount: 0, rollbackLimit: limit, forcedFinal: false, issues: { P0: 0, P1: 0, P2: 0, P3: 0 } }
847
843
  const controller = makeController()
848
844
  active = { runId, session, controller }
849
- pushLog('流水线已启动: ' + args.rounds + ' × 五阶段 ' + PER_ROUND + ' 个子阶段(中控台双重质疑:扫描验证 + 评审决策),输出目录 ' + args.wsDir + (resumeCount > 0 ? '(断点续跑:从第 ' + (resumeCount + 1) + ' 个子阶段继续)' : '') + '(关闭本聊天窗口不影响后台执行)')
845
+ pushLog('流水线已启动: 五阶段 ' + PER_ROUND + ' 个子阶段(中控台双重质疑:扫描验证 + 评审决策;同一阶段回滚上限 ' + limit + ' ),输出目录 ' + args.wsDir + (resumeCount > 0 ? '(断点续跑:从第 ' + (resumeCount + 1) + ' 个子阶段继续)' : '') + '(关闭本聊天窗口不影响后台执行)')
850
846
  if (args.vision) pushLog('识图能力探测: ' + (args.vision.source === 'service' ? '检测到识图服务 ' + args.vision.key : '检测到识图工具 ' + (args.vision.tools || []).join(',')) + '(涉及图表的子阶段将自动调用)')
851
847
  else pushLog('识图能力探测:未检测到独立识图插件,涉及图表的子阶段将标记"待人工确认"')
852
848
  if (explicitModel) pushLog('子任务模型路由:强制 ' + args.agentOptions.provider + '/' + args.agentOptions.model + '(--model 显式覆盖)')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-mcmp",
3
- "version": "3.0.0",
3
+ "version": "3.1.1",
4
4
  "description": "DeepSeek Harness 插件:数学建模竞赛自动化论文撰写系统 v3(中控台双重质疑 + 五阶段流水线:扫描验证/评审决策、P0-P3 评级与阶段级回滚、教训持久化、索引导航、兜底定稿 Final_Paper.md、断点续跑、进度面板)",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",