agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
110 lines • 5.56 kB
TypeScript
/**
* Ablation — the counterfactual seam (RFC-003 Part B, D8 stage 4 + the
* D9 stats engine).
*
* Three pieces:
*
* 1. **Adapters** — `ablationForSuspect` maps a classified suspect to
* the spec that removes it (tool → drop from catalog; injection /
* fact / skill → exclude the `Injection.id`; memory → filter the
* `MemoryEntry.id`; arg → consumer-override note).
*
* 2. **The seam** — `applyAblations` filters the inputs an agent is
* BUILT from. Documented here because the seam did not previously
* exist: `AgentOptions` has no `ignoredTools` runtime kill-switch, so
* tool ablation happens at construction (the consumer's
* `AblationRunner` rebuilds the agent from filtered inputs). Same for
* injections and memory entries.
*
* 3. **The probe engine** — `runAblationProbe` calls the consumer's
* runner N seeded times, measures embedding similarity to the
* original output, counts outcome flips, and returns variance —
* never a single-run verdict (D9 discipline).
*
* §B2: only `runAblationProbe`-derived verdicts are causal claims; every
* score elsewhere is a correlational proxy.
*/
import type { Embedder } from '../influence-core/index.js';
import type { AblationRerun, AblationRunStats, AblationSpec, AblationVerdict, CostStats, OutcomeComparator, SimilarityStats, Suspect } from './types.js';
/**
* The spec that removes one suspect — or `undefined` for kind `'stage'`
* (plain pipeline stages have no removable input; re-rank or refactor).
*/
export declare function ablationForSuspect(suspect: Suspect): AblationSpec | undefined;
/** Anything with a stable id — `Injection` and `MemoryEntry` both fit. */
interface Identified {
readonly id: string;
}
/** Anything with a named schema — the library's `Tool` fits. */
interface NamedTool {
readonly schema: {
readonly name: string;
};
}
export interface AblationTargets<TTool extends NamedTool = NamedTool, TInjection extends Identified = Identified, TMemoryEntry extends Identified = Identified> {
readonly tools?: readonly TTool[];
readonly injections?: readonly TInjection[];
readonly memoryEntries?: readonly TMemoryEntry[];
}
/**
* Apply ablation specs to the inputs an agent is constructed from —
* THE documented seam (see module docs). Generic over the concrete tool /
* injection / memory-entry types so it filters without importing them.
*
* `'arg'` specs are deliberately NOT handled here: run input belongs to
* the consumer's runner (`spec.note` says so).
*
* @example inside an AblationRunner
* ```ts
* const { tools, injections } = applyAblations(specs, {
* tools: ALL_TOOLS, injections: ALL_FACTS,
* });
* const agent = Agent.create({ provider: freshProvider(), model })
* .tools([...tools]);
* for (const inj of injections) agent.fact(inj);
* ```
*/
export declare function applyAblations<TTool extends NamedTool, TInjection extends Identified, TMemoryEntry extends Identified>(specs: readonly AblationSpec[], targets: AblationTargets<TTool, TInjection, TMemoryEntry>): {
tools: readonly TTool[];
injections: readonly TInjection[];
memoryEntries: readonly TMemoryEntry[];
};
/** Resolve the seeded-rerun count: default on non-finite, floor, clamp to >= 2
* (no single-run verdicts — D9). Shared by the ablation + restoration probes. */
export declare function resolveSamples(samples: number | undefined): number;
/** Median of a numeric sample (mean of the two middles for even length). */
export declare function median(values: readonly number[]): number;
/** Build a probe's CostStats from per-seed loop/token samples — undefined when
* the runner reported no cost (keeps quality-only behavior byte-identical). */
export declare function costStatsFrom(samples: number, loops: readonly number[], tokens: readonly number[]): CostStats | undefined;
export declare function similarityStats(values: readonly number[]): SimilarityStats;
/** The default comparator: embedding similarity below the threshold. */
export declare function defaultOutcomeComparator(embedder: Embedder, flipThreshold: number): OutcomeComparator;
/** Resolved probe configuration shared by D8 and D9. */
export interface ProbeConfig {
readonly rerun: AblationRerun;
readonly embedder: Embedder;
}
/**
* Run ONE probe: call the consumer's runner with `specs` once per seed
* (0..samples-1), measure each output's embedding similarity to the
* original, and count outcome flips. Variance is always reported.
*
* `samples` is clamped to ≥ 2 — D9: never single-run verdicts.
*/
export declare function runAblationProbe(config: ProbeConfig, specs: readonly AblationSpec[]): Promise<AblationRunStats>;
/** Majority-flip rule shared by D8 verdicts and D9 probes. */
export declare function probeFlipped(stats: AblationRunStats): boolean;
/**
* Translate probe evidence into the verdict — the ONLY causal claim tier
* (§B2). `baselineStable=false` (the un-ablated scenario itself flipped)
* forces `'inconclusive'`: no ablation verdict is trustworthy on an
* unstable baseline.
*/
export declare function verdictFor(label: string, stats: AblationRunStats, baselineStable: boolean,
/** The counterfactual intervention. `'ablating'` (default) for present
* suspects; `'restoring'` for missing-context candidates (interface #3).
* Default keeps every claim string byte-identical to before. */
action?: 'ablating' | 'restoring'): AblationVerdict;
export {};
//# sourceMappingURL=ablation.d.ts.map