kc-beta 0.7.5 → 0.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -0
- package/package.json +3 -2
- package/src/agent/context.js +17 -1
- package/src/agent/engine.js +467 -100
- package/src/agent/llm-client.js +24 -1
- package/src/agent/pipelines/_advance-hints.js +92 -0
- package/src/agent/pipelines/_milestone-derive.js +325 -20
- package/src/agent/pipelines/skill-authoring.js +49 -3
- package/src/agent/tools/agent-tool.js +2 -2
- package/src/agent/tools/consult-skill.js +15 -0
- package/src/agent/tools/dashboard-render.js +48 -1
- package/src/agent/tools/document-parse.js +31 -2
- package/src/agent/tools/phase-advance.js +17 -13
- package/src/agent/tools/release.js +343 -7
- package/src/agent/tools/sandbox-exec.js +65 -8
- package/src/agent/tools/worker-llm-call.js +95 -15
- package/src/agent/workspace.js +25 -4
- package/src/cli/components.js +4 -1
- package/src/cli/index.js +125 -8
- package/src/config.js +19 -2
- package/src/marathon/driver.js +217 -0
- package/src/marathon/prompts.js +93 -0
- package/template/.env.template +17 -1
- package/template/AGENT.md +2 -2
- package/template/skills/en/auto-model-selection/SKILL.md +55 -35
- package/template/skills/en/bootstrap-workspace/SKILL.md +27 -0
- package/template/skills/en/compliance-judgment/SKILL.md +14 -0
- package/template/skills/en/confidence-system/SKILL.md +30 -8
- package/template/skills/en/corner-case-management/SKILL.md +53 -33
- package/template/skills/en/cross-document-verification/SKILL.md +88 -83
- package/template/skills/en/dashboard-reporting/SKILL.md +91 -66
- package/template/skills/en/dashboard-reporting/scripts/generate_dashboard.py +1 -1
- package/template/skills/en/data-sensibility/SKILL.md +19 -12
- package/template/skills/en/document-chunking/SKILL.md +99 -15
- package/template/skills/en/entity-extraction/SKILL.md +14 -4
- package/template/skills/en/quality-control/SKILL.md +23 -0
- package/template/skills/en/rule-extraction/SKILL.md +92 -94
- package/template/skills/en/rule-extraction/references/chunking-strategies.md +7 -78
- package/template/skills/en/skill-authoring/SKILL.md +85 -2
- package/template/skills/en/skill-creator/SKILL.md +25 -3
- package/template/skills/en/skill-to-workflow/SKILL.md +73 -1
- package/template/skills/en/task-decomposition/SKILL.md +1 -1
- package/template/skills/en/tree-processing/SKILL.md +1 -1
- package/template/skills/en/version-control/SKILL.md +15 -0
- package/template/skills/en/work-decomposition/SKILL.md +52 -32
- package/template/skills/phase_skills.yaml +5 -0
- package/template/skills/zh/auto-model-selection/SKILL.md +54 -33
- package/template/skills/zh/bootstrap-workspace/SKILL.md +27 -0
- package/template/skills/zh/compliance-judgment/SKILL.md +51 -37
- package/template/skills/zh/compliance-judgment/references/output-format.md +62 -62
- package/template/skills/zh/confidence-system/SKILL.md +34 -9
- package/template/skills/zh/corner-case-management/SKILL.md +71 -104
- package/template/skills/zh/cross-document-verification/SKILL.md +90 -195
- package/template/skills/zh/cross-document-verification/references/contradiction-taxonomy.md +36 -36
- package/template/skills/zh/dashboard-reporting/SKILL.md +82 -232
- package/template/skills/zh/dashboard-reporting/scripts/generate_dashboard.py +1 -1
- package/template/skills/zh/data-sensibility/SKILL.md +13 -0
- package/template/skills/zh/document-chunking/SKILL.md +101 -18
- package/template/skills/zh/document-parsing/SKILL.md +65 -65
- package/template/skills/zh/document-parsing/references/parser-catalog.md +26 -26
- package/template/skills/zh/entity-extraction/SKILL.md +78 -68
- package/template/skills/zh/evolution-loop/references/convergence-guide.md +38 -38
- package/template/skills/zh/quality-control/SKILL.md +23 -0
- package/template/skills/zh/quality-control/references/qa-layers.md +65 -65
- package/template/skills/zh/quality-control/references/sampling-strategies.md +49 -49
- package/template/skills/zh/rule-extraction/SKILL.md +199 -188
- package/template/skills/zh/rule-extraction/references/chunking-strategies.md +5 -78
- package/template/skills/zh/skill-authoring/SKILL.md +136 -58
- package/template/skills/zh/skill-authoring/references/skill-format-spec.md +39 -39
- package/template/skills/zh/skill-creator/SKILL.md +215 -201
- package/template/skills/zh/skill-creator/references/schemas.md +60 -60
- package/template/skills/zh/skill-to-workflow/SKILL.md +73 -1
- package/template/skills/zh/skill-to-workflow/references/worker-llm-catalog.md +24 -24
- package/template/skills/zh/task-decomposition/SKILL.md +1 -1
- package/template/skills/zh/task-decomposition/references/decision-matrix.md +54 -54
- package/template/skills/zh/tree-processing/SKILL.md +67 -63
- package/template/skills/zh/version-control/SKILL.md +15 -0
- package/template/skills/zh/version-control/references/trace-id-spec.md +34 -34
- package/template/skills/zh/work-decomposition/SKILL.md +52 -30
- package/template/workflows/common/llm_client.py +168 -0
- package/template/workflows/common/utils.py +132 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
// v0.8.1 P8-A — marathon driver as inline state machine.
|
|
2
|
+
//
|
|
3
|
+
// v0.8.0 shipped this as a separate-process driver (bin/kc-marathon.js)
|
|
4
|
+
// that tailed events.jsonl + wrote prompts to .kc_marathon/inbox.jsonl.
|
|
5
|
+
// E2E #11 audits found both drivers died silently within 10 min when
|
|
6
|
+
// the terminal closed or laptop slept (SIGHUP/SIGTERM unhandled). The
|
|
7
|
+
// engine survived both deaths because it lives in a different process.
|
|
8
|
+
//
|
|
9
|
+
// v0.8.1 redesign per user proposal (2026-05-15):
|
|
10
|
+
// - Single process: driver runs inline as part of the engine
|
|
11
|
+
// - Activated via `/marathon <goal>` slash command in kc-beta TUI
|
|
12
|
+
// - Engine calls decideNext(state) after each turn_complete to get
|
|
13
|
+
// the next continuation prompt (or null if marathon should end)
|
|
14
|
+
// - No filesystem IPC (no inbox, no active marker, no state.json)
|
|
15
|
+
// - State persists via engine's existing session-state.json
|
|
16
|
+
//
|
|
17
|
+
// The state machine logic from v0.8.0 is preserved verbatim — only
|
|
18
|
+
// the I/O wrapper changes. Templates (renderPrompt) unchanged.
|
|
19
|
+
|
|
20
|
+
import { renderPrompt } from "./prompts.js";
|
|
21
|
+
|
|
22
|
+
const DEFAULT_STUCK_AFTER_MS = 30 * 60 * 1000; // 30 min
|
|
23
|
+
const DEFAULT_MAX_WALLCLOCK_MS = 12 * 60 * 60 * 1000; // 12 h
|
|
24
|
+
|
|
25
|
+
export class MarathonDriver {
|
|
26
|
+
/**
|
|
27
|
+
* @param {object} opts
|
|
28
|
+
* @param {string} opts.goal — the marathon goal-description prompt
|
|
29
|
+
* @param {string} [opts.language] — "en" or "zh"
|
|
30
|
+
* @param {number} [opts.maxWallclockMs] — stop after this much wall time
|
|
31
|
+
* @param {number} [opts.stuckAfterMs] — emit unstick prompt after idle
|
|
32
|
+
*/
|
|
33
|
+
constructor(opts = {}) {
|
|
34
|
+
if (!opts.goal || typeof opts.goal !== "string") {
|
|
35
|
+
throw new Error("MarathonDriver requires a non-empty `goal` string");
|
|
36
|
+
}
|
|
37
|
+
this.goal = opts.goal;
|
|
38
|
+
this.language = opts.language === "zh" ? "zh" : "en";
|
|
39
|
+
this.maxWallclockMs = opts.maxWallclockMs ?? DEFAULT_MAX_WALLCLOCK_MS;
|
|
40
|
+
this.stuckAfterMs = opts.stuckAfterMs ?? DEFAULT_STUCK_AFTER_MS;
|
|
41
|
+
|
|
42
|
+
this.startedAt = Date.now();
|
|
43
|
+
this.lastDecisionAt = 0;
|
|
44
|
+
this.decisionCount = 0;
|
|
45
|
+
this.currentPhase = "bootstrap";
|
|
46
|
+
this.lastMilestones = {};
|
|
47
|
+
this.turnsThisPhase = 0;
|
|
48
|
+
this.lastEventTs = Date.now();
|
|
49
|
+
this.initialDelivered = false;
|
|
50
|
+
this.stopped = false;
|
|
51
|
+
this.stopReason = null;
|
|
52
|
+
|
|
53
|
+
// Decision history (kept in-memory; surfaced in /marathon status).
|
|
54
|
+
// Bounded to last 100 to cap memory.
|
|
55
|
+
this.decisions = [];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Engine calls this once BEFORE the initial turn after /marathon was
|
|
60
|
+
* typed. Returns the goal-description prompt to feed into runTurn.
|
|
61
|
+
*/
|
|
62
|
+
getInitialPrompt() {
|
|
63
|
+
const out = renderPrompt(
|
|
64
|
+
"initial",
|
|
65
|
+
this._stateSnapshot(),
|
|
66
|
+
this.language,
|
|
67
|
+
);
|
|
68
|
+
this._recordDecision("initial", "marathon kickoff", out);
|
|
69
|
+
this.initialDelivered = true;
|
|
70
|
+
return out;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Engine calls decideNext(state) after each turn_complete event.
|
|
75
|
+
* Returns { prompt, template, reason } if marathon should continue,
|
|
76
|
+
* or null if a stop condition is met (engine will exit marathon mode).
|
|
77
|
+
*
|
|
78
|
+
* @param {object} state — engine snapshot:
|
|
79
|
+
* {currentPhase, milestones, phaseChanged, errorSeen, turnsThisPhase}
|
|
80
|
+
*/
|
|
81
|
+
decideNext(state = {}) {
|
|
82
|
+
if (this.stopped) return null;
|
|
83
|
+
|
|
84
|
+
// Update tracked state from engine
|
|
85
|
+
if (state.currentPhase && state.currentPhase !== this.currentPhase) {
|
|
86
|
+
this.currentPhase = state.currentPhase;
|
|
87
|
+
this.turnsThisPhase = 0;
|
|
88
|
+
}
|
|
89
|
+
if (state.milestones) this.lastMilestones = state.milestones;
|
|
90
|
+
if (typeof state.turnsThisPhase === "number") {
|
|
91
|
+
this.turnsThisPhase = state.turnsThisPhase;
|
|
92
|
+
} else {
|
|
93
|
+
this.turnsThisPhase += 1;
|
|
94
|
+
}
|
|
95
|
+
this.lastEventTs = Date.now();
|
|
96
|
+
|
|
97
|
+
// Stop conditions
|
|
98
|
+
if (this._shouldStop()) {
|
|
99
|
+
this.stopped = true;
|
|
100
|
+
// Emit one final "stop" prompt so the agent has a chance to wrap up.
|
|
101
|
+
const out = renderPrompt("stop", this._stateSnapshot(), this.language);
|
|
102
|
+
this._recordDecision("stop", this.stopReason, out);
|
|
103
|
+
return { prompt: out, template: "stop", reason: this.stopReason };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
let template = "continue_phase";
|
|
107
|
+
let reason = "turn_complete in same phase";
|
|
108
|
+
|
|
109
|
+
if (state.errorSeen) {
|
|
110
|
+
template = "unstick";
|
|
111
|
+
reason = "engine emitted error event";
|
|
112
|
+
} else if (state.phaseChanged) {
|
|
113
|
+
if (this.currentPhase === "finalization") {
|
|
114
|
+
template = "finalize";
|
|
115
|
+
reason = "reached finalization";
|
|
116
|
+
} else {
|
|
117
|
+
template = "continue_phase";
|
|
118
|
+
reason = `entered ${this.currentPhase}`;
|
|
119
|
+
}
|
|
120
|
+
} else {
|
|
121
|
+
const idleMs = Date.now() - this.lastEventTs;
|
|
122
|
+
if (idleMs > this.stuckAfterMs) {
|
|
123
|
+
template = "unstick";
|
|
124
|
+
reason = `idle for ${Math.round(idleMs / 60000)} min`;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const out = renderPrompt(template, this._stateSnapshot(), this.language);
|
|
129
|
+
this._recordDecision(template, reason, out);
|
|
130
|
+
return { prompt: out, template, reason };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** User-invoked manual stop (e.g., `/marathon off`). */
|
|
134
|
+
stop(reason = "user_off") {
|
|
135
|
+
this.stopped = true;
|
|
136
|
+
this.stopReason = reason;
|
|
137
|
+
this._recordDecision("manual_stop", reason, "");
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** Snapshot for /marathon status command + audit. */
|
|
141
|
+
getStatus() {
|
|
142
|
+
return {
|
|
143
|
+
active: !this.stopped,
|
|
144
|
+
goal: this.goal,
|
|
145
|
+
language: this.language,
|
|
146
|
+
startedAt: new Date(this.startedAt).toISOString(),
|
|
147
|
+
runtimeMs: Date.now() - this.startedAt,
|
|
148
|
+
currentPhase: this.currentPhase,
|
|
149
|
+
turnsThisPhase: this.turnsThisPhase,
|
|
150
|
+
decisionCount: this.decisionCount,
|
|
151
|
+
lastDecisionAt: this.lastDecisionAt ? new Date(this.lastDecisionAt).toISOString() : null,
|
|
152
|
+
stopReason: this.stopReason,
|
|
153
|
+
maxWallclockMs: this.maxWallclockMs,
|
|
154
|
+
stuckAfterMs: this.stuckAfterMs,
|
|
155
|
+
recentDecisions: this.decisions.slice(-5),
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** Serialize for session-state.json persistence (NOT used for auto-resume per user-locked decision; included for audit visibility only). */
|
|
160
|
+
toJSON() {
|
|
161
|
+
return {
|
|
162
|
+
goal: this.goal,
|
|
163
|
+
language: this.language,
|
|
164
|
+
maxWallclockMs: this.maxWallclockMs,
|
|
165
|
+
stuckAfterMs: this.stuckAfterMs,
|
|
166
|
+
startedAt: this.startedAt,
|
|
167
|
+
currentPhase: this.currentPhase,
|
|
168
|
+
turnsThisPhase: this.turnsThisPhase,
|
|
169
|
+
decisionCount: this.decisionCount,
|
|
170
|
+
initialDelivered: this.initialDelivered,
|
|
171
|
+
stopped: this.stopped,
|
|
172
|
+
stopReason: this.stopReason,
|
|
173
|
+
// Note: decisions array not persisted (memory-only)
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// ─── internals ──────────────────────────────────────────────────
|
|
178
|
+
|
|
179
|
+
_stateSnapshot() {
|
|
180
|
+
return {
|
|
181
|
+
goal: this.goal,
|
|
182
|
+
currentPhase: this.currentPhase,
|
|
183
|
+
milestones: this.lastMilestones,
|
|
184
|
+
idleSec: Math.round((Date.now() - this.lastEventTs) / 1000),
|
|
185
|
+
lastEventType: this._lastEventType || null,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
_shouldStop() {
|
|
190
|
+
if (this.stopped) return true;
|
|
191
|
+
if (Date.now() - this.startedAt > this.maxWallclockMs) {
|
|
192
|
+
this.stopReason = "max_wallclock";
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
if (
|
|
196
|
+
this.currentPhase === "finalization" &&
|
|
197
|
+
this.turnsThisPhase >= 5
|
|
198
|
+
) {
|
|
199
|
+
this.stopReason = "finalization_settled";
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
_recordDecision(template, reason, prompt) {
|
|
206
|
+
this.decisionCount += 1;
|
|
207
|
+
this.lastDecisionAt = Date.now();
|
|
208
|
+
this.decisions.push({
|
|
209
|
+
ts: new Date().toISOString(),
|
|
210
|
+
template,
|
|
211
|
+
reason,
|
|
212
|
+
currentPhase: this.currentPhase,
|
|
213
|
+
promptPreview: (prompt || "").slice(0, 200),
|
|
214
|
+
});
|
|
215
|
+
if (this.decisions.length > 100) this.decisions.shift();
|
|
216
|
+
}
|
|
217
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// v0.8 P4 — templated continuation prompts for the marathon driver.
|
|
2
|
+
//
|
|
3
|
+
// Driver state → prompt template mapping. Templates are deterministic
|
|
4
|
+
// (no LLM in the driver), bilingual (en + zh per workspace LANGUAGE).
|
|
5
|
+
// Goal: surface to KC's main conductor the smallest useful nudge to
|
|
6
|
+
// keep the pipeline moving without leaking marathon-implementation
|
|
7
|
+
// details into the agent's context.
|
|
8
|
+
//
|
|
9
|
+
// Each template is a function (engineState, goal) → string. State
|
|
10
|
+
// fields used:
|
|
11
|
+
// currentPhase, milestones, idleSec, lastEventType, goal
|
|
12
|
+
//
|
|
13
|
+
// Add new templates here as the driver state machine grows.
|
|
14
|
+
|
|
15
|
+
const TEMPLATES_EN = {
|
|
16
|
+
initial: (s) => `Goal: ${s.goal}\n\n` +
|
|
17
|
+
`You are running in marathon mode (no manual user check-ins). Advance the pipeline phase by phase. ` +
|
|
18
|
+
`Engine derives milestones from filesystem facts; produce real artifacts, then call phase_advance. ` +
|
|
19
|
+
`If you get stuck on a specific phase, surface the blocker in your next response and the driver will ` +
|
|
20
|
+
`inject a diagnostic prompt next turn.`,
|
|
21
|
+
|
|
22
|
+
continue_phase: (s) => `Continue with ${s.currentPhase} work. ` +
|
|
23
|
+
`Engine status: ${formatMilestones(s.milestones)}.`,
|
|
24
|
+
|
|
25
|
+
advance_phase: (s) => `${s.currentPhase} milestones look complete (${formatMilestones(s.milestones)}). ` +
|
|
26
|
+
`Verify the gate conditions then call \`phase_advance\` to the next phase.`,
|
|
27
|
+
|
|
28
|
+
unstick: (s) => `No phase progress in the last ${Math.round(s.idleSec / 60)} minutes. ` +
|
|
29
|
+
`Either (1) surface the blocker explicitly so the developer user can intervene, or (2) ` +
|
|
30
|
+
`consult the relevant meta-skill for the current phase and try a different approach. ` +
|
|
31
|
+
`If you've genuinely finished and the engine gate is wrong, force phase_advance with reason.`,
|
|
32
|
+
|
|
33
|
+
finalize: (s) => `You've reached finalization. Wrap the deliverable bundle: ` +
|
|
34
|
+
`verify rule_skills/coverage_report.md is substantive, output/releases/<slug>/ is current ` +
|
|
35
|
+
`(re-run release tool if workflows changed after the last snapshot), and final_dashboard.html ` +
|
|
36
|
+
`reflects the latest QC data. When done, just say so — the marathon will exit.`,
|
|
37
|
+
|
|
38
|
+
stop: () => `Marathon stop condition reached. Save state and summarize what was accomplished.`,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const TEMPLATES_ZH = {
|
|
42
|
+
initial: (s) => `目标:${s.goal}\n\n` +
|
|
43
|
+
`你正运行在 marathon 模式(无人工 check-in)。按阶段推进整条流水线。` +
|
|
44
|
+
`引擎从文件系统事实派生里程碑;先把真实交付物产出来,再调用 phase_advance。` +
|
|
45
|
+
`如果在某个阶段卡住了,直接在下一回合的回复里把阻塞点说清楚,驱动器会在下一回合注入诊断提示。`,
|
|
46
|
+
|
|
47
|
+
continue_phase: (s) => `继续 ${s.currentPhase} 阶段的工作。` +
|
|
48
|
+
`引擎状态:${formatMilestones(s.milestones)}。`,
|
|
49
|
+
|
|
50
|
+
advance_phase: (s) => `${s.currentPhase} 阶段的里程碑看起来已经完成(${formatMilestones(s.milestones)})。` +
|
|
51
|
+
`核对一遍门控条件,然后调用 \`phase_advance\` 进入下一阶段。`,
|
|
52
|
+
|
|
53
|
+
unstick: (s) => `已经 ${Math.round(s.idleSec / 60)} 分钟没有阶段推进了。` +
|
|
54
|
+
`两条路:(1) 明确说出阻塞在哪里、让开发者用户介入;(2) 查阅当前阶段相关的 meta-skill 换个思路再试。` +
|
|
55
|
+
`如果你确实已经做完、但引擎门控判断错误,用 reason 强制 phase_advance。`,
|
|
56
|
+
|
|
57
|
+
finalize: (s) => `已经进入 finalization。收尾打包:` +
|
|
58
|
+
`确认 rule_skills/coverage_report.md 内容充实、output/releases/<slug>/ 是最新的(` +
|
|
59
|
+
`如果 workflows 在最近一次快照之后还有改动,重新跑 release 工具),final_dashboard.html 反映最新 QC 数据。` +
|
|
60
|
+
`做完之后直接说一声,marathon 会退出。`,
|
|
61
|
+
|
|
62
|
+
stop: () => `Marathon 停止条件已触发。保存状态、总结已完成的工作。`,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
function formatMilestones(m) {
|
|
66
|
+
if (!m || typeof m !== "object") return "(unknown)";
|
|
67
|
+
const parts = [];
|
|
68
|
+
for (const [k, v] of Object.entries(m)) {
|
|
69
|
+
if (typeof v === "number") parts.push(`${k}:${v}`);
|
|
70
|
+
else if (typeof v === "boolean") parts.push(`${k}:${v ? "yes" : "no"}`);
|
|
71
|
+
else if (Array.isArray(v)) parts.push(`${k}:${v.length}`);
|
|
72
|
+
}
|
|
73
|
+
return parts.slice(0, 6).join(", ") || "(empty)";
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Render a continuation prompt for the given driver state.
|
|
78
|
+
*
|
|
79
|
+
* @param {string} templateKey — one of: initial, continue_phase, advance_phase, unstick, finalize, stop
|
|
80
|
+
* @param {object} state — {goal, currentPhase, milestones, idleSec, lastEventType}
|
|
81
|
+
* @param {string} [language] — "en" or "zh" (defaults to "en")
|
|
82
|
+
* @returns {string}
|
|
83
|
+
*/
|
|
84
|
+
export function renderPrompt(templateKey, state, language = "en") {
|
|
85
|
+
const tmpls = language === "zh" ? TEMPLATES_ZH : TEMPLATES_EN;
|
|
86
|
+
const tmpl = tmpls[templateKey];
|
|
87
|
+
if (!tmpl) {
|
|
88
|
+
throw new Error(`Unknown marathon prompt template: ${templateKey}`);
|
|
89
|
+
}
|
|
90
|
+
return tmpl(state);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export const PROMPT_TEMPLATES = Object.freeze(Object.keys(TEMPLATES_EN));
|
package/template/.env.template
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# === KC
|
|
1
|
+
# === KC Configuration ===
|
|
2
2
|
|
|
3
3
|
# Language: en | zh
|
|
4
4
|
LANGUAGE=en
|
|
@@ -29,3 +29,19 @@ MONITOR_FREQUENCY=mid
|
|
|
29
29
|
# === Evolution Control ===
|
|
30
30
|
# Maximum evolution iterations per rule before escalating to developer user
|
|
31
31
|
MAX_ITERATIONS=20
|
|
32
|
+
|
|
33
|
+
# === sandbox_exec Timeout (v0.8 P1-F) ===
|
|
34
|
+
# Default timeout when the agent doesn't pass timeout_ms (ms).
|
|
35
|
+
# Claude Code parity = 120000 (2 min). Raise if your default workloads
|
|
36
|
+
# routinely exceed 2 min (e.g., document-parsing benchmarks).
|
|
37
|
+
# KC_EXEC_DEFAULT_TIMEOUT_MS=120000
|
|
38
|
+
#
|
|
39
|
+
# Hard ceiling — agent's timeout_ms is clamped to this (ms). Raise for
|
|
40
|
+
# v0.9 jyppx integration where parser_building can take 10-30 min per
|
|
41
|
+
# corpus. Don't go above 1800000 (30 min) without a specific reason.
|
|
42
|
+
# KC_EXEC_MAX_TIMEOUT_MS=600000
|
|
43
|
+
#
|
|
44
|
+
# Legacy alias (seconds) for KC_EXEC_DEFAULT_TIMEOUT_MS. Deprecated as of
|
|
45
|
+
# v0.8 — prefer the ms-based name. The seconds value is multiplied by
|
|
46
|
+
# 1000 if KC_EXEC_DEFAULT_TIMEOUT_MS isn't set.
|
|
47
|
+
# KC_EXEC_TIMEOUT=120
|
package/template/AGENT.md
CHANGED
|
@@ -15,7 +15,7 @@ update as you learn about this specific business scenario.
|
|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
|
18
|
-
# KC
|
|
18
|
+
# KC — Document Verification Workspace
|
|
19
19
|
|
|
20
20
|
## What This Workspace Is
|
|
21
21
|
|
|
@@ -93,7 +93,7 @@ The skill body is the methodology. Skills convey philosophy and decision framewo
|
|
|
93
93
|
|
|
94
94
|
---
|
|
95
95
|
|
|
96
|
-
# KC
|
|
96
|
+
# KC — 文档核查工作区
|
|
97
97
|
|
|
98
98
|
> **技能优先级**: meta-meta 技能是架构层面 —— 当指导冲突时,
|
|
99
99
|
> meta-meta 凌驾于 meta (技法层面) 之上。架构师的框架约束技法。
|
|
@@ -2,53 +2,73 @@
|
|
|
2
2
|
name: auto-model-selection
|
|
3
3
|
tier: meta
|
|
4
4
|
description: >
|
|
5
|
-
Use Context7 CLI
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
(npm i -g context7).
|
|
5
|
+
Use Context7 CLI for up-to-date model facts (size, API format, context window) and the
|
|
6
|
+
guidance below for "what kind of models are good for what part of a doc verification app".
|
|
7
|
+
Consult whenever you need to pick a model for a tier slot, decide between provider
|
|
8
|
+
alternatives, or sanity-check an existing tier assignment. Context7 gives you fresh
|
|
9
|
+
facts; this skill gives you the heuristic that maps those facts onto KC's pipeline.
|
|
10
|
+
Optional plugin (Context7 install: npm i -g context7).
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
-
# Auto Model Selection
|
|
13
|
+
# Auto Model Selection
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
Model selection is not a frequently called skill — for most users, the tiers in workspace `.env` are already set sensibly, and a 4-tier cost-sensitive selection is overkill. This skill exists for two moments: when the conductor is bootstrapping fresh tier assignments (rare), and as a reference inside `skill-to-workflow` when a workflow needs to pick which worker LLM is right for its job.
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
- `c7 library <query>` — search for a library/provider by name
|
|
19
|
-
- `c7 docs <libraryId> <query>` — get specific documentation and code examples
|
|
17
|
+
The teaching below is empirical — what the author has found works in this domain. Treat it as a starting heuristic with a 3-6 month shelf life, since model families update quickly.
|
|
20
18
|
|
|
21
|
-
##
|
|
19
|
+
## Worker LLM family — practical heuristic
|
|
22
20
|
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
- Onboarding `/models` endpoint failed and curated list is stale
|
|
21
|
+
- **Qwen family** — robust and cheap for general worker work. The flagship MoE in this family at any given time is usually one of the best workhorses for routine extraction / classification. Smaller sizes (3B-70B) are plentiful and reliable. Good default.
|
|
22
|
+
- **DeepSeek** — excellent on more complicated tasks. Worth reaching for when the rule involves multi-step reasoning, nested judgment, or anything where Qwen's shorter-context behavior shows strain.
|
|
23
|
+
- **GLM and Kimi** — also strong for the same complicated-task range as DeepSeek. Trade-off: they don't usually ship smaller variants (3B-70B), so they're tier1/tier2 only, not tier3/tier4.
|
|
27
24
|
|
|
28
|
-
##
|
|
25
|
+
## Flagship-MoE shape and the tier1 baseline
|
|
29
26
|
|
|
30
|
-
|
|
31
|
-
2. Use `c7 library <provider-name>` to find the provider's library ID
|
|
32
|
-
3. Use `c7 docs <id> "available models"` to get current model listings
|
|
33
|
-
4. From the docs, identify: model names, capabilities (reasoning, coding, vision), context window sizes, pricing tiers
|
|
34
|
-
5. Assign models to tiers based on capability and cost:
|
|
35
|
-
- LLM tier1: most capable (complex judgment, extraction)
|
|
36
|
-
- LLM tier2-3: mid-range (routine extraction, simple judgment)
|
|
37
|
-
- LLM tier4: cheapest (high-volume simple tasks)
|
|
38
|
-
- VLM tier1-3: vision models for document parsing/OCR
|
|
39
|
-
6. Update `model-tiers.json` or workspace `.env` with assignments
|
|
27
|
+
The current generation of flagship MoE LLMs has a recognizable shape: total parameter count in the 200-400B range, ~20B of activated expert size per token. Examples (these will be stale within months): Qwen's flagship MoE in the 200-400B-A20B band, DeepSeek-V4-Flash, etc.
|
|
40
28
|
|
|
41
|
-
|
|
29
|
+
This shape is a good first-choice worker LLM — not necessarily the absolute best at tier1, but a reasonable benchmark to start from. When you're picking a tier1 model, start from one of these and only move if you have a specific reason.
|
|
42
30
|
|
|
43
|
-
|
|
44
|
-
- Regex is tier0 — smaller than any LLM
|
|
45
|
-
- Not all tiers need to be filled — blank tiers are fine if the provider lacks suitable models
|
|
46
|
-
- Record what works in AGENT.md for future reference
|
|
31
|
+
## Smaller LLMs — basically free below 30B
|
|
47
32
|
|
|
48
|
-
|
|
33
|
+
Once you drop below ~30B parameters, models are extremely cheap on most providers. Qwen ships a ton of choices in this range and they work well.
|
|
34
|
+
|
|
35
|
+
Two rules for picking in this range:
|
|
36
|
+
- **Avoid "coder" variants** (models with `coder` / `code` in the name) at small sizes — these are mostly unreliable for general worker tasks. Use them only when the task is literally code-related.
|
|
37
|
+
- **Prefer no-thinking-mode variants** when available. Tasks assigned to small workers are simple and fixed; extra thinking buys nothing and adds latency + token cost.
|
|
38
|
+
|
|
39
|
+
## VLM / OCR selection
|
|
40
|
+
|
|
41
|
+
First question: what's the visual task?
|
|
42
|
+
|
|
43
|
+
- **Characters in scanned docs, seal stamps, handwriting** — use a dedicated OCR model. Current strong choices (subject to change): Paddle-OCR family, GLM-OCR, DeepSeek-OCR. Previous versions still work fine. No need to use a larger general VLM for plain character recognition.
|
|
44
|
+
- **Graphs, complex tables, structures with strange or no frame lines** — try a larger and more expensive general VLM. The structure-understanding gap between OCR-specific models and general VLMs widens fast on these cases.
|
|
45
|
+
|
|
46
|
+
When in doubt, run the cheapest OCR option first and only upgrade if the cheap path misses structure information.
|
|
47
|
+
|
|
48
|
+
## Context7 — model facts on demand
|
|
49
|
+
|
|
50
|
+
The heuristics above are about *which kind* of model to pick. For the specific facts (current model names, exact context window, pricing, API format), use Context7:
|
|
49
51
|
|
|
50
52
|
```bash
|
|
51
|
-
|
|
53
|
+
c7 library <provider-name>
|
|
54
|
+
c7 docs <libraryId> "available models"
|
|
52
55
|
```
|
|
53
56
|
|
|
54
|
-
|
|
57
|
+
Two commands. The first finds the provider's library ID, the second fetches up-to-date docs and code examples. Useful when:
|
|
58
|
+
|
|
59
|
+
- Workspace `model-tiers.json` looks stale (KC hasn't been updated since the last model launch)
|
|
60
|
+
- User switched providers and needs model discovery
|
|
61
|
+
- The `/models` endpoint on a new provider was empty or unhelpful
|
|
62
|
+
- You're sanity-checking that a model name in `.env` is still served
|
|
63
|
+
|
|
64
|
+
Install: `npm i -g context7`. Verify: `c7 library openai` should return results.
|
|
65
|
+
|
|
66
|
+
## When tier1/tier2 are picked
|
|
67
|
+
|
|
68
|
+
The tier assignment ends up driving cost. Cheapest model that meets the accuracy bar wins. Regex is the implicit "tier 0" and should be the first reach when the rule can be satisfied by pattern matching alone — see `skill-to-workflow` for when to escalate from regex to worker LLMs.
|
|
69
|
+
|
|
70
|
+
Not all tiers need to be filled. Blank tier3/tier4 slots are fine if the provider doesn't ship suitable small models. Record what works in `AGENT.md` so the next session inherits the choice.
|
|
71
|
+
|
|
72
|
+
## Refresh cadence
|
|
73
|
+
|
|
74
|
+
This skill's heuristics will drift. Author plans to revisit every 3-6 months as new model generations land. If you find yourself applying advice here that contradicts what Context7 shows today, trust Context7 — the facts have moved.
|
|
@@ -73,6 +73,33 @@ Once a project is past bootstrap and into production, fresh documents often arri
|
|
|
73
73
|
|
|
74
74
|
Discuss the cadence with the developer user during bootstrap — knowing the production input rhythm shapes how skills and workflows should be written (batch vs streaming, idempotency requirements, etc.).
|
|
75
75
|
|
|
76
|
+
## Per-project memory: keep AGENT.md alive
|
|
77
|
+
|
|
78
|
+
`AGENT.md` at the workspace root has per-project memory sections (`Project`, `Decisions`, `Domain Notes`, `User Preferences`). These are intentionally placeholder comments at bootstrap — they're for YOU to fill in as the work surfaces things worth remembering across phases or future sessions.
|
|
79
|
+
|
|
80
|
+
What belongs there:
|
|
81
|
+
- **Project**: corpus identity (regulation name + scope), language, primary vs auxiliary rules, sample doc set composition.
|
|
82
|
+
- **Decisions**: design choices that aren't obvious from code — "non-标 35% limit is bank-level not per-product, so single-doc reports get WARNING not FAIL", "季报 not applicable for R02-06/R02-08 per regulation §39", etc.
|
|
83
|
+
- **Domain Notes**: regulatory or business-domain nuance worth surfacing — "PT/RT/LZ are three distinct product types with different disclosure templates", terminology disambiguation.
|
|
84
|
+
- **User Preferences**: how the developer user wants you to operate on THIS project — verbosity, naming conventions, when to ask vs proceed.
|
|
85
|
+
|
|
86
|
+
Update AGENT.md at natural checkpoints: after the developer user gives you a substantive clarification, after you finish a phase, after you discover a design constraint that affects subsequent phases. Don't wait for a `/remember` instruction — the memory is yours to maintain.
|
|
87
|
+
|
|
88
|
+
A future session resumes by reading AGENT.md first. The richer it is, the less re-explanation the developer user has to do.
|
|
89
|
+
|
|
90
|
+
### Phase-transition cadence
|
|
91
|
+
|
|
92
|
+
A recurring failure mode worth flagging: agents bootstrap AGENT.md richly, then never touch it again — many hours of phase work pass without a single AGENT.md commit. That defeats the long-term-memory purpose.
|
|
93
|
+
|
|
94
|
+
Cadence to adopt: **append a one-line decision log to AGENT.md at each phase transition**. Format:
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
[<timestamp> | rule_extraction → skill_authoring]
|
|
98
|
+
N rules extracted; coverage_audit complete; R03/R05/R07 flagged judgment-heavy.
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Three lines of friction per phase transition; thirty lines of insight for the next auditor / next session. The format is loose — the cadence matters more than perfect prose.
|
|
102
|
+
|
|
76
103
|
## When to Re-Bootstrap
|
|
77
104
|
|
|
78
105
|
Return to this skill when:
|
|
@@ -81,3 +81,17 @@ Map these dependencies in the rule catalog. Execute rules in dependency order. P
|
|
|
81
81
|
- **Multiple values**: The document contains the entity in multiple places with different values. Flag as `uncertain`. Report all found values.
|
|
82
82
|
- **Conditional rules**: "If the loan exceeds 1M, then collateral is required." Check the condition before applying the rule. If the condition is not met, the rule does not apply — result is `pass` (or `not_applicable` if you add that category).
|
|
83
83
|
- **Negative results**: Some rules check for absence. "The document must NOT contain guarantees to related parties." Searching for absence is harder than searching for presence. Be thorough in the search, then be confident in the negative.
|
|
84
|
+
|
|
85
|
+
## Cross-document consistency of confidence bars
|
|
86
|
+
|
|
87
|
+
For a rule that judges multiple documents (which is the common case), **the confidence threshold for "pass" must be the same across all documents** for that rule. A rule that requires 0.85 confidence to pass on document A and 0.75 on document B isn't a rule — it's two rules pretending to be one.
|
|
88
|
+
|
|
89
|
+
When the worker LLM is the judge, set the threshold in the prompt or in postprocessing, not "let the LLM decide" per call. Random variation between LLM calls means each call lands somewhere on a distribution; your job is to set the bar that distribution must clear.
|
|
90
|
+
|
|
91
|
+
Two ways to enforce:
|
|
92
|
+
- **In the prompt**: explicit "output 'PASS' only if confidence ≥ 0.85" language. Cheap. Vulnerable to LLM drift between calls.
|
|
93
|
+
- **In postprocessing**: ask the LLM for verdict + confidence separately, then apply the threshold in a small Python wrapper. More reliable. Engine sees the threshold as code, not prompt prose.
|
|
94
|
+
|
|
95
|
+
For rules with stable patterns (formats, presence checks), prefer postprocessing. For rules with subjective judgment (adequacy, sufficiency), the prompt-level threshold is easier to write but worth auditing — sample a batch and check whether the LLM honored the bar.
|
|
96
|
+
|
|
97
|
+
The `confidence-system` skill describes how to compose confidence from multiple signals; this section is about applying it consistently across documents for the same rule.
|
|
@@ -50,21 +50,43 @@ Does this document match any known corner case pattern?
|
|
|
50
50
|
- Near miss: slightly lower confidence.
|
|
51
51
|
- No match: neutral (no adjustment).
|
|
52
52
|
|
|
53
|
+
### Signal: Format-Pattern Conformance
|
|
54
|
+
Does the extracted value match a regex-expressible format that the rule implies? Many fields have a known shape — phone numbers, ID numbers, dates, currency amounts, regulatory codes — that can be checked cheaply with a regex independent of the LLM's confidence in its own output.
|
|
55
|
+
- Value matches the expected format pattern: positive signal.
|
|
56
|
+
- Value violates the format pattern (e.g., a phone field with letters): strong negative signal, often a hallucination indicator.
|
|
57
|
+
- No applicable format pattern for the field type: neutral.
|
|
58
|
+
|
|
59
|
+
This catches a common failure: the LLM correctly identifies *where* the value lives but gets the format wrong (typos a digit, drops a country code, returns a stand-in like "see appendix").
|
|
60
|
+
|
|
61
|
+
### Signal: Statistical Outlier
|
|
62
|
+
For numeric values, does this value sit far from the typical range seen across other documents for the same field?
|
|
63
|
+
- Within 1 standard deviation of average: neutral / mild positive.
|
|
64
|
+
- 2-3 standard deviations: mild negative.
|
|
65
|
+
- Beyond 3 std dev or outside a domain-plausible range: strong negative — often a unit error (yuan vs. 万元), a decimal mistake, or a hallucinated digit.
|
|
66
|
+
|
|
67
|
+
Useful especially for amounts, percentages, ratios. Compute the reference range from QC-confirmed historical data; refresh periodically. For categorical fields, the analog is "value not in the observed value-set" — flag novel values for review.
|
|
68
|
+
|
|
53
69
|
### Combining Signals
|
|
54
70
|
|
|
55
|
-
|
|
71
|
+
Combine the signals into a single confidence score. The form is usually a weighted sum:
|
|
56
72
|
|
|
57
73
|
```
|
|
58
|
-
confidence =
|
|
74
|
+
confidence = w_method * method_prior
|
|
75
|
+
+ w_source * source_presence
|
|
76
|
+
+ w_history * historical_accuracy
|
|
77
|
+
+ w_corner * corner_case_adjustment
|
|
78
|
+
+ w_format * format_conformance
|
|
79
|
+
+ w_outlier * outlier_check
|
|
59
80
|
```
|
|
60
81
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
-
|
|
64
|
-
-
|
|
65
|
-
-
|
|
82
|
+
How to set the weights is **your judgment call** per rule and per project. A few orienting principles, not prescriptions:
|
|
83
|
+
|
|
84
|
+
- Historical accuracy is the most predictive signal once you have data — favor it heavily after the first few QC cycles.
|
|
85
|
+
- Method prior and source presence are always available — they carry the early iterations before history exists.
|
|
86
|
+
- Format conformance and outlier signals are independent of the LLM — they keep working even when the LLM is overconfident, which is exactly when you want them.
|
|
87
|
+
- Corner-case adjustment is usually small but should fire decisively when a known corner case is matched.
|
|
66
88
|
|
|
67
|
-
When historical accuracy is not yet available (early iterations), redistribute its weight to the other signals.
|
|
89
|
+
When historical accuracy is not yet available (early iterations), redistribute its weight to the other signals. The right weights for this rule on this corpus will reveal themselves through calibration — that's the loop the next section describes.
|
|
68
90
|
|
|
69
91
|
## Threshold Bands
|
|
70
92
|
|