dsh-math-modeling-agent 0.2.6 → 0.2.8
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
|
@@ -108,7 +108,7 @@ D3 方向/D4 裁决),`run-state.mjs gate` 在每次状态转移前强制校
|
|
|
108
108
|
安装:
|
|
109
109
|
|
|
110
110
|
```bash
|
|
111
|
-
dsh plugin --profile web add github:yohanchen1/MathModelingAgent#v0.2.
|
|
111
|
+
dsh plugin --profile web add github:yohanchen1/MathModelingAgent#v0.2.8
|
|
112
112
|
dsh --profile web --dump-config # 检查组合层(应看到 dsh-math-modeling-agent-skills 行)
|
|
113
113
|
dsh web # 重启以加载插件
|
|
114
114
|
```
|
package/package.json
CHANGED
|
@@ -46,7 +46,8 @@ until the user says "可以了". Mathematical modeling is a deep activity;
|
|
|
46
46
|
4. **隐含信息与歧义清单**:numbered; each entry = the LLM's reading + its
|
|
47
47
|
basis + the risk if the reading is wrong. This is the heart of the
|
|
48
48
|
exchange: the user corrects entries here.
|
|
49
|
-
5.
|
|
49
|
+
5. **各子问题数学本质判定**:every sub-question of the problem — however
|
|
50
|
+
many there are (one or ten) — is classified (fitting/optimization/
|
|
50
51
|
geometric optics/simulation/…), with why.
|
|
51
52
|
6. **待澄清问题**:open questions inviting the user to add background,
|
|
52
53
|
correct a reading, or supply their own references — never a yes/no list.
|
|
@@ -68,6 +69,16 @@ After the user answers, problem-brief.md is updated and the delta is shown.
|
|
|
68
69
|
4. The D-R interaction record goes into ledger.scope.interactions; the gate
|
|
69
70
|
refuses CANDIDATES_READY without it.
|
|
70
71
|
|
|
72
|
+
## Honest boundary of the gate
|
|
73
|
+
|
|
74
|
+
The gate is a MECHANISM guarantee, not an anti-fraud guarantee: it proves a
|
|
75
|
+
record exists, never that the record is true — every interaction record is
|
|
76
|
+
written by the agent itself. Falsified records are therefore out of the
|
|
77
|
+
gate's reach; the defense against them is the blind verifier subagent
|
|
78
|
+
(subagent-dispatch.md), which re-derives key claims from data alone, and the
|
|
79
|
+
artifact checks (research/sources.jsonl, attempts/N/report.md) that tie each
|
|
80
|
+
record to real files on disk.
|
|
81
|
+
|
|
71
82
|
## Rules
|
|
72
83
|
|
|
73
84
|
- D0/D1/D-R: once per task. D2/D3: once per subproblem. D2': only when a
|
|
@@ -81,7 +92,26 @@ After the user answers, problem-brief.md is updated and the delta is shown.
|
|
|
81
92
|
ATTEMPT-from-CANDIDATES_READY without D3, terminals-from-VERIFY without D4,
|
|
82
93
|
and REVISE/RESEARCH (from VERIFY) whose reason fails to reference a
|
|
83
94
|
`decisionStack` entry id. The CLI `transition` command enforces the same
|
|
84
|
-
gate
|
|
95
|
+
gate and has NO escape hatch. Every D4 record carries the attempt number it
|
|
96
|
+
adjudicates (`attempt: N`); the terminal gate requires the D4 for the
|
|
97
|
+
CURRENT attempt. ATTEMPT from CANDIDATES_READY requires `--subproblem <id>`.
|
|
98
|
+
Terminal gates additionally require `scope.cleanupPassed: true` and the
|
|
99
|
+
current attempt's report.md on disk. Mechanism-level tests and unattended
|
|
100
|
+
batch runs may declare `scope.contractExempt: true` — production modeling
|
|
101
|
+
must never set it.
|
|
102
|
+
|
|
103
|
+
## Phase pace — interactive beats between phases (v2.8)
|
|
104
|
+
|
|
105
|
+
The exposition is delivered PHASE BY PHASE, never as a firehose. The phase
|
|
106
|
+
sequence is: restatement → analysis → assumptions → modeling → solving →
|
|
107
|
+
verification → robustness → evaluation. Between every two phases there MUST
|
|
108
|
+
be a user interaction point: the completed phase is presented, one line asks
|
|
109
|
+
whether to proceed to the next phase, and the user may interrupt, ask for a
|
|
110
|
+
deeper explanation of any step of the just-finished phase, or contribute
|
|
111
|
+
knowledge. Sensitivity/robustness NEVER follows solving automatically — it
|
|
112
|
+
only starts after the verification phase, the user's go-ahead, and its own
|
|
113
|
+
D-interaction. Within a phase with ≥3 major steps, offer the user one
|
|
114
|
+
interruption opportunity after the first step.
|
|
85
115
|
|
|
86
116
|
## L2 display points (MUST show, never ask)
|
|
87
117
|
|
|
@@ -24,21 +24,67 @@ Do not copy full prior solutions or private reasoning.
|
|
|
24
24
|
|
|
25
25
|
State the frozen question, final scientific status, answer/recommendation, verified claims and evidence links, conditional/unresolved claims, assumptions and sensitivity, candidate comparison, data and parameter sources, validation, failure cases, limitations, reproducibility commands, and resume conditions when non-SOLVED.
|
|
26
26
|
|
|
27
|
-
## Chat display — the runlog digest (MUST output, v2)
|
|
27
|
+
## Chat display — the runlog digest (MUST output, v2.7)
|
|
28
28
|
|
|
29
29
|
The following is shown DIRECTLY IN THE CHAT, not only on disk.
|
|
30
30
|
|
|
31
|
+
### Readability standard — write for a human reader, never a bullet list
|
|
32
|
+
|
|
33
|
+
Every section is an EXPOSITION, not a summary: state WHY the step is taken,
|
|
34
|
+
HOW the reasoning runs (derivation, mechanism, first-principles chain), WHAT
|
|
35
|
+
came out (numbers with units), and HOW it is checked. A reader who knows
|
|
36
|
+
nothing about the problem must be able to follow each section without
|
|
37
|
+
guessing.
|
|
38
|
+
|
|
39
|
+
Style: step-by-step numbered exposition with plain direct language (this is
|
|
40
|
+
a working document, not a final paper — no paper-style formal phrasing
|
|
41
|
+
required), but with MORE detail than a paper would carry: every parameter
|
|
42
|
+
choice gets its reason, every formula is followed by a plain-language
|
|
43
|
+
explanation of each symbol, every step ends with a transition line
|
|
44
|
+
("这一步保证了…,为下一步…做好准备"), and every conclusion states what it
|
|
45
|
+
would take to be wrong. Minimum expectations per section:
|
|
46
|
+
|
|
47
|
+
- 问题重述:restate the question in your own words, explaining every
|
|
48
|
+
parameter's meaning and where it comes from in the problem text.
|
|
49
|
+
- 问题分析:walk the physical/geometric mechanism step by step (e.g. why the
|
|
50
|
+
nodes move radially, how the focal geometry pins the paraboloid), each
|
|
51
|
+
step justified by problem text, data, or derivation.
|
|
52
|
+
- 模型假设:for EVERY assumption give the reason it is justified
|
|
53
|
+
(original wording / data statistic / derivation / literature) and what
|
|
54
|
+
happens if it fails.
|
|
55
|
+
- 模型建立与求解:write the derivation in full (equations, variable
|
|
56
|
+
definitions, units, how the equation is solved, numerical method and why
|
|
57
|
+
it was chosen), not just the final formula.
|
|
58
|
+
- 验证:explain the verification LOGIC for each obligation (what would
|
|
59
|
+
falsify it, what tolerance is used and why), then the outcome.
|
|
60
|
+
- 鲁棒性:for each perturbation say what was varied, why that parameter,
|
|
61
|
+
and what would count as a conclusion flip.
|
|
62
|
+
- 评价与改进 / 参考文献:state the comparison criterion and map each
|
|
63
|
+
reference to the exact method it supports.
|
|
64
|
+
|
|
31
65
|
### Intake digest (once, at intake completion) — the six-section deep restatement
|
|
32
66
|
|
|
33
|
-
【题目要点摘录】problem's own numbers and constraints, quoted
|
|
34
|
-
【背景解读】engineering/physical mechanism
|
|
35
|
-
【数据语义】every attachment column by column (meaning/units/coordinate system/magnitudes)
|
|
36
|
-
【隐含信息与歧义清单】numbered; each = LLM reading + basis + risk (the user corrects entries here)
|
|
37
|
-
|
|
67
|
+
【题目要点摘录】the problem's own numbers and constraints, quoted, each explained
|
|
68
|
+
【背景解读】engineering/physical mechanism, walked through
|
|
69
|
+
【数据语义】every attachment column by column (meaning/units/coordinate system/magnitudes), with observed statistics
|
|
70
|
+
【隐含信息与歧义清单】numbered; each = LLM reading + derivation/basis + risk (the user corrects entries here)
|
|
71
|
+
【各子问题数学本质判定】every sub-question (any count) classified + why
|
|
38
72
|
【待澄清问题】open questions inviting user background/corrections/references
|
|
39
73
|
|
|
40
74
|
After the D0 exchange, show the delta applied to problem-brief.md.
|
|
41
75
|
|
|
76
|
+
### Quality self-check (MUST run before every D4 verdict)
|
|
77
|
+
|
|
78
|
+
Before presenting a D4 verdict, run the self-check on the current attempt's
|
|
79
|
+
report and show the result in the chat: for each of the eight digest sections,
|
|
80
|
+
confirm whether it answers all four questions — WHY (why this step), HOW
|
|
81
|
+
(how the reasoning runs), WHAT (result with units), CHECK (how it is
|
|
82
|
+
verified, or what would falsify it). Any section failing one of the four is
|
|
83
|
+
flagged as incomplete and the round is NOT presented for verdict until fixed.
|
|
84
|
+
The self-check is a human-facing declaration: the user decides whether the
|
|
85
|
+
exposition is actually sufficient. This is the quality gate; the machine
|
|
86
|
+
gates above only guarantee existence and structure.
|
|
87
|
+
|
|
42
88
|
### Per-round digest (after EVERY attempt round)
|
|
43
89
|
|
|
44
90
|
```text
|
|
@@ -366,7 +366,7 @@ function validateJournal(events, diagnostics) {
|
|
|
366
366
|
})
|
|
367
367
|
}
|
|
368
368
|
|
|
369
|
-
export async function initRun(root, { taskId, mode = 'standard', budget, budgets }, runtime = {}) {
|
|
369
|
+
export async function initRun(root, { taskId, mode = 'standard', budget, budgets, contract }, runtime = {}) {
|
|
370
370
|
assertTaskId(taskId); assertMode(mode)
|
|
371
371
|
const effectiveBudget = { ...MODE_DEFAULTS[mode], ...(budget ?? budgets ?? {}) }
|
|
372
372
|
const budgetDiagnostics = []
|
|
@@ -379,7 +379,8 @@ export async function initRun(root, { taskId, mode = 'standard', budget, budgets
|
|
|
379
379
|
bestCandidateId: null, budget: effectiveBudget, createdAt: timestamp, updatedAt: timestamp,
|
|
380
380
|
}
|
|
381
381
|
const ledger = {
|
|
382
|
-
schemaVersion: SCHEMA_VERSION, taskId,
|
|
382
|
+
schemaVersion: SCHEMA_VERSION, taskId,
|
|
383
|
+
scope: { independentAuditPassed: false, interactions: [], decisionStack: [], robustnessExempt: false, ...(contract === false ? { contractExempt: true } : {}) },
|
|
383
384
|
assumptions: [], claims: [], obligations: [], subproblems: [], candidates: [], issues: [],
|
|
384
385
|
}
|
|
385
386
|
const event = { schemaVersion: SCHEMA_VERSION, sequence: 0, type: 'RUN_INITIALIZED', taskId, timestamp, snapshot: clone(run) }
|
|
@@ -412,9 +413,14 @@ export async function initRun(root, { taskId, mode = 'standard', budget, budgets
|
|
|
412
413
|
}
|
|
413
414
|
|
|
414
415
|
/** The v2 interaction contract: decision records, decision-stack backtracking, and the robustness gate. */
|
|
415
|
-
function hasDecisionRecord(scope, decisionPoint) {
|
|
416
|
+
function hasDecisionRecord(scope, decisionPoint, { allowAuto = true } = {}) {
|
|
416
417
|
return Array.isArray(scope?.interactions) && scope.interactions.some(entry =>
|
|
417
|
-
entry && (entry.decisionPoint === decisionPoint || entry.auto === true))
|
|
418
|
+
entry && (entry.decisionPoint === decisionPoint || (allowAuto && entry.auto === true)))
|
|
419
|
+
}
|
|
420
|
+
/** D4 verdict records carry the attempt number they adjudicate: {decisionPoint:'D4', attempt: 2, ...} */
|
|
421
|
+
function hasD4ForAttempt(scope, attempt) {
|
|
422
|
+
return Array.isArray(scope?.interactions) && scope.interactions.some(entry =>
|
|
423
|
+
entry && (entry.auto === true || (entry.decisionPoint === 'D4' && entry.attempt === attempt)))
|
|
418
424
|
}
|
|
419
425
|
function decisionStackIds(scope) {
|
|
420
426
|
return Array.isArray(scope?.decisionStack)
|
|
@@ -430,6 +436,7 @@ function hasSubproblemD2(scope, subproblem) {
|
|
|
430
436
|
function interactionContractViolations(run, ledger, to, reason, subproblem) {
|
|
431
437
|
if (ledger.schemaVersion !== SCHEMA_VERSION) return []
|
|
432
438
|
const scope = ledger.scope ?? {}
|
|
439
|
+
if (scope.contractExempt === true) return []
|
|
433
440
|
const violations = []
|
|
434
441
|
const transitional = to !== undefined
|
|
435
442
|
const earlyStop = ['BLOCKED', 'CANCELLED'].includes(run.status)
|
|
@@ -439,16 +446,17 @@ function interactionContractViolations(run, ledger, to, reason, subproblem) {
|
|
|
439
446
|
}
|
|
440
447
|
const afterCandidates = ['CANDIDATES_READY', 'ATTEMPT', 'EXECUTE', 'VERIFY', 'REVISE', 'FORK', ...FINAL_STATES]
|
|
441
448
|
const candidatesGate = transitional ? (to === 'CANDIDATES_READY') : (afterCandidates.includes(run.status) && !earlyStop)
|
|
442
|
-
if (candidatesGate && !hasDecisionRecord(scope, 'D-R')) violations.push('D-R literature-research interaction record missing (pre-modeling literature survey is mandatory)')
|
|
449
|
+
if (candidatesGate && !hasDecisionRecord(scope, 'D-R', { allowAuto: false })) violations.push('D-R literature-research interaction record missing (pre-modeling literature survey is mandatory; auto-authorization is not accepted)')
|
|
443
450
|
const enteringAttemptFromCandidates = transitional ? (to === 'ATTEMPT' && run.status === 'CANDIDATES_READY') : (run.currentAttempt >= 1)
|
|
444
451
|
if (enteringAttemptFromCandidates) {
|
|
452
|
+
if (transitional && !subproblem) violations.push('ATTEMPT from CANDIDATES_READY requires --subproblem <id>')
|
|
445
453
|
if (!hasDecisionRecord(scope, 'D3')) violations.push('D3 direction-selection interaction record missing')
|
|
446
|
-
if (transitional && !hasSubproblemD2(scope, subproblem)) violations.push(`D2 assumption interaction record missing for subproblem ${subproblem} (run the gate with --subproblem <id>)`)
|
|
454
|
+
if (transitional && subproblem && !hasSubproblemD2(scope, subproblem)) violations.push(`D2 assumption interaction record missing for subproblem ${subproblem} (run the gate with --subproblem <id>)`)
|
|
447
455
|
}
|
|
448
456
|
const enteringTerminalFromVerify = transitional
|
|
449
457
|
? (FINAL_STATES.includes(to) && run.status === 'VERIFY')
|
|
450
458
|
: (FINAL_STATES.includes(run.status) && !['BLOCKED', 'CANCELLED'].includes(run.status))
|
|
451
|
-
if (enteringTerminalFromVerify && !
|
|
459
|
+
if (enteringTerminalFromVerify && !hasD4ForAttempt(scope, run.currentAttempt)) violations.push(`D4 verdict interaction record for attempt ${run.currentAttempt} missing`)
|
|
452
460
|
if (transitional && (to === 'REVISE' || (to === 'RESEARCH' && run.status === 'VERIFY'))) {
|
|
453
461
|
const ids = decisionStackIds(scope)
|
|
454
462
|
if (ids.length > 0 && !ids.some(id => typeof reason === 'string' && reason.includes(id))) {
|
|
@@ -458,9 +466,12 @@ function interactionContractViolations(run, ledger, to, reason, subproblem) {
|
|
|
458
466
|
const terminalAndExempt = transitional
|
|
459
467
|
? (FINAL_STATES.includes(to) && !['BLOCKED', 'CANCELLED'].includes(to))
|
|
460
468
|
: (FINAL_STATES.includes(run.status) && !['BLOCKED', 'CANCELLED'].includes(run.status))
|
|
461
|
-
if (terminalAndExempt
|
|
462
|
-
|
|
463
|
-
|
|
469
|
+
if (terminalAndExempt) {
|
|
470
|
+
if (scope.robustnessExempt !== true) {
|
|
471
|
+
const passed = Array.isArray(ledger.obligations) && ledger.obligations.some(o => o && o.kind === 'robustness' && o.status === 'PASS')
|
|
472
|
+
if (!passed) violations.push('terminal state requires a PASSED robustness-kind obligation (or scope.robustnessExempt: true)')
|
|
473
|
+
}
|
|
474
|
+
if (scope.cleanupPassed !== true) violations.push('terminal state requires scope.cleanupPassed: true (run the cleanup checklist in run-directory.md)')
|
|
464
475
|
}
|
|
465
476
|
return violations
|
|
466
477
|
}
|
|
@@ -550,7 +561,7 @@ async function validateRunUnlocked(root, expectedTaskId) {
|
|
|
550
561
|
if (ledger.schemaVersion !== SCHEMA_VERSION) warnings.push(`legacy run (ledger schema v${ledger.schemaVersion}): interaction contract not enforced`)
|
|
551
562
|
else {
|
|
552
563
|
for (const violation of interactionContractViolations(run, ledger, undefined, undefined)) {
|
|
553
|
-
|
|
564
|
+
diagnostics.push(`interaction contract: ${violation}`)
|
|
554
565
|
}
|
|
555
566
|
if (run.currentAttempt >= 1) {
|
|
556
567
|
const reportPath = join(root, 'attempts', String(run.currentAttempt), 'report.md')
|
|
@@ -574,7 +585,7 @@ export async function validateRun(root, expectedTaskId, runtime = {}) {
|
|
|
574
585
|
/**
|
|
575
586
|
* Check the v2 interaction contract for a PROSPECTIVE transition without
|
|
576
587
|
* mutating anything. The agent workflow must call this before `transition`;
|
|
577
|
-
* the CLI `transition` command enforces it internally
|
|
588
|
+
* the CLI `transition` command enforces it internally and has NO escape hatch.
|
|
578
589
|
*/
|
|
579
590
|
export async function gateTransition(root, { to, reason, subproblem }, runtime = {}) {
|
|
580
591
|
return withLock(root, MUTATION_LOCK_FILE, 'gate', async () => {
|
|
@@ -582,6 +593,23 @@ export async function gateTransition(root, { to, reason, subproblem }, runtime =
|
|
|
582
593
|
const run = await readJson(p.run)
|
|
583
594
|
const ledger = await readJson(p.ledger)
|
|
584
595
|
const violations = interactionContractViolations(run, ledger, to, reason, subproblem)
|
|
596
|
+
const exempt = ledger.scope?.contractExempt === true
|
|
597
|
+
if (to === 'CANDIDATES_READY' && !exempt) {
|
|
598
|
+
const sourcesPath = join(root, 'research', 'sources.jsonl')
|
|
599
|
+
try {
|
|
600
|
+
if (!(await readFile(sourcesPath, 'utf8')).trim()) violations.push('research/sources.jsonl is empty (pre-modeling literature survey artifacts are mandatory)')
|
|
601
|
+
} catch {
|
|
602
|
+
violations.push('research/sources.jsonl missing (pre-modeling literature survey artifacts are mandatory)')
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
if (FINAL_STATES.includes(to) && !['BLOCKED', 'CANCELLED'].includes(to) && !exempt) {
|
|
606
|
+
const reportPath = join(root, 'attempts', String(run.currentAttempt), 'report.md')
|
|
607
|
+
try {
|
|
608
|
+
await readFile(reportPath, 'utf8')
|
|
609
|
+
} catch {
|
|
610
|
+
violations.push(`attempts/${run.currentAttempt}/report.md missing (write the runlog report before a terminal transition)`)
|
|
611
|
+
}
|
|
612
|
+
}
|
|
585
613
|
return { allowed: violations.length === 0, violations, status: run.status }
|
|
586
614
|
}, runtime)
|
|
587
615
|
}
|
|
@@ -632,12 +660,10 @@ async function cli(argv) {
|
|
|
632
660
|
return result
|
|
633
661
|
}
|
|
634
662
|
if (command === 'transition') {
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
return { error: `interaction contract violated: ${gate.violations.join('; ')}`, gate }
|
|
640
|
-
}
|
|
663
|
+
const gate = await gateTransition(root, { to: options.to, reason: options.reason, subproblem: options.subproblem })
|
|
664
|
+
if (!gate.allowed) {
|
|
665
|
+
process.exitCode = 1
|
|
666
|
+
return { error: `interaction contract violated: ${gate.violations.join('; ')}`, gate }
|
|
641
667
|
}
|
|
642
668
|
return transitionRun(root, { to: options.to, reason: options.reason, evidenceIds: options.evidence ? options.evidence.split(',').filter(Boolean) : [], issueIds: options.issues ? options.issues.split(',').filter(Boolean) : [], patch: options.candidate ? { bestCandidateId: options.candidate } : undefined })
|
|
643
669
|
}
|