Phase 22 — Internal Knowledge Library (KB)
Meta-layer over Phase 19 playbook_memory. Phase 19 answers "which WORKERS worked for this event"; KB answers "which CONFIG worked for this playbook signature" — model choice, budget hints, pathway notes, error corrections. tests/multi-agent/kb.ts: - computeSignature(): stable sha256 hash of the (kind, role, count, city, state) tuple sequence. Same scenario shape → same sig. - indexRun(): extracts sig, embeds spec digest via sidecar, appends outcome record, upserts signature to data/_kb/signatures.jsonl. - findNeighbors(): cosine-ranks the k most-similar signatures from prior runs for a target spec. - detectErrorCorrections(): scans outcomes for same-sig fail→succeed pairs, diffs the model set, logs to error_corrections.jsonl. - recommendFor(): feeds target digest + k-NN neighbors + recent corrections to the overview model, gets back a structured JSON recommendation (top_models, budget_hints, pathway_notes), appends to pathway_recommendations.jsonl. JSON-shape constrained so the executor can inherit it mechanically. - loadRecommendation(): at scenario start, pulls newest rec matching this sig (or nearest). scenario.ts: - Reads KB recommendation at startup (alongside prior lessons). - Injects pathway_notes into guidanceFor() executor context. - After retrospective, indexes the run + synthesizes next rec. Cold-start behavior: first run with no history writes a low-confidence "no prior data" rec so the signal that something was attempted is captured. Second run gets "low confidence, 0 neighbors" until a third distinct sig gives the embedder something to compare against — hence the upcoming scenario generator. VERIFIED: - data/_kb/ populated after one scenario run: 1 outcome (sig=4674…, 4/5 ok, 16 turns total), 1 signature, 2 recs (cold + post-run). - Recommendation JSON-parsed cleanly from gpt-oss:20b overview model. PRD Phase 22 added with file layout, cycle description, and the rationale for file-based MVP → Rust port progression that matches how Phase 21 primitives shipped. What's NOT here yet (batched follow-ups per J's request, tested between each): - Lift the k=10 hybrid_search cap to adaptive k=max(count*5, 20) - Scenario generator to bulk-populate KB with varied signatures - Rust re-weighting: push playbook_memory success signal INTO hybrid_search scoring, not just post-hoc boost
This commit is contained in:
@@ -35,6 +35,7 @@ import {
|
||||
reviewerPrompt,
|
||||
GATEWAY,
|
||||
} from "./agent.ts";
|
||||
import { indexRun, recommendFor, loadRecommendation, type PathwayRecommendation } from "./kb.ts";
|
||||
import { mkdir, writeFile, appendFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
|
||||
@@ -154,6 +155,7 @@ interface ScenarioContext {
|
||||
results: EventResult[];
|
||||
gap_signals: Array<{ event: string; category: string; detail: string }>;
|
||||
prior_lessons: PriorLesson[];
|
||||
pathway_rec?: PathwayRecommendation | null;
|
||||
}
|
||||
|
||||
interface PriorLesson {
|
||||
@@ -580,7 +582,14 @@ CAST(reliability AS DOUBLE) > 0.7.`;
|
||||
).join("\n")
|
||||
: "";
|
||||
|
||||
return `${schemaLock}\n\nEVENT FOCUS:\n${base}${priorHint}`;
|
||||
// Phase 22 pathway recommendation — if the KB synthesized a "best
|
||||
// path" from neighbor runs, inject it as concrete pre-run guidance.
|
||||
// Keep terse; the full rationale lives in the KB file.
|
||||
const pathwayHint = ctx.pathway_rec && ctx.pathway_rec.pathway_notes
|
||||
? `\n\nKB PATHWAY RECOMMENDATION (synthesized from ${ctx.pathway_rec.neighbors_consulted.length} neighbor runs, confidence=${ctx.pathway_rec.confidence}):\n${ctx.pathway_rec.pathway_notes.slice(0, 600)}`
|
||||
: "";
|
||||
|
||||
return `${schemaLock}\n\nEVENT FOCUS:\n${base}${priorHint}${pathwayHint}`;
|
||||
}
|
||||
|
||||
// =================== Artifact generation ===================
|
||||
@@ -1101,6 +1110,7 @@ async function writeRetrospective(ctx: ScenarioContext): Promise<void> {
|
||||
// =================== Main driver ===================
|
||||
|
||||
async function main() {
|
||||
const runStart = Date.now();
|
||||
const specPath = process.argv[2];
|
||||
const spec: ScenarioSpec = specPath
|
||||
? JSON.parse(await Bun.file(specPath).text())
|
||||
@@ -1112,6 +1122,18 @@ async function main() {
|
||||
|
||||
const prior_lessons = await loadPriorLessons(spec);
|
||||
|
||||
// Phase 22 KB — load any pathway recommendation for this signature.
|
||||
// The recommender is called at END of prior runs and synthesizes
|
||||
// configuration + pathway notes from nearest-neighbor history.
|
||||
// Nothing on first run (cold start); populates over time.
|
||||
const pathwayRec = await loadRecommendation(spec).catch(() => null);
|
||||
if (pathwayRec) {
|
||||
console.log(`▶ KB recommendation loaded: confidence=${pathwayRec.confidence} from ${pathwayRec.neighbors_consulted.length} neighbors`);
|
||||
if (pathwayRec.pathway_notes) {
|
||||
console.log(` pathway: ${pathwayRec.pathway_notes.slice(0, 120)}${pathwayRec.pathway_notes.length > 120 ? "…" : ""}`);
|
||||
}
|
||||
}
|
||||
|
||||
const ctx: ScenarioContext = {
|
||||
spec,
|
||||
out_dir,
|
||||
@@ -1119,6 +1141,7 @@ async function main() {
|
||||
results: [],
|
||||
gap_signals: [],
|
||||
prior_lessons,
|
||||
pathway_rec: pathwayRec,
|
||||
};
|
||||
|
||||
// Initialize output files
|
||||
@@ -1254,6 +1277,30 @@ async function main() {
|
||||
|
||||
await writeRetrospective(ctx);
|
||||
|
||||
// Phase 22 KB — index this run + synthesize recommendation for next
|
||||
// time this signature (or similar ones) show up. Event-driven cycle:
|
||||
// run ends → KB updates → next run reads rec at startup.
|
||||
try {
|
||||
const elapsed = (Date.now() - runStart) / 1000;
|
||||
const { sig_hash } = await indexRun(out_dir, spec, {
|
||||
executor: EXECUTOR_MODEL,
|
||||
reviewer: REVIEWER_MODEL,
|
||||
overview: OVERVIEW_MODEL,
|
||||
overview_cloud: OVERVIEW_CLOUD,
|
||||
}, elapsed);
|
||||
console.log(`▶ KB indexed: sig=${sig_hash} (${elapsed.toFixed(1)}s)`);
|
||||
const newRec = await recommendFor(spec, {
|
||||
overview_model: OVERVIEW_MODEL,
|
||||
cloud: OVERVIEW_CLOUD,
|
||||
k: 5,
|
||||
});
|
||||
if (newRec) {
|
||||
console.log(`▶ KB recommendation written: confidence=${newRec.confidence} (${newRec.neighbors_consulted.length} neighbors consulted)`);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(` (KB update skipped: ${(e as Error).message})`);
|
||||
}
|
||||
|
||||
const okCount = ctx.results.filter(r => r.ok).length;
|
||||
if (okCount < ctx.results.length) {
|
||||
console.log(`\n⚠ ${okCount}/${ctx.results.length} events succeeded. See ${out_dir}/report.md for gaps.`);
|
||||
|
||||
Reference in New Issue
Block a user