Evaluate
Prove the guarantee holds on your corpus. Python’s evaluate() runs each row of
a golden CSV through ask() and returns an EvaluationReport. Go and JavaScript
have no evaluate() — you drive Ask / ask over the rows and count, which
produces the same numbers because it is the same flow.
All three tabs below run the same two-row golden.csv against the same two-doc
corpus and print identical counts and rates.
from citenexus.evaluate import EvaluationReport
report: EvaluationReport = rag.evaluate("golden.csv")print(report.total, report.answered, report.refused)print(report.groundedness_rate, report.citation_rate, report.expected_support_rate)# 2 1 1# 1.0 1.0 0.5// No evaluate() in the Go port — drive Ask over the golden rows and count.import ( "encoding/csv" "fmt" "os" "strings"
"github.com/muthuishere/citenexus/golang/answer" "github.com/muthuishere/citenexus/golang/gate" "github.com/muthuishere/citenexus/golang/result")
corpus := []answer.Doc{ {DocumentID: "nda", Text: "The employee shall not disclose confidential information."}, {DocumentID: "leave", Text: "Employees accrue twenty days of annual leave."},}
f, err := os.Open("golden.csv")if err != nil { panic(err)}defer f.Close()rows, err := csv.NewReader(f).ReadAll()if err != nil { panic(err)}
total, answered, refused, grounded, cited, supported := 0, 0, 0, 0, 0, 0for _, row := range rows[1:] { // skip the header question, expected := row[0], row[1] total++ res := answer.Ask(corpus, question, answer.DefaultTopK) if res.Evidence.Decision != result.DecisionAnswered { refused++ continue } answered++ if res.Evidence.AllClaimsVerified { grounded++ } if len(res.Sources) > 0 { cited++ } // expected-support: an empty `expected` counts any answer (see the warning below). if strings.TrimSpace(expected) == "" || gate.IsSupportedV2(expected, res.Answer) { supported++ }}fmt.Println(total, answered, refused)fmt.Printf("%.1f %.1f %.1f\n", float64(grounded)/float64(answered), float64(cited)/float64(answered), float64(supported)/float64(total))// 2 1 1// 1.0 1.0 0.5// No evaluate() in the JavaScript port — drive ask over the golden rows and count.import { readFileSync } from "node:fs";import { ask, isSupportedV2 } from "@muthuishere/citenexus";
const corpus = [ { document_id: "nda", text: "The employee shall not disclose confidential information." }, { document_id: "leave", text: "Employees accrue twenty days of annual leave." },];
// A golden row is `question,expected` with either field optionally quoted.const parseRow = (line) => (line.match(/("[^"]*"|[^,]*)(?:,|$)/g) ?? []) .slice(0, 2) .map((f) => f.replace(/,$/, "").replace(/^"|"$/g, ""));
const rows = readFileSync("golden.csv", "utf8").trim().split("\n").slice(1).map(parseRow);
let total = 0, answered = 0, refused = 0, grounded = 0, cited = 0, supported = 0;for (const [question, expected] of rows) { total++; const res = ask(corpus, question); if (res.evidence.decision !== "answered") { refused++; continue; } answered++; if (res.evidence.all_claims_verified) grounded++; if (res.sources.length > 0) cited++; // expected-support: an empty `expected` counts any answer (see the warning below). if (!expected.trim() || isSupportedV2(expected, res.answer)) supported++;}console.log(total, answered, refused);console.log((grounded / answered).toFixed(1), (cited / answered).toFixed(1), (supported / total).toFixed(1));// 2 1 1// 1.0 1.0 0.5The golden CSV
Section titled “The golden CSV”The CSV needs a question column (query is also accepted) and an optional
expected column:
question,expected"Can the employee disclose confidential information?","shall not disclose""What is the capital of France?",- A row with an
expectedvalue counts as expected-supported when that text’s content tokens are a subset of the answer’s — a lightweight grounding check. - A row with an empty
expectedcounts as expected-supported iff the row was answered (evaluate.py:76-77).