Documentation
¶
Overview ¶
Package eval replays recorded incident cases through the investigation loop and scores whether the agent identifies the root cause — a reproducible RCA benchmark (cf. ITBench). A case records the evidence each tool returns, so the eval measures the model+loop's reasoning over fixed evidence, independent of a live cluster.
Index ¶
- Variables
- func GateError(c Campaign, failUnder float64) error
- func RubricMax() int
- func WriteCase(dir string, c Case) error
- type Call
- type Campaign
- type Case
- type CaseAggregate
- type Coverage
- type Dimension
- type Expected
- type GroundTruth
- type Judge
- type LiveReport
- type LiveResult
- type LiveRunner
- type ModelJudge
- type Recorder
- type Report
- type Result
- type RunOutcome
- type Runner
- type Scenario
- type StepRunner
- type Trigger
- type Verdict
Constants ¶
This section is empty.
Variables ¶
var Rubric = []Dimension{
{"root_cause", 3},
{"evidence", 3},
{"solution", 3},
{"description", 3},
{"calibration", 2},
}
Rubric is the RCA-quality grading rubric (matches the design spec §5).
Functions ¶
Types ¶
type Campaign ¶
type Campaign struct {
N int
Aggregates []CaseAggregate
}
Campaign is the aggregate of a multi-repeat replay run.
func (Campaign) FlakyNames ¶
FlakyNames lists cases whose repeats disagreed too much to trust.
func (Campaign) ReachedCases ¶
ReachedCases counts cases whose pass-rate met the k-of-n bar.
type Case ¶
type Case struct {
Name string `yaml:"name"`
Prompt string `yaml:"prompt"` // the incident description (seeds the loop)
Tools map[string]string `yaml:"tools"` // tool name -> recorded evidence the tool returns
Expected Expected `yaml:"expected"`
}
Case is one replayable incident.
func RecordedCase ¶
RecordedCase converts a live run's recorded tool calls into a replayable Case (the existing examples/eval format). v1 keeps the LAST output per tool: the replay staticTool returns one fixed output per tool regardless of args, so multi-call tools are flattened. submit_findings is excluded (it is the model's own output, not evidence).
type CaseAggregate ¶
type CaseAggregate struct {
Name string
Runs int
PassRate float64 // fraction of repeats whose Result.Pass is true
Reached bool // PassRate >= evalMinPassRate
Flaky bool // PassRate in (1-evalMinPassRate, evalMinPassRate): runs disagree
Confidence float64 // median confidence over repeats
Missing []string // union of missing keywords/entities across repeats
OverClaimed []string // union of over-claimed distractors across repeats
}
CaseAggregate is the k-of-n verdict for one case over N replay repeats.
type Coverage ¶
type Coverage struct {
Touched []string // mandatory source groups actually exercised
Missing []string // mandatory groups never touched
Bonus []string // optional groups touched
CrossSignal bool // >=2 distinct source groups exercised
ToolErrors []string // distinct tool names that returned an error
Ratio float64 // |touched| / |expected| (1.0 when no expected sources)
}
Coverage is the deterministic data-source coverage result for one run.
func ScoreCoverage ¶
ScoreCoverage computes coverage of the mandatory expected sources from the recorded calls. optional sources count as Bonus and never affect Ratio.
type Expected ¶
type Expected struct {
MustContain []string `yaml:"must_contain"` // keywords that must appear in the findings (recall, over full findings text)
MinConfidence float64 `yaml:"min_confidence"` // confidence floor (0 = no floor)
RootCauseEntities []string `yaml:"root_cause_entities"` // entities that MUST be named as the cause (entity recall, over claim text)
Distractors []string `yaml:"distractors"` // plausible-but-wrong entities that must NOT be blamed (over-claim/FP); only evaluated when root_cause_entities is non-empty
}
Expected is the RCA scoring spec for a case.
type GroundTruth ¶
type GroundTruth struct {
RootCause string `yaml:"root_cause"`
ExpectedSources []string `yaml:"expected_sources"` // MANDATORY data-source groups -> coverage gate
OptionalSources []string `yaml:"optional_sources"` // bonus if touched, never gates
ExpectedAction string `yaml:"expected_action"`
MustReachRoot bool `yaml:"must_reach_root"`
}
GroundTruth is the human-authored truth a scenario is graded against.
type Judge ¶
type Judge interface {
Grade(ctx context.Context, scn Scenario, inv providers.Investigation) (Verdict, error)
}
Judge grades an investigation against a scenario's ground truth.
type LiveReport ¶
type LiveReport struct {
At string `json:"at"`
N int `json:"n"` // runs per scenario (live mode); 0 in replay mode
Ran int `json:"ran"` // scenarios actually investigated (not skipped)
Passed int `json:"passed"`
Skipped int `json:"skipped"`
Results []LiveResult `json:"results"`
}
LiveReport is the serializable output of one live-fire campaign run.
func NewLiveReport ¶
func NewLiveReport(at string, n int, results []LiveResult) LiveReport
NewLiveReport tallies results into a report.
func (LiveReport) JSON ¶
func (rep LiveReport) JSON() []byte
JSON is the machine-readable sibling of the markdown report.
func (LiveReport) Markdown ¶
func (rep LiveReport) Markdown() string
Markdown renders the human report: summary, per-scenario table, coverage heatmap.
func (LiveReport) RegressionsVS ¶
func (rep LiveReport) RegressionsVS(prev LiveReport) []string
RegressionsVS returns scenarios that passed in prev but fail/skip now.
type LiveResult ¶
type LiveResult struct {
Scenario string
Skipped bool
SkipReason string
Runs []RunOutcome
CoverageRatio float64 // median
DimMedian map[string]int // median per rubric dimension
DimVariance map[string]float64
ConfidentWrong bool // any run confident-wrong
Flaky bool // root_cause scores vary too much across runs to trust
ToolErrors []string // union across runs
Pass bool
}
LiveResult aggregates the N runs of one scenario.
type LiveRunner ¶
type LiveRunner struct {
Model providers.ModelProvider
BaseTools []investigate.Tool
Judge Judge
Steps StepRunner
Log *slog.Logger
N int // runs per scenario (default 1 if 0)
OnRecord func(Scenario, []Call) // optional: persist the run's calls (replay corpus)
Recall *investigate.Recall // optional; when set, runOnce takes the instant-recall short-circuit (production path). nil ⇒ no recall.
}
LiveRunner runs scenarios against real tools, grading coverage + RCA quality. BaseTools and Model are the LIVE tools/model (built by cmd/lore via buildModelAndTools); Judge uses a separate, stronger model.
func (*LiveRunner) RunScenario ¶
func (lr *LiveRunner) RunScenario(ctx context.Context, scn Scenario) LiveResult
RunScenario runs setup (or precheck), N investigations, judging each, then always tears down. Pass gate: at least evalMinPassRate of runs reach root_cause >= evalRootCauseBar, coverage median == 1.0, no confident-wrong run, and root_cause variance within evalMaxRootCauseVariance (not flaky).
type ModelJudge ¶
type ModelJudge struct {
Model providers.ModelProvider
}
ModelJudge grades with an LLM (use a stronger model than the one under test).
func (ModelJudge) Grade ¶
func (j ModelJudge) Grade(ctx context.Context, scn Scenario, inv providers.Investigation) (Verdict, error)
Grade builds a blind grading prompt and parses the JSON verdict.
type Recorder ¶
type Recorder struct {
// contains filtered or unexported fields
}
Recorder collects tool calls made during one investigation run. Safe for concurrent use (the loop is sequential today, but tools may fan out later).
type Report ¶
type Report struct {
Results []Result
}
Report aggregates case results.
type Result ¶
type Result struct {
Name string
Pass bool
Confidence float64
Missing []string // expected keywords/entities not found (or an error note); includes "over-claimed: <e>" markers
OverClaimed []string // distractor entities the investigation wrongly blamed (over-claim/FP)
}
Result is the score for one case.
func Score ¶
func Score(name string, inv providers.Investigation, exp Expected) Result
Score reports whether the investigation identifies the expected root cause. Keyword recall (must_contain) is matched over the full findings text. Entity scoring — recall over root_cause_entities and an over-claim penalty over distractors — is matched over the CLAIM text only (what was blamed), and engages only when root_cause_entities is set. A case passes when nothing is missing, no distractor was blamed, and confidence meets the floor.
type RunOutcome ¶
type RunOutcome struct {
Investigation providers.Investigation
Coverage Coverage
Verdict Verdict
}
RunOutcome is one of the N runs of a scenario.
type Runner ¶
type Runner struct {
Model providers.ModelProvider
Log *slog.Logger
}
Runner replays cases through the investigation loop with a given model.
type Scenario ¶
type Scenario struct {
ID string `yaml:"id"`
Category string `yaml:"category"` // what-changed | saturation | network | cloud | dependency | cert | dns | storage | instant-recall
Description string `yaml:"description"`
Invasive bool `yaml:"invasive"` // true => has setup/teardown; false => natural failure
Precheck string `yaml:"precheck"` // optional shell; non-zero exit => SKIP (natural scenarios)
Setup []string `yaml:"setup"` // shell steps (kubectl/flux) to induce the fault
Trigger Trigger `yaml:"trigger"`
GroundTruth GroundTruth `yaml:"ground_truth"`
Teardown []string `yaml:"teardown"` // shell steps to revert; always run
}
Scenario is one live-fire eval case: an induced-or-natural failure, how to trigger an investigation, and the ground truth to grade against. It is a superset of Case (which is the recorded/replay form this produces).
func LoadScenarios ¶
LoadScenarios reads every *.yaml / *.yml scenario in dir.
type StepRunner ¶
StepRunner executes a scenario's shell setup/teardown/precheck steps. The real implementation shells out (kubectl/flux); tests use a fake.
type Trigger ¶
type Trigger struct {
Mode string `yaml:"mode"` // "cli" (default) | "webhook"
Symptom string `yaml:"symptom"` // free-text incident description
Namespace string `yaml:"namespace"` // affected namespace (optional)
}
Trigger describes how the investigation is started.