loopforge 1.12.0 → 1.15.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/dist/backends/fs.d.ts.map +1 -1
- package/dist/backends/fs.js +12 -8
- package/dist/backends/fs.js.map +1 -1
- package/dist/builder.d.ts +8 -1
- package/dist/builder.d.ts.map +1 -1
- package/dist/builder.js +28 -72
- package/dist/builder.js.map +1 -1
- package/dist/enforcement-gate.d.ts +44 -0
- package/dist/enforcement-gate.d.ts.map +1 -0
- package/dist/enforcement-gate.js +253 -0
- package/dist/enforcement-gate.js.map +1 -0
- package/dist/engine.d.ts +11 -4
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +87 -118
- package/dist/engine.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/loop-compiler.d.ts.map +1 -1
- package/dist/loop-compiler.js +689 -480
- package/dist/loop-compiler.js.map +1 -1
- package/dist/mcp/session.d.ts +9 -1
- package/dist/mcp/session.d.ts.map +1 -1
- package/dist/mcp/session.js +100 -44
- package/dist/mcp/session.js.map +1 -1
- package/dist/observability.d.ts +1 -1
- package/dist/observability.js +1 -1
- package/dist/policy.d.ts +72 -2
- package/dist/policy.d.ts.map +1 -1
- package/dist/policy.js +96 -2
- package/dist/policy.js.map +1 -1
- package/dist/protocol.d.ts +33 -2
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +22 -2
- package/dist/protocol.js.map +1 -1
- package/dist/runtime.d.ts +8 -4
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +102 -108
- package/dist/runtime.js.map +1 -1
- package/dist/tests/builder.test.js +5 -3
- package/dist/tests/builder.test.js.map +1 -1
- package/dist/tests/enforcement-gate.test.d.ts +2 -0
- package/dist/tests/enforcement-gate.test.d.ts.map +1 -0
- package/dist/tests/enforcement-gate.test.js +328 -0
- package/dist/tests/enforcement-gate.test.js.map +1 -0
- package/dist/tests/engine.test.js +9 -5
- package/dist/tests/engine.test.js.map +1 -1
- package/dist/tests/generate-schema.test.js +2 -2
- package/dist/tests/loop-compiler.test.js +28 -20
- package/dist/tests/loop-compiler.test.js.map +1 -1
- package/dist/tests/mcp.test.js +22 -2
- package/dist/tests/mcp.test.js.map +1 -1
- package/dist/tests/policy.test.js +1 -1
- package/dist/tests/policy.test.js.map +1 -1
- package/dist/tests/runtime.test.js +19 -5
- package/dist/tests/runtime.test.js.map +1 -1
- package/dist/verification-gate.d.ts +6 -0
- package/dist/verification-gate.d.ts.map +1 -1
- package/dist/verification-gate.js +3 -2
- package/dist/verification-gate.js.map +1 -1
- package/loop_policy.json +8 -3
- package/package.json +1 -1
- package/skills/perception/SKILL.md +35 -17
- package/skills/prompt-techniques/SKILL.md +52 -52
- package/skills/prompt-techniques/references/chain-of-thought.md +207 -207
- package/skills/prompt-techniques/references/few-shot.md +174 -174
- package/skills/prompt-techniques/references/least-to-most.md +182 -182
- package/skills/prompt-techniques/references/step-back.md +160 -160
- package/skills/prompt-techniques/references/tree-of-thought.md +191 -191
- package/skills/prompt-techniques/references/zero-shot.md +97 -97
package/dist/loop-compiler.js
CHANGED
|
@@ -13,7 +13,7 @@ import { createHash } from "node:crypto";
|
|
|
13
13
|
import { readFileSync } from "node:fs";
|
|
14
14
|
import { getPolicy } from "./policy.js";
|
|
15
15
|
import { AgentStatus, makeCheckpointSummary, makeLoopCompileResponse, makeLoopHealth, makeLoopObjective, makeRollingSummary, makeTaskAlignment, } from "./protocol.js";
|
|
16
|
-
import { routeTechniqueAdaptive } from "./builder.js";
|
|
16
|
+
import { routeTechniqueAdaptive, SKILLS_DIR } from "./builder.js";
|
|
17
17
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
18
18
|
// Repair cue detection
|
|
19
19
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -251,6 +251,192 @@ function getRecentRounds(loopId, n, vaultContext) {
|
|
|
251
251
|
rounds.sort((a, b) => b.round - a.round);
|
|
252
252
|
return rounds.slice(0, n);
|
|
253
253
|
}
|
|
254
|
+
/** Extract the technique used in the most recent round before currentRound.
|
|
255
|
+
* Scans vault entries for this loop_id and returns the technique_used field
|
|
256
|
+
* from the highest round number below currentRound.
|
|
257
|
+
* Returns null if no prior rounds exist (round 1 scenario). */
|
|
258
|
+
function getPreviousTechnique(loopId, currentRound, vaultContext) {
|
|
259
|
+
if (vaultContext === null || currentRound <= 1)
|
|
260
|
+
return null;
|
|
261
|
+
const results = vaultContext.results || [];
|
|
262
|
+
if (!results.length)
|
|
263
|
+
return null;
|
|
264
|
+
let bestTechnique = null;
|
|
265
|
+
let bestRound = -1;
|
|
266
|
+
for (const r of results) {
|
|
267
|
+
const lineage = (r.loop_lineage || r.lineage || {});
|
|
268
|
+
if (lineage.loop_id !== loopId)
|
|
269
|
+
continue;
|
|
270
|
+
const rnd = lineage.round;
|
|
271
|
+
if (rnd >= currentRound || rnd <= bestRound)
|
|
272
|
+
continue;
|
|
273
|
+
// technique_used can be on the entry directly or in lineage
|
|
274
|
+
const technique = r.technique_used ??
|
|
275
|
+
lineage.technique_used ?? "";
|
|
276
|
+
if (technique) {
|
|
277
|
+
bestTechnique = technique;
|
|
278
|
+
bestRound = rnd;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return bestTechnique;
|
|
282
|
+
}
|
|
283
|
+
function techniqueChanged(loopId, newTechnique, currentRound, vaultContext) {
|
|
284
|
+
if (currentRound === 1)
|
|
285
|
+
return true; // Round 1: first selection is always a "change"
|
|
286
|
+
const prevTech = getPreviousTechnique(loopId, currentRound, vaultContext);
|
|
287
|
+
if (!prevTech)
|
|
288
|
+
return true; // No prior technique found → treat as change
|
|
289
|
+
return newTechnique !== prevTech;
|
|
290
|
+
}
|
|
291
|
+
/** Returns the mandatory state file read instruction that goes at the top
|
|
292
|
+
* of every compiled prompt. Tells the agent to read the loop state file
|
|
293
|
+
* before executing the round. */
|
|
294
|
+
function renderStateFilePointer(loopId) {
|
|
295
|
+
return [
|
|
296
|
+
`⚠️ **BEFORE anything else**: Read \`.loopforge/state/${loopId}-state.md\``,
|
|
297
|
+
"for accumulated loop state — objective, constraints, progress, and cross-round summary.",
|
|
298
|
+
"This file is rewritten each round and is the single source of truth for loop state.",
|
|
299
|
+
"",
|
|
300
|
+
].join("\n");
|
|
301
|
+
}
|
|
302
|
+
/** Render the full loop state file as markdown. Called by compileL2() every round.
|
|
303
|
+
* The file is a snapshot — rewritten in full each round, not appended.
|
|
304
|
+
* All sections have hard size caps from policy to prevent bloat.
|
|
305
|
+
*
|
|
306
|
+
* Sections: Loop Objective, Progress, Cross-Round Summary, Active Constraints,
|
|
307
|
+
* Checkpoints, Delegation Summary. */
|
|
308
|
+
function renderStateFile(params) {
|
|
309
|
+
const lines = [];
|
|
310
|
+
const sfPolicy = getPolicy().state_file;
|
|
311
|
+
// ── Header ──
|
|
312
|
+
lines.push(`# LoopForge State — ${params.loopId}`);
|
|
313
|
+
lines.push("");
|
|
314
|
+
lines.push(`**Round**: ${params.currentRound}/${params.maxRounds}`);
|
|
315
|
+
lines.push("");
|
|
316
|
+
// ── Loop Objective ──
|
|
317
|
+
if (params.loopObjective) {
|
|
318
|
+
const lo = params.loopObjective;
|
|
319
|
+
lines.push("## Loop Objective");
|
|
320
|
+
if (lo.version && lo.version > 1) {
|
|
321
|
+
lines.push(`> Version ${lo.version}, created at Round ${lo.created_at_round}`);
|
|
322
|
+
}
|
|
323
|
+
lines.push("");
|
|
324
|
+
lines.push(`**Objective**: ${lo.objective}`);
|
|
325
|
+
lines.push("");
|
|
326
|
+
if (lo.success_criteria.length) {
|
|
327
|
+
lines.push("### Success Criteria");
|
|
328
|
+
for (const sc of lo.success_criteria) {
|
|
329
|
+
// Check if this criterion has been met (from execution_evidence)
|
|
330
|
+
lines.push(`- ${sc}`);
|
|
331
|
+
}
|
|
332
|
+
lines.push("");
|
|
333
|
+
}
|
|
334
|
+
if (lo.hard_constraints.length) {
|
|
335
|
+
lines.push("### Hard Constraints");
|
|
336
|
+
for (const hc of lo.hard_constraints) {
|
|
337
|
+
lines.push(`- ${hc}`);
|
|
338
|
+
}
|
|
339
|
+
lines.push("");
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
// ── Progress Dashboard ──
|
|
343
|
+
if (params.progressDashboard) {
|
|
344
|
+
lines.push(params.progressDashboard);
|
|
345
|
+
lines.push("");
|
|
346
|
+
}
|
|
347
|
+
// ── Cross-Round Summary ──
|
|
348
|
+
if (params.rollingSummary && params.rollingSummary.rounds_sampled > 0) {
|
|
349
|
+
const rs = params.rollingSummary;
|
|
350
|
+
const maxRounds = sfPolicy.max_summary_rounds;
|
|
351
|
+
lines.push(`## Cross-Round Summary (Last ${Math.min(rs.rounds_sampled, maxRounds)} Rounds)`);
|
|
352
|
+
lines.push("");
|
|
353
|
+
if (rs.key_outcomes.length) {
|
|
354
|
+
lines.push("**Key Outcomes**:");
|
|
355
|
+
for (const ko of rs.key_outcomes.slice(0, maxRounds)) {
|
|
356
|
+
lines.push(`- ${ko}`);
|
|
357
|
+
}
|
|
358
|
+
lines.push("");
|
|
359
|
+
}
|
|
360
|
+
if (rs.recurring_issues.length) {
|
|
361
|
+
lines.push("**Recurring Issues**:");
|
|
362
|
+
for (const ri of rs.recurring_issues) {
|
|
363
|
+
lines.push(`- ⚠️ ${ri}`);
|
|
364
|
+
}
|
|
365
|
+
lines.push("");
|
|
366
|
+
}
|
|
367
|
+
if (rs.failed_patterns && rs.failed_patterns.length) {
|
|
368
|
+
lines.push("**Failure Patterns**:");
|
|
369
|
+
for (const fp of rs.failed_patterns) {
|
|
370
|
+
lines.push(`- 🚫 ${fp}`);
|
|
371
|
+
}
|
|
372
|
+
lines.push("");
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
// ── Active Constraints ──
|
|
376
|
+
lines.push("## Active Constraints");
|
|
377
|
+
lines.push("");
|
|
378
|
+
if (params.constraints.length) {
|
|
379
|
+
for (const c of params.constraints) {
|
|
380
|
+
lines.push(`- ${c}`);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
else {
|
|
384
|
+
lines.push("_(none)_");
|
|
385
|
+
}
|
|
386
|
+
lines.push("");
|
|
387
|
+
if (params.retiredConstraints.length) {
|
|
388
|
+
lines.push("## Retired Constraints");
|
|
389
|
+
lines.push("");
|
|
390
|
+
for (const c of params.retiredConstraints.slice(0, 10)) {
|
|
391
|
+
lines.push(`- ~${c}~`);
|
|
392
|
+
}
|
|
393
|
+
lines.push("");
|
|
394
|
+
}
|
|
395
|
+
// ── Checkpoints ──
|
|
396
|
+
if (params.checkpoints.length) {
|
|
397
|
+
const maxCp = sfPolicy.max_checkpoints;
|
|
398
|
+
const shown = params.checkpoints.slice(-maxCp).reverse(); // newest first
|
|
399
|
+
lines.push(`## Checkpoints (${shown.length})`);
|
|
400
|
+
lines.push("");
|
|
401
|
+
for (const cp of shown) {
|
|
402
|
+
lines.push(`### ${cp.label || `Round ${cp.declared_at_round}`}`);
|
|
403
|
+
lines.push(`> Declared at Round ${cp.declared_at_round}`);
|
|
404
|
+
lines.push("");
|
|
405
|
+
if (cp.outcome) {
|
|
406
|
+
lines.push(`**Outcome**: ${cp.outcome}`);
|
|
407
|
+
lines.push("");
|
|
408
|
+
}
|
|
409
|
+
if (cp.carried_constraints.length) {
|
|
410
|
+
lines.push("**Carried Constraints**:");
|
|
411
|
+
for (const cc of cp.carried_constraints) {
|
|
412
|
+
lines.push(`- ${cc}`);
|
|
413
|
+
}
|
|
414
|
+
lines.push("");
|
|
415
|
+
}
|
|
416
|
+
if (cp.resolved_constraints.length) {
|
|
417
|
+
lines.push("**Resolved Constraints**:");
|
|
418
|
+
for (const rc of cp.resolved_constraints) {
|
|
419
|
+
lines.push(`- ✅ ${rc}`);
|
|
420
|
+
}
|
|
421
|
+
lines.push("");
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
// ── Delegation Summary ──
|
|
426
|
+
if (params.delegationSummary) {
|
|
427
|
+
lines.push(params.delegationSummary);
|
|
428
|
+
}
|
|
429
|
+
// ── External Context (current round only) ──
|
|
430
|
+
if (params.externalContext) {
|
|
431
|
+
lines.push("---");
|
|
432
|
+
lines.push("");
|
|
433
|
+
lines.push("## External Context (This Round)");
|
|
434
|
+
lines.push("");
|
|
435
|
+
lines.push(params.externalContext);
|
|
436
|
+
lines.push("");
|
|
437
|
+
}
|
|
438
|
+
return lines.join("\n");
|
|
439
|
+
}
|
|
254
440
|
function getPreviousRoundTask(loopId, roundNum, vaultContext) {
|
|
255
441
|
const prev = getPreviousRound(loopId, roundNum, vaultContext);
|
|
256
442
|
return prev?.task ?? "";
|
|
@@ -298,13 +484,6 @@ function countConsecutiveHashMismatches(loopId, vaultContext) {
|
|
|
298
484
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
299
485
|
// Strategy collapse detection
|
|
300
486
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
301
|
-
function strategyCollapse(loopId, vaultContext) {
|
|
302
|
-
const recent = getRecentRounds(loopId, 3, vaultContext);
|
|
303
|
-
if (recent.length < 3)
|
|
304
|
-
return false;
|
|
305
|
-
return recent.every((r) => r.success === false);
|
|
306
|
-
}
|
|
307
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
308
487
|
// Constraint Retirement (v3.5)
|
|
309
488
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
310
489
|
function computeConstraintRetirement(activeConstraints, loopId, currentRound, vaultContext) {
|
|
@@ -739,6 +918,116 @@ function loadLatestCheckpoint(loopId, currentRound, vaultContext) {
|
|
|
739
918
|
});
|
|
740
919
|
}
|
|
741
920
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
921
|
+
// Shared state evolution (P0/P1/P5) — used by both compileL1 and compileL2
|
|
922
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
923
|
+
/** Apply P1 objective refinement from last round's self-evaluation.
|
|
924
|
+
* Appends the refinement to the objective with a round marker.
|
|
925
|
+
* Returns the updated loop objective (or unchanged if no refinement). */
|
|
926
|
+
function applyObjectiveRefinement(loopObjective, lastRoundResult, currentRound) {
|
|
927
|
+
if (!loopObjective || !lastRoundResult?.objective_refinement)
|
|
928
|
+
return loopObjective;
|
|
929
|
+
const refinement = lastRoundResult.objective_refinement.trim();
|
|
930
|
+
if (!refinement)
|
|
931
|
+
return loopObjective;
|
|
932
|
+
const version = (loopObjective.version ?? 1) + 1;
|
|
933
|
+
const history = [...(loopObjective.refinement_history ?? []), refinement];
|
|
934
|
+
const policy = getPolicy();
|
|
935
|
+
const maxVersions = policy.evolution?.max_objective_versions ?? 10;
|
|
936
|
+
const prunedHistory = history.slice(-maxVersions);
|
|
937
|
+
const refinedObjective = `${loopObjective.objective} [R${currentRound}: ${refinement}]`;
|
|
938
|
+
return makeLoopObjective({
|
|
939
|
+
...loopObjective,
|
|
940
|
+
objective: refinedObjective,
|
|
941
|
+
version,
|
|
942
|
+
refinement_history: prunedHistory,
|
|
943
|
+
});
|
|
944
|
+
}
|
|
945
|
+
/** Apply P0 discovered constraints and P5 retractions/revisions.
|
|
946
|
+
* Pure function — returns updated constraints, loop objective, and retired list. */
|
|
947
|
+
function applyConstraintEvolution(constraints, loopObjective, lastRoundResult, currentRound) {
|
|
948
|
+
const retired = [];
|
|
949
|
+
let lo = loopObjective;
|
|
950
|
+
let cs = [...constraints];
|
|
951
|
+
// P0: Merge discovered constraints
|
|
952
|
+
if (lastRoundResult?.discovered_constraints?.length) {
|
|
953
|
+
const policy = getPolicy();
|
|
954
|
+
const maxPerRound = policy.evolution?.max_discovered_constraints_per_round ?? 5;
|
|
955
|
+
const maxActive = policy.evolution?.max_active_constraints ?? 15;
|
|
956
|
+
const newConstraints = lastRoundResult.discovered_constraints
|
|
957
|
+
.slice(0, maxPerRound)
|
|
958
|
+
.filter((c) => !cs.includes(c));
|
|
959
|
+
cs = [...cs, ...newConstraints].slice(0, maxActive);
|
|
960
|
+
}
|
|
961
|
+
// P5a: Retracted constraints
|
|
962
|
+
if (lastRoundResult?.retracted_constraints?.length) {
|
|
963
|
+
for (const rc of lastRoundResult.retracted_constraints) {
|
|
964
|
+
const idx = cs.indexOf(rc);
|
|
965
|
+
if (idx >= 0) {
|
|
966
|
+
cs.splice(idx, 1);
|
|
967
|
+
retired.push(`[agent-retracted] ${rc}`);
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
// P5b: Revised success criteria
|
|
972
|
+
if (lastRoundResult?.revised_success_criteria?.length && lo) {
|
|
973
|
+
const sc = [...lo.success_criteria];
|
|
974
|
+
for (const rev of lastRoundResult.revised_success_criteria) {
|
|
975
|
+
const idx = sc.indexOf(rev.old);
|
|
976
|
+
if (idx >= 0)
|
|
977
|
+
sc[idx] = rev.new;
|
|
978
|
+
else
|
|
979
|
+
sc.push(rev.new);
|
|
980
|
+
}
|
|
981
|
+
const version = (lo.version ?? 1) + 1;
|
|
982
|
+
const history = [
|
|
983
|
+
...(lo.refinement_history ?? []),
|
|
984
|
+
`R${currentRound}: revised ${lastRoundResult.revised_success_criteria.length} success criteria`,
|
|
985
|
+
];
|
|
986
|
+
const updatedLo = makeLoopObjective({
|
|
987
|
+
...lo,
|
|
988
|
+
success_criteria: sc,
|
|
989
|
+
version,
|
|
990
|
+
refinement_history: history,
|
|
991
|
+
});
|
|
992
|
+
// Rebuild constraints to include revised criteria, deduplicate
|
|
993
|
+
cs = [...new Set([
|
|
994
|
+
...cs.filter((c) => !updatedLo.hard_constraints.includes(c) && !updatedLo.success_criteria.includes(c)),
|
|
995
|
+
...updatedLo.hard_constraints,
|
|
996
|
+
...updatedLo.success_criteria,
|
|
997
|
+
])];
|
|
998
|
+
lo = updatedLo;
|
|
999
|
+
}
|
|
1000
|
+
return { constraints: cs, loopObjective: lo, retired };
|
|
1001
|
+
}
|
|
1002
|
+
/** Collect checkpoint summaries from vault entries for a given loop.
|
|
1003
|
+
* Shared between compileL1 and compileL2 — same vault scanning logic. */
|
|
1004
|
+
function collectVaultCheckpoints(vaultContext, loopId, excludeRounds) {
|
|
1005
|
+
const checkpoints = [];
|
|
1006
|
+
if (!vaultContext)
|
|
1007
|
+
return checkpoints;
|
|
1008
|
+
const results = vaultContext.results || [];
|
|
1009
|
+
for (const r of results) {
|
|
1010
|
+
const lineage = (r.loop_lineage || r.lineage || {});
|
|
1011
|
+
if (lineage.loop_id !== loopId)
|
|
1012
|
+
continue;
|
|
1013
|
+
const cp = r.checkpoint_summary;
|
|
1014
|
+
if (cp && typeof cp.declared_at_round === "number") {
|
|
1015
|
+
if (excludeRounds?.has(cp.declared_at_round))
|
|
1016
|
+
continue;
|
|
1017
|
+
if (checkpoints.some((c) => c.declared_at_round === cp.declared_at_round))
|
|
1018
|
+
continue;
|
|
1019
|
+
checkpoints.push(makeCheckpointSummary({
|
|
1020
|
+
label: cp.label ?? "",
|
|
1021
|
+
declared_at_round: cp.declared_at_round,
|
|
1022
|
+
outcome: cp.outcome ?? "",
|
|
1023
|
+
carried_constraints: cp.carried_constraints ?? [],
|
|
1024
|
+
resolved_constraints: cp.resolved_constraints ?? [],
|
|
1025
|
+
}));
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
return checkpoints;
|
|
1029
|
+
}
|
|
1030
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
742
1031
|
// External Context formatting (v1.7 — memory system integration)
|
|
743
1032
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
744
1033
|
/** Format an external context string for injection into an L2 prompt.
|
|
@@ -779,43 +1068,35 @@ export function decideLevel(request, vaultContext) {
|
|
|
779
1068
|
return request.force_level;
|
|
780
1069
|
}
|
|
781
1070
|
}
|
|
782
|
-
//
|
|
783
|
-
|
|
1071
|
+
// ── L2 triggers: structural restarts ──────────────────────────────────
|
|
1072
|
+
// Gate 2: First call or explicit plan → full rebuild
|
|
1073
|
+
if (request.round === 1 || request.plan_source)
|
|
784
1074
|
return "l2";
|
|
785
|
-
|
|
786
|
-
|
|
1075
|
+
// Gate 3: Checkpoint boundary — subtask completed, re-plan
|
|
1076
|
+
if (request.last_round_result?.compression_checkpoint === true)
|
|
1077
|
+
return "l2";
|
|
1078
|
+
// Gate 4: goal_id stability
|
|
787
1079
|
const goalId = deriveGoalId(request.loop_id, request.task, request.goal_id);
|
|
788
1080
|
const prev = getPreviousRound(request.loop_id, request.round - 1, vaultContext);
|
|
789
1081
|
if (prev === null)
|
|
790
1082
|
return "l2";
|
|
791
|
-
// Gate 3: goal_id stability
|
|
792
1083
|
if (goalId !== prev.goal_id)
|
|
793
1084
|
return "l2";
|
|
794
|
-
//
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
}
|
|
810
|
-
// Gate 5: Explicit failures or new constraints → patch
|
|
811
|
-
const hasNewConstraints = request.constraints_from_plan.length > 0;
|
|
812
|
-
const hasNewFailures = request.last_round_result !== null &&
|
|
813
|
-
!request.last_round_result.success;
|
|
814
|
-
const hasRepair = detectsRepairSignal(request);
|
|
815
|
-
if (hasNewConstraints || hasNewFailures || hasRepair)
|
|
816
|
-
return "l1";
|
|
817
|
-
// Nothing triggered → fast path
|
|
818
|
-
return "l0";
|
|
1085
|
+
// ── L0 trigger: honest failure with no new information ────────────────
|
|
1086
|
+
const lr = request.last_round_result;
|
|
1087
|
+
const prevFailed = lr !== null && !lr.success;
|
|
1088
|
+
const hasNoNewInfo = (lr?.discovered_constraints?.length ?? 0) === 0 &&
|
|
1089
|
+
(lr?.objective_refinement?.trim().length ?? 0) === 0 &&
|
|
1090
|
+
(lr?.emerged_subtasks?.length ?? 0) === 0 &&
|
|
1091
|
+
(lr?.retracted_constraints?.length ?? 0) === 0 &&
|
|
1092
|
+
(lr?.revised_success_criteria?.length ?? 0) === 0 &&
|
|
1093
|
+
(lr?.wrong_assumptions?.length ?? 0) === 0 &&
|
|
1094
|
+
request.constraints_from_plan.length === 0 &&
|
|
1095
|
+
!detectsRepairSignal(request);
|
|
1096
|
+
if (prevFailed && hasNoNewInfo)
|
|
1097
|
+
return "l0";
|
|
1098
|
+
// ── L1: default path — all P0-P5 state changes handled here ───────────
|
|
1099
|
+
return "l1";
|
|
819
1100
|
}
|
|
820
1101
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
821
1102
|
// LAYER 2 — Soft Advisories (NEVER change compile level)
|
|
@@ -977,11 +1258,6 @@ export function computeAdvisories(request, vaultContext) {
|
|
|
977
1258
|
warnings.push(`goal_text_hash changed (${prev.goal_text_hash} → ${currentHash}) ` +
|
|
978
1259
|
"but goal_id matched — wording drift detected");
|
|
979
1260
|
}
|
|
980
|
-
// Strategy collapse check
|
|
981
|
-
if (strategyCollapse(request.loop_id, vaultContext)) {
|
|
982
|
-
warnings.push("strategy_collapse: 3 consecutive failed rounds — " +
|
|
983
|
-
"consider force_level=L2 rebuild");
|
|
984
|
-
}
|
|
985
1261
|
// Repair cue detection
|
|
986
1262
|
if (detectsRepairSignal(request)) {
|
|
987
1263
|
warnings.push("repair signal detected — L1 patch applied");
|
|
@@ -1170,6 +1446,96 @@ function computeLoopObjectiveFromTask(request, _vaultContext) {
|
|
|
1170
1446
|
});
|
|
1171
1447
|
}
|
|
1172
1448
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
1449
|
+
// P4: Shared consistency checks and progress dashboard (used by L1 & L2)
|
|
1450
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
1451
|
+
/** Run P4 consistency validation checks on agent execution evidence.
|
|
1452
|
+
* Returns warning strings. Called by both compileL1 and compileL2.
|
|
1453
|
+
* Three checks:
|
|
1454
|
+
* 1. Agent claims file changes but files_changed is empty
|
|
1455
|
+
* 2. Agent reports success but test results show failures
|
|
1456
|
+
* 3. Progress estimate mismatch (objective vs subjective) */
|
|
1457
|
+
function runP4ConsistencyChecks(evidence, outputSummary, success, loopObjective) {
|
|
1458
|
+
const warnings = [];
|
|
1459
|
+
// Check 1: Agent claims action but reports no files changed
|
|
1460
|
+
if (evidence.files_changed.length === 0 && outputSummary) {
|
|
1461
|
+
const actionWords = /\b(fix|modif|chang|updat|refactor|implement|add|remov|delet|rewrit|修补|修改|更改|更新|重构|实现|添加|删除|重写)\b/i;
|
|
1462
|
+
if (actionWords.test(outputSummary)) {
|
|
1463
|
+
warnings.push("P4: Agent output claims changes but execution_evidence.files_changed is empty — possible oversight");
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
// Check 2: Agent reports success but tests failed
|
|
1467
|
+
if (evidence.test_results && evidence.test_results.failed > 0 && success) {
|
|
1468
|
+
warnings.push(`P4: Agent reports success=true but ${evidence.test_results.failed} tests failed — claims may not match reality`);
|
|
1469
|
+
}
|
|
1470
|
+
// Check 3: Progress estimate mismatch with criteria tracking
|
|
1471
|
+
if (loopObjective && loopObjective.success_criteria.length > 0) {
|
|
1472
|
+
const metCount = evidence.success_criteria_met.length;
|
|
1473
|
+
const totalCount = loopObjective.success_criteria.length;
|
|
1474
|
+
const objectiveProgress = totalCount > 0 ? metCount / totalCount : 0;
|
|
1475
|
+
const subjectiveProgress = evidence.progress_estimate;
|
|
1476
|
+
const policy = getPolicy();
|
|
1477
|
+
const mismatchThreshold = policy.evolution?.progress_mismatch_threshold ?? 0.3;
|
|
1478
|
+
if (Math.abs(objectiveProgress - subjectiveProgress) > mismatchThreshold) {
|
|
1479
|
+
warnings.push(`P4: Progress estimate mismatch — agent estimates ${(subjectiveProgress * 100).toFixed(0)}% but ${metCount}/${totalCount} criteria met (${(objectiveProgress * 100).toFixed(0)}%)`);
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
return warnings;
|
|
1483
|
+
}
|
|
1484
|
+
/** Build a progress dashboard string for prompt or state file injection.
|
|
1485
|
+
* Used by both compileL1 and compileL2. When showTrend is true, also
|
|
1486
|
+
* computes a trend arrow from vault history (L2 path). */
|
|
1487
|
+
function buildProgressDashboard(evidence, loopObjective, vaultContext, loopId, currentRound, showTrend) {
|
|
1488
|
+
const totalCriteria = loopObjective?.success_criteria.length ?? 0;
|
|
1489
|
+
const metCriteria = evidence.success_criteria_met.length;
|
|
1490
|
+
const remainingCriteria = evidence.success_criteria_remaining;
|
|
1491
|
+
const objProgress = totalCriteria > 0 ? metCriteria / totalCriteria : evidence.progress_estimate;
|
|
1492
|
+
const subjProgress = evidence.progress_estimate;
|
|
1493
|
+
const lines = [
|
|
1494
|
+
"",
|
|
1495
|
+
"### Progress Dashboard",
|
|
1496
|
+
"",
|
|
1497
|
+
`**Overall**: ${metCriteria}/${totalCriteria} criteria met (${(objProgress * 100).toFixed(0)}%)`,
|
|
1498
|
+
];
|
|
1499
|
+
if (Math.abs(objProgress - subjProgress) > 0.05) {
|
|
1500
|
+
lines.push(`**Agent estimate**: ${(subjProgress * 100).toFixed(0)}%`);
|
|
1501
|
+
}
|
|
1502
|
+
if (evidence.files_changed.length) {
|
|
1503
|
+
lines.push(`**Files changed**: ${evidence.files_changed.join(", ")}`);
|
|
1504
|
+
}
|
|
1505
|
+
if (evidence.test_results) {
|
|
1506
|
+
const tr = evidence.test_results;
|
|
1507
|
+
lines.push(`**Tests**: ${tr.passed} passed, ${tr.failed} failed, ${tr.skipped} skipped`);
|
|
1508
|
+
}
|
|
1509
|
+
if (remainingCriteria.length) {
|
|
1510
|
+
lines.push("**Remaining criteria**:");
|
|
1511
|
+
for (const rc of remainingCriteria.slice(0, 5)) {
|
|
1512
|
+
lines.push(`- ${rc}`);
|
|
1513
|
+
}
|
|
1514
|
+
if (remainingCriteria.length > 5) {
|
|
1515
|
+
lines.push(`- ... and ${remainingCriteria.length - 5} more`);
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
lines.push("");
|
|
1519
|
+
// Trend arrow — only computed in L2 path where full vault history is available
|
|
1520
|
+
if (showTrend && vaultContext && loopObjective) {
|
|
1521
|
+
const prevEvidence = getPreviousRoundEvidence(loopId, currentRound - 1, vaultContext);
|
|
1522
|
+
if (prevEvidence) {
|
|
1523
|
+
const trend = objProgress - (prevEvidence.success_criteria_met.length / Math.max(totalCriteria, 1));
|
|
1524
|
+
if (trend > 0.03) {
|
|
1525
|
+
lines.push(`**Trend**: ↑ advancing (+${(trend * 100).toFixed(0)}% this round)`);
|
|
1526
|
+
}
|
|
1527
|
+
else if (trend > -0.03) {
|
|
1528
|
+
lines.push("**Trend**: → stable");
|
|
1529
|
+
}
|
|
1530
|
+
else {
|
|
1531
|
+
lines.push(`**Trend**: ↓ regressing (${(trend * 100).toFixed(0)}% this round)`);
|
|
1532
|
+
}
|
|
1533
|
+
lines.push("");
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
return lines.join("\n");
|
|
1537
|
+
}
|
|
1538
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
1173
1539
|
// Compilation — L0 / L1 / L2
|
|
1174
1540
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
1175
1541
|
function compileL0(request, vaultContext, prevRound) {
|
|
@@ -1202,100 +1568,169 @@ function compileL0(request, vaultContext, prevRound) {
|
|
|
1202
1568
|
return l2Response;
|
|
1203
1569
|
}
|
|
1204
1570
|
function compileL1(request, vaultContext, prevRound) {
|
|
1205
|
-
const prev = prevRound ??
|
|
1206
|
-
getPreviousRound(request.loop_id, request.round - 1, vaultContext);
|
|
1207
1571
|
const goalId = deriveGoalId(request.loop_id, request.task, request.goal_id);
|
|
1208
|
-
const
|
|
1209
|
-
|
|
1210
|
-
//
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1572
|
+
const lr = request.last_round_result;
|
|
1573
|
+
// ── v1.14: Technique routing (Tier 1 only — 4 techniques) ────────────
|
|
1574
|
+
// L1 is the default path: technique evolves within Tier 1 unless
|
|
1575
|
+
// decideLevel already escalated to L2 (strategy collapse / checkpoint).
|
|
1576
|
+
const analysis = routeTechniqueAdaptive(request.task, vaultContext, request.loop_id,
|
|
1577
|
+
/* isCheckpoint */ false);
|
|
1578
|
+
const technique = analysis.technique;
|
|
1579
|
+
const referenceFile = analysis.reference_file;
|
|
1580
|
+
// Determine if technique changed from last round
|
|
1581
|
+
const techChanged = techniqueChanged(request.loop_id, technique, request.round, vaultContext);
|
|
1582
|
+
// ── Loop Objective: load from vault, apply P1 refinement ─────────────
|
|
1583
|
+
let loopObjective = null;
|
|
1584
|
+
const vaultLo = vaultGetLoopObjective(request.loop_id, vaultContext);
|
|
1585
|
+
if (vaultLo) {
|
|
1586
|
+
const loData = vaultLo.loop_objective;
|
|
1587
|
+
if (loData) {
|
|
1588
|
+
loopObjective = makeLoopObjective({
|
|
1589
|
+
objective: loData.objective ?? "",
|
|
1590
|
+
success_criteria: loData.success_criteria ?? [],
|
|
1591
|
+
hard_constraints: loData.hard_constraints ?? [],
|
|
1592
|
+
created_at_round: loData.created_at_round ?? 1,
|
|
1593
|
+
loop_id: loData.loop_id ?? request.loop_id,
|
|
1594
|
+
version: loData.version ?? 1,
|
|
1595
|
+
refinement_history: loData.refinement_history ?? [],
|
|
1596
|
+
});
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
if (!loopObjective && request.loop_objective) {
|
|
1600
|
+
loopObjective = request.loop_objective;
|
|
1601
|
+
}
|
|
1602
|
+
// P1: Apply objective refinement (shared)
|
|
1603
|
+
loopObjective = applyObjectiveRefinement(loopObjective, lr, request.round);
|
|
1604
|
+
// ── Build active constraints (base + LO) ─────────────────────────────
|
|
1605
|
+
let constraints = [...request.constraints_from_plan];
|
|
1606
|
+
if (loopObjective) {
|
|
1607
|
+
constraints = [
|
|
1608
|
+
...new Set([
|
|
1609
|
+
...constraints,
|
|
1610
|
+
...loopObjective.hard_constraints,
|
|
1611
|
+
...loopObjective.success_criteria,
|
|
1612
|
+
]),
|
|
1613
|
+
];
|
|
1614
|
+
}
|
|
1615
|
+
// ── P0/P5: Constraint evolution (shared) ─────────────────────────────
|
|
1616
|
+
const { constraints: evolvedCs, loopObjective: evolvedLo, retired } = applyConstraintEvolution(constraints, loopObjective, lr, request.round);
|
|
1617
|
+
constraints = evolvedCs;
|
|
1618
|
+
loopObjective = evolvedLo;
|
|
1619
|
+
// ── Constraint retirement ────────────────────────────────────────────
|
|
1620
|
+
const { active, retired: autoRetired } = computeConstraintRetirement(constraints, request.loop_id, request.round, vaultContext);
|
|
1621
|
+
for (const r of autoRetired) {
|
|
1622
|
+
if (!retired.includes(r))
|
|
1623
|
+
retired.push(r);
|
|
1624
|
+
}
|
|
1625
|
+
// ── Rolling summary + P5c wrong assumptions ──────────────────────────
|
|
1215
1626
|
const rollingSummary = buildRollingSummary(request.loop_id, request.round, vaultContext);
|
|
1627
|
+
if (lr?.wrong_assumptions?.length) {
|
|
1628
|
+
if (rollingSummary) {
|
|
1629
|
+
rollingSummary.key_outcomes = [
|
|
1630
|
+
...rollingSummary.key_outcomes,
|
|
1631
|
+
...lr.wrong_assumptions.map((a) => `Wrong assumption corrected: ${a}`),
|
|
1632
|
+
];
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1216
1635
|
const rollingText = formatRollingSummaryForPrompt(rollingSummary);
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
if (
|
|
1220
|
-
|
|
1636
|
+
// ── P2: Emerged subtasks → suggested next task ───────────────────────
|
|
1637
|
+
let suggestedNextTask = "";
|
|
1638
|
+
if (lr?.emerged_subtasks?.length) {
|
|
1639
|
+
suggestedNextTask = lr.emerged_subtasks.slice(0, 3).join("; ");
|
|
1221
1640
|
}
|
|
1222
|
-
|
|
1223
|
-
|
|
1641
|
+
// ── P4: Consistency validation ───────────────────────────────────────
|
|
1642
|
+
const evidence = lr?.execution_evidence;
|
|
1643
|
+
const warnings = evidence
|
|
1644
|
+
? runP4ConsistencyChecks(evidence, lr.output_summary, lr.success, loopObjective)
|
|
1645
|
+
: [];
|
|
1646
|
+
// ── Route to specialist (all 4 techniques available via Tier 1) ──────
|
|
1647
|
+
let prompt;
|
|
1648
|
+
if (technique === "step-back") {
|
|
1649
|
+
prompt = compileStepBack(request, goalId, active, loopObjective, rollingText, analysis, { embedSkeleton: techChanged, level: "l1" });
|
|
1224
1650
|
}
|
|
1225
|
-
if (
|
|
1226
|
-
|
|
1651
|
+
else if (technique === "least-to-most") {
|
|
1652
|
+
prompt = compileLeastToMost(request, goalId, active, loopObjective, rollingText, analysis, { embedSkeleton: techChanged, level: "l1" });
|
|
1227
1653
|
}
|
|
1228
|
-
if (
|
|
1229
|
-
|
|
1654
|
+
else if (technique === "tree-of-thought") {
|
|
1655
|
+
prompt = compileToT(request, goalId, active, loopObjective, rollingText, analysis, { embedSkeleton: techChanged, level: "l1" });
|
|
1230
1656
|
}
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
`## Loop Round ${request.round} — L1 Patch`,
|
|
1234
|
-
"",
|
|
1235
|
-
`**Goal**: ${request.task}`,
|
|
1236
|
-
`**Loop ID**: ${request.loop_id}`,
|
|
1237
|
-
`**Goal ID**: ${goalId}`,
|
|
1238
|
-
"",
|
|
1239
|
-
];
|
|
1240
|
-
if (rollingText) {
|
|
1241
|
-
lines.push(rollingText);
|
|
1242
|
-
lines.push("");
|
|
1657
|
+
else {
|
|
1658
|
+
prompt = compileGeneric(request, goalId, active, loopObjective, rollingText, analysis, referenceFile, technique, { embedSkeleton: techChanged, level: "l1" });
|
|
1243
1659
|
}
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1660
|
+
// ── P4: Progress Dashboard ───────────────────────────────────────────
|
|
1661
|
+
const progressDashboard = (evidence && loopObjective)
|
|
1662
|
+
? buildProgressDashboard(evidence, loopObjective, vaultContext, request.loop_id, request.round, /* showTrend */ false)
|
|
1663
|
+
: "";
|
|
1664
|
+
// ── v1.14: Render state file ─────────────────────────────────────────
|
|
1665
|
+
const sfPolicy = getPolicy().state_file;
|
|
1666
|
+
let stateFileContent;
|
|
1667
|
+
if (sfPolicy.enabled) {
|
|
1668
|
+
const allCheckpoints = collectVaultCheckpoints(vaultContext, request.loop_id);
|
|
1669
|
+
const delegationSummary = vaultContext
|
|
1670
|
+
? buildDelegationSummary(vaultContext)
|
|
1671
|
+
: "";
|
|
1672
|
+
stateFileContent = renderStateFile({
|
|
1673
|
+
loopId: request.loop_id,
|
|
1674
|
+
currentRound: request.round,
|
|
1675
|
+
maxRounds: request.max_rounds ?? sfPolicy.max_summary_rounds * 2,
|
|
1676
|
+
loopObjective,
|
|
1677
|
+
rollingSummary,
|
|
1678
|
+
constraints: active,
|
|
1679
|
+
retiredConstraints: retired,
|
|
1680
|
+
checkpoints: allCheckpoints,
|
|
1681
|
+
delegationSummary,
|
|
1682
|
+
progressDashboard,
|
|
1683
|
+
externalContext: request.external_context,
|
|
1684
|
+
});
|
|
1685
|
+
prompt = renderStateFilePointer(request.loop_id) + "\n" + prompt;
|
|
1686
|
+
}
|
|
1687
|
+
// ── Build thin diff ──────────────────────────────────────────────────
|
|
1688
|
+
const violations = lr?.constraint_violations ?? [];
|
|
1689
|
+
const diffParts = [];
|
|
1690
|
+
if (request.constraints_from_plan.length) {
|
|
1691
|
+
diffParts.push(`new constraints: [${request.constraints_from_plan.join(", ")}]`);
|
|
1249
1692
|
}
|
|
1250
1693
|
if (retired.length) {
|
|
1251
|
-
|
|
1252
|
-
for (const c of retired)
|
|
1253
|
-
lines.push(`- ~${c}~`);
|
|
1254
|
-
lines.push("");
|
|
1694
|
+
diffParts.push(`retired constraints: [${retired.join(", ")}]`);
|
|
1255
1695
|
}
|
|
1256
1696
|
if (violations.length) {
|
|
1257
|
-
|
|
1258
|
-
for (const v of violations)
|
|
1259
|
-
lines.push(`- ${v}`);
|
|
1260
|
-
lines.push("");
|
|
1697
|
+
diffParts.push(`violations: [${violations.join(", ")}]`);
|
|
1261
1698
|
}
|
|
1262
1699
|
if (request.new_since_last_round) {
|
|
1263
|
-
|
|
1264
|
-
lines.push(request.new_since_last_round);
|
|
1265
|
-
lines.push("");
|
|
1266
|
-
}
|
|
1267
|
-
if (request.last_round_result?.output_summary) {
|
|
1268
|
-
lines.push("### Last Round Summary");
|
|
1269
|
-
lines.push(request.last_round_result.output_summary);
|
|
1270
|
-
lines.push("");
|
|
1700
|
+
diffParts.push(`delta: ${request.new_since_last_round.slice(0, 200)}`);
|
|
1271
1701
|
}
|
|
1272
|
-
|
|
1273
|
-
|
|
1702
|
+
// v1.14: When state file is enabled, progressDashboard lives in the state
|
|
1703
|
+
// file (see renderStateFile above). Only inline it in the prompt when the
|
|
1704
|
+
// state file is disabled — this keeps the prompt truly thin.
|
|
1705
|
+
const finalPrompt = sfPolicy.enabled ? prompt : prompt + progressDashboard;
|
|
1274
1706
|
return makeLoopCompileResponse({
|
|
1275
1707
|
status: AgentStatus.OK,
|
|
1276
|
-
prompt:
|
|
1708
|
+
prompt: finalPrompt,
|
|
1277
1709
|
recompile_level: "l1",
|
|
1278
|
-
diff_from_previous: diffParts.length ? diffParts.join("; ") : "
|
|
1710
|
+
diff_from_previous: diffParts.length ? diffParts.join("; ") : "L1 continue — state updated in file.",
|
|
1279
1711
|
lineage: [`${request.loop_id}:r${request.round}`],
|
|
1280
1712
|
constraints_active: active,
|
|
1281
1713
|
constraints_retired: retired,
|
|
1282
|
-
technique_used:
|
|
1714
|
+
technique_used: technique,
|
|
1715
|
+
reference_file: referenceFile,
|
|
1283
1716
|
rolling_summary: rollingSummary,
|
|
1284
1717
|
loop_id: request.loop_id,
|
|
1285
1718
|
round: request.round,
|
|
1286
1719
|
goal_id: goalId,
|
|
1287
1720
|
goal_text_hash: computeGoalTextHash(request.task),
|
|
1721
|
+
loop_objective: loopObjective,
|
|
1288
1722
|
plan_source: request.plan_source,
|
|
1289
|
-
|
|
1723
|
+
suggested_next_task: suggestedNextTask,
|
|
1724
|
+
warnings,
|
|
1725
|
+
state_file_content: stateFileContent,
|
|
1290
1726
|
});
|
|
1291
1727
|
}
|
|
1292
1728
|
export function compileL2(request, vaultContext) {
|
|
1293
1729
|
const goalId = deriveGoalId(request.loop_id, request.task, request.goal_id);
|
|
1294
|
-
//
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
const referenceFile = analysis.reference_file;
|
|
1730
|
+
// v1.15: L2 no longer auto-selects a technique. The Agent reads the technique
|
|
1731
|
+
// catalog (skills/prompt-techniques/SKILL.md), freely chooses the best fit
|
|
1732
|
+
// based on loop state, reads the corresponding reference file, and applies
|
|
1733
|
+
// it directly to the task.
|
|
1299
1734
|
// v1.10: Checkpoint — load existing or build new from last round's self-eval
|
|
1300
1735
|
let checkpointSummary = null;
|
|
1301
1736
|
let checkpointRound = 0;
|
|
@@ -1359,26 +1794,9 @@ export function compileL2(request, vaultContext) {
|
|
|
1359
1794
|
loopObjective = request.loop_objective;
|
|
1360
1795
|
}
|
|
1361
1796
|
}
|
|
1362
|
-
// ── P1: Apply
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
if (refinement) {
|
|
1366
|
-
const version = (loopObjective.version ?? 1) + 1;
|
|
1367
|
-
const history = [...(loopObjective.refinement_history ?? []), refinement];
|
|
1368
|
-
// Enforce max versions from policy
|
|
1369
|
-
const policy = getPolicy();
|
|
1370
|
-
const maxVersions = policy.evolution?.max_objective_versions ?? 10;
|
|
1371
|
-
const prunedHistory = history.slice(-maxVersions);
|
|
1372
|
-
const refinedObjective = `${loopObjective.objective} [R${request.round}: ${refinement}]`;
|
|
1373
|
-
loopObjective = makeLoopObjective({
|
|
1374
|
-
...loopObjective,
|
|
1375
|
-
objective: refinedObjective,
|
|
1376
|
-
version,
|
|
1377
|
-
refinement_history: prunedHistory,
|
|
1378
|
-
});
|
|
1379
|
-
}
|
|
1380
|
-
}
|
|
1381
|
-
// ── Build active constraints (base + LO + P0 discovered) ──
|
|
1797
|
+
// ── P1: Apply objective refinement (shared) ──
|
|
1798
|
+
loopObjective = applyObjectiveRefinement(loopObjective, lr, request.round);
|
|
1799
|
+
// ── Build active constraints (base + LO) ──
|
|
1382
1800
|
let constraints = [...request.constraints_from_plan];
|
|
1383
1801
|
if (loopObjective) {
|
|
1384
1802
|
constraints = [
|
|
@@ -1389,63 +1807,14 @@ export function compileL2(request, vaultContext) {
|
|
|
1389
1807
|
]),
|
|
1390
1808
|
];
|
|
1391
1809
|
}
|
|
1392
|
-
// P0: Merge discovered constraints from last round
|
|
1393
|
-
if (request.last_round_result?.discovered_constraints?.length) {
|
|
1394
|
-
const policy = getPolicy();
|
|
1395
|
-
const maxPerRound = policy.evolution?.max_discovered_constraints_per_round ?? 5;
|
|
1396
|
-
const maxActive = policy.evolution?.max_active_constraints ?? 15;
|
|
1397
|
-
const newConstraints = request.last_round_result.discovered_constraints
|
|
1398
|
-
.slice(0, maxPerRound)
|
|
1399
|
-
.filter((c) => !constraints.includes(c));
|
|
1400
|
-
constraints = [...constraints, ...newConstraints].slice(0, maxActive);
|
|
1401
|
-
}
|
|
1402
1810
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
1403
|
-
// P5:
|
|
1404
|
-
// so the technician compiler sees the corrected constraints and loop objective.
|
|
1811
|
+
// P0/P5: Constraint evolution (shared) — retractions/revisions BEFORE prompt
|
|
1405
1812
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
1406
1813
|
const warnings = [];
|
|
1407
|
-
const
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
const idx = constraints.indexOf(rc);
|
|
1412
|
-
if (idx >= 0) {
|
|
1413
|
-
constraints.splice(idx, 1);
|
|
1414
|
-
retired.push(`[agent-retracted] ${rc}`);
|
|
1415
|
-
}
|
|
1416
|
-
}
|
|
1417
|
-
}
|
|
1418
|
-
// P5b: Revised success criteria — update Loop Objective
|
|
1419
|
-
if (lr?.revised_success_criteria?.length && loopObjective) {
|
|
1420
|
-
const sc = [...loopObjective.success_criteria];
|
|
1421
|
-
for (const rev of lr.revised_success_criteria) {
|
|
1422
|
-
const idx = sc.indexOf(rev.old);
|
|
1423
|
-
if (idx >= 0) {
|
|
1424
|
-
sc[idx] = rev.new;
|
|
1425
|
-
}
|
|
1426
|
-
else {
|
|
1427
|
-
sc.push(rev.new);
|
|
1428
|
-
}
|
|
1429
|
-
}
|
|
1430
|
-
const version = (loopObjective.version ?? 1) + 1;
|
|
1431
|
-
const history = [
|
|
1432
|
-
...(loopObjective.refinement_history ?? []),
|
|
1433
|
-
`R${request.round}: revised ${lr.revised_success_criteria.length} success criteria`,
|
|
1434
|
-
];
|
|
1435
|
-
const updatedLo = makeLoopObjective({
|
|
1436
|
-
...loopObjective,
|
|
1437
|
-
success_criteria: sc,
|
|
1438
|
-
version,
|
|
1439
|
-
refinement_history: history,
|
|
1440
|
-
});
|
|
1441
|
-
// Rebuild constraints to include revised criteria
|
|
1442
|
-
constraints = [...new Set([
|
|
1443
|
-
...constraints.filter((c) => !updatedLo.hard_constraints.includes(c) && !updatedLo.success_criteria.includes(c)),
|
|
1444
|
-
...updatedLo.hard_constraints,
|
|
1445
|
-
...updatedLo.success_criteria,
|
|
1446
|
-
])];
|
|
1447
|
-
loopObjective = updatedLo;
|
|
1448
|
-
}
|
|
1814
|
+
const evoResult = applyConstraintEvolution(constraints, loopObjective, lr, request.round);
|
|
1815
|
+
constraints = evoResult.constraints;
|
|
1816
|
+
loopObjective = evoResult.loopObjective;
|
|
1817
|
+
const retired = evoResult.retired;
|
|
1449
1818
|
// ── v1.10: Build checkpoint summary if last round declared one ──
|
|
1450
1819
|
if (checkpointRound > 0 && !checkpointSummary && lr) {
|
|
1451
1820
|
// checkpointRound was set but no summary yet → fresh declaration
|
|
@@ -1461,20 +1830,11 @@ export function compileL2(request, vaultContext) {
|
|
|
1461
1830
|
};
|
|
1462
1831
|
checkpointSummary = buildCheckpointSummary(synthEval, checkpointRound, constraints, retired);
|
|
1463
1832
|
}
|
|
1464
|
-
// ──
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
else if (technique === "least-to-most") {
|
|
1470
|
-
prompt = compileLeastToMost(request, goalId, constraints, loopObjective, rollingText, analysis);
|
|
1471
|
-
}
|
|
1472
|
-
else if (technique === "tree-of-thought") {
|
|
1473
|
-
prompt = compileToT(request, goalId, constraints, loopObjective, rollingText, analysis);
|
|
1474
|
-
}
|
|
1475
|
-
else {
|
|
1476
|
-
prompt = compileGeneric(request, goalId, constraints, loopObjective, rollingText, analysis, referenceFile, technique);
|
|
1477
|
-
}
|
|
1833
|
+
// ── v1.15: Build L2 prompt with Technique Selection block ──
|
|
1834
|
+
// The Agent freely chooses a technique by reading the catalog and reference
|
|
1835
|
+
// files. LoopForge provides the loop state (objective, constraints, rolling
|
|
1836
|
+
// summary) — the Agent provides the reasoning strategy.
|
|
1837
|
+
let prompt = buildL2Prompt(request, goalId, constraints, loopObjective, rollingText);
|
|
1478
1838
|
// ── v1.10: Inject checkpoint block (before rolling summary in prompt) ──
|
|
1479
1839
|
// The checkpoint is rendered before the external context so it appears
|
|
1480
1840
|
// in a natural position: after Loop Objective, before Cross-Round Summary.
|
|
@@ -1498,29 +1858,7 @@ export function compileL2(request, vaultContext) {
|
|
|
1498
1858
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
1499
1859
|
const evidence = lr?.execution_evidence;
|
|
1500
1860
|
if (evidence) {
|
|
1501
|
-
|
|
1502
|
-
if (evidence.files_changed.length === 0 && lr.output_summary) {
|
|
1503
|
-
const actionWords = /\b(fix|modif|chang|updat|refactor|implement|add|remov|delet|rewrit|修补|修改|更改|更新|重构|实现|添加|删除|重写)\b/i;
|
|
1504
|
-
if (actionWords.test(lr.output_summary)) {
|
|
1505
|
-
warnings.push("P4: Agent output claims changes but execution_evidence.files_changed is empty — possible oversight");
|
|
1506
|
-
}
|
|
1507
|
-
}
|
|
1508
|
-
// Check 2: Agent reports success but tests failed
|
|
1509
|
-
if (evidence.test_results && evidence.test_results.failed > 0 && lr.success) {
|
|
1510
|
-
warnings.push(`P4: Agent reports success=true but ${evidence.test_results.failed} tests failed — claims may not match reality`);
|
|
1511
|
-
}
|
|
1512
|
-
// Check 3: Progress estimate mismatch with criteria tracking
|
|
1513
|
-
if (loopObjective && loopObjective.success_criteria.length > 0) {
|
|
1514
|
-
const metCount = evidence.success_criteria_met.length;
|
|
1515
|
-
const totalCount = loopObjective.success_criteria.length;
|
|
1516
|
-
const objectiveProgress = totalCount > 0 ? metCount / totalCount : 0;
|
|
1517
|
-
const subjectiveProgress = evidence.progress_estimate;
|
|
1518
|
-
const policy = getPolicy();
|
|
1519
|
-
const mismatchThreshold = policy.evolution?.progress_mismatch_threshold ?? 0.3;
|
|
1520
|
-
if (Math.abs(objectiveProgress - subjectiveProgress) > mismatchThreshold) {
|
|
1521
|
-
warnings.push(`P4: Progress estimate mismatch — agent estimates ${(subjectiveProgress * 100).toFixed(0)}% but ${metCount}/${totalCount} criteria met (${(objectiveProgress * 100).toFixed(0)}%)`);
|
|
1522
|
-
}
|
|
1523
|
-
}
|
|
1861
|
+
warnings.push(...runP4ConsistencyChecks(evidence, lr.output_summary, lr.success, loopObjective));
|
|
1524
1862
|
}
|
|
1525
1863
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
1526
1864
|
// P4: Progress gradient — early stall detection
|
|
@@ -1544,70 +1882,56 @@ export function compileL2(request, vaultContext) {
|
|
|
1544
1882
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
1545
1883
|
// P4: Build Progress Dashboard for prompt injection
|
|
1546
1884
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
if (
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
// Compute trend from previous round
|
|
1581
|
-
if (vaultContext) {
|
|
1582
|
-
const prevEvidence = getPreviousRoundEvidence(request.loop_id, request.round - 1, vaultContext);
|
|
1583
|
-
if (prevEvidence) {
|
|
1584
|
-
const trend = objProgress - (prevEvidence.success_criteria_met.length / Math.max(totalCriteria, 1));
|
|
1585
|
-
if (trend > 0.03) {
|
|
1586
|
-
lines.push(`**Trend**: ↑ advancing (+${(trend * 100).toFixed(0)}% this round)`);
|
|
1587
|
-
}
|
|
1588
|
-
else if (trend > -0.03) {
|
|
1589
|
-
lines.push("**Trend**: → stable");
|
|
1590
|
-
}
|
|
1591
|
-
else {
|
|
1592
|
-
lines.push(`**Trend**: ↓ regressing (${(trend * 100).toFixed(0)}% this round)`);
|
|
1593
|
-
}
|
|
1594
|
-
lines.push("");
|
|
1595
|
-
}
|
|
1596
|
-
}
|
|
1597
|
-
progressDashboard = lines.join("\n");
|
|
1885
|
+
const progressDashboard = (evidence && loopObjective)
|
|
1886
|
+
? buildProgressDashboard(evidence, loopObjective, vaultContext, request.loop_id, request.round, /* showTrend */ true)
|
|
1887
|
+
: "";
|
|
1888
|
+
// ── v1.14: Render state file ──────────────────────────────────────────
|
|
1889
|
+
// Collect all accumulated state and render it as a markdown snapshot.
|
|
1890
|
+
// The caller writes this to .loopforge/state/{loopId}-state.md.
|
|
1891
|
+
const sfPolicy = getPolicy().state_file;
|
|
1892
|
+
let stateFileContent;
|
|
1893
|
+
if (sfPolicy.enabled) {
|
|
1894
|
+
const excludeRounds = checkpointSummary
|
|
1895
|
+
? new Set([checkpointSummary.declared_at_round])
|
|
1896
|
+
: undefined;
|
|
1897
|
+
const allCheckpoints = collectVaultCheckpoints(vaultContext, request.loop_id, excludeRounds);
|
|
1898
|
+
if (checkpointSummary)
|
|
1899
|
+
allCheckpoints.unshift(checkpointSummary);
|
|
1900
|
+
const delegationSummary = vaultContext
|
|
1901
|
+
? buildDelegationSummary(vaultContext)
|
|
1902
|
+
: "";
|
|
1903
|
+
stateFileContent = renderStateFile({
|
|
1904
|
+
loopId: request.loop_id,
|
|
1905
|
+
currentRound: request.round,
|
|
1906
|
+
maxRounds: request.max_rounds ?? sfPolicy.max_summary_rounds * 2,
|
|
1907
|
+
loopObjective,
|
|
1908
|
+
rollingSummary,
|
|
1909
|
+
constraints,
|
|
1910
|
+
retiredConstraints: retired,
|
|
1911
|
+
checkpoints: allCheckpoints,
|
|
1912
|
+
delegationSummary,
|
|
1913
|
+
progressDashboard,
|
|
1914
|
+
externalContext: request.external_context,
|
|
1915
|
+
});
|
|
1916
|
+
// Prepend state file pointer to the prompt
|
|
1917
|
+
prompt = renderStateFilePointer(request.loop_id) + "\n" + prompt;
|
|
1598
1918
|
}
|
|
1919
|
+
// v1.14: When state file is enabled, progressDashboard lives in the state
|
|
1920
|
+
// file (see renderStateFile above). Only inline it in the prompt when the
|
|
1921
|
+
// state file is disabled — this keeps the prompt truly thin.
|
|
1922
|
+
const finalPrompt = sfPolicy.enabled ? prompt : prompt + progressDashboard;
|
|
1599
1923
|
return makeLoopCompileResponse({
|
|
1600
1924
|
status: AgentStatus.OK,
|
|
1601
|
-
prompt:
|
|
1925
|
+
prompt: finalPrompt,
|
|
1602
1926
|
recompile_level: "l2",
|
|
1603
1927
|
diff_from_previous: request.round === 1 || request.plan_source
|
|
1604
1928
|
? "Full recompile — new goal or first call."
|
|
1605
|
-
: "Full recompile — goal_id changed or
|
|
1929
|
+
: "Full recompile — goal_id changed or checkpoint boundary.",
|
|
1606
1930
|
lineage: [`${request.loop_id}:r${request.round}`],
|
|
1607
1931
|
constraints_active: constraints,
|
|
1608
1932
|
constraints_retired: retired,
|
|
1609
|
-
technique_used:
|
|
1610
|
-
reference_file:
|
|
1933
|
+
technique_used: "agent-selected",
|
|
1934
|
+
reference_file: `${SKILLS_DIR}/prompt-techniques/SKILL.md`,
|
|
1611
1935
|
rolling_summary: rollingSummary,
|
|
1612
1936
|
checkpoint_summary: checkpointSummary,
|
|
1613
1937
|
loop_id: request.loop_id,
|
|
@@ -1618,11 +1942,51 @@ export function compileL2(request, vaultContext) {
|
|
|
1618
1942
|
plan_source: request.plan_source,
|
|
1619
1943
|
suggested_next_task: suggestedNextTask,
|
|
1620
1944
|
warnings,
|
|
1945
|
+
state_file_content: stateFileContent,
|
|
1621
1946
|
});
|
|
1622
1947
|
}
|
|
1948
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
1949
|
+
// L2 Prompt Builder (v1.15 — Agent technique autonomy)
|
|
1950
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
1951
|
+
/** Build the L2 restart prompt with a Technique Selection block.
|
|
1952
|
+
* The Agent reads the technique catalog (SKILL.md), freely chooses the best
|
|
1953
|
+
* technique based on loop state, reads the corresponding reference file, and
|
|
1954
|
+
* applies it directly to the task. LoopForge provides context — the Agent
|
|
1955
|
+
* provides the reasoning strategy. */
|
|
1956
|
+
function buildL2Prompt(request, goalId, constraints, loopObjective, rollingText) {
|
|
1957
|
+
const ctx = {
|
|
1958
|
+
request, goalId, constraints, loopObjective, rollingText, level: "l2",
|
|
1959
|
+
};
|
|
1960
|
+
const lines = [
|
|
1961
|
+
`## LoopForge L2 Compile — Round ${request.round}`,
|
|
1962
|
+
"",
|
|
1963
|
+
];
|
|
1964
|
+
// Cross-round summary
|
|
1965
|
+
if (rollingText) {
|
|
1966
|
+
lines.push(rollingText);
|
|
1967
|
+
lines.push("");
|
|
1968
|
+
}
|
|
1969
|
+
// Technique Selection — Agent freely chooses
|
|
1970
|
+
lines.push("### Technique Selection", "", "This is a strategy restart point. Read the technique catalog to choose", "the best reasoning approach for this round:", "", ` \`${SKILLS_DIR}/prompt-techniques/SKILL.md\``, "", "Based on the loop state (state file + context below), freely select", "the technique that best fits this round's challenge. Then read the", "full reference for your chosen technique:", "", ` \`${SKILLS_DIR}/prompt-techniques/references/<chosen>.md\``, "", "Apply the technique's method directly to the task — do NOT generate", "an intermediate prompt. Execute the task using the chosen technique", "as your thinking framework.", "");
|
|
1971
|
+
// Loop Objective
|
|
1972
|
+
lines.push(...buildLoopObjectiveBlock(loopObjective));
|
|
1973
|
+
// Active Constraints
|
|
1974
|
+
lines.push(...buildConstraintsBlock(constraints));
|
|
1975
|
+
// Plan Source
|
|
1976
|
+
if (request.plan_source) {
|
|
1977
|
+
lines.push(`**Plan Source**: ${request.plan_source}`);
|
|
1978
|
+
lines.push("");
|
|
1979
|
+
}
|
|
1980
|
+
// Task
|
|
1981
|
+
lines.push(...buildTaskBlock(ctx));
|
|
1982
|
+
// Loop Identity
|
|
1983
|
+
lines.push(...buildIdentityBlock(ctx));
|
|
1984
|
+
return lines.join("\n");
|
|
1985
|
+
}
|
|
1623
1986
|
function buildHeader(ctx, technique) {
|
|
1987
|
+
const levelLabel = ctx.level.toUpperCase();
|
|
1624
1988
|
const lines = [
|
|
1625
|
-
`## LoopForge
|
|
1989
|
+
`## LoopForge ${levelLabel} Compile — Round ${ctx.request.round}`,
|
|
1626
1990
|
`**Technique**: ${technique} (embedded)`,
|
|
1627
1991
|
"",
|
|
1628
1992
|
];
|
|
@@ -1683,240 +2047,85 @@ function buildIdentityBlock(ctx) {
|
|
|
1683
2047
|
];
|
|
1684
2048
|
}
|
|
1685
2049
|
/** Generic compiler — reads technique reference file (fallback for zero-shot/few-shot/cot). */
|
|
1686
|
-
function compileGeneric(request, goalId, constraints, loopObjective, rollingText, analysis, referenceFile, technique) {
|
|
1687
|
-
const
|
|
2050
|
+
function compileGeneric(request, goalId, constraints, loopObjective, rollingText, analysis, referenceFile, technique, opts = {}) {
|
|
2051
|
+
const embedSkeleton = opts.embedSkeleton !== false; // default true
|
|
2052
|
+
const ctx = { request, goalId, constraints, loopObjective, rollingText, level: opts.level ?? "l2" };
|
|
1688
2053
|
const lines = [
|
|
1689
2054
|
...buildHeader(ctx, technique),
|
|
1690
|
-
`Read the technique reference BEFORE generating the prompt:`,
|
|
1691
|
-
` Technique: ${technique}`,
|
|
1692
|
-
` Reference: ${referenceFile}`,
|
|
1693
|
-
` Rationale: ${analysis.rationale}`,
|
|
1694
|
-
"",
|
|
1695
|
-
...buildLoopObjectiveBlock(loopObjective),
|
|
1696
|
-
...buildConstraintsBlock(constraints),
|
|
1697
|
-
...(request.plan_source ? [`**Plan Source**: ${request.plan_source}`, ""] : []),
|
|
1698
|
-
...buildTaskBlock(ctx),
|
|
1699
|
-
...buildIdentityBlock(ctx),
|
|
1700
|
-
"### Generation Instructions",
|
|
1701
|
-
`1. Read \`${referenceFile}\` — study its structure rules, section count, and format requirements`,
|
|
1702
|
-
"2. Generate a complete prompt following that technique's structure",
|
|
1703
|
-
"3. The prompt must be self-contained — ready for a coding agent to execute",
|
|
1704
|
-
"4. Output only the generated prompt — no preamble, no meta-commentary",
|
|
1705
2055
|
];
|
|
2056
|
+
if (embedSkeleton) {
|
|
2057
|
+
lines.push(`Read the technique reference BEFORE generating the prompt:`, ` Technique: ${technique}`, ` Reference: ${referenceFile}`, ` Rationale: ${analysis.rationale}`, "");
|
|
2058
|
+
}
|
|
2059
|
+
else {
|
|
2060
|
+
lines.push(`**Technique**: ${technique} (see state file for full reference)`, `**Rationale**: ${analysis.rationale}`, "");
|
|
2061
|
+
}
|
|
2062
|
+
lines.push(...buildLoopObjectiveBlock(loopObjective), ...buildConstraintsBlock(constraints), ...(request.plan_source ? [`**Plan Source**: ${request.plan_source}`, ""] : []), ...buildTaskBlock(ctx), ...buildIdentityBlock(ctx));
|
|
2063
|
+
if (embedSkeleton) {
|
|
2064
|
+
lines.push("### Generation Instructions", `1. Read \`${referenceFile}\` — study its structure rules, section count, and format requirements`, "2. Generate a complete prompt following that technique's structure", "3. The prompt must be self-contained — ready for a coding agent to execute", "4. Output only the generated prompt — no preamble, no meta-commentary");
|
|
2065
|
+
}
|
|
1706
2066
|
return lines.join("\n");
|
|
1707
2067
|
}
|
|
1708
2068
|
// ── Step-Back specialist ───────────────────────────────────────────────────────
|
|
1709
|
-
function compileStepBack(request, goalId, constraints, loopObjective, rollingText, analysis) {
|
|
1710
|
-
const
|
|
2069
|
+
function compileStepBack(request, goalId, constraints, loopObjective, rollingText, analysis, opts = {}) {
|
|
2070
|
+
const embedSkeleton = opts.embedSkeleton !== false; // default true
|
|
2071
|
+
const ctx = { request, goalId, constraints, loopObjective, rollingText, level: opts.level ?? "l2" };
|
|
1711
2072
|
const lines = [
|
|
1712
2073
|
...buildHeader(ctx, "step-back"),
|
|
1713
2074
|
`**Rationale**: ${analysis.rationale}`,
|
|
1714
2075
|
"",
|
|
1715
|
-
"Generate a complete prompt using the **Step-Back** technique. Follow the 8-section skeleton below.",
|
|
1716
|
-
"Embed all structural constraints — do NOT reference an external file.",
|
|
1717
|
-
"",
|
|
1718
|
-
...buildLoopObjectiveBlock(loopObjective),
|
|
1719
|
-
...buildConstraintsBlock(constraints),
|
|
1720
|
-
...(request.plan_source ? [`**Plan Source**: ${request.plan_source}`, ""] : []),
|
|
1721
|
-
...buildTaskBlock(ctx),
|
|
1722
|
-
...buildIdentityBlock(ctx),
|
|
1723
|
-
"",
|
|
1724
|
-
"### 8-Section Skeleton (REQUIRED)",
|
|
1725
|
-
"",
|
|
1726
|
-
"| # | Section | Required | Notes |",
|
|
1727
|
-
"|---|---------|----------|-------|",
|
|
1728
|
-
"| 1 | 角色 | ✓ | Domain expert role appropriate to the task |",
|
|
1729
|
-
"| 2 | 任务 | ✓ | One-sentence task summary |",
|
|
1730
|
-
"| 3 | 输入 | ✓ | Target module/problem to analyse |",
|
|
1731
|
-
"| 4 | 输出格式 | ✓ | Numbered list of expected outputs |",
|
|
1732
|
-
"| 5 | **Step-Back 抽象框架** | ✓ | See format rules below |",
|
|
1733
|
-
"| 6 | 具体实现要求 | ✓ | MUST start with: **\"基于上述抽象框架,实现以下所有功能\"** |",
|
|
1734
|
-
"| 7 | 硬约束 | ✓ | Numbered list of hard constraints |",
|
|
1735
|
-
"| 8 | 生成要求 | ✓ | Acceptance criteria |",
|
|
1736
|
-
"",
|
|
1737
|
-
"### Section 5 — Step-Back 抽象框架 (CRITICAL)",
|
|
1738
|
-
"",
|
|
1739
|
-
"Must contain **2-3 abstract frameworks**. Each framework is an independent ASCII diagram.",
|
|
1740
|
-
"Frameworks are PARALLEL (peer-level), not sequential.",
|
|
1741
|
-
"",
|
|
1742
|
-
"Each framework format:",
|
|
1743
|
-
"```",
|
|
1744
|
-
"### 框架N:[Framework Name]",
|
|
1745
|
-
"",
|
|
1746
|
-
"[ASCII diagram with principles/formulas/classification tables/rule tables]",
|
|
1747
|
-
"",
|
|
1748
|
-
"Diagram content requirements:",
|
|
1749
|
-
"- Core principle/formula (e.g. \"Total = Σ(dimension × weight)\")",
|
|
1750
|
-
"- Component table (e.g. weight allocation table, type mapping table)",
|
|
1751
|
-
"- Rule description (e.g. \"Missing data dimensions use neutral value\")",
|
|
1752
|
-
"```",
|
|
1753
|
-
"",
|
|
1754
|
-
"**Tightening rules**:",
|
|
1755
|
-
"- The step-back question must be MORE ABSTRACT than the original problem, but cover all required information",
|
|
1756
|
-
"- Abstract ≠ vague. Use precise formulas, principles, standards, definitions, or causal mechanisms",
|
|
1757
|
-
"- Tighten to the MINIMUM generalisation layer that still covers the original problem",
|
|
1758
|
-
"- Reasoning must RETURN to the original problem — do not stay at the principle level",
|
|
1759
|
-
"",
|
|
1760
|
-
"**Section 6 transition sentence (MANDATORY)**:",
|
|
1761
|
-
"\"基于上述抽象框架,实现以下所有功能。\"",
|
|
1762
|
-
"",
|
|
1763
|
-
"### Generation Instructions",
|
|
1764
|
-
"1. Extract 2-3 abstract principles/frameworks from the task domain",
|
|
1765
|
-
"2. Build ASCII diagrams for each framework (formulas, tables, rules)",
|
|
1766
|
-
"3. Apply the abstraction back to the concrete task",
|
|
1767
|
-
"4. Verify: the generated prompt satisfies all structural rules in the skeleton above",
|
|
1768
|
-
"5. Output only the generated prompt — no preamble, no meta-commentary",
|
|
1769
2076
|
];
|
|
2077
|
+
if (embedSkeleton) {
|
|
2078
|
+
lines.push("Generate a complete prompt using the **Step-Back** technique. Follow the 8-section skeleton below.", "Embed all structural constraints — do NOT reference an external file.", "");
|
|
2079
|
+
}
|
|
2080
|
+
else {
|
|
2081
|
+
lines.push("**Technique**: step-back (see state file for the full skeleton).", "Generate a complete prompt following this technique.", "");
|
|
2082
|
+
}
|
|
2083
|
+
lines.push(...buildLoopObjectiveBlock(loopObjective), ...buildConstraintsBlock(constraints), ...(request.plan_source ? [`**Plan Source**: ${request.plan_source}`, ""] : []), ...buildTaskBlock(ctx), ...buildIdentityBlock(ctx));
|
|
2084
|
+
if (embedSkeleton) {
|
|
2085
|
+
lines.push("", "### 8-Section Skeleton (REQUIRED)", "", "| # | Section | Required | Notes |", "|---|---------|----------|-------|", "| 1 | 角色 | ✓ | Domain expert role appropriate to the task |", "| 2 | 任务 | ✓ | One-sentence task summary |", "| 3 | 输入 | ✓ | Target module/problem to analyse |", "| 4 | 输出格式 | ✓ | Numbered list of expected outputs |", "| 5 | **Step-Back 抽象框架** | ✓ | See format rules below |", "| 6 | 具体实现要求 | ✓ | MUST start with: **\"基于上述抽象框架,实现以下所有功能\"** |", "| 7 | 硬约束 | ✓ | Numbered list of hard constraints |", "| 8 | 生成要求 | ✓ | Acceptance criteria |", "", "### Section 5 — Step-Back 抽象框架 (CRITICAL)", "", "Must contain **2-3 abstract frameworks**. Each framework is an independent ASCII diagram.", "Frameworks are PARALLEL (peer-level), not sequential.", "", "Each framework format:", "```", "### 框架N:[Framework Name]", "", "[ASCII diagram with principles/formulas/classification tables/rule tables]", "", "Diagram content requirements:", "- Core principle/formula (e.g. \"Total = Σ(dimension × weight)\")", "- Component table (e.g. weight allocation table, type mapping table)", "- Rule description (e.g. \"Missing data dimensions use neutral value\")", "```", "", "**Tightening rules**:", "- The step-back question must be MORE ABSTRACT than the original problem, but cover all required information", "- Abstract ≠ vague. Use precise formulas, principles, standards, definitions, or causal mechanisms", "- Tighten to the MINIMUM generalisation layer that still covers the original problem", "- Reasoning must RETURN to the original problem — do not stay at the principle level", "", "**Section 6 transition sentence (MANDATORY)**:", "\"基于上述抽象框架,实现以下所有功能。\"", "", "### Generation Instructions", "1. Extract 2-3 abstract principles/frameworks from the task domain", "2. Build ASCII diagrams for each framework (formulas, tables, rules)", "3. Apply the abstraction back to the concrete task", "4. Verify: the generated prompt satisfies all structural rules in the skeleton above", "5. Output only the generated prompt — no preamble, no meta-commentary");
|
|
2086
|
+
}
|
|
1770
2087
|
return lines.join("\n");
|
|
1771
2088
|
}
|
|
1772
2089
|
// ── Least-to-Most specialist ───────────────────────────────────────────────────
|
|
1773
|
-
function compileLeastToMost(request, goalId, constraints, loopObjective, rollingText, analysis) {
|
|
1774
|
-
const
|
|
2090
|
+
function compileLeastToMost(request, goalId, constraints, loopObjective, rollingText, analysis, opts = {}) {
|
|
2091
|
+
const embedSkeleton = opts.embedSkeleton !== false; // default true
|
|
2092
|
+
const ctx = { request, goalId, constraints, loopObjective, rollingText, level: opts.level ?? "l2" };
|
|
1775
2093
|
const lines = [
|
|
1776
2094
|
...buildHeader(ctx, "least-to-most"),
|
|
1777
2095
|
`**Rationale**: ${analysis.rationale}`,
|
|
1778
2096
|
"",
|
|
1779
|
-
"Generate a complete prompt using the **Least-to-Most** technique. Follow the 8-section skeleton below.",
|
|
1780
|
-
"Embed all structural constraints — do NOT reference an external file.",
|
|
1781
|
-
"",
|
|
1782
|
-
...buildLoopObjectiveBlock(loopObjective),
|
|
1783
|
-
...buildConstraintsBlock(constraints),
|
|
1784
|
-
...(request.plan_source ? [`**Plan Source**: ${request.plan_source}`, ""] : []),
|
|
1785
|
-
...buildTaskBlock(ctx),
|
|
1786
|
-
...buildIdentityBlock(ctx),
|
|
1787
|
-
"",
|
|
1788
|
-
"### 8-Section Skeleton (REQUIRED)",
|
|
1789
|
-
"",
|
|
1790
|
-
"| # | Section | Required | Notes |",
|
|
1791
|
-
"|---|---------|----------|-------|",
|
|
1792
|
-
"| 1 | 角色 | ✓ | Domain expert role appropriate to the task |",
|
|
1793
|
-
"| 2 | 任务 | ✓ | One-sentence task summary |",
|
|
1794
|
-
"| 3 | 输入 | ✓ | Target module |",
|
|
1795
|
-
"| 4 | 输出格式 | ✓ | Numbered list (e.g. \"1. DDL, 2. API, 3. Enum...\") |",
|
|
1796
|
-
"| 5 | **Least-to-Most 逐步推理框架** | ✓ | See format rules below |",
|
|
1797
|
-
"| 6 | 具体实现要求 | ✓ | Expand per output format list (NOT per sub-problem — sub-problems fully expanded in §5) |",
|
|
1798
|
-
"| 7 | 硬约束 | ✓ | Numbered list |",
|
|
1799
|
-
"| 8 | 生成要求 | ✓ | Acceptance criteria, must include: \"严格按照子问题顺序逐步实现\" |",
|
|
1800
|
-
"",
|
|
1801
|
-
"### Section 5 — Least-to-Most 逐步推理框架 (CRITICAL)",
|
|
1802
|
-
"",
|
|
1803
|
-
"Must contain **4-6 ordered sub-problems**. Each sub-problem format:",
|
|
1804
|
-
"",
|
|
1805
|
-
"```",
|
|
1806
|
-
"### 子问题 N:[Sub-problem Name]",
|
|
1807
|
-
"",
|
|
1808
|
-
"**目标:** [What this sub-problem solves, which prior sub-problem(s) it depends on]",
|
|
1809
|
-
"",
|
|
1810
|
-
"**要求:**",
|
|
1811
|
-
"- Specific requirement list",
|
|
1812
|
-
"- [If sub-problem involves enums/mapping tables, list them]",
|
|
1813
|
-
"",
|
|
1814
|
-
"---",
|
|
1815
|
-
"```",
|
|
1816
|
-
"",
|
|
1817
|
-
"**Critical rules**:",
|
|
1818
|
-
"- Sub-problem 1 is the SIMPLEST (e.g. \"Define enums and base data structures\")",
|
|
1819
|
-
"- Each sub-problem declares: \"基于子问题 N-1 的结论\"",
|
|
1820
|
-
"- The LAST sub-problem MUST be: **\"综合实现完整模块\"** — list all components to integrate",
|
|
1821
|
-
"- Sub-problems separated by `---`",
|
|
1822
|
-
"- Order goes from LEAST complex → MOST complex, dependency chain must be explicit",
|
|
1823
|
-
"- Each sub-problem must serve the original task — no unrelated steps",
|
|
1824
|
-
"- The final sub-problem must be equivalent to or directly address the original task",
|
|
1825
|
-
"",
|
|
1826
|
-
"### Generation Instructions",
|
|
1827
|
-
"1. Decompose the task into 4-6 ordered sub-problems with explicit dependencies",
|
|
1828
|
-
"2. Sub-problem 1 starts with the simplest building block (data structures, enums, base config)",
|
|
1829
|
-
"3. Each subsequent sub-problem builds on prior results",
|
|
1830
|
-
"4. Final sub-problem integrates all components into the complete module",
|
|
1831
|
-
"5. Verify: the generated prompt satisfies all structural rules in the skeleton above",
|
|
1832
|
-
"6. Output only the generated prompt — no preamble, no meta-commentary",
|
|
1833
2097
|
];
|
|
2098
|
+
if (embedSkeleton) {
|
|
2099
|
+
lines.push("Generate a complete prompt using the **Least-to-Most** technique. Follow the 8-section skeleton below.", "Embed all structural constraints — do NOT reference an external file.", "");
|
|
2100
|
+
}
|
|
2101
|
+
else {
|
|
2102
|
+
lines.push("**Technique**: least-to-most (see state file for the full skeleton).", "Generate a complete prompt following this technique.", "");
|
|
2103
|
+
}
|
|
2104
|
+
lines.push(...buildLoopObjectiveBlock(loopObjective), ...buildConstraintsBlock(constraints), ...(request.plan_source ? [`**Plan Source**: ${request.plan_source}`, ""] : []), ...buildTaskBlock(ctx), ...buildIdentityBlock(ctx));
|
|
2105
|
+
if (embedSkeleton) {
|
|
2106
|
+
lines.push("", "### 8-Section Skeleton (REQUIRED)", "", "| # | Section | Required | Notes |", "|---|---------|----------|-------|", "| 1 | 角色 | ✓ | Domain expert role appropriate to the task |", "| 2 | 任务 | ✓ | One-sentence task summary |", "| 3 | 输入 | ✓ | Target module |", "| 4 | 输出格式 | ✓ | Numbered list (e.g. \"1. DDL, 2. API, 3. Enum...\") |", "| 5 | **Least-to-Most 逐步推理框架** | ✓ | See format rules below |", "| 6 | 具体实现要求 | ✓ | Expand per output format list (NOT per sub-problem — sub-problems fully expanded in §5) |", "| 7 | 硬约束 | ✓ | Numbered list |", "| 8 | 生成要求 | ✓ | Acceptance criteria, must include: \"严格按照子问题顺序逐步实现\" |", "", "### Section 5 — Least-to-Most 逐步推理框架 (CRITICAL)", "", "Must contain **4-6 ordered sub-problems**. Each sub-problem format:", "", "```", "### 子问题 N:[Sub-problem Name]", "", "**目标:** [What this sub-problem solves, which prior sub-problem(s) it depends on]", "", "**要求:**", "- Specific requirement list", "- [If sub-problem involves enums/mapping tables, list them]", "", "---", "```", "", "**Critical rules**:", "- Sub-problem 1 is the SIMPLEST (e.g. \"Define enums and base data structures\")", "- Each sub-problem declares: \"基于子问题 N-1 的结论\"", "- The LAST sub-problem MUST be: **\"综合实现完整模块\"** — list all components to integrate", "- Sub-problems separated by `---`", "- Order goes from LEAST complex → MOST complex, dependency chain must be explicit", "- Each sub-problem must serve the original task — no unrelated steps", "- The final sub-problem must be equivalent to or directly address the original task", "", "### Generation Instructions", "1. Decompose the task into 4-6 ordered sub-problems with explicit dependencies", "2. Sub-problem 1 starts with the simplest building block (data structures, enums, base config)", "3. Each subsequent sub-problem builds on prior results", "4. Final sub-problem integrates all components into the complete module", "5. Verify: the generated prompt satisfies all structural rules in the skeleton above", "6. Output only the generated prompt — no preamble, no meta-commentary");
|
|
2107
|
+
}
|
|
1834
2108
|
return lines.join("\n");
|
|
1835
2109
|
}
|
|
1836
2110
|
// ── Tree-of-Thought specialist ──────────────────────────────────────────────────
|
|
1837
|
-
function compileToT(request, goalId, constraints, loopObjective, rollingText, analysis) {
|
|
1838
|
-
const
|
|
2111
|
+
function compileToT(request, goalId, constraints, loopObjective, rollingText, analysis, opts = {}) {
|
|
2112
|
+
const embedSkeleton = opts.embedSkeleton !== false; // default true
|
|
2113
|
+
const ctx = { request, goalId, constraints, loopObjective, rollingText, level: opts.level ?? "l2" };
|
|
1839
2114
|
const lines = [
|
|
1840
2115
|
...buildHeader(ctx, "tree-of-thought"),
|
|
1841
2116
|
`**Rationale**: ${analysis.rationale}`,
|
|
1842
2117
|
"",
|
|
1843
|
-
"Generate a complete prompt using the **Tree-of-Thought** technique. Follow the 8-section skeleton below.",
|
|
1844
|
-
"Embed all structural constraints — do NOT reference an external file.",
|
|
1845
|
-
"",
|
|
1846
|
-
...buildLoopObjectiveBlock(loopObjective),
|
|
1847
|
-
...buildConstraintsBlock(constraints),
|
|
1848
|
-
...(request.plan_source ? [`**Plan Source**: ${request.plan_source}`, ""] : []),
|
|
1849
|
-
...buildTaskBlock(ctx),
|
|
1850
|
-
...buildIdentityBlock(ctx),
|
|
1851
|
-
"",
|
|
1852
|
-
"### 8-Section Skeleton (REQUIRED)",
|
|
1853
|
-
"",
|
|
1854
|
-
"| # | Section | Required | Notes |",
|
|
1855
|
-
"|---|---------|----------|-------|",
|
|
1856
|
-
"| 1 | 角色 | ✓ | Multi-path problem-solving expert; may introduce 3 expert personas |",
|
|
1857
|
-
"| 2 | 任务 | ✓ | One-sentence task summary |",
|
|
1858
|
-
"| 3 | 输入 | ✓ | High-risk complex problem |",
|
|
1859
|
-
"| 4 | 输出格式 | ✓ | \"先输出思维树过程,再输出最终答案/方案\" |",
|
|
1860
|
-
"| 5 | **思维树探索框架** | ✓ | See 3 sub-blocks below (REQUIRED) |",
|
|
1861
|
-
"| 6 | 具体实现要求 | ✓ | Expand per output format |",
|
|
1862
|
-
"| 7 | 硬约束 | ✓ | Include branch_count, max_depth, pruning rules, safety/performance constraints |",
|
|
1863
|
-
"| 8 | 生成要求 | ✓ | \"先探索多路径,再选择最优方案\" |",
|
|
1864
|
-
"",
|
|
1865
|
-
"### Section 5 — 思维树探索框架 (CRITICAL — 3 sub-blocks REQUIRED)",
|
|
1866
|
-
"",
|
|
1867
|
-
"**Sub-block A: Search Strategy Declaration**",
|
|
1868
|
-
"```",
|
|
1869
|
-
"搜索策略: [beam / dfs / expert-panel]",
|
|
1870
|
-
"分支数(branch_count): 2-4",
|
|
1871
|
-
"最大深度(max_depth): ≤3",
|
|
1872
|
-
"每轮保留数(keep_count): 1-2",
|
|
1873
|
-
"```",
|
|
1874
|
-
"Strategy selection guide:",
|
|
1875
|
-
"- **beam** (default): planning, creative, math search — generate multiple candidates per turn, keep top b",
|
|
1876
|
-
"- **dfs**: puzzles, constraint satisfaction, debugging — go deep on highest-score branch, backtrack on failure",
|
|
1877
|
-
"- **expert-panel**: 3+ experts each generate candidates, then cross-evaluate and revise",
|
|
1878
|
-
"",
|
|
1879
|
-
"**Sub-block B: Evaluation Criteria Table**",
|
|
1880
|
-
"| 标准 | 权重 | 说明 |",
|
|
1881
|
-
"|------|------|------|",
|
|
1882
|
-
"| 正确性 | 最高 | Logic/math correctness |",
|
|
1883
|
-
"| 可行性 | 高 | Can be actually implemented |",
|
|
1884
|
-
"| 约束匹配 | 高 | Satisfies hard constraints |",
|
|
1885
|
-
"| 性能 | 中 | Time/space efficiency |",
|
|
1886
|
-
"| 安全性 | 最高 | No vulnerabilities/privilege escalation |",
|
|
1887
|
-
"",
|
|
1888
|
-
"**Sub-block C: Thought Tree State Table Format**",
|
|
1889
|
-
"Require the model to output in this table format:",
|
|
1890
|
-
"```",
|
|
1891
|
-
"| 轮次 | 分支 | 候选方案 | 评估 | 决策 |",
|
|
1892
|
-
"|------|------|---------|------|------|",
|
|
1893
|
-
"| 1 | A | [方案描述] | 评分/判断 | 保留/剪枝 |",
|
|
1894
|
-
"| 1 | B | [方案描述] | 评分/判断 | 保留/剪枝 |",
|
|
1895
|
-
"| 1 | C | [方案描述] | 评分/判断 | 保留/剪枝 |",
|
|
1896
|
-
"| 2 | A1 | [深入展开] | ... | ... |",
|
|
1897
|
-
"| ... | ... | ... | ... | ... |",
|
|
1898
|
-
"",
|
|
1899
|
-
"最终选择: [最优方案 + 选择理由]",
|
|
1900
|
-
"```",
|
|
1901
|
-
"",
|
|
1902
|
-
"### Critical Rules",
|
|
1903
|
-
"- Hard constraints MUST be ranked FIRST in evaluation criteria",
|
|
1904
|
-
"- Branch count: 2-4, depth: ≤3 (prevents token explosion)",
|
|
1905
|
-
"- Every turn: generate multiple candidates → evaluate → keep/prune — never single-path",
|
|
1906
|
-
"- If all branches are low quality: backtrack to previous turn and regenerate",
|
|
1907
|
-
"- The core of ToT is NOT \"3 experts chatting\" — it's: candidate generation → state evaluation → search/prune → final selection",
|
|
1908
|
-
"- thought is a PUBLIC intermediate semantic unit, not hidden chain-of-thought",
|
|
1909
|
-
"- State table MUST have: round, branch ID, candidate description, evaluation score/judgment, keep/prune decision",
|
|
1910
|
-
"",
|
|
1911
|
-
"### Generation Instructions",
|
|
1912
|
-
"1. Select the appropriate search strategy (beam/dfs/expert-panel) based on task type",
|
|
1913
|
-
"2. Define 2-4 candidate approaches as initial branches",
|
|
1914
|
-
"3. Build evaluation criteria table with hard constraints ranked first",
|
|
1915
|
-
"4. Define the state table format for tracking exploration",
|
|
1916
|
-
"5. Set branch count and max depth to prevent token explosion",
|
|
1917
|
-
"6. Verify: the generated prompt satisfies all structural rules in the skeleton above",
|
|
1918
|
-
"7. Output only the generated prompt — no preamble, no meta-commentary",
|
|
1919
2118
|
];
|
|
2119
|
+
if (embedSkeleton) {
|
|
2120
|
+
lines.push("Generate a complete prompt using the **Tree-of-Thought** technique. Follow the 8-section skeleton below.", "Embed all structural constraints — do NOT reference an external file.", "");
|
|
2121
|
+
}
|
|
2122
|
+
else {
|
|
2123
|
+
lines.push("**Technique**: tree-of-thought (see state file for the full skeleton).", "Generate a complete prompt following this technique.", "");
|
|
2124
|
+
}
|
|
2125
|
+
lines.push(...buildLoopObjectiveBlock(loopObjective), ...buildConstraintsBlock(constraints), ...(request.plan_source ? [`**Plan Source**: ${request.plan_source}`, ""] : []), ...buildTaskBlock(ctx), ...buildIdentityBlock(ctx));
|
|
2126
|
+
if (embedSkeleton) {
|
|
2127
|
+
lines.push("", "### 8-Section Skeleton (REQUIRED)", "", "| # | Section | Required | Notes |", "|---|---------|----------|-------|", "| 1 | 角色 | ✓ | Multi-path problem-solving expert; may introduce 3 expert personas |", "| 2 | 任务 | ✓ | One-sentence task summary |", "| 3 | 输入 | ✓ | High-risk complex problem |", "| 4 | 输出格式 | ✓ | \"先输出思维树过程,再输出最终答案/方案\" |", "| 5 | **思维树探索框架** | ✓ | See 3 sub-blocks below (REQUIRED) |", "| 6 | 具体实现要求 | ✓ | Expand per output format |", "| 7 | 硬约束 | ✓ | Include branch_count, max_depth, pruning rules, safety/performance constraints |", "| 8 | 生成要求 | ✓ | \"先探索多路径,再选择最优方案\" |", "", "### Section 5 — 思维树探索框架 (CRITICAL — 3 sub-blocks REQUIRED)", "", "**Sub-block A: Search Strategy Declaration**", "```", "搜索策略: [beam / dfs / expert-panel]", "分支数(branch_count): 2-4", "最大深度(max_depth): ≤3", "每轮保留数(keep_count): 1-2", "```", "Strategy selection guide:", "- **beam** (default): planning, creative, math search — generate multiple candidates per turn, keep top b", "- **dfs**: puzzles, constraint satisfaction, debugging — go deep on highest-score branch, backtrack on failure", "- **expert-panel**: 3+ experts each generate candidates, then cross-evaluate and revise", "", "**Sub-block B: Evaluation Criteria Table**", "| 标准 | 权重 | 说明 |", "|------|------|------|", "| 正确性 | 最高 | Logic/math correctness |", "| 可行性 | 高 | Can be actually implemented |", "| 约束匹配 | 高 | Satisfies hard constraints |", "| 性能 | 中 | Time/space efficiency |", "| 安全性 | 最高 | No vulnerabilities/privilege escalation |", "", "**Sub-block C: Thought Tree State Table Format**", "Require the model to output in this table format:", "```", "| 轮次 | 分支 | 候选方案 | 评估 | 决策 |", "|------|------|---------|------|------|", "| 1 | A | [方案描述] | 评分/判断 | 保留/剪枝 |", "| 1 | B | [方案描述] | 评分/判断 | 保留/剪枝 |", "| 1 | C | [方案描述] | 评分/判断 | 保留/剪枝 |", "| 2 | A1 | [深入展开] | ... | ... |", "| ... | ... | ... | ... | ... |", "", "最终选择: [最优方案 + 选择理由]", "```", "", "### Critical Rules", "- Hard constraints MUST be ranked FIRST in evaluation criteria", "- Branch count: 2-4, depth: ≤3 (prevents token explosion)", "- Every turn: generate multiple candidates → evaluate → keep/prune — never single-path", "- If all branches are low quality: backtrack to previous turn and regenerate", "- The core of ToT is NOT \"3 experts chatting\" — it's: candidate generation → state evaluation → search/prune → final selection", "- thought is a PUBLIC intermediate semantic unit, not hidden chain-of-thought", "- State table MUST have: round, branch ID, candidate description, evaluation score/judgment, keep/prune decision", "", "### Generation Instructions", "1. Select the appropriate search strategy (beam/dfs/expert-panel) based on task type", "2. Define 2-4 candidate approaches as initial branches", "3. Build evaluation criteria table with hard constraints ranked first", "4. Define the state table format for tracking exploration", "5. Set branch count and max depth to prevent token explosion", "6. Verify: the generated prompt satisfies all structural rules in the skeleton above", "7. Output only the generated prompt — no preamble, no meta-commentary");
|
|
2128
|
+
}
|
|
1920
2129
|
return lines.join("\n");
|
|
1921
2130
|
}
|
|
1922
2131
|
// ═══════════════════════════════════════════════════════════════════════════
|