Documentation
¶
Overview ¶
Package bench measures whether the distillation is actually worth doing.
This is the core deliverable, not an afterthought. The claim is that an agent given an artifact answers better, faster and far more cheaply than the same agent given the raw page, and a claim like that is worth exactly as much as the harness behind it.
What is measured, and what each number is allowed to mean ¶
Token cost Input tokens the API actually charged. A measurement, not
an estimate: both conditions report usage from the same API.
Time to answer Wall clock for the answering call.
Accuracy Graded against hand-written ground truth, 0 to 1.
Coverage Share of ground-truth facts present in the artifact at all.
This is the *real* coverage number -- the one measured where
the right answer is known. The per-artifact self-audit
deliberately calls its figure "graph retention" instead,
because that one compares output against what the capture
observed and would cheerfully report 100% of a page it only
half saw.
Fidelity Share of artifact statements verifiable in the source. A
distiller that invents content is worse than no distiller,
so this one gates the release.
Stability Distill twice, compare. Reported separately for the tier
decision and for the content, because a tool that wavers
between tiers and one that extracts inconsistently are
different failures with different causes.
Index ¶
Constants ¶
const ( TargetTokenReduction = 0.90 TargetAccuracyGain = 0.20 TargetCoverage = 0.90 // A distiller that invents content is worse than no distiller, so this is // the highest bar of the four and the one that gates the release. TargetFidelity = 0.98 )
Targets are the success criteria for v1.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Answer ¶
type Answer struct {
QuestionID string `json:"question_id"`
Condition Condition `json:"condition"`
Text string `json:"text"`
Usage llm.Usage `json:"usage"`
LatencyMS int64 `json:"latency_ms"`
Refused bool `json:"refused,omitempty"`
Error string `json:"error,omitempty"`
// Accuracy and FactsFound are filled by the grader.
Accuracy float64 `json:"accuracy"`
FactsFound []string `json:"facts_found,omitempty"`
FactsMissed []string `json:"facts_missed,omitempty"`
GraderNote string `json:"grader_note,omitempty"`
// Graded records that a grade was actually reached.
//
// An answer that was produced but never graded -- the grader rate limited,
// out of quota, or returning unparseable JSON -- has an Accuracy of zero
// that means "not measured", not "wrong". The distinction is the same one
// Error draws for the answering call, and it was missing here: a run that
// answered every question and graded none reported an accuracy of 0.000
// for both conditions, which reads as total failure of the extraction when
// nothing had been measured at all.
Graded bool `json:"graded"`
}
Answer is one question answered under one condition.
type Band ¶
type Band string
Band groups questions by what they test.
const ( // BandFactual: what is the founding year, what materials are listed. BandFactual Band = "factual" // BandStructural: what sections exist, what is the navigation hierarchy. BandStructural Band = "structural" // BandActionable: how does a visitor make an enquiry, what fields are required. BandActionable Band = "actionable" )
type CallFailure ¶
CallFailure is one reason calls failed, and how many times it did.
type ConditionMetrics ¶
type ConditionMetrics struct {
Questions int `json:"questions"`
// Answered is how many questions actually produced an answer. It differs
// from Questions whenever a call failed, and the accuracy below is the mean
// over these rather than over all of them.
Answered int `json:"answered"`
// Scored is how many of those answers were graded, and Ungraded how many
// were not. Accuracy is the mean over Scored: an answer the grader never
// reached is excluded rather than counted as wrong.
Scored int `json:"scored"`
Ungraded int `json:"ungraded"`
MeanAccuracy float64 `json:"mean_accuracy"`
MeanInputToks float64 `json:"mean_input_tokens"`
TotalInputToks int64 `json:"total_input_tokens"`
MeanLatencyMS float64 `json:"mean_latency_ms"`
Refusals int `json:"refusals"`
Errors int `json:"errors"`
ByBand map[Band]float64 `json:"accuracy_by_band"`
}
ConditionMetrics aggregates one side of the comparison.
type CoverageResult ¶
type CoverageResult struct {
Coverage float64 `json:"coverage"`
Total int `json:"total"`
Found int `json:"found"`
Facts []FactCheck `json:"facts"`
}
CoverageResult is the whole check.
func CheckCoverage ¶
func CheckCoverage(set *Set, g *graph.Graph) CoverageResult
CheckCoverage measures coverage without calling a model.
Coverage is pure string matching against the artifact, so it never needed a provider -- but it was only reachable by running the full benchmark, which does. Anyone writing a question set therefore had to spend a complete graded run to discover that a fact was worded in a way the page never uses, and several such mistakes were only found by hand-editing a temporary test. A set is a test; checking a test should be free.
type FactCheck ¶
type FactCheck struct {
QuestionID string `json:"question_id"`
Fact string `json:"fact"`
Present bool `json:"present"`
// Missing lists the fact's distinctive words that appear nowhere in the
// artifact. It is what turns "coverage 0.81" into something actionable:
// usually either a real gap in the extraction or a fact whose wording was
// never on the page to begin with.
Missing []string `json:"missing,omitempty"`
}
FactCheck is one ground-truth fact and whether the artifact carries it.
type Input ¶
type Input struct {
Set *Set
// Artifact is the distilled graph.
Artifact *graph.Graph
// RawHTML is what an unaided agent would have had to read.
RawHTML string
}
Input is what a run needs.
type Options ¶
type Options struct {
Model string
GraderModel string
APIKey string
// Budget caps total tokens across the whole run.
Budget int64
// Concurrency bounds simultaneous API calls.
Concurrency int
// BaseURL points the run at an OpenAI-compatible provider. Empty means
// Anthropic, or whatever LLM_BASE_URL names.
BaseURL string
// RawContextTokens caps how much of the raw page the control is given.
//
// Without a cap the control simply errors on any large page, so the
// benchmark could measure everything except the case the tool exists for:
// pear.no serves around four hundred and seventy thousand tokens and no
// model accepts it. Truncating models what an unaided agent actually gets
// -- the top of the document and no more -- and the report says how much
// was left behind, so nobody mistakes the handicap for a result.
RawContextTokens int
// GraderRepeats re-grades a sample of answers to measure how much the
// grader agrees with itself. Zero disables it.
GraderRepeats int
Logf func(format string, args ...any)
}
Options configures a run.
func DefaultOptions ¶
func DefaultOptions() Options
DefaultOptions returns usable settings.
The model is left empty rather than pinned to the Anthropic default, because naming it here would override whatever provider the user configured. It used to be pinned, and the effect was that pointing sieve at another provider sent that provider a model name it had never heard of: a request to Groq asking for claude-opus-5, and a 404 that read like a broken build rather than a misconfiguration. Resolution belongs in one place, and that place is the client, which knows which provider it is talking to.
type Question ¶
type Question struct {
ID string `yaml:"id" json:"id"`
Band Band `yaml:"band" json:"band"`
Ask string `yaml:"ask" json:"ask"`
// Expect is the ground-truth answer, written by hand against the real page.
Expect string `yaml:"expect" json:"expect"`
// Facts are the atomic claims a correct answer must contain. They are what
// coverage is measured against, and they are why coverage means something
// here and not in the self-audit.
Facts []string `yaml:"facts" json:"facts"`
}
Question is one item in a question set.
type Report ¶
type Report struct {
Target string `json:"target"`
GeneratedAt time.Time `json:"generated_at"`
Model string `json:"model"`
GraderModel string `json:"grader_model"`
ContentHash string `json:"content_hash"`
Tier string `json:"tier"`
// RawTokens is the size of the page an unaided agent would have been
// handed, and RawTokensSent is how much of it actually fitted.
//
// These differ on exactly the pages sieve exists for. A context ceiling is
// not a limitation of this benchmark, it is the condition the control is
// under in real use -- an agent given a four-hundred-thousand-token page
// does not read four hundred thousand tokens of it either. Recording both
// is what keeps that honest rather than hidden.
RawTokens int `json:"raw_tokens"`
RawTokensSent int `json:"raw_tokens_sent"`
RawTruncated bool `json:"raw_truncated"`
// RawVisibleChars is how much readable text the served page carried, with
// markup and scripts removed. It decides which criteria can be judged at
// all: a page that serves nothing can neither verify the artifact nor be
// reduced in size.
RawVisibleChars int `json:"raw_visible_chars"`
Answers []Answer `json:"answers"`
// CallFailures groups the distinct reasons calls failed, commonest first.
//
// A failure count on its own is a dead end. The reason is always in the
// report's per-answer records, but a reader watching the terminal sees only
// a number, and the difference between a wrong model name, an expired key
// and a rate limit is the difference between a five-second fix and an
// afternoon. The provider already says which it is; this is only a matter
// of not throwing it away.
CallFailures []CallFailure `json:"call_failures,omitempty"`
Metrics struct {
Raw ConditionMetrics `json:"raw"`
Artifact ConditionMetrics `json:"artifact"`
} `json:"metrics"`
// Coverage is measured against ground-truth facts, not against what the
// capture happened to observe.
Coverage float64 `json:"coverage"`
// Fidelity is the share of sampled artifact statements verifiable in the
// source. It gates the release: a distiller that invents content is worse
// than no distiller.
Fidelity float64 `json:"fidelity"`
// FidelityMeasured distinguishes a fidelity of zero from a check that could
// not run. They are opposite conclusions -- one says the artifact invented
// everything, the other that nothing could be verified -- and collapsing
// them into a single number condemned sieve for extracting content which
// was correctly absent from the served HTML.
FidelityMeasured bool `json:"fidelity_measured"`
FidelityNotes []string `json:"fidelity_notes,omitempty"`
// GraderAgreement is how often the grader gave the same verdict when asked
// twice about the same answer. It is the error bar on every accuracy figure
// above: a grader that disagrees with itself a fifth of the time cannot
// support a claim of a five-point difference, and without measuring it
// there is no way to know which claims are safe.
GraderAgreement float64 `json:"grader_agreement,omitempty"`
GraderRegraded int `json:"grader_regraded,omitempty"`
// Stability is reported only when a stability run was requested.
Stability *Stability `json:"stability,omitempty"`
// Verdict states plainly whether the run met the success criteria.
Verdict Verdict `json:"verdict"`
}
Report is a complete benchmark run.
type Runner ¶
type Runner struct {
// contains filtered or unexported fields
}
Runner executes a benchmark.
func (*Runner) Model ¶
Model reports the model the runner resolved to, so a caller can name it before the run rather than after.
type Set ¶
type Set struct {
URL string `yaml:"url" json:"url"`
Name string `yaml:"name" json:"name"`
Questions []Question `yaml:"questions" json:"questions"`
}
Set is a question set for one target.
type Stability ¶
type Stability struct {
// TierStable reports whether both runs chose the same escalation tier.
// It is separate from content stability on purpose: a tool that wavers
// between tiers and one that extracts inconsistently are different bugs.
TierStable bool `json:"tier_stable"`
TierA string `json:"tier_a"`
TierB string `json:"tier_b"`
// HashStable reports whether the semantic content hash matched.
HashStable bool `json:"hash_stable"`
// BlockAgreement is the share of blocks present in both runs with identical
// text. A page that is genuinely personalised will score low here, and
// correctly so.
BlockAgreement float64 `json:"block_agreement"`
BlocksA int `json:"blocks_a"`
BlocksB int `json:"blocks_b"`
OnlyInA []string `json:"only_in_a,omitempty"`
OnlyInB []string `json:"only_in_b,omitempty"`
}
Stability is what two runs of the same URL produced.
func MeasureStability ¶
MeasureStability compares two distillations of the same URL.
type Verdict ¶
type Verdict struct {
TokenReduction float64 `json:"token_reduction"`
// AccuracyGain is the mean difference over questions answered under both
// conditions, not the difference of the two means. See judge.
AccuracyGain float64 `json:"accuracy_gain"`
// ComparedQuestions is how many questions that pairing covered.
ComparedQuestions int `json:"compared_questions"`
MetTokenTarget bool `json:"met_token_target"`
MetAccuracyTarget bool `json:"met_accuracy_target"`
MetCoverageTarget bool `json:"met_coverage_target"`
MetFidelityTarget bool `json:"met_fidelity_target"`
// TokenReductionApplies is false on a page that served no readable text,
// where there was nothing to reduce. MetTokenTarget is then true by
// vacancy rather than by achievement, and this is how to tell them apart.
TokenReductionApplies bool `json:"token_reduction_applies"`
Passed bool `json:"passed"`
Summary string `json:"summary"`
// Notes record criteria that did not apply, so a pass is never mistaken
// for having cleared a bar that was never raised.
Notes []string `json:"notes,omitempty"`
}
Verdict states whether the run met the criteria for a release.