@mingyuxu/check 0.1.0
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 +7 -0
- package/dist/critic/critique.d.ts +31 -0
- package/dist/critic/critique.d.ts.map +1 -0
- package/dist/critic/critique.js +68 -0
- package/dist/critic/critique.js.map +1 -0
- package/dist/critic/observe.d.ts +48 -0
- package/dist/critic/observe.d.ts.map +1 -0
- package/dist/critic/observe.js +62 -0
- package/dist/critic/observe.js.map +1 -0
- package/dist/domain/actions.d.ts +67 -0
- package/dist/domain/actions.d.ts.map +1 -0
- package/dist/domain/actions.js +123 -0
- package/dist/domain/actions.js.map +1 -0
- package/dist/domain/invalidation.d.ts +18 -0
- package/dist/domain/invalidation.d.ts.map +1 -0
- package/dist/domain/invalidation.js +44 -0
- package/dist/domain/invalidation.js.map +1 -0
- package/dist/domain/registers.d.ts +45 -0
- package/dist/domain/registers.d.ts.map +1 -0
- package/dist/domain/registers.js +121 -0
- package/dist/domain/registers.js.map +1 -0
- package/dist/domain/view.d.ts +48 -0
- package/dist/domain/view.d.ts.map +1 -0
- package/dist/domain/view.js +70 -0
- package/dist/domain/view.js.map +1 -0
- package/dist/forge/attribution.d.ts +18 -0
- package/dist/forge/attribution.d.ts.map +1 -0
- package/dist/forge/attribution.js +83 -0
- package/dist/forge/attribution.js.map +1 -0
- package/dist/forge/bakeoff.d.ts +35 -0
- package/dist/forge/bakeoff.d.ts.map +1 -0
- package/dist/forge/bakeoff.js +48 -0
- package/dist/forge/bakeoff.js.map +1 -0
- package/dist/forge/contracts.d.ts +67 -0
- package/dist/forge/contracts.d.ts.map +1 -0
- package/dist/forge/contracts.js +70 -0
- package/dist/forge/contracts.js.map +1 -0
- package/dist/forge/evaluate-scenario.d.ts +17 -0
- package/dist/forge/evaluate-scenario.d.ts.map +1 -0
- package/dist/forge/evaluate-scenario.js +93 -0
- package/dist/forge/evaluate-scenario.js.map +1 -0
- package/dist/forge/evaluate.d.ts +25 -0
- package/dist/forge/evaluate.d.ts.map +1 -0
- package/dist/forge/evaluate.js +81 -0
- package/dist/forge/evaluate.js.map +1 -0
- package/dist/forge/feedback.d.ts +21 -0
- package/dist/forge/feedback.d.ts.map +1 -0
- package/dist/forge/feedback.js +41 -0
- package/dist/forge/feedback.js.map +1 -0
- package/dist/forge/reflect.d.ts +35 -0
- package/dist/forge/reflect.d.ts.map +1 -0
- package/dist/forge/reflect.js +72 -0
- package/dist/forge/reflect.js.map +1 -0
- package/dist/forge/scorer.d.ts +67 -0
- package/dist/forge/scorer.d.ts.map +1 -0
- package/dist/forge/scorer.js +99 -0
- package/dist/forge/scorer.js.map +1 -0
- package/dist/forge/trace.d.ts +25 -0
- package/dist/forge/trace.d.ts.map +1 -0
- package/dist/forge/trace.js +51 -0
- package/dist/forge/trace.js.map +1 -0
- package/dist/index.d.ts +78 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +47 -0
- package/dist/index.js.map +1 -0
- package/dist/knowledge/loader.d.ts +19 -0
- package/dist/knowledge/loader.d.ts.map +1 -0
- package/dist/knowledge/loader.js +76 -0
- package/dist/knowledge/loader.js.map +1 -0
- package/dist/knowledge/retrieval.d.ts +23 -0
- package/dist/knowledge/retrieval.d.ts.map +1 -0
- package/dist/knowledge/retrieval.js +100 -0
- package/dist/knowledge/retrieval.js.map +1 -0
- package/dist/knowledge/skill.d.ts +22 -0
- package/dist/knowledge/skill.d.ts.map +1 -0
- package/dist/knowledge/skill.js +112 -0
- package/dist/knowledge/skill.js.map +1 -0
- package/dist/knowledge/write.d.ts +35 -0
- package/dist/knowledge/write.d.ts.map +1 -0
- package/dist/knowledge/write.js +84 -0
- package/dist/knowledge/write.js.map +1 -0
- package/dist/manage.d.ts +7 -0
- package/dist/manage.d.ts.map +1 -0
- package/dist/manage.js +36 -0
- package/dist/manage.js.map +1 -0
- package/dist/methods/loader.d.ts +18 -0
- package/dist/methods/loader.d.ts.map +1 -0
- package/dist/methods/loader.js +65 -0
- package/dist/methods/loader.js.map +1 -0
- package/dist/nodes/aggregate.d.ts +2 -0
- package/dist/nodes/aggregate.d.ts.map +1 -0
- package/dist/nodes/aggregate.js +101 -0
- package/dist/nodes/aggregate.js.map +1 -0
- package/dist/nodes/base.d.ts +31 -0
- package/dist/nodes/base.d.ts.map +1 -0
- package/dist/nodes/base.js +44 -0
- package/dist/nodes/base.js.map +1 -0
- package/dist/nodes/cognition.d.ts +2 -0
- package/dist/nodes/cognition.d.ts.map +1 -0
- package/dist/nodes/cognition.js +263 -0
- package/dist/nodes/cognition.js.map +1 -0
- package/dist/nodes/confidence.d.ts +9 -0
- package/dist/nodes/confidence.d.ts.map +1 -0
- package/dist/nodes/confidence.js +19 -0
- package/dist/nodes/confidence.js.map +1 -0
- package/dist/nodes/deliver.d.ts +2 -0
- package/dist/nodes/deliver.d.ts.map +1 -0
- package/dist/nodes/deliver.js +37 -0
- package/dist/nodes/deliver.js.map +1 -0
- package/dist/nodes/evidence.d.ts +2 -0
- package/dist/nodes/evidence.d.ts.map +1 -0
- package/dist/nodes/evidence.js +118 -0
- package/dist/nodes/evidence.js.map +1 -0
- package/dist/nodes/ground.d.ts +2 -0
- package/dist/nodes/ground.d.ts.map +1 -0
- package/dist/nodes/ground.js +22 -0
- package/dist/nodes/ground.js.map +1 -0
- package/dist/nodes/parse.d.ts +2 -0
- package/dist/nodes/parse.d.ts.map +1 -0
- package/dist/nodes/parse.js +72 -0
- package/dist/nodes/parse.js.map +1 -0
- package/dist/nodes/scope.d.ts +4 -0
- package/dist/nodes/scope.d.ts.map +1 -0
- package/dist/nodes/scope.js +63 -0
- package/dist/nodes/scope.js.map +1 -0
- package/dist/nodes/shape.d.ts +2 -0
- package/dist/nodes/shape.d.ts.map +1 -0
- package/dist/nodes/shape.js +111 -0
- package/dist/nodes/shape.js.map +1 -0
- package/dist/ontology/checkskill.d.ts +26 -0
- package/dist/ontology/checkskill.d.ts.map +1 -0
- package/dist/ontology/checkskill.js +89 -0
- package/dist/ontology/checkskill.js.map +1 -0
- package/dist/ontology/functions.d.ts +29 -0
- package/dist/ontology/functions.d.ts.map +1 -0
- package/dist/ontology/functions.js +143 -0
- package/dist/ontology/functions.js.map +1 -0
- package/dist/ontology/loader.d.ts +44 -0
- package/dist/ontology/loader.d.ts.map +1 -0
- package/dist/ontology/loader.js +254 -0
- package/dist/ontology/loader.js.map +1 -0
- package/dist/ontology/runtime-graph.d.ts +22 -0
- package/dist/ontology/runtime-graph.d.ts.map +1 -0
- package/dist/ontology/runtime-graph.js +111 -0
- package/dist/ontology/runtime-graph.js.map +1 -0
- package/dist/ontology/store.d.ts +65 -0
- package/dist/ontology/store.d.ts.map +1 -0
- package/dist/ontology/store.js +179 -0
- package/dist/ontology/store.js.map +1 -0
- package/dist/parsers/exec.d.ts +9 -0
- package/dist/parsers/exec.d.ts.map +1 -0
- package/dist/parsers/exec.js +41 -0
- package/dist/parsers/exec.js.map +1 -0
- package/dist/parsers/runner.d.ts +2 -0
- package/dist/parsers/runner.d.ts.map +1 -0
- package/dist/parsers/runner.js +26 -0
- package/dist/parsers/runner.js.map +1 -0
- package/dist/parsers/store.d.ts +32 -0
- package/dist/parsers/store.d.ts.map +1 -0
- package/dist/parsers/store.js +64 -0
- package/dist/parsers/store.js.map +1 -0
- package/dist/parsers/synthesize.d.ts +26 -0
- package/dist/parsers/synthesize.d.ts.map +1 -0
- package/dist/parsers/synthesize.js +63 -0
- package/dist/parsers/synthesize.js.map +1 -0
- package/dist/planner.d.ts +89 -0
- package/dist/planner.d.ts.map +1 -0
- package/dist/planner.js +785 -0
- package/dist/planner.js.map +1 -0
- package/dist/project.d.ts +4 -0
- package/dist/project.d.ts.map +1 -0
- package/dist/project.js +42 -0
- package/dist/project.js.map +1 -0
- package/dist/scenario-builder.d.ts +56 -0
- package/dist/scenario-builder.d.ts.map +1 -0
- package/dist/scenario-builder.js +366 -0
- package/dist/scenario-builder.js.map +1 -0
- package/dist/schemas/index.d.ts +12 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +8 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/schemas/judgment.d.ts +38 -0
- package/dist/schemas/judgment.d.ts.map +1 -0
- package/dist/schemas/judgment.js +45 -0
- package/dist/schemas/judgment.js.map +1 -0
- package/dist/schemas/payloads.d.ts +53 -0
- package/dist/schemas/payloads.d.ts.map +1 -0
- package/dist/schemas/payloads.js +54 -0
- package/dist/schemas/payloads.js.map +1 -0
- package/dist/schemas/result.d.ts +36 -0
- package/dist/schemas/result.d.ts.map +1 -0
- package/dist/schemas/result.js +26 -0
- package/dist/schemas/result.js.map +1 -0
- package/dist/schemas/spec.d.ts +40 -0
- package/dist/schemas/spec.d.ts.map +1 -0
- package/dist/schemas/spec.js +82 -0
- package/dist/schemas/spec.js.map +1 -0
- package/dist/schemas/target.d.ts +28 -0
- package/dist/schemas/target.d.ts.map +1 -0
- package/dist/schemas/target.js +36 -0
- package/dist/schemas/target.js.map +1 -0
- package/dist/schemas/workflow.d.ts +21 -0
- package/dist/schemas/workflow.d.ts.map +1 -0
- package/dist/schemas/workflow.js +100 -0
- package/dist/schemas/workflow.js.map +1 -0
- package/dist/skills/judge-runner.d.ts +2 -0
- package/dist/skills/judge-runner.d.ts.map +1 -0
- package/dist/skills/judge-runner.js +29 -0
- package/dist/skills/judge-runner.js.map +1 -0
- package/dist/skills/judge-synth.d.ts +31 -0
- package/dist/skills/judge-synth.d.ts.map +1 -0
- package/dist/skills/judge-synth.js +87 -0
- package/dist/skills/judge-synth.js.map +1 -0
- package/dist/subagents/worker.d.ts +5 -0
- package/dist/subagents/worker.d.ts.map +1 -0
- package/dist/subagents/worker.js +58 -0
- package/dist/subagents/worker.js.map +1 -0
- package/dist/workflows/context.d.ts +26 -0
- package/dist/workflows/context.d.ts.map +1 -0
- package/dist/workflows/context.js +23 -0
- package/dist/workflows/context.js.map +1 -0
- package/dist/workflows/executor.d.ts +44 -0
- package/dist/workflows/executor.d.ts.map +1 -0
- package/dist/workflows/executor.js +146 -0
- package/dist/workflows/executor.js.map +1 -0
- package/dist/workflows/merge.d.ts +4 -0
- package/dist/workflows/merge.d.ts.map +1 -0
- package/dist/workflows/merge.js +21 -0
- package/dist/workflows/merge.js.map +1 -0
- package/dist/workflows/run.d.ts +20 -0
- package/dist/workflows/run.d.ts.map +1 -0
- package/dist/workflows/run.js +66 -0
- package/dist/workflows/run.js.map +1 -0
- package/knowledge/methods/prose-rule.method.md +21 -0
- package/knowledge/methods/ratio-limit.method.md +31 -0
- package/knowledge/ontology/verification.yaml +268 -0
- package/package.json +36 -0
package/dist/planner.js
ADDED
|
@@ -0,0 +1,785 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VerificationAgent —— typed domain capabilities + 旧相位兼容宿主。
|
|
3
|
+
* Commander 只见 Ontology/Method/CheckSkill 领域函数;legacy phase 仅供旧 API/冻结适配。
|
|
4
|
+
* LLM 异步:runPhase 直接 await builder;ToolRegistry 只放同步展示/推进/知识工具。
|
|
5
|
+
*/
|
|
6
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, cpSync, copyFileSync } from "node:fs";
|
|
7
|
+
import { join, basename } from "node:path";
|
|
8
|
+
import { Session, ToolRegistry, Sandbox, spawnSubagent, waitSubagent, reconcile, } from "@mingyuxu/core";
|
|
9
|
+
import { Ledger } from "@mingyuxu/ai";
|
|
10
|
+
import { Target, PipelineSpec, DocumentIR } from "./schemas/index.js";
|
|
11
|
+
import { ScenarioBuilder, defaultSchemaProfile } from "./scenario-builder.js";
|
|
12
|
+
import { loadPack } from "./knowledge/loader.js";
|
|
13
|
+
import { proposeKnowledgeDelta } from "./knowledge/write.js";
|
|
14
|
+
import { Knowledge } from "./knowledge/retrieval.js";
|
|
15
|
+
import { basePackContext } from "./project.js";
|
|
16
|
+
import { reviseFromReport } from "./forge/reflect.js";
|
|
17
|
+
import { fileURLToPath } from "node:url";
|
|
18
|
+
import { loadOntology } from "./ontology/loader.js";
|
|
19
|
+
import { loadMethods, methodRef, selectMethod } from "./methods/loader.js";
|
|
20
|
+
import { inferRuleKind, instantiateDomain, targetDigest } from "./domain/registers.js";
|
|
21
|
+
import { clearStale, isStale, loadStale, markChanged } from "./domain/invalidation.js";
|
|
22
|
+
import { ScenarioOntologyStore } from "./ontology/store.js";
|
|
23
|
+
import { createVerificationActionRegistry } from "./domain/actions.js";
|
|
24
|
+
const WHITELIST = new Set([
|
|
25
|
+
"read_rules", "read_samples", "analyze_rules", "observe_samples", "propose_specs",
|
|
26
|
+
"write_pack", "bakeoff", "load_targets", "write_asset", "revise_targets",
|
|
27
|
+
"show_targets", "show_specs", "show_report", "advance_phase", "propose_knowledge_delta",
|
|
28
|
+
]);
|
|
29
|
+
export class VerificationAgent {
|
|
30
|
+
workspace;
|
|
31
|
+
root;
|
|
32
|
+
policy;
|
|
33
|
+
llm;
|
|
34
|
+
ledger;
|
|
35
|
+
builder;
|
|
36
|
+
tools;
|
|
37
|
+
curSession = null;
|
|
38
|
+
constructor(opts) {
|
|
39
|
+
this.workspace = opts.workspace;
|
|
40
|
+
this.root = opts.root ?? opts.workspace;
|
|
41
|
+
this.policy = opts.policy;
|
|
42
|
+
this.llm = opts.llm;
|
|
43
|
+
this.ledger = opts.ledger ?? new Ledger();
|
|
44
|
+
this.builder = new ScenarioBuilder(opts.llm, this.root);
|
|
45
|
+
this.tools = new ToolRegistry(WHITELIST, { now: () => this.now() });
|
|
46
|
+
this.tools.register("load_targets", (a) => this.loadTargets(a.session));
|
|
47
|
+
this.tools.register("write_asset", (a) => this.writeAsset(a.session, a.rel, a.data));
|
|
48
|
+
this.tools.register("show_targets", (a) => this.showTargets(a.session));
|
|
49
|
+
this.tools.register("show_specs", (a) => this.showSpecs(a.session));
|
|
50
|
+
this.tools.register("show_report", (a) => this.showReport(a.session));
|
|
51
|
+
this.tools.register("advance_phase", (a) => { throw new Error("advance_phase 走 orchestrator advance 动作"); });
|
|
52
|
+
this.tools.register("propose_knowledge_delta", (a) => this.proposeKnowledge(a));
|
|
53
|
+
this.tools.register("revise_targets", (a) => this.confirmTargets(a.session, { drop: a.drop, feedback: a.feedback }));
|
|
54
|
+
}
|
|
55
|
+
_lastNow = 0;
|
|
56
|
+
// ①b 需求确认:drop 按展示编号删除(确定性),feedback 走 LLM 修订。返回人读摘要。
|
|
57
|
+
async confirmTargets(session, o) {
|
|
58
|
+
let targets = this.loadTargets(session);
|
|
59
|
+
if (o.drop?.length) {
|
|
60
|
+
const kept = targets.filter((_, i) => !o.drop.includes(i + 1));
|
|
61
|
+
if (!kept.length)
|
|
62
|
+
return "不能删光所有目标——至少保留一个,或用调整要求重写。";
|
|
63
|
+
targets = kept;
|
|
64
|
+
session.event(this.workspace, "targets_dropped", { dropped: o.drop, remain: targets.length }, this.now());
|
|
65
|
+
}
|
|
66
|
+
if (o.feedback) {
|
|
67
|
+
targets = await this.builder.reviseTargets(targets, o.feedback, session.taskType);
|
|
68
|
+
session.event(this.workspace, "targets_revised", { feedback: o.feedback, count: targets.length }, this.now());
|
|
69
|
+
}
|
|
70
|
+
this.writeTargets(session, targets);
|
|
71
|
+
markChanged(session.home(this.workspace), "rule_model", "用户修订了核查目标或规则映射", this.now());
|
|
72
|
+
session.event(this.workspace, "assets_invalidated", { source: "rule_model", reason: "targets revised" }, this.now());
|
|
73
|
+
return `已修订为 ${targets.length} 个目标:\n` + this.showTargets(session).split("\n").slice(1).join("\n");
|
|
74
|
+
}
|
|
75
|
+
now() {
|
|
76
|
+
this._lastNow = Math.max(Date.now() / 1000, this._lastNow + 1e-4);
|
|
77
|
+
return this._lastNow;
|
|
78
|
+
}
|
|
79
|
+
ledgerSummary() { return { ...this.ledger.summary() }; }
|
|
80
|
+
// ---- Commander 自主循环能力面 -----------------------------------------
|
|
81
|
+
// 能力是领域原语,不是控制流。Commander 每轮看盘上状态后自行选择、执行、再观察。
|
|
82
|
+
capabilities(session) {
|
|
83
|
+
const state = this.stateSnapshot(session);
|
|
84
|
+
const reflections = Number(state.reflections ?? 0);
|
|
85
|
+
const maxReflect = Number(this.policy.loop?.max_reflect_iterations ?? 3);
|
|
86
|
+
const evalMet = state.evaluation?.met;
|
|
87
|
+
const lastCapability = String(state.last_capability ?? "");
|
|
88
|
+
const recommended = !state.rule_clauses ? "extract_rule_clauses"
|
|
89
|
+
: !state.observations ? "observe_examples"
|
|
90
|
+
: !state.checkskills?.current ? "synthesize_checkskills"
|
|
91
|
+
: !state.workflow ? "compose_workflow"
|
|
92
|
+
: !state.report || lastCapability === "diagnose_attempt" ? "evaluate_attempt"
|
|
93
|
+
: evalMet === false && reflections < maxReflect ? "diagnose_attempt"
|
|
94
|
+
: "";
|
|
95
|
+
return [
|
|
96
|
+
{ name: "extract_rule_clauses", description: "extractRules(Regulation) → RuleClause[] + VerificationTarget[]", ready: true, recommended: recommended === "extract_rule_clauses" },
|
|
97
|
+
{ name: "observe_examples", description: "从样本文档观察真实章节与取证锚点,可在合成前后重跑", ready: state.targets, reason: state.targets ? "已有目标对象" : "需要 RuleClause/Target", recommended: recommended === "observe_examples" },
|
|
98
|
+
{ name: "synthesize_checkskills", description: "按 rule_kind 选择 Method,执行 synthesizeSkill(Target) → CheckSkill", ready: state.rule_clauses, reason: state.rule_clauses ? "RuleClause 已实例化" : "需要先提取 RuleClause", recommended: recommended === "synthesize_checkskills" },
|
|
99
|
+
{ name: "compose_workflow", description: "composeWorkflow(CheckSkill[]) → 候选 Workflow,并保留技能绑定", ready: Boolean(state.checkskills?.current), reason: state.checkskills?.current ? "CheckSkill 与当前目标一致" : "需先合成最新 CheckSkill", recommended: recommended === "compose_workflow" },
|
|
100
|
+
{ name: "evaluate_attempt", description: "在独立 judge 中评测当前 attempt,写 trace/grounding/场景快照", ready: Boolean(state.checkskills?.current && state.workflow), reason: state.workflow ? "Workflow 已装配" : "需要 CheckSkill 与 Workflow", recommended: recommended === "evaluate_attempt" },
|
|
101
|
+
{ name: "diagnose_attempt", description: "读取失败 attempt/trace,判断修改 CheckSkill 还是升级 Method", ready: Boolean(state.report && evalMet === false && reflections < maxReflect), reason: evalMet === false ? `评测未达标,可诊断 ${maxReflect - reflections} 轮` : "仅在失败 attempt 后可用", recommended: recommended === "diagnose_attempt" },
|
|
102
|
+
];
|
|
103
|
+
}
|
|
104
|
+
async executeCapability(session, name) {
|
|
105
|
+
return await createVerificationActionRegistry(this, session).execute(name, {}, { agent: this, session });
|
|
106
|
+
}
|
|
107
|
+
/** 仅供 DomainActionRegistry 的受控 handler 调用;外部必须走 executeCapability。 */
|
|
108
|
+
async executeCapabilityUnchecked(session, name) {
|
|
109
|
+
if (name === "extract_rule_clauses")
|
|
110
|
+
return this.runNamedBuildCapability(session, "analyze", "已实例化 Regulation、RuleClause 与 VerificationTarget");
|
|
111
|
+
if (name === "observe_examples")
|
|
112
|
+
return this.runNamedBuildCapability(session, "observe", "已从样本观察取证锚点;旧 CheckSkill 如有将自动标为 stale");
|
|
113
|
+
if (name === "synthesize_checkskills") {
|
|
114
|
+
await this.synthesizeCheckSkills(session);
|
|
115
|
+
session.phase = this.deriveLifecycle(session);
|
|
116
|
+
session.save(this.workspace, this.now());
|
|
117
|
+
return { observation: "已按 rule_kind 选择 Method 并合成带完整血缘的 CheckSkill", changed: true };
|
|
118
|
+
}
|
|
119
|
+
if (name === "compose_workflow")
|
|
120
|
+
return this.runNamedBuildCapability(session, "propose", "已由当前 CheckSkill 集合装配候选 Workflow");
|
|
121
|
+
if (name === "evaluate_attempt") {
|
|
122
|
+
session.phase = "test";
|
|
123
|
+
const ev = await this.selfEvaluate(session);
|
|
124
|
+
session.event(this.workspace, "commander_self_eval", { met: ev.met, summary: ev.summary }, this.now());
|
|
125
|
+
if (ev.met === true || ev.met === null)
|
|
126
|
+
session.phase = "freeze";
|
|
127
|
+
session.save(this.workspace, this.now());
|
|
128
|
+
return { observation: ev.summary, changed: true };
|
|
129
|
+
}
|
|
130
|
+
if (name === "diagnose_attempt") {
|
|
131
|
+
session.phase = "test";
|
|
132
|
+
const rr = await this.reflect(session);
|
|
133
|
+
if (rr.revised)
|
|
134
|
+
markChanged(session.home(this.workspace), "rule_model", "失败归因后修订了目标", this.now());
|
|
135
|
+
session.event(this.workspace, "commander_reflect", { revised: rr.revised, note: rr.note }, this.now());
|
|
136
|
+
session.save(this.workspace, this.now());
|
|
137
|
+
return { observation: rr.revised ? `已按 trace 归因修订目标级 CheckSkill 输入:${rr.note};需重新合成后再评测` : `诊断未找到目标级修订项:${rr.note};应考虑升级对应 Method`, changed: rr.revised };
|
|
138
|
+
}
|
|
139
|
+
throw new Error(`未注册构建能力 ${name}`);
|
|
140
|
+
}
|
|
141
|
+
stateSnapshot(session) {
|
|
142
|
+
const home = session.home(this.workspace);
|
|
143
|
+
const has = (rel) => existsSync(join(home, rel));
|
|
144
|
+
const report = has("build/test_report.json") ? this.readTestReport(session) : null;
|
|
145
|
+
const planPath = join(home, ".session", "agent-plan.json");
|
|
146
|
+
let plan = null;
|
|
147
|
+
try {
|
|
148
|
+
if (existsSync(planPath))
|
|
149
|
+
plan = JSON.parse(readFileSync(planPath, "utf-8"));
|
|
150
|
+
}
|
|
151
|
+
catch { /* ignore malformed observability file */ }
|
|
152
|
+
const capabilitySteps = (plan?.steps ?? []).filter((s) => s.status === "done" && s.action?.op === "capability");
|
|
153
|
+
const lastCapability = capabilitySteps.at(-1)?.action?.capability ?? "";
|
|
154
|
+
const met = report ? this.meetsStandard(report) : undefined;
|
|
155
|
+
const stale = loadStale(home).entries;
|
|
156
|
+
let skillIndex = null;
|
|
157
|
+
try {
|
|
158
|
+
if (has("build/checkskills/index.json"))
|
|
159
|
+
skillIndex = JSON.parse(readFileSync(join(home, "build", "checkskills", "index.json"), "utf-8"));
|
|
160
|
+
}
|
|
161
|
+
catch { /* malformed = stale */ }
|
|
162
|
+
let currentDigest = "";
|
|
163
|
+
try {
|
|
164
|
+
if (has("build/targets.json"))
|
|
165
|
+
currentDigest = targetDigest(this.loadTargets(session));
|
|
166
|
+
}
|
|
167
|
+
catch { /* incomplete target set */ }
|
|
168
|
+
const attempts = existsSync(join(home, "build")) ? readdirSync(join(home, "build")).filter((n) => /^attempt-\d+$/.test(n)).sort() : [];
|
|
169
|
+
return {
|
|
170
|
+
lifecycle: session.phase,
|
|
171
|
+
ontology_version: (() => { try {
|
|
172
|
+
return loadOntology(this.root).version;
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
return "";
|
|
176
|
+
} })(),
|
|
177
|
+
targets: has("build/targets.json"),
|
|
178
|
+
rule_clauses: has("build/rule_clauses.json"),
|
|
179
|
+
observations: has("build/observations.json"),
|
|
180
|
+
checkskills: skillIndex ? { count: Number(skillIndex.count ?? 0), current: skillIndex.target_digest === currentDigest && !stale.checkskills, target_digest: currentDigest, methods: skillIndex.methods ?? [], stale: stale.checkskills ?? null } : null,
|
|
181
|
+
workflow: has("build/specs.json") && has("build/proposals.json") && has("build/workflow_manifest.json") && !stale.workflow,
|
|
182
|
+
specs: has("build/specs.json") && has("build/proposals.json"),
|
|
183
|
+
report: Boolean(report) && !stale.evaluation,
|
|
184
|
+
evaluation: report ? { met, oracle: Boolean(report.oracle), winner: report.winner ?? "", current: !stale.evaluation, stale: stale.evaluation ?? null } : null,
|
|
185
|
+
grounding: has("build/cross_check.json") ? JSON.parse(readFileSync(join(home, "build", "cross_check.json"), "utf-8")) : null,
|
|
186
|
+
reflections: capabilitySteps.filter((s) => s.action.capability === "diagnose_attempt").length,
|
|
187
|
+
last_capability: lastCapability,
|
|
188
|
+
attempts: { count: attempts.length, latest: attempts.at(-1) ?? "" },
|
|
189
|
+
traces: has("build/eval/history.jsonl") ? readFileSync(join(home, "build", "eval", "history.jsonl"), "utf-8").split("\n").filter(Boolean).length : 0,
|
|
190
|
+
budget: this.ledgerSummary(),
|
|
191
|
+
stale,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
promotionState(session) {
|
|
195
|
+
if (session.phase === "frozen")
|
|
196
|
+
return { ready: false, reason: "生产线已经冻结" };
|
|
197
|
+
const home = session.home(this.workspace);
|
|
198
|
+
const stale = loadStale(home).entries;
|
|
199
|
+
// pack stale 表示“旧冻结包需要重编”,它本身不应阻止当前候选进入冻结门;
|
|
200
|
+
// 上游 checkskills/workflow/evaluation stale 才是晋升硬阻断。
|
|
201
|
+
const staleOrder = ["checkskills", "workflow", "evaluation"];
|
|
202
|
+
const firstStale = staleOrder.find((name) => stale[name]);
|
|
203
|
+
if (firstStale)
|
|
204
|
+
return { ready: false, reason: `${firstStale} 已失效:${stale[firstStale].reason}` };
|
|
205
|
+
if (!existsSync(join(home, "build", "targets.json")))
|
|
206
|
+
return { ready: false, reason: "缺 VerificationTarget" };
|
|
207
|
+
const digest = targetDigest(this.loadTargets(session));
|
|
208
|
+
if (!this.checkSkillsCurrent(session))
|
|
209
|
+
return { ready: false, reason: "CheckSkill 缺失或已 stale,需按 Method 重新合成" };
|
|
210
|
+
const wfPath = join(home, "build", "workflow_manifest.json");
|
|
211
|
+
if (!existsSync(wfPath))
|
|
212
|
+
return { ready: false, reason: "缺 Workflow manifest" };
|
|
213
|
+
const wf = JSON.parse(readFileSync(wfPath, "utf-8"));
|
|
214
|
+
if (wf.target_digest !== digest)
|
|
215
|
+
return { ready: false, reason: "Workflow 绑定的是旧 CheckSkill 集合,需重新 compose" };
|
|
216
|
+
const reportPath = join(home, "build", "test_report.json");
|
|
217
|
+
if (!existsSync(reportPath))
|
|
218
|
+
return { ready: false, reason: "缺测试报告" };
|
|
219
|
+
const attempts = readdirSync(join(home, "build")).filter((n) => /^attempt-\d+$/.test(n)).sort();
|
|
220
|
+
const latest = attempts.at(-1);
|
|
221
|
+
if (!latest)
|
|
222
|
+
return { ready: false, reason: "缺可复现 attempt 快照" };
|
|
223
|
+
const attempt = JSON.parse(readFileSync(join(home, "build", latest, "attempt.json"), "utf-8"));
|
|
224
|
+
if (attempt.target_digest !== digest)
|
|
225
|
+
return { ready: false, reason: "测试报告属于旧 target digest,必须重评当前资产" };
|
|
226
|
+
const report = this.readTestReport(session);
|
|
227
|
+
const met = this.meetsStandard(report);
|
|
228
|
+
if (met === false)
|
|
229
|
+
return { ready: false, reason: "自评未达到 policy 门槛,必须反思后重评" };
|
|
230
|
+
const ccPath = join(home, "build", "cross_check.json");
|
|
231
|
+
if (existsSync(ccPath)) {
|
|
232
|
+
const cc = JSON.parse(readFileSync(ccPath, "utf-8"));
|
|
233
|
+
if ((cc.grounding_flags ?? []).length)
|
|
234
|
+
return { ready: false, reason: "grounding 复验有未清零标记" };
|
|
235
|
+
}
|
|
236
|
+
const note = met === null ? "无 oracle,无法自动判达标,需人工评估" : "自评达标且 grounding 干净";
|
|
237
|
+
return { ready: true, reason: note, summary: `${note}\n${this.showReport(session)}\n批准冻结仍是人工专属动作。` };
|
|
238
|
+
}
|
|
239
|
+
async runNamedBuildCapability(session, phase, observation) {
|
|
240
|
+
session.phase = phase;
|
|
241
|
+
await this.runPhase(session);
|
|
242
|
+
const missing = reconcile(session.home(this.workspace), phase, session.artifacts[phase] ?? []);
|
|
243
|
+
if (missing.length)
|
|
244
|
+
throw new Error(`能力 ${phase} 声称完成但磁盘缺产物:${missing.join("、")}`);
|
|
245
|
+
session.phase = this.deriveLifecycle(session);
|
|
246
|
+
session.event(this.workspace, "commander_capability_completed", { capability: phase, next: session.phase }, this.now());
|
|
247
|
+
session.save(this.workspace, this.now());
|
|
248
|
+
return { observation, changed: true };
|
|
249
|
+
}
|
|
250
|
+
deriveLifecycle(session) {
|
|
251
|
+
const home = session.home(this.workspace);
|
|
252
|
+
if (!existsSync(join(home, "build", "targets.json")))
|
|
253
|
+
return "analyze";
|
|
254
|
+
if (!existsSync(join(home, "build", "observations.json")))
|
|
255
|
+
return "observe";
|
|
256
|
+
if (!existsSync(join(home, "build", "checkskills", "index.json")))
|
|
257
|
+
return "propose";
|
|
258
|
+
if (!(existsSync(join(home, "build", "specs.json")) && existsSync(join(home, "build", "workflow_manifest.json"))))
|
|
259
|
+
return "propose";
|
|
260
|
+
return "test";
|
|
261
|
+
}
|
|
262
|
+
// ---- 会话 --------------------------------------------------------------
|
|
263
|
+
start(o) {
|
|
264
|
+
const state = join(this.workspace, o.scenarioKey, ".session", "state.json");
|
|
265
|
+
if (existsSync(state))
|
|
266
|
+
throw new Error(`会话已存在:${state}`);
|
|
267
|
+
const s = new Session({
|
|
268
|
+
scenarioKey: o.scenarioKey, goal: o.goal, taskType: o.taskType ?? "predicate",
|
|
269
|
+
inputs: { rules: o.rules, samples: o.samples, oracle: o.oracle ?? "" },
|
|
270
|
+
createdAt: this.now(), lastProgressAt: this.now(),
|
|
271
|
+
});
|
|
272
|
+
s.save(this.workspace, this.now());
|
|
273
|
+
s.event(this.workspace, "session_started", { goal: o.goal }, this.now());
|
|
274
|
+
return s;
|
|
275
|
+
}
|
|
276
|
+
resume(scenarioKey) { return Session.load(this.workspace, scenarioKey); }
|
|
277
|
+
// ---- 旧相位调度(兼容 advanceOnePhase / freeze,不是 Commander 控制流)---
|
|
278
|
+
async runPhase(session) {
|
|
279
|
+
this.curSession = session;
|
|
280
|
+
const impl = {
|
|
281
|
+
analyze: () => this.analyze(session), observe: () => this.observe(session),
|
|
282
|
+
propose: () => this.propose(session), test: () => this.test(session), freeze: () => this.freeze(session),
|
|
283
|
+
};
|
|
284
|
+
await impl[session.phase]();
|
|
285
|
+
for (const line of this.builder.log)
|
|
286
|
+
session.event(this.workspace, "builder_log", { line }, this.now());
|
|
287
|
+
this.builder.log.length = 0;
|
|
288
|
+
}
|
|
289
|
+
// ---- 目标驱动循环能力(v2):自评 + 反思 ------------------------------
|
|
290
|
+
// 自评:跑一次评测体系(bakeoff/grounding,test() 内含隔离判卷),读报告判是否达标。
|
|
291
|
+
async selfEvaluate(session) {
|
|
292
|
+
await this.test(session);
|
|
293
|
+
const rep = this.readTestReport(session);
|
|
294
|
+
const met = this.meetsStandard(rep);
|
|
295
|
+
const row = (rep.ranking ?? []).find((r) => r.candidate === rep.winner) ?? (rep.ranking ?? [])[0];
|
|
296
|
+
const acc = row?.accuracy;
|
|
297
|
+
const bar = Number(this.policy.eval?.accuracy_bar ?? 0.9);
|
|
298
|
+
const summary = rep.oracle ? `准确率 ${acc ?? "?"}(门槛 ${bar})→ ${met ? "达标" : "未达标"}` : "无 oracle:评测体系无法自判达标,交人裁决";
|
|
299
|
+
return { met, summary };
|
|
300
|
+
}
|
|
301
|
+
// 反思:读评测体系的 per-target FN/FP 明细 → 逐目标归因 → 定向修改(漏报放宽取证 / 误报收紧+负向词)。
|
|
302
|
+
async reflect(session) {
|
|
303
|
+
const rep = this.readTestReport(session);
|
|
304
|
+
const wrep = rep.winner_report;
|
|
305
|
+
if (!wrep || !(wrep.mismatches ?? []).length) { // 无明细/无错判 → 无从定向改
|
|
306
|
+
session.event(this.workspace, "reflect_diagnose", { note: "无 winner 明细或无错判,改无可改" }, this.now());
|
|
307
|
+
return { revised: false, note: "无 winner 明细可归因" };
|
|
308
|
+
}
|
|
309
|
+
const oraclePath = session.inputs.oracle ?? "";
|
|
310
|
+
const oracle = oraclePath && existsSync(oraclePath) ? JSON.parse(readFileSync(oraclePath, "utf-8")) : undefined;
|
|
311
|
+
const targets = this.loadTargets(session);
|
|
312
|
+
const { revised, diagnoses } = reviseFromReport(targets, wrep, oracle);
|
|
313
|
+
if (revised)
|
|
314
|
+
this.writeTargets(session, targets);
|
|
315
|
+
this.writeAsset(session, "build/reflection.json", { diagnoses }); // 可观测:每目标归因+动作
|
|
316
|
+
for (const d of diagnoses)
|
|
317
|
+
session.event(this.workspace, "reflect_diagnose", { target: d.target_id, fn: d.fn, fp: d.fp, suspect: d.suspect, action: d.action }, this.now());
|
|
318
|
+
const note = diagnoses.map((d) => `${d.target_id}:${d.suspect}`).join(";") || "无定向修改";
|
|
319
|
+
return { revised, note };
|
|
320
|
+
}
|
|
321
|
+
meetsStandard(rep) {
|
|
322
|
+
if (!rep.oracle)
|
|
323
|
+
return null; // 无评测体系可判 → 交人
|
|
324
|
+
const row = (rep.ranking ?? []).find((r) => r.candidate === rep.winner) ?? (rep.ranking ?? [])[0];
|
|
325
|
+
const acc = row?.accuracy;
|
|
326
|
+
if (acc == null)
|
|
327
|
+
return null;
|
|
328
|
+
return Number(acc) >= Number(this.policy.eval?.accuracy_bar ?? 0.9);
|
|
329
|
+
}
|
|
330
|
+
readTestReport(session) {
|
|
331
|
+
const p = join(session.home(this.workspace), "build", "test_report.json");
|
|
332
|
+
return existsSync(p) ? JSON.parse(readFileSync(p, "utf-8")) : { oracle: false, ranking: [] };
|
|
333
|
+
}
|
|
334
|
+
async analyze(session) {
|
|
335
|
+
let rulesText = readFileSync(session.inputs.rules, "utf-8");
|
|
336
|
+
const base = basePackContext(this.root, Number(this.policy.context?.project_memory_cap_kb ?? 5));
|
|
337
|
+
if (base) { // 领域背景包:术语参考,不是规则本身
|
|
338
|
+
rulesText = `【领域背景参考——帮助理解术语,本身不是待拆解的规则】\n${base}\n\n【规则文本】\n${rulesText}`;
|
|
339
|
+
session.event(this.workspace, "base_packs_injected", { chars: base.length }, this.now());
|
|
340
|
+
}
|
|
341
|
+
const chunkChars = Number(this.policy.build?.rule_chunk_chars ?? 3000);
|
|
342
|
+
const targets = await this.builder.analyzeRules(rulesText, session.taskType, 8, chunkChars);
|
|
343
|
+
const regulationId = basename(session.inputs.rules);
|
|
344
|
+
for (const t of targets) {
|
|
345
|
+
const prov = t.meta.provenance ??= {};
|
|
346
|
+
prov.regulation_id = regulationId;
|
|
347
|
+
prov.source = session.inputs.rules;
|
|
348
|
+
t.meta.rule_clause_id = `${regulationId}:${t.target_id}`;
|
|
349
|
+
t.meta.rule_kind = inferRuleKind(t);
|
|
350
|
+
}
|
|
351
|
+
const chunks = this.builder.lastChunks;
|
|
352
|
+
this.writeAsset(session, "build/chunking.json", { chunk_chars: chunkChars, n_chunks: chunks.length, chunks: chunks.map((c) => ({ chunk_id: c.chunk_id, section: c.section, chars: c.text.length })) });
|
|
353
|
+
if (!existsSync(join(session.home(this.workspace), "build", "schema_profile.json"))) {
|
|
354
|
+
this.writeAsset(session, "build/schema_profile.json", defaultSchemaProfile(session.taskType));
|
|
355
|
+
}
|
|
356
|
+
this.writeTargets(session, targets);
|
|
357
|
+
this.writeAsset(session, "build/rule_clauses.json", {
|
|
358
|
+
regulation: { regulation_id: regulationId, source_file: session.inputs.rules },
|
|
359
|
+
clauses: targets.map((t) => ({
|
|
360
|
+
clause_id: t.meta.rule_clause_id,
|
|
361
|
+
source_ref: { regulation_id: regulationId, article: t.meta.provenance?.section ?? t.meta.provenance?.chunk_id },
|
|
362
|
+
text: t.meta.provenance?.source_quote || t.judge.rubric_text || "",
|
|
363
|
+
rule_kind: t.meta.rule_kind || "unclassified",
|
|
364
|
+
target_ref: t.target_id,
|
|
365
|
+
})),
|
|
366
|
+
});
|
|
367
|
+
markChanged(session.home(this.workspace), "rule_model", "规则条款和核查目标已重新实例化", this.now());
|
|
368
|
+
}
|
|
369
|
+
async observe(session) {
|
|
370
|
+
let targets = this.loadTargets(session);
|
|
371
|
+
const samples = this.readSamples(session.inputs.samples);
|
|
372
|
+
targets = this.builder.observeSamples(targets, samples);
|
|
373
|
+
this.writeTargets(session, targets);
|
|
374
|
+
this.writeAsset(session, "build/observations.json", { sample_count: samples.length, enriched: targets.filter((t) => (t.retrieval.chapter_keywords ?? []).length).map((t) => t.target_id) });
|
|
375
|
+
this.research(session, targets); // researcher:查领域知识丰富理解(回流)
|
|
376
|
+
markChanged(session.home(this.workspace), "observations", "样本观察或领域知识引用已更新", this.now());
|
|
377
|
+
}
|
|
378
|
+
// ② researcher 知识注入(回流):查领域知识库,综述喂 refine,命中概念挂 target ---
|
|
379
|
+
research(session, targets) {
|
|
380
|
+
const kbRoot = this.knowledgeRoot();
|
|
381
|
+
if (!kbRoot)
|
|
382
|
+
return;
|
|
383
|
+
const kb = new Knowledge(kbRoot);
|
|
384
|
+
const terms = [];
|
|
385
|
+
for (const t of targets)
|
|
386
|
+
terms.push(...(t.retrieval.content_keywords ?? []).slice(0, 3));
|
|
387
|
+
const query = [...new Set(terms)].join(" ") || session.goal;
|
|
388
|
+
const hits = kb.retrieve(query, { limit: 5 });
|
|
389
|
+
const entries = hits.map((e) => ({ id: e.id, kind: e.kind, scope: e.scope, title: e.title, source: e.source, score: e.score, excerpt: e.text.slice(0, 500) }));
|
|
390
|
+
this.writeAsset(session, "build/research.json", { query, n: entries.length, entries });
|
|
391
|
+
// 回流①:综述喂 refine 的 rubric 修订
|
|
392
|
+
this.builder.domainContext = entries.slice(0, 4).map((e) => `[${e.kind}] ${e.title}:${e.excerpt.slice(0, 200)}`).join("\n");
|
|
393
|
+
// 回流②:命中概念挂到关键词相关 target(知识↔规则连接)
|
|
394
|
+
for (const t of targets) {
|
|
395
|
+
const kws = new Set(t.retrieval.content_keywords ?? []);
|
|
396
|
+
const refs = entries.filter((e) => [...kws].some((k) => (e.title + e.excerpt).includes(k))).slice(0, 5).map((e) => ({ id: e.id, kind: e.kind, source: e.source }));
|
|
397
|
+
if (refs.length)
|
|
398
|
+
t.meta.knowledge_refs = refs;
|
|
399
|
+
}
|
|
400
|
+
this.writeTargets(session, targets);
|
|
401
|
+
session.event(this.workspace, "research_done", { query, hits: entries.length }, this.now());
|
|
402
|
+
}
|
|
403
|
+
knowledgeRoot() {
|
|
404
|
+
const proj = join(this.root, "knowledge");
|
|
405
|
+
if (existsSync(join(proj, "domain")))
|
|
406
|
+
return proj;
|
|
407
|
+
// 回落包内知识库(check 包目录的 ../knowledge,若存在)
|
|
408
|
+
try {
|
|
409
|
+
const here = fileURLToPath(new URL(".", import.meta.url));
|
|
410
|
+
const pkg = join(here, "..", "knowledge");
|
|
411
|
+
if (existsSync(join(pkg, "domain")))
|
|
412
|
+
return pkg;
|
|
413
|
+
}
|
|
414
|
+
catch { /* ignore */ }
|
|
415
|
+
return null;
|
|
416
|
+
}
|
|
417
|
+
async propose(session) {
|
|
418
|
+
if (!this.checkSkillsCurrent(session))
|
|
419
|
+
await this.synthesizeCheckSkills(session); // legacy 五步适配器:确保依赖对象存在
|
|
420
|
+
const specs = this.builder.proposeSpecs(session.taskType, session.scenarioKey);
|
|
421
|
+
const rationale = {};
|
|
422
|
+
for (const name of Object.keys(specs)) {
|
|
423
|
+
const bias = name.endsWith("_wide") ? "recall_first" : "cost_first";
|
|
424
|
+
rationale[name] = { proposer: bias, rationale: bias === "recall_first" ? "召回优先:宽证据窗口,防漏召" : "成本优先:窄证据窗口,省 LLM 输入" };
|
|
425
|
+
}
|
|
426
|
+
this.writeAsset(session, "build/specs.json", Object.fromEntries(Object.entries(specs).map(([n, s]) => [n, specDict(s)])));
|
|
427
|
+
this.writeAsset(session, "build/proposals.json", { n_proposers: Object.keys(specs).length, candidates: rationale, arbitration: "胜负由 bake-off 沙箱分数定,不由说服力定" });
|
|
428
|
+
const idx = JSON.parse(readFileSync(join(session.home(this.workspace), "build", "checkskills", "index.json"), "utf-8"));
|
|
429
|
+
this.writeAsset(session, "build/workflow_manifest.json", {
|
|
430
|
+
function: "composeWorkflow",
|
|
431
|
+
input_checkskills: idx.skills,
|
|
432
|
+
target_digest: idx.target_digest,
|
|
433
|
+
candidates: Object.keys(specs),
|
|
434
|
+
note: "Workflow 只编排 CheckSkill,不内嵌规则判据;判据与 Method 血缘留在 skill.json。",
|
|
435
|
+
});
|
|
436
|
+
markChanged(session.home(this.workspace), "workflow", "Workflow 已重新编排", this.now());
|
|
437
|
+
clearStale(session.home(this.workspace), "workflow");
|
|
438
|
+
}
|
|
439
|
+
/** v5 三寄存器真正接线:Ontology 对象实例 → Method 选择 → CheckSkill 数据资产。 */
|
|
440
|
+
async synthesizeCheckSkills(session) {
|
|
441
|
+
const targets = this.loadTargets(session);
|
|
442
|
+
const ontology = loadOntology(this.root);
|
|
443
|
+
const methods = loadMethods(this.root);
|
|
444
|
+
await this.applyMethodRecipes(targets, methods);
|
|
445
|
+
const domain = instantiateDomain(targets, methods, {
|
|
446
|
+
ontologyVersion: ontology.version,
|
|
447
|
+
regulationPath: session.inputs.rules,
|
|
448
|
+
lifecycle: "draft",
|
|
449
|
+
});
|
|
450
|
+
this.writeAsset(session, "build/domain/unresolved.json", { unresolved: domain.unresolved });
|
|
451
|
+
if (domain.unresolved.length) {
|
|
452
|
+
throw new Error("Method 选择失败:" + domain.unresolved.map((x) => `${x.target_id}(${x.rule_kind})`).join("、"));
|
|
453
|
+
}
|
|
454
|
+
const graph = new ScenarioOntologyStore(session.home(this.workspace), ontology).replace(domain.objects, domain.links, "synthesize_checkskills");
|
|
455
|
+
this.writeTargets(session, targets); // 持久化 rule_kind / built_by_method / function_ref
|
|
456
|
+
this.writeAsset(session, "build/domain/objects.json", { ontology_version: ontology.version, objects: domain.objects });
|
|
457
|
+
this.writeAsset(session, "build/domain/links.json", { ontology_version: ontology.version, links: domain.links });
|
|
458
|
+
this.writeAsset(session, "build/method_selection.json", { selections: domain.method_selection });
|
|
459
|
+
for (const [targetId, skill] of Object.entries(domain.checkskills)) {
|
|
460
|
+
this.writeAsset(session, `build/checkskills/${targetId}/skill.json`, skill);
|
|
461
|
+
}
|
|
462
|
+
this.writeAsset(session, "build/checkskills/index.json", {
|
|
463
|
+
ontology_version: ontology.version,
|
|
464
|
+
target_digest: domain.target_digest,
|
|
465
|
+
count: Object.keys(domain.checkskills).length,
|
|
466
|
+
skills: Object.keys(domain.checkskills).map((id) => ({ target_id: id, path: `checkskills/${id}/skill.json` })),
|
|
467
|
+
methods: [...new Set(domain.method_selection.map((s) => s.method_ref))],
|
|
468
|
+
graph_revision: graph.revision,
|
|
469
|
+
});
|
|
470
|
+
markChanged(session.home(this.workspace), "checkskills", "CheckSkill 集合已重新合成", this.now());
|
|
471
|
+
clearStale(session.home(this.workspace), "checkskills");
|
|
472
|
+
session.event(this.workspace, "checkskills_synthesized", { count: domain.method_selection.length, methods: [...new Set(domain.method_selection.map((s) => s.method_ref))] }, this.now());
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* Method 是构建期 prompt/program,不是标签。真实模型读取正文来生成目标级参数;
|
|
476
|
+
* 输出仍被字段白名单收口,Method 指定的 function_ref 由代码盖章,之后还必须过 eval。
|
|
477
|
+
*/
|
|
478
|
+
async applyMethodRecipes(targets, methods) {
|
|
479
|
+
for (const t of targets) {
|
|
480
|
+
const ruleKind = inferRuleKind(t);
|
|
481
|
+
const method = selectMethod(methods, ruleKind);
|
|
482
|
+
if (!method)
|
|
483
|
+
continue;
|
|
484
|
+
const system = [
|
|
485
|
+
"你在执行一个受控 Method,把 RuleClause/VerificationTarget 合成为 CheckSkill 草稿。",
|
|
486
|
+
"严格遵守下方 Method;只输出 JSON,可给 retrieval、judge、applicability 三个对象。",
|
|
487
|
+
"不得编造规则事实;无法从输入确定的字段保持原值。function_ref 不由你决定。",
|
|
488
|
+
`【Method ${methodRef(method)}】\n${method.body}`,
|
|
489
|
+
].join("\n\n");
|
|
490
|
+
let out = {};
|
|
491
|
+
try {
|
|
492
|
+
out = await this.llm.completeJson(system, JSON.stringify({
|
|
493
|
+
rule_kind: ruleKind,
|
|
494
|
+
clause: { id: t.meta.rule_clause_id, source: t.meta.provenance },
|
|
495
|
+
target: targetJson(t),
|
|
496
|
+
}));
|
|
497
|
+
}
|
|
498
|
+
catch (e) {
|
|
499
|
+
this.builder.log.push(`Method ${methodRef(method)} 模型编译失败,保留确定性草稿:${e.message}`);
|
|
500
|
+
}
|
|
501
|
+
if (out.retrieval && typeof out.retrieval === "object") {
|
|
502
|
+
for (const k of ["content_keywords", "chapter_keywords", "negative_keywords"]) {
|
|
503
|
+
if (Array.isArray(out.retrieval[k]))
|
|
504
|
+
t.retrieval[k] = out.retrieval[k].map(String).filter(Boolean);
|
|
505
|
+
}
|
|
506
|
+
if (Number.isFinite(Number(out.retrieval.limit)))
|
|
507
|
+
t.retrieval.limit = Number(out.retrieval.limit);
|
|
508
|
+
}
|
|
509
|
+
if (out.judge && typeof out.judge === "object") {
|
|
510
|
+
for (const k of ["kind", "strategy", "rubric_text"])
|
|
511
|
+
if (typeof out.judge[k] === "string" && out.judge[k].trim())
|
|
512
|
+
t.judge[k] = out.judge[k];
|
|
513
|
+
if (out.judge.constraint && typeof out.judge.constraint === "object")
|
|
514
|
+
t.judge.constraint = out.judge.constraint;
|
|
515
|
+
}
|
|
516
|
+
if (out.applicability && typeof out.applicability === "object" && !Array.isArray(out.applicability))
|
|
517
|
+
t.applicability = out.applicability;
|
|
518
|
+
t.meta.method_compilation = { method_ref: methodRef(method), source: method.source, model_applied: Object.keys(out).length > 0 };
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
checkSkillsCurrent(session) {
|
|
522
|
+
if (isStale(session.home(this.workspace), "checkskills"))
|
|
523
|
+
return false;
|
|
524
|
+
const p = join(session.home(this.workspace), "build", "checkskills", "index.json");
|
|
525
|
+
if (!existsSync(p))
|
|
526
|
+
return false;
|
|
527
|
+
try {
|
|
528
|
+
return JSON.parse(readFileSync(p, "utf-8")).target_digest === targetDigest(this.loadTargets(session));
|
|
529
|
+
}
|
|
530
|
+
catch {
|
|
531
|
+
return false;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
async test(session) {
|
|
535
|
+
const home = session.home(this.workspace);
|
|
536
|
+
const targets = this.loadTargets(session);
|
|
537
|
+
const specs = this.loadSpecs(session);
|
|
538
|
+
const oraclePath = session.inputs.oracle ?? "";
|
|
539
|
+
const evalCfg = this.policy.eval ?? {};
|
|
540
|
+
const bar = Number(evalCfg.accuracy_bar ?? 0.9);
|
|
541
|
+
const report = { oracle: Boolean(oraclePath), ranking: [], accuracy_bar: bar };
|
|
542
|
+
if (oraclePath && existsSync(oraclePath)) {
|
|
543
|
+
const packDir = join(home, "build", "pack");
|
|
544
|
+
this.builder.writePack(packDir, session.scenarioKey, targets, specs);
|
|
545
|
+
const oracle = JSON.parse(readFileSync(oraclePath, "utf-8"));
|
|
546
|
+
// ④ bake-off 判卷在独立进程跑(I6 判卷≠做题);失败降级进程内
|
|
547
|
+
const rep = await this.bakeoffIsolated(session, packDir, oraclePath, bar, evalCfg.winner_metric ?? "accuracy");
|
|
548
|
+
session.winner = rep.winner ?? "";
|
|
549
|
+
Object.assign(report, { ranking: rep.ranking, winner: session.winner, eval_isolation: rep.eval_isolation, winner_report: rep.winner_report ?? null });
|
|
550
|
+
// 冻结从草稿生成,写回最终包(含 schema_profile 与 oracle)
|
|
551
|
+
this.builder.writePack(packDir, session.scenarioKey, targets, specs, { winner: session.winner, oracle, schemaProfile: this.schemaProfile(session) });
|
|
552
|
+
// judge 只判卷,不在同一动作里改 doer 资产。未达标时 Commander 另选 diagnose_attempt。
|
|
553
|
+
report.refine_history = [];
|
|
554
|
+
const refined = loadPack(packDir).targets;
|
|
555
|
+
// 评审页占位(就绪门要求存在)—— L1 沙箱:只能写进 build home 内(越界 fail-closed)
|
|
556
|
+
new Sandbox(home).safeWrite(join(home, "build", "review.html"), `<h1>review · ${session.scenarioKey}</h1>`);
|
|
557
|
+
session.recordArtifact("build/review.html", this.now());
|
|
558
|
+
// ④ 对抗 grounding 复验:胜者证据是否真在原文(防幻觉证据溜进冻结包)
|
|
559
|
+
await this.crossCheck(session, refined, packDir, oracle);
|
|
560
|
+
}
|
|
561
|
+
else {
|
|
562
|
+
session.winner = Object.keys(specs)[0];
|
|
563
|
+
Object.assign(report, { winner: session.winner, warning: "无 oracle:Eval 门无法自动判达标,晋升只剩人工批准一道门。" });
|
|
564
|
+
}
|
|
565
|
+
this.writeAsset(session, "build/test_report.json", report);
|
|
566
|
+
this.snapshotAttempt(session, report);
|
|
567
|
+
markChanged(home, "evaluation", "当前资产已产生新的评测结果", this.now());
|
|
568
|
+
clearStale(home, "evaluation");
|
|
569
|
+
}
|
|
570
|
+
/** v3 attempt = 可复现沙箱 Scenario;每轮评测都保留输入、产物、分数和 trace 指针。 */
|
|
571
|
+
snapshotAttempt(session, report) {
|
|
572
|
+
const home = session.home(this.workspace);
|
|
573
|
+
const build = join(home, "build");
|
|
574
|
+
const nums = readdirSync(build).map((n) => /^attempt-(\d+)$/.exec(n)?.[1]).filter(Boolean).map(Number);
|
|
575
|
+
const n = (nums.length ? Math.max(...nums) : 0) + 1;
|
|
576
|
+
const name = `attempt-${String(n).padStart(3, "0")}`;
|
|
577
|
+
const out = join(build, name);
|
|
578
|
+
mkdirSync(out, { recursive: true });
|
|
579
|
+
for (const rel of ["targets.json", "rule_clauses.json", "observations.json", "method_selection.json", "specs.json", "proposals.json", "workflow_manifest.json", "test_report.json", "cross_check.json"]) {
|
|
580
|
+
const src = join(build, rel);
|
|
581
|
+
if (existsSync(src))
|
|
582
|
+
copyFileSync(src, join(out, rel));
|
|
583
|
+
}
|
|
584
|
+
for (const rel of ["checkskills", "domain", "pack", "eval"]) {
|
|
585
|
+
const src = join(build, rel);
|
|
586
|
+
if (existsSync(src))
|
|
587
|
+
cpSync(src, join(out, rel), { recursive: true });
|
|
588
|
+
}
|
|
589
|
+
writeFileSync(join(out, "attempt.json"), JSON.stringify({
|
|
590
|
+
attempt_id: name,
|
|
591
|
+
scenario: session.scenarioKey,
|
|
592
|
+
target_digest: targetDigest(this.loadTargets(session)),
|
|
593
|
+
winner: report.winner ?? "",
|
|
594
|
+
oracle: report.oracle ?? false,
|
|
595
|
+
created_at: this.now(),
|
|
596
|
+
immutable_snapshot: true,
|
|
597
|
+
}, null, 2), "utf-8");
|
|
598
|
+
session.recordArtifact(`build/${name}/attempt.json`, this.now());
|
|
599
|
+
session.event(this.workspace, "attempt_snapshot", { attempt: name, winner: report.winner ?? "" }, this.now());
|
|
600
|
+
}
|
|
601
|
+
// ④ bake-off 判卷隔离(I6):独立 node 进程跑,与出方案/改资产的主 agent 物理分开;失败降级内联。
|
|
602
|
+
async bakeoffIsolated(session, packDir, oraclePath, bar, winnerMetric) {
|
|
603
|
+
const home = session.home(this.workspace);
|
|
604
|
+
const history = join(home, "build", "eval", "history.jsonl");
|
|
605
|
+
const longrun = this.policy.longrun ?? {};
|
|
606
|
+
const brief = {
|
|
607
|
+
goal: `对 ${session.scenarioKey} 的候选流水线做 bake-off 打分,只认 oracle`,
|
|
608
|
+
inputs: [packDir, oraclePath],
|
|
609
|
+
output_schema: { winner: "str", ranking: "list", ledger: "dict" },
|
|
610
|
+
budget: { max_llm_calls: Number(this.policy.budget?.max_llm_calls_per_build ?? 500) },
|
|
611
|
+
items_declared: 2,
|
|
612
|
+
};
|
|
613
|
+
const workerScript = fileURLToPath(new URL("./subagents/worker.js", import.meta.url));
|
|
614
|
+
try {
|
|
615
|
+
const handle = spawnSubagent(join(home, ".session"), "bakeoff", { pack_dir: packDir, oracle_path: oraclePath, history_path: history, accuracy_bar: bar, winner_metric: winnerMetric }, brief, { workerScript });
|
|
616
|
+
const rep = await waitSubagent(handle, Number(longrun.subagent_timeout_seconds ?? 1800));
|
|
617
|
+
const led = rep.ledger ?? {}; // subagent 的账并进主账本
|
|
618
|
+
this.ledger.calls += led.calls ?? 0;
|
|
619
|
+
this.ledger.promptTokens += led.promptTokens ?? 0;
|
|
620
|
+
this.ledger.completionTokens += led.completionTokens ?? 0;
|
|
621
|
+
rep.eval_isolation = "subagent";
|
|
622
|
+
session.event(this.workspace, "eval_subagent_done", { dir: handle.dir, winner: rep.winner }, this.now());
|
|
623
|
+
return rep;
|
|
624
|
+
}
|
|
625
|
+
catch (e) {
|
|
626
|
+
session.event(this.workspace, "eval_isolation_fallback", { error: e.message }, this.now());
|
|
627
|
+
const pack = loadPack(packDir);
|
|
628
|
+
const oracle = JSON.parse(readFileSync(oraclePath, "utf-8"));
|
|
629
|
+
const rep = await this.builder.bakeoff({ profile: pack.profile, refdata: pack.refdata, targets: pack.targets }, pack.frozen, oracle, { llm: this.llm, accuracyBar: bar, winnerMetric, historyPath: history });
|
|
630
|
+
rep.eval_isolation = "inline";
|
|
631
|
+
return rep;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
// ④ 对抗 grounding:独立复验胜者证据是否真在原文,落 build/cross_check.json ---------
|
|
635
|
+
async crossCheck(session, targets, packDir, oracle) {
|
|
636
|
+
const pack = loadPack(packDir);
|
|
637
|
+
const executor = pack.executor("", { llm: this.llm });
|
|
638
|
+
if (executor.spec.unit() !== "per_doc_rule")
|
|
639
|
+
return;
|
|
640
|
+
const flags = [];
|
|
641
|
+
let n = 0;
|
|
642
|
+
for (const c of oracle.cases ?? []) {
|
|
643
|
+
for (const d of c.docs ?? []) {
|
|
644
|
+
const docText = d.text ?? "";
|
|
645
|
+
const res = await executor.runDocument(new DocumentIR(d.document_id), targets, { rawText: docText });
|
|
646
|
+
for (const r of res.results) {
|
|
647
|
+
for (const e of r.evidence ?? []) {
|
|
648
|
+
const quote = (e.quote ?? e.text ?? "").trim();
|
|
649
|
+
if (!quote)
|
|
650
|
+
continue;
|
|
651
|
+
n += 1;
|
|
652
|
+
if (!docText.includes(quote))
|
|
653
|
+
flags.push({ id: `${c.case_id}:${r.target_id}`, reason: "证据未在原文中找到(疑似幻觉/过度推断)", evidence: quote.slice(0, 200) });
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
this.writeAsset(session, "build/cross_check.json", { n_evidence: n, grounding_flags: flags, clean: flags.length === 0 });
|
|
659
|
+
session.event(this.workspace, "cross_check_done", { grounding_flags: flags.length, clean: flags.length === 0 }, this.now());
|
|
660
|
+
}
|
|
661
|
+
async freeze(session) {
|
|
662
|
+
if (!session.approved)
|
|
663
|
+
throw new Error("晋升门未批准:freeze 前必须显式 approve。");
|
|
664
|
+
const home = session.home(this.workspace);
|
|
665
|
+
const targets = this.loadTargets(session);
|
|
666
|
+
const audit = auditTargets(targets);
|
|
667
|
+
if (audit.length)
|
|
668
|
+
throw new Error("桩产物审计不通过,拒绝冻结:\n - " + audit.join("\n - "));
|
|
669
|
+
const ccPath = join(home, "build", "cross_check.json"); // 对抗 grounding 门(证据必落原文)
|
|
670
|
+
if (existsSync(ccPath)) {
|
|
671
|
+
const cc = JSON.parse(readFileSync(ccPath, "utf-8"));
|
|
672
|
+
if ((cc.grounding_flags ?? []).length) {
|
|
673
|
+
session.event(this.workspace, "grounding_gate_rejected", { flags: cc.grounding_flags.length }, this.now());
|
|
674
|
+
throw new Error(`对抗 grounding 门不通过:${cc.grounding_flags.length} 处证据未落原文,拒绝冻结。`);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
const promotion = this.promotionState(session);
|
|
678
|
+
if (!promotion.ready)
|
|
679
|
+
throw new Error(`晋升资产门不通过:${promotion.reason}`);
|
|
680
|
+
const specs = this.loadSpecs(session);
|
|
681
|
+
const oracleRaw = session.inputs.oracle ?? "";
|
|
682
|
+
const oracle = oracleRaw && existsSync(oracleRaw) ? JSON.parse(readFileSync(oracleRaw, "utf-8")) : undefined;
|
|
683
|
+
const packRoot = join(home, "pack");
|
|
684
|
+
const frozenAt = this.now();
|
|
685
|
+
const report = this.readTestReport(session);
|
|
686
|
+
this.builder.writePack(packRoot, session.scenarioKey, targets, specs, { winner: session.winner || Object.keys(specs)[0], oracle, schemaProfile: this.schemaProfile(session), lifecycle: "frozen", frozenAt, eval: { winner: report.winner, ranking: report.ranking, accuracy_bar: report.accuracy_bar } });
|
|
687
|
+
const methodIndex = existsSync(join(home, "build", "checkskills", "index.json")) ? JSON.parse(readFileSync(join(home, "build", "checkskills", "index.json"), "utf-8")) : {};
|
|
688
|
+
const manifest = { scenario_key: session.scenarioKey, goal: session.goal, task_type: session.taskType, winner: session.winner, n_targets: targets.length, approved: true, frozen_at: frozenAt, ontology_version: loadOntology(this.root).version, methods: methodIndex.methods ?? [], checkskill_digest: methodIndex.target_digest ?? "", ledger: this.ledgerSummary(), stub_audit: "passed" };
|
|
689
|
+
writeFileSync(join(packRoot, "freeze_manifest.json"), JSON.stringify(manifest, null, 2), "utf-8");
|
|
690
|
+
clearStale(home, "pack");
|
|
691
|
+
session.recordArtifact("pack/pack.json", this.now());
|
|
692
|
+
session.recordArtifact("pack/freeze_manifest.json", this.now());
|
|
693
|
+
}
|
|
694
|
+
// ---- 盘上产物 ----------------------------------------------------------
|
|
695
|
+
writeAsset(session, rel, data) {
|
|
696
|
+
const path = join(session.home(this.workspace), rel);
|
|
697
|
+
mkdirSync(join(path, ".."), { recursive: true });
|
|
698
|
+
writeFileSync(path, JSON.stringify(data, null, 2), "utf-8");
|
|
699
|
+
session.recordArtifact(rel, this.now());
|
|
700
|
+
}
|
|
701
|
+
writeTargets(session, targets) {
|
|
702
|
+
this.writeAsset(session, "build/targets.json", { task_type: session.taskType, targets: targets.map((t) => targetJson(t)) });
|
|
703
|
+
}
|
|
704
|
+
loadTargets(session) {
|
|
705
|
+
const data = JSON.parse(readFileSync(join(session.home(this.workspace), "build", "targets.json"), "utf-8"));
|
|
706
|
+
return data.targets.map((d) => Target.fromDict(d));
|
|
707
|
+
}
|
|
708
|
+
loadSpecs(session) {
|
|
709
|
+
const raw = JSON.parse(readFileSync(join(session.home(this.workspace), "build", "specs.json"), "utf-8"));
|
|
710
|
+
return Object.fromEntries(Object.entries(raw).map(([n, d]) => [n, PipelineSpec.fromDict(d)]));
|
|
711
|
+
}
|
|
712
|
+
schemaProfile(session) {
|
|
713
|
+
const p = join(session.home(this.workspace), "build", "schema_profile.json");
|
|
714
|
+
return existsSync(p) ? JSON.parse(readFileSync(p, "utf-8")) : null;
|
|
715
|
+
}
|
|
716
|
+
readSamples(dir) {
|
|
717
|
+
if (!existsSync(dir))
|
|
718
|
+
return [];
|
|
719
|
+
return readdirSync(dir).filter((f) => /\.(txt|md)$/i.test(f)).sort().map((f) => readFileSync(join(dir, f), "utf-8"));
|
|
720
|
+
}
|
|
721
|
+
// ---- 展示/知识工具 -----------------------------------------------------
|
|
722
|
+
showTargets(session) {
|
|
723
|
+
const targets = this.loadTargets(session);
|
|
724
|
+
const lines = [`当前 ${targets.length} 个核查目标(相位 ${session.phase}):`];
|
|
725
|
+
targets.forEach((t, i) => {
|
|
726
|
+
const rubric = (t.judge.rubric_text ?? "").replace(/\n/g, " ").slice(0, 60);
|
|
727
|
+
const kws = (t.retrieval.content_keywords ?? []).slice(0, 4).join("、");
|
|
728
|
+
lines.push(` ${i + 1}. [${t.target_id}] ${t.meta.name ?? t.target_id}`, ` 判据: ${rubric}`);
|
|
729
|
+
if (kws)
|
|
730
|
+
lines.push(` 定位词: ${kws}`);
|
|
731
|
+
});
|
|
732
|
+
return lines.join("\n");
|
|
733
|
+
}
|
|
734
|
+
showSpecs(session) {
|
|
735
|
+
const p = join(session.home(this.workspace), "build", "specs.json");
|
|
736
|
+
if (!existsSync(p))
|
|
737
|
+
return "还没有候选生产线(③出方案 尚未执行)。";
|
|
738
|
+
const specs = JSON.parse(readFileSync(p, "utf-8"));
|
|
739
|
+
return `候选生产线 ${Object.keys(specs).length} 条(胜负由测试定):\n` + Object.keys(specs).map((n) => ` - ${n}`).join("\n");
|
|
740
|
+
}
|
|
741
|
+
showReport(session) {
|
|
742
|
+
const p = join(session.home(this.workspace), "build", "test_report.json");
|
|
743
|
+
if (!existsSync(p))
|
|
744
|
+
return "还没有测试报告(④测试 尚未执行)。";
|
|
745
|
+
const rep = JSON.parse(readFileSync(p, "utf-8"));
|
|
746
|
+
const lines = ["测试报告(bake-off 沙箱分数):"];
|
|
747
|
+
for (const row of rep.ranking ?? [])
|
|
748
|
+
lines.push(` ${row.candidate}: 准确率 ${row.accuracy} · LLM ${row.llm_calls} 次` + (row.recall != null ? ` · 召回 ${row.recall}(漏报 ${row.fn})` : ""));
|
|
749
|
+
if (rep.winner)
|
|
750
|
+
lines.push(`胜出: ${rep.winner}`);
|
|
751
|
+
if (rep.warning)
|
|
752
|
+
lines.push(`⚠ ${rep.warning}`);
|
|
753
|
+
return lines.join("\n");
|
|
754
|
+
}
|
|
755
|
+
proposeKnowledge(a) {
|
|
756
|
+
const delta = proposeKnowledgeDelta(this.root, { kind: a.kind, name: a.name, scope: a.scope ?? "finance", body: a.body ?? "", sections: a.sections, now: this.now(), provenance: { via: "orchestrator", scenario: a.session?.scenarioKey } });
|
|
757
|
+
a.session?.event(this.workspace, "knowledge_delta_proposed", { id: delta.id, kind: a.kind, name: a.name }, this.now());
|
|
758
|
+
return `已提议知识 delta ${delta.id}(${delta.supersedes ? "更新" : "新增"} ${a.kind}「${a.name}」)。人工确认:kcagent knowledge apply --id ${delta.id}(写入后跑 eval --gate)`;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
// ---- 桩审计(机器门半边)---------------------------------------------------
|
|
762
|
+
export function auditTargets(targets) {
|
|
763
|
+
const issues = [];
|
|
764
|
+
if (!targets.length)
|
|
765
|
+
return ["目标集为空:没有任何核查目标,无物可冻结"];
|
|
766
|
+
const seen = new Set();
|
|
767
|
+
for (const t of targets) {
|
|
768
|
+
if (seen.has(t.target_id))
|
|
769
|
+
issues.push(`${t.target_id}: target_id 重复`);
|
|
770
|
+
seen.add(t.target_id);
|
|
771
|
+
const rubric = t.judge.rubric_text || t.judge.strategy || "";
|
|
772
|
+
if (!String(rubric).trim())
|
|
773
|
+
issues.push(`${t.target_id}: 无判据(rubric/strategy 均为空)——空壳目标`);
|
|
774
|
+
if (!(t.retrieval.content_keywords?.length || t.retrieval.chapter_keywords?.length))
|
|
775
|
+
issues.push(`${t.target_id}: 无任何检索线索`);
|
|
776
|
+
}
|
|
777
|
+
return issues;
|
|
778
|
+
}
|
|
779
|
+
function targetJson(t) {
|
|
780
|
+
return { target_id: t.target_id, task_type: t.task_type, applicability: t.applicability, retrieval: t.retrieval, judge: t.judge, outcome_domain: t.outcome_domain, postprocess: t.postprocess, reference: t.reference, meta: t.meta };
|
|
781
|
+
}
|
|
782
|
+
function specDict(s) {
|
|
783
|
+
return { spec_version: s.spec_version, scenario_key: s.scenario_key, skeleton: s.skeleton, skip: s.skip, fanout: s.fanout, budget: s.budget };
|
|
784
|
+
}
|
|
785
|
+
//# sourceMappingURL=planner.js.map
|