dsh-mcmp 2.2.1 → 2.2.3
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 +1 -0
- package/lib/index.js +51 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -43,6 +43,7 @@ DeepSeek Harness **持久化 Cordis 插件**:面向全国大学生数学建模
|
|
|
43
43
|
- `/loopbegin` —— 默认 1 轮
|
|
44
44
|
- `/loopbegin --round=3` —— 3 轮外循环(上限 10)
|
|
45
45
|
- `/loopbegin --from 题目.txt` —— 从文件读取赛题(支持含空格/中文的文件名,如 `--from 2024 年高教社杯全国大学生数学建模竞赛题目.md`,也可用 `--from=文件` 形式)
|
|
46
|
+
- `/loopbegin --model glm-vision/glm-4.7-Flash` —— 可选显式覆盖(也可用 `--provider 提供商 --model 模型` 分拆形式)。**默认不需要此参数**:流水线子任务的模型**强制跟随你在对话右下角选择的模型**(每次启动时读取当前选择);若无法读取(未安装默认模型服务),兜底使用 `deepseek-official/deepseek-v4-flash`(high 推理由 deepseek 提供商默认提供)
|
|
46
47
|
3. **查看进度**:右下角浮动面板(百分比、步骤打点、日志、文件)+ 聊天区工作流卡片。
|
|
47
48
|
|
|
48
49
|
辅助命令:`/loopstatus`(查看进度)、`/loopabort`(中止,已产出成果保留)、`/loopreset`(清空状态与断点续跑记录,下次 `/loopbegin` 从头全新开始;等价于面板「重置(全新开始)」按钮)。
|
package/lib/index.js
CHANGED
|
@@ -35,6 +35,26 @@ export function apply(ctx) {
|
|
|
35
35
|
// 全流水线缺陷台账:跨步骤错误登记、裁决与向上修复的唯一权威文件
|
|
36
36
|
const LEDGER = '00_缺陷与裁决台账.md'
|
|
37
37
|
|
|
38
|
+
// 子任务模型路由解析:
|
|
39
|
+
// 1) 显式 --model 覆盖(优先级最高)
|
|
40
|
+
// 2) 父对话当前选择的模型(右下角模型选择器 → agent-default-model 设置)
|
|
41
|
+
// 3) 兜底:DeepSeek V4 Flash(high 推理由 deepseek 提供商默认提供)
|
|
42
|
+
// 无论哪种情况,都强制把 agentOptions 传给每个子任务,
|
|
43
|
+
// 避免子任务继承父会话"创建时固化"的模型选项(聊天里切换模型不生效的老问题)。
|
|
44
|
+
const FALLBACK_MODEL = { provider: 'deepseek-official', model: 'deepseek-v4-flash' }
|
|
45
|
+
function resolveForcedModel() {
|
|
46
|
+
try {
|
|
47
|
+
const adm = ctx.get('agentDefaultModel')
|
|
48
|
+
if (adm && typeof adm.currentSelection === 'function') {
|
|
49
|
+
const sel = adm.currentSelection()
|
|
50
|
+
if (sel && typeof sel.provider === 'string' && sel.provider && typeof sel.model === 'string' && sel.model) {
|
|
51
|
+
return { provider: sel.provider, model: sel.model }
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
} catch (err) { /* ignore */ }
|
|
55
|
+
return { ...FALLBACK_MODEL }
|
|
56
|
+
}
|
|
57
|
+
|
|
38
58
|
// ---------- 识图能力探测(宿主侧,仅供参考;以子智能体自检为准) ----------
|
|
39
59
|
function detectVision() {
|
|
40
60
|
// 1) 宿主服务:常见的识图服务键
|
|
@@ -426,7 +446,8 @@ export function apply(ctx) {
|
|
|
426
446
|
}
|
|
427
447
|
|
|
428
448
|
// 启动一个子智能体并返回 { ok, text, outcome }
|
|
429
|
-
|
|
449
|
+
// agentOptions:可选 { provider, model } —— 强制子任务使用指定模型路由(覆盖父会话的固化选项)
|
|
450
|
+
async function runChild(provider, session, agent, controller, label, promptText, onStarted, agentOptions) {
|
|
430
451
|
let child = null
|
|
431
452
|
try {
|
|
432
453
|
const parent = (agents && agents.get(session.id)) || agent
|
|
@@ -435,6 +456,7 @@ export function apply(ctx) {
|
|
|
435
456
|
prompt: [{ type: 'text', text: promptText }],
|
|
436
457
|
parent,
|
|
437
458
|
signal: controller.signal,
|
|
459
|
+
...(agentOptions && agentOptions.provider && agentOptions.model ? { agentOptions } : {}),
|
|
438
460
|
})
|
|
439
461
|
if (onStarted) { try { onStarted(child) } catch (err) { /* ignore */ } }
|
|
440
462
|
const res = await child.result
|
|
@@ -479,7 +501,7 @@ export function apply(ctx) {
|
|
|
479
501
|
const res = await runChild(provider, session, agent, controller, label, buildPrompt(r, f.s, f.i, s, it, args), (child) => {
|
|
480
502
|
started = true
|
|
481
503
|
appendRec(runId, session, 'tool-workflow/agent-start', { runId, seq: g + 1, label, phase: s.phase, childId: String(child.id) })
|
|
482
|
-
})
|
|
504
|
+
}, args.agentOptions)
|
|
483
505
|
outcome = res.outcome
|
|
484
506
|
note = (res.text || '').trim().slice(0, 300)
|
|
485
507
|
if (res.err) note = '执行异常: ' + String((res.err && res.err.message) || res.err).slice(0, 200)
|
|
@@ -506,7 +528,7 @@ export function apply(ctx) {
|
|
|
506
528
|
if (controller.signal.aborted) { cancelled = true; break }
|
|
507
529
|
if (failStreak >= 3) {
|
|
508
530
|
stopReason = 'error'
|
|
509
|
-
errorMsg = '连续 3
|
|
531
|
+
errorMsg = '连续 3 个子任务失败,流水线提前终止。常见原因:模型服务限流(429)/账户余额不足(402)/上下文过长/工作区文件问题;可尝试 /loopbegin --model 提供商/模型 换用其他模型,或 /loopreset 后重来。已有成果全部保留。'
|
|
510
532
|
break
|
|
511
533
|
}
|
|
512
534
|
}
|
|
@@ -521,7 +543,7 @@ export function apply(ctx) {
|
|
|
521
543
|
// 否则面板「中止」与 /loopabort 只会 abort 已失效的主控制器,兜底定稿无法被中止
|
|
522
544
|
if (active && active.runId === runId) active.controller = fresh
|
|
523
545
|
const reason = stopReason === 'cancelled' ? '用户中止' : stopReason === 'error' ? errorMsg : '正常流程已结束但定稿步骤未完成'
|
|
524
|
-
const fres = await runChild(provider, session, agent, fresh, '兜底·论文定稿(不论成败)', buildFinalizePrompt(args, reason))
|
|
546
|
+
const fres = await runChild(provider, session, agent, fresh, '兜底·论文定稿(不论成败)', buildFinalizePrompt(args, reason), undefined, args.agentOptions)
|
|
525
547
|
if (fres.outcome === 'completed') {
|
|
526
548
|
finalDone = true
|
|
527
549
|
pushLog('兜底定稿完成: 论文定稿.md 已生成')
|
|
@@ -560,6 +582,27 @@ export function apply(ctx) {
|
|
|
560
582
|
if (!Number.isSafeInteger(rounds) || rounds < 1) rounds = 1
|
|
561
583
|
if (rounds > 10) return { kind: 'error', text: '--round 最大为 10。每轮 19 次迭代、每次迭代启动一个专家子智能体,轮数过大会非常耗时。' }
|
|
562
584
|
const fresh = /(?:^|\s)--fresh\b/i.test(raw)
|
|
585
|
+
// 子任务模型:默认强制跟随父对话当前选择的模型(右下角模型选择器);
|
|
586
|
+
// 可选 --model 提供商/模型 显式覆盖;无法读取时兜底 DeepSeek V4 Flash。
|
|
587
|
+
let explicitAgentOptions = undefined
|
|
588
|
+
{
|
|
589
|
+
const mProvider = /(?:^|\s)--provider\s*[=:]?\s*([A-Za-z0-9._\-]+)/i.exec(raw)
|
|
590
|
+
const mModel = /(?:^|\s)--model\s*[=:]?\s*([A-Za-z0-9._\-]+(?:\/[A-Za-z0-9._\-]+)?)/i.exec(raw)
|
|
591
|
+
if (mModel) {
|
|
592
|
+
const rawModel = mModel[1]
|
|
593
|
+
const slash = rawModel.indexOf('/')
|
|
594
|
+
if (slash >= 0) {
|
|
595
|
+
explicitAgentOptions = { provider: rawModel.slice(0, slash), model: rawModel.slice(slash + 1) }
|
|
596
|
+
} else if (mProvider) {
|
|
597
|
+
explicitAgentOptions = { provider: mProvider[1], model: rawModel }
|
|
598
|
+
} else {
|
|
599
|
+
return { kind: 'error', text: '--model 需要指定提供商/模型,例如 --model glm-vision/glm-4.7-Flash;也可配合 --provider 提供商 --model 模型 使用。' }
|
|
600
|
+
}
|
|
601
|
+
} else if (mProvider) {
|
|
602
|
+
return { kind: 'error', text: '--provider 必须配合 --model 使用,例如 --provider glm-vision --model glm-4.7-Flash。' }
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
const agentOptions = explicitAgentOptions || resolveForcedModel()
|
|
563
606
|
// --from 路径:允许含空格,遇到下一个 -- 标志或行尾即结束,避免吞掉后面的 --round/--fresh
|
|
564
607
|
const mFrom = /(?:^|\s)--from(?:=)?\s*([^\r\n]+?)(?=\s+--|\r?$)/i.exec(raw)
|
|
565
608
|
let problem = null
|
|
@@ -581,7 +624,7 @@ export function apply(ctx) {
|
|
|
581
624
|
const problemPath = problem.kind === 'file' ? problem.path : wsDir + '/00_赛题原文.md'
|
|
582
625
|
const title = problem.kind === 'text' ? problem.text.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0)[0] : problem.path
|
|
583
626
|
const vision = detectVision()
|
|
584
|
-
const args = { rounds, wsDir, problemPath, problem, title: title ? title.slice(0, 40) : '赛题', vision }
|
|
627
|
+
const args = { rounds, wsDir, problemPath, problem, title: title ? title.slice(0, 40) : '赛题', vision, agentOptions }
|
|
585
628
|
// 断点续跑:扫描本会话落盘的 v2 工作流记录,跳过已完成的迭代
|
|
586
629
|
const resumeCount = fresh ? 0 : completedIterations(session)
|
|
587
630
|
args.startIndex = resumeCount
|
|
@@ -613,6 +656,8 @@ export function apply(ctx) {
|
|
|
613
656
|
pushLog('流水线已启动: ' + rounds + ' 轮 × 19 次迭代,输出目录 ' + wsDir + (resumeCount > 0 ? '(断点续跑:跳过已完成的 ' + resumeCount + ' 次迭代)' : '') + '(关闭本聊天窗口不影响后台执行)')
|
|
614
657
|
if (vision) pushLog('识图能力探测: ' + (vision.source === 'service' ? '检测到识图服务 ' + vision.key : '检测到识图工具 ' + (vision.tools || []).join(',')) + '(S5 将自动调用)')
|
|
615
658
|
else pushLog('识图能力探测:未检测到独立识图插件,S5 将让子智能体自检(无识图则走脚本分析)')
|
|
659
|
+
if (explicitAgentOptions) pushLog('子任务模型路由:强制 ' + agentOptions.provider + '/' + agentOptions.model + '(--model 显式覆盖)')
|
|
660
|
+
else pushLog('子任务模型路由:跟随父对话当前选择 ' + agentOptions.provider + '/' + agentOptions.model + ' (无法读取时兜底 deepseek-official/deepseek-v4-flash)')
|
|
616
661
|
refreshFiles()
|
|
617
662
|
void runPipeline(runId, session, agent, args, controller).catch((err) => {
|
|
618
663
|
pushLog('流水线异常: ' + String((err && err.message) || err))
|
|
@@ -636,7 +681,7 @@ export function apply(ctx) {
|
|
|
636
681
|
commands.register({
|
|
637
682
|
name: 'loopbegin',
|
|
638
683
|
description: '启动数学建模论文自动化流水线 v2(8 步骤:含图表视觉自检、评审修复闭环、论文定稿;每轮 19 次迭代、多轮外循环,成果全部落盘)',
|
|
639
|
-
input: { hint: '--round=N 外循环轮数(默认 1);可选 --from 题目文件 指定赛题文件;--fresh 从头重跑' },
|
|
684
|
+
input: { hint: '--round=N 外循环轮数(默认 1);--model 提供商/模型 强制子任务模型(如 --model glm-vision/glm-4.7-Flash);可选 --from 题目文件 指定赛题文件;--fresh 从头重跑' },
|
|
640
685
|
handler: (invocation) => startPipeline(invocation.agent, invocation.rawInput),
|
|
641
686
|
})
|
|
642
687
|
} catch (err) { console.warn('dsh-mcmp: loopbegin 注册失败(可能被其他实例占用): ' + String((err && err.message) || err)) }
|
package/package.json
CHANGED