dsh-math-modeling-agent 0.2.0 → 0.2.2

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
@@ -8,6 +8,8 @@
8
8
 
9
9
  ## 工作方式
10
10
 
11
+ <p align="center"><img src="./assets/mathmodelingagent-overview.svg" alt="MathModelingAgent 工作流总览" width="100%"/></p>
12
+
11
13
  1. **题目与数据**:明确问题目标、输入、约束、缺失信息与歧义。
12
14
  2. **理解与拆解**:拆成可验证的子问题,登记假设、候选模型与关键主张。
13
15
  3. **建模与执行**:提出候选方案,用数值计算、优化、仿真、符号计算、文献、形式验证执行并保留产物。
@@ -81,14 +83,20 @@ NOT_CHECKED → DERIVED → EXECUTED → VERIFIED → INDEPENDENTLY_VERIFIED →
81
83
  默认运行目录 `math-modeling-runs/<task-id>/`:
82
84
 
83
85
  ```text
84
- run.json # 原子状态快照
85
- ledger.json # 范围、假设、主张、义务、子问题、候选、issue
86
- events.jsonl # 追加式转移日志
87
- attempts/<n>/ # 每轮报告与产物
88
- research/ walls/ # 文献检索 / 放弃方向的突破备忘录
86
+ run.json ledger.json events.jsonl # 原子状态 + 契约(interactions/decisionStack)+ 日志
87
+ problem-brief.md inputs.json # 冻结的问题重述 + 输入清单
88
+ attempts/<n>/ # 每轮:report.md + code/ + data/ + plots/ + _drafts/
89
+ failed/directions/<id>/ # 方向级失败:wall memo + 代码 + 放弃理由
90
+ failed/code-drafts/<attempt>-<name>/ # 实现级失败:bug 版/超时版/弃用版(不删除)
91
+ failed/issues.md # 失败账本(DATA/TOOL/IMPLEMENTATION/MODEL/VALIDATION/EVIDENCE/RESEARCH_GAP)
92
+ research/ walls/ # 文献检索 / 突破备忘录
89
93
  reproducibility.json final-report.md
90
94
  ```
91
95
 
96
+ v0.2.0 起:每个会改变模型结构的决策在对话中交互确认并写入 ledger(D0 重述/D1 路由/
97
+ D2 子问题假设/D3 方向/D4 裁决),`run-state.mjs gate` 在每次状态转移前强制校验;
98
+ 终态前强制鲁棒性敏感度分析;失败尝试按类归档到独立目录。
99
+
92
100
  崩溃恢复:跨进程互斥锁 + stale 锁与 reclaim guard 回收 + Windows 共享冲突重试;已完成且输入未变的工作不重跑;任何失败都保留最佳候选与原始产物。
93
101
 
94
102
  ## MCM / ICM 终审(audit Skill)
@@ -100,7 +108,7 @@ reproducibility.json final-report.md
100
108
  安装:
101
109
 
102
110
  ```bash
103
- dsh plugin --profile web add github:yohanchen1/MathModelingAgent#v0.2.0
111
+ dsh plugin --profile web add github:yohanchen1/MathModelingAgent#v0.2.2
104
112
  dsh --profile web --dump-config # 检查组合层
105
113
  dsh web # 然后重启
106
114
  ```
@@ -0,0 +1,92 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1066" height="620" viewBox="0 0 1066 620" role="img" aria-label="MathModelingAgent 工作流总览">
2
+ <defs>
3
+ <filter id="shadow" x="-4%" y="-4%" width="108%" height="112%">
4
+ <feDropShadow dx="0" dy="2" stdDeviation="4" flood-color="#0f172a" flood-opacity="0.06"/>
5
+ </filter>
6
+ </defs>
7
+ <rect width="1066" height="620" rx="16" fill="#f8fafc"/>
8
+ <text x="40" y="52" font-family="system-ui,-apple-system,'Segoe UI',sans-serif" font-size="30" font-weight="700" fill="#0f172a">MathModelingAgent</text>
9
+ <text x="40" y="84" font-family="system-ui,-apple-system,'Segoe UI',sans-serif" font-size="15" fill="#475569">从题目到可复现结论 · 模型负责提出,证据负责裁决</text>
10
+ <g font-family="system-ui,-apple-system,'Segoe UI',sans-serif" font-size="11" fill="#334155">
11
+ <rect x="774" y="26" width="116" height="28" rx="14" fill="#ffffff" stroke="#e2e8f0"/>
12
+ <text x="832" y="44" text-anchor="middle" fill="#475569">不绑定具体状态名</text>
13
+ <rect x="900" y="26" width="122" height="28" rx="14" fill="#ffffff" stroke="#e2e8f0"/>
14
+ <text x="961" y="44" text-anchor="middle" fill="#475569">不绑定文件结构</text>
15
+ <rect x="1032" y="26" width="104" height="28" rx="14" fill="#ffffff" stroke="#e2e8f0"/>
16
+ <text x="1084" y="44" text-anchor="middle" fill="#475569">工具可替换</text>
17
+ </g>
18
+ <g font-family="system-ui,-apple-system,'Segoe UI',sans-serif">
19
+ <rect x="40" y="112" width="184" height="250" rx="14" fill="#ffffff" stroke="#e2e8f0" filter="url(#shadow)"/>
20
+ <rect x="40" y="112" width="184" height="8" rx="4" fill="#34d399"/>
21
+ <text x="56" y="156" font-size="17" font-weight="700" fill="#0f172a">题目与数据</text>
22
+ <text x="56" y="178" font-size="11" font-weight="700" letter-spacing="1" fill="#34d399">INPUT</text>
23
+ <circle cx="62" cy="208" r="4" fill="#34d399"/><text x="74" y="212" font-size="13" fill="#475569">题目 / PDF</text>
24
+ <circle cx="62" cy="240" r="4" fill="#34d399"/><text x="74" y="244" font-size="13" fill="#475569">数据与附件</text>
25
+ <circle cx="62" cy="272" r="4" fill="#34d399"/><text x="74" y="276" font-size="13" fill="#475569">目标与约束</text>
26
+
27
+ <rect x="246" y="112" width="184" height="250" rx="14" fill="#ffffff" stroke="#e2e8f0" filter="url(#shadow)"/>
28
+ <rect x="246" y="112" width="184" height="8" rx="4" fill="#60a5fa"/>
29
+ <text x="262" y="156" font-size="17" font-weight="700" fill="#0f172a">理解与拆解</text>
30
+ <text x="262" y="178" font-size="11" font-weight="700" letter-spacing="1" fill="#60a5fa">UNDERSTAND</text>
31
+ <circle cx="268" cy="208" r="4" fill="#60a5fa"/><text x="280" y="212" font-size="13" fill="#475569">明确问题边界</text>
32
+ <circle cx="268" cy="240" r="4" fill="#60a5fa"/><text x="280" y="244" font-size="13" fill="#475569">拆成可验证子问题</text>
33
+ <circle cx="268" cy="272" r="4" fill="#60a5fa"/><text x="280" y="276" font-size="13" fill="#475569">登记关键假设</text>
34
+
35
+ <rect x="452" y="112" width="184" height="250" rx="14" fill="#ffffff" stroke="#e2e8f0" filter="url(#shadow)"/>
36
+ <rect x="452" y="112" width="184" height="8" rx="4" fill="#a78bfa"/>
37
+ <text x="468" y="156" font-size="17" font-weight="700" fill="#0f172a">建模与执行</text>
38
+ <text x="468" y="178" font-size="11" font-weight="700" letter-spacing="1" fill="#a78bfa">EXPLORE</text>
39
+ <circle cx="474" cy="208" r="4" fill="#a78bfa"/><text x="486" y="212" font-size="13" fill="#475569">提出候选模型</text>
40
+ <circle cx="474" cy="240" r="4" fill="#a78bfa"/><text x="486" y="244" font-size="13" fill="#475569">计算 / 搜索 / 推导</text>
41
+ <circle cx="474" cy="272" r="4" fill="#a78bfa"/><text x="486" y="276" font-size="13" fill="#475569">保留过程产物</text>
42
+
43
+ <rect x="658" y="112" width="184" height="250" rx="14" fill="#ffffff" stroke="#e2e8f0" filter="url(#shadow)"/>
44
+ <rect x="658" y="112" width="184" height="8" rx="4" fill="#fb923c"/>
45
+ <text x="674" y="156" font-size="17" font-weight="700" fill="#0f172a">证据验证</text>
46
+ <text x="674" y="178" font-size="11" font-weight="700" letter-spacing="1" fill="#fb923c">VERIFY</text>
47
+ <circle cx="680" cy="208" r="4" fill="#fb923c"/><text x="692" y="212" font-size="13" fill="#475569">主张 ↔ 验证义务</text>
48
+ <circle cx="680" cy="240" r="4" fill="#fb923c"/><text x="692" y="244" font-size="13" fill="#475569">独立重算 / 反例攻击</text>
49
+ <circle cx="680" cy="272" r="4" fill="#fb923c"/><text x="692" y="276" font-size="13" fill="#475569">证据不足就回去修正</text>
50
+
51
+ <rect x="864" y="112" width="184" height="250" rx="14" fill="#ffffff" stroke="#e2e8f0" filter="url(#shadow)"/>
52
+ <rect x="864" y="112" width="184" height="8" rx="4" fill="#f472b6"/>
53
+ <text x="880" y="156" font-size="17" font-weight="700" fill="#0f172a">结果交付</text>
54
+ <text x="880" y="178" font-size="11" font-weight="700" letter-spacing="1" fill="#f472b6">DELIVER</text>
55
+ <circle cx="886" cy="208" r="4" fill="#f472b6"/><text x="898" y="212" font-size="13" fill="#475569">结论 + 局限</text>
56
+ <circle cx="886" cy="240" r="4" fill="#f472b6"/><text x="898" y="244" font-size="13" fill="#475569">代码 / 数据 / 复现材料</text>
57
+ <circle cx="886" cy="272" r="4" fill="#f472b6"/><text x="898" y="276" font-size="13" fill="#475569">审计 / 终态报告</text>
58
+ </g>
59
+ <g stroke="#94a3b8" stroke-width="2.5" fill="none" marker-end="url(#arrow)">
60
+ <line x1="224" y1="237" x2="242" y2="237"/>
61
+ <line x1="430" y1="237" x2="448" y2="237"/>
62
+ <line x1="636" y1="237" x2="654" y2="237"/>
63
+ <line x1="842" y1="237" x2="860" y2="237"/>
64
+ </g>
65
+ <defs><marker id="arrow" markerWidth="8" markerHeight="8" refX="6" refY="4" orient="auto"><path d="M0,0 L8,4 L0,8 Z" fill="#94a3b8"/></marker></defs>
66
+ <path d="M750 362 C 750 430, 700 452, 544 452 L 544 366" fill="none" stroke="#6366f1" stroke-width="2" stroke-dasharray="6 5" marker-end="url(#arrowLoop)"/>
67
+ <defs><marker id="arrowLoop" markerWidth="8" markerHeight="8" refX="6" refY="4" orient="auto"><path d="M0,0 L8,4 L0,8 Z" fill="#6366f1"/></marker></defs>
68
+ <rect x="404" y="430" width="284" height="28" rx="14" fill="#eef2ff" stroke="#c7d2fe"/>
69
+ <text x="546" y="448" text-anchor="middle" font-family="system-ui,-apple-system,'Segoe UI',sans-serif" font-size="13" fill="#4338ca">证据不够 → 修正,而不是硬通过</text>
70
+
71
+ <rect x="40" y="478" width="1008" height="118" rx="14" fill="#ffffff" stroke="#e2e8f0"/>
72
+ <text x="64" y="512" font-family="system-ui,-apple-system,'Segoe UI',sans-serif" font-size="15" font-weight="700" fill="#0f172a">可插拔能力</text>
73
+ <text x="64" y="534" font-family="system-ui,-apple-system,'Segoe UI',sans-serif" font-size="12" fill="#64748b">实现可以换,语义不变:任何工具都只是为"建模"或"验证"提供证据。</text>
74
+ <g font-family="system-ui,-apple-system,'Segoe UI',sans-serif" text-anchor="middle">
75
+ <rect x="64" y="552" width="180" height="26" rx="8" fill="#f1f5f9"/>
76
+ <text x="154" y="569" font-size="12" font-weight="600" fill="#0f172a">数值计算</text>
77
+ <text x="154" y="582" font-size="10" fill="#64748b">Python / 其他运行时</text>
78
+ <rect x="258" y="552" width="180" height="26" rx="8" fill="#f1f5f9"/>
79
+ <text x="348" y="569" font-size="12" font-weight="600" fill="#0f172a">形式验证</text>
80
+ <text x="348" y="582" font-size="10" fill="#64748b">Lean / theorem prover</text>
81
+ <rect x="452" y="552" width="180" height="26" rx="8" fill="#f1f5f9"/>
82
+ <text x="542" y="569" font-size="12" font-weight="600" fill="#0f172a">符号计算</text>
83
+ <text x="542" y="582" font-size="10" fill="#64748b">Wolfram / CAS</text>
84
+ <rect x="646" y="552" width="180" height="26" rx="8" fill="#f1f5f9"/>
85
+ <text x="736" y="569" font-size="12" font-weight="600" fill="#0f172a">研究检索</text>
86
+ <text x="736" y="582" font-size="10" fill="#64748b">文献 / Web</text>
87
+ <rect x="840" y="552" width="180" height="26" rx="8" fill="#f1f5f9"/>
88
+ <text x="930" y="569" font-size="12" font-weight="600" fill="#0f172a">终审</text>
89
+ <text x="930" y="582" font-size="10" fill="#64748b">MCM / ICM audit</text>
90
+ </g>
91
+ <text x="1032" y="612" text-anchor="end" font-family="system-ui,-apple-system,'Segoe UI',sans-serif" font-size="10" fill="#94a3b8">README overview</text>
92
+ </svg>
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "dsh-math-modeling-agent",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Evidence-driven mathematical modeling and verification skills for DeepSeek Harness",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "cordis.patch.yml",
8
8
  "skills",
9
+ "assets",
9
10
  "README.md",
10
11
  "LICENSE"
11
12
  ],
@@ -1,4 +1,4 @@
1
- # Minimal Run Example
1
+ # Minimal Run Example (v2)
2
2
 
3
3
  Problem: solve 2x + 3 = 11.
4
4
 
@@ -11,4 +11,10 @@ Evidence E-002: substitution 2(4) + 3 = 11.
11
11
 
12
12
  Status: SOLVED.
13
13
 
14
+ v2 interaction records (ledger.scope.interactions): D0 restatement (auto), D1 routing
15
+ (algebra / Fast), D2 assumptions (none beyond the equation), D3 direction
16
+ (direct inverse), D4 verdict (user accepted). scope.robustnessExempt = true —
17
+ a pure algebraic equation has no perturbable parameters, so the robustness
18
+ gate is exempted with that reason. decisionStack holds the D3 entry.
19
+
14
20
  No Python, Lean, Wolfram, research directory, or empty artifact directory is required.
@@ -7,7 +7,9 @@ Resume sequence:
7
7
  1. run `node scripts/run-state.mjs validate <run-root>`;
8
8
  2. run `node scripts/run-state.mjs recover <run-root>`;
9
9
  3. confirm C-003 remains the best candidate;
10
- 4. rerun only the incomplete verifier;
11
- 5. append a new evidence-linked transition.
10
+ 4. run `node scripts/run-state.mjs gate <run-root> --to REVISE --reason "回退到 D3-SP1:..."` (the reason must reference the decisionStack entry);
11
+ 5. rerun only the incomplete verifier;
12
+ 6. append a new evidence-linked transition (the CLI enforces the gate).
12
13
 
13
- Previously hash-stable Python outputs are not recomputed.
14
+ Previously hash-stable Python outputs are not recomputed. Interaction records,
15
+ the decision stack, and the failure ledger survive the crash unchanged.
@@ -421,17 +421,27 @@ function decisionStackIds(scope) {
421
421
  ? scope.decisionStack.filter(entry => entry && typeof entry.id === 'string').map(entry => entry.id)
422
422
  : []
423
423
  }
424
- function interactionContractViolations(run, ledger, to, reason) {
424
+ /** D2 records carry the subproblem id(s) they cover: {decisionPoint:'D2', scope:['SP1'], ...} */
425
+ function hasSubproblemD2(scope, subproblem) {
426
+ if (!subproblem || !Array.isArray(scope?.interactions)) return true
427
+ return scope.interactions.some(entry => entry && entry.decisionPoint === 'D2' &&
428
+ (Array.isArray(entry.scope) ? entry.scope.includes(subproblem) : entry.subproblem === subproblem))
429
+ }
430
+ function interactionContractViolations(run, ledger, to, reason, subproblem) {
425
431
  if (ledger.schemaVersion !== SCHEMA_VERSION) return []
426
432
  const scope = ledger.scope ?? {}
427
433
  const violations = []
428
434
  const transitional = to !== undefined
429
435
  const earlyStop = ['BLOCKED', 'CANCELLED'].includes(run.status)
430
436
  if ((transitional && to === 'SCOPE_FROZEN') || (!transitional && run.status !== 'TRIAGE' && !earlyStop)) {
437
+ if (!hasDecisionRecord(scope, 'D0')) violations.push('D0 restatement interaction record missing (ledger.scope.interactions)')
431
438
  if (!hasDecisionRecord(scope, 'D1')) violations.push('D1 routing interaction record missing (ledger.scope.interactions)')
432
439
  }
433
440
  const enteringAttemptFromCandidates = transitional ? (to === 'ATTEMPT' && run.status === 'CANDIDATES_READY') : (run.currentAttempt >= 1)
434
- if (enteringAttemptFromCandidates && !hasDecisionRecord(scope, 'D3')) violations.push('D3 direction-selection interaction record missing')
441
+ if (enteringAttemptFromCandidates) {
442
+ if (!hasDecisionRecord(scope, 'D3')) violations.push('D3 direction-selection interaction record missing')
443
+ if (transitional && !hasSubproblemD2(scope, subproblem)) violations.push(`D2 assumption interaction record missing for subproblem ${subproblem} (run the gate with --subproblem <id>)`)
444
+ }
435
445
  const enteringTerminalFromVerify = transitional
436
446
  ? (FINAL_STATES.includes(to) && run.status === 'VERIFY')
437
447
  : (FINAL_STATES.includes(run.status) && !['BLOCKED', 'CANCELLED'].includes(run.status))
@@ -539,6 +549,17 @@ async function validateRunUnlocked(root, expectedTaskId) {
539
549
  for (const violation of interactionContractViolations(run, ledger, undefined, undefined)) {
540
550
  warnings.push(`interaction contract: ${violation}`)
541
551
  }
552
+ if (run.currentAttempt >= 1) {
553
+ const reportPath = join(root, 'attempts', String(run.currentAttempt), 'report.md')
554
+ try {
555
+ const reportText = await readFile(reportPath, 'utf8')
556
+ for (const section of ['问题重述', '问题分析', '模型假设', '模型建立与求解', '验证', '鲁棒性', '评价与改进', '参考文献']) {
557
+ if (!reportText.includes(section)) warnings.push(`attempt ${run.currentAttempt} report.md missing runlog digest section: ${section}`)
558
+ }
559
+ } catch {
560
+ warnings.push(`attempt ${run.currentAttempt} report.md unreadable (runlog digest required)`)
561
+ }
562
+ }
542
563
  }
543
564
  return { valid: diagnostics.length === 0, diagnostics, warnings, run: clone(run) }
544
565
  }
@@ -552,12 +573,12 @@ export async function validateRun(root, expectedTaskId, runtime = {}) {
552
573
  * mutating anything. The agent workflow must call this before `transition`;
553
574
  * the CLI `transition` command enforces it internally unless --skip-gate.
554
575
  */
555
- export async function gateTransition(root, { to, reason }, runtime = {}) {
576
+ export async function gateTransition(root, { to, reason, subproblem }, runtime = {}) {
556
577
  return withLock(root, MUTATION_LOCK_FILE, 'gate', async () => {
557
578
  const p = paths(root)
558
579
  const run = await readJson(p.run)
559
580
  const ledger = await readJson(p.ledger)
560
- const violations = interactionContractViolations(run, ledger, to, reason)
581
+ const violations = interactionContractViolations(run, ledger, to, reason, subproblem)
561
582
  return { allowed: violations.length === 0, violations, status: run.status }
562
583
  }, runtime)
563
584
  }
@@ -603,13 +624,13 @@ async function cli(argv) {
603
624
  const options = parseOptions(rest)
604
625
  if (command === 'init') return initRun(root, { taskId: options['task-id'], mode: options.mode ?? 'standard' })
605
626
  if (command === 'gate') {
606
- const result = await gateTransition(root, { to: options.to, reason: options.reason })
627
+ const result = await gateTransition(root, { to: options.to, reason: options.reason, subproblem: options.subproblem })
607
628
  if (!result.allowed) process.exitCode = 1
608
629
  return result
609
630
  }
610
631
  if (command === 'transition') {
611
632
  if (options['skip-gate'] !== 'true') {
612
- const gate = await gateTransition(root, { to: options.to, reason: options.reason })
633
+ const gate = await gateTransition(root, { to: options.to, reason: options.reason, subproblem: options.subproblem })
613
634
  if (!gate.allowed) {
614
635
  process.exitCode = 1
615
636
  return { error: `interaction contract violated: ${gate.violations.join('; ')}`, gate }