diff --git a/src/kb-improvement/evaluation.ts b/src/kb-improvement/evaluation.ts index 617116e..c9ee0cb 100644 --- a/src/kb-improvement/evaluation.ts +++ b/src/kb-improvement/evaluation.ts @@ -16,6 +16,7 @@ import { runRagKnowledgeImprovementLoop, } from '../rag-improvement-loop' import { readinessFor } from '../readiness-helpers' +import { mean } from '../statistics' import { type ValidateKnowledgeResult, validateKnowledgeIndex } from '../validate' import type { KnowledgeImprovementCandidateRecord, @@ -497,7 +498,7 @@ function defaultKnowledgeImprovementMetric( const blockingReadiness = blockingTotal === 0 ? 1 : Math.max(0, blockingTotal - blockingMissing) / blockingTotal const answerQuality = lifecycle?.answerQuality - ? average(Object.values(lifecycle.answerQuality.metrics).filter(Number.isFinite)) + ? mean(Object.values(lifecycle.answerQuality.metrics)) : 1 const promotionDecision = lifecycle?.promotion ? (lifecycle.promotion.promoted ? 1 : 0) : 1 const dimensions = { @@ -515,7 +516,7 @@ function defaultKnowledgeImprovementMetric( : `${blockingMissing}/${blockingTotal} blocking knowledge requirements still missing`, ].filter((reason): reason is string => Boolean(reason)) return { - score: average(Object.values(dimensions)), + score: mean(Object.values(dimensions)), passed: failedReasons.length === 0, dimensions, notes: @@ -556,9 +557,3 @@ function applyLifecycleFailures( function normalizeMetric(metric: KnowledgeImprovementMetric): KnowledgeImprovementMetric { return improvementMetricSchema.parse(metric) } - -function average(values: readonly number[]): number { - const finite = values.filter(Number.isFinite) - if (finite.length === 0) return 0 - return finite.reduce((sum, value) => sum + value, 0) / finite.length -} diff --git a/src/rag-eval/answer.ts b/src/rag-eval/answer.ts index ed1ac05..b15b649 100644 --- a/src/rag-eval/answer.ts +++ b/src/rag-eval/answer.ts @@ -1,4 +1,5 @@ import { groundClaimInText } from '../claim-grounding' +import { mean } from '../statistics' import type { RagAnswerEvalArtifact, RagAnswerEvalScenario, @@ -146,7 +147,7 @@ export function scoreAnswerCorrectness( const expectedScore = expected.length === 0 ? scoreAnswerRelevance(artifact, scenario, abstained) - : average(expected.map((claim) => textSupportScore(claim, artifact.answer))) + : mean(expected.map((claim) => textSupportScore(claim, artifact.answer))) const forbiddenPenalty = forbidden.length === 0 ? 0 @@ -210,11 +211,6 @@ export function neutralScore(condition: boolean): number { return condition ? 1 : 0 } -export function average(values: readonly number[]): number { - const finite = values.filter(Number.isFinite) - return finite.length === 0 ? 0 : finite.reduce((sum, value) => sum + value, 0) / finite.length -} - export function clamp01(value: number): number { if (!Number.isFinite(value)) return 0 return Math.max(0, Math.min(1, value)) diff --git a/src/rag-eval/scoring.ts b/src/rag-eval/scoring.ts index 38577bd..61f44f5 100644 --- a/src/rag-eval/scoring.ts +++ b/src/rag-eval/scoring.ts @@ -1,7 +1,7 @@ import type { JudgeConfig } from '@tangle-network/agent-eval/campaign' import type { RagGapFinding } from '../rag-improvement-loop' +import { mean } from '../statistics' import { - average, claimSupport, clamp01, contextIsRelevant, @@ -103,7 +103,7 @@ export function scoreRagAnswerArtifact( ? scenario.unanswerable ? 1 : 0 - : average(artifact.contexts.map((context) => contextRelevanceScore(context, scenario))) + : mean(artifact.contexts.map((context) => contextRelevanceScore(context, scenario))) const contextSufficiency = requiredContextCount === 0 ? contextRelevance : contextRecall const support = claims.map((claim) => claimSupport(claim.text, artifact.contexts, options)) const supportedClaimCount = support.filter(Boolean).length @@ -241,9 +241,9 @@ export function aggregateRagAnswerMetrics( } const out: Record = {} for (const key of [...keys].sort()) { - out[key] = average(summaries.map((summary) => summary.metrics[key])) + out[key] = mean(summaries.map((summary) => summary.metrics[key])) } - out.composite = average(summaries.map((summary) => summary.composite)) + out.composite = mean(summaries.map((summary) => summary.composite)) return out }