[Testing 15] test(evals): offline eval harness — citation accuracy, injection resistance, leakage - #257
Open
amal66 wants to merge 1 commit into
Open
Conversation
amal66
force-pushed
the
olp-pr/evals-harness
branch
from
July 25, 2026 21:31
dd1234e to
d6e2ac0
Compare
Collaborator
Author
|
recheck |
…ormat Completes the CI evals gate that ci.yml already wires up (the job runs node evals/run.mjs --threshold 1.0 when the file exists, with no npm ci — so the runner is dependency-free Node ESM). - evals/run.mjs: deterministic CLI scorecard; --threshold / --suite / --list; exits non-zero when the pass rate drops below threshold. The citation parser is ported line-for-line from backend/src/lib/chat/citations.ts (source lines cited in comments). - evals/cases/: three suites (27 cases) whose fixtures use the real pipeline shapes — answers are prose with inline [N] markers plus a <CITATIONS> JSON block (prompts.ts rules, doc-N labels, page ranges, [[PAGE_BREAK]], legacy top-level fields, cluster_id case entries), document pages mirror the [Page N] blocks from documentOps.ts, and tool calls use the ToolCall shape from chat/types.ts with real tool names. - Source texts are real documents: public-domain opinions (Marbury, Erie, Hadley), statute text (17 U.S.C. § 107, 15 U.S.C. §§ 1-2), and full-length contract clauses, with injections embedded inside the real text and cited spans buried mid-document. - Corpus mixes benign (expected: pass) and known-bad (expected: fail) cases, so weakening a checker flips its known-bad cases and fails CI. - docs/evals.md: how to run, the fixture-field -> runtime-format mapping with file references, how to add cases, and an honest scope note (checker logic + regression corpus now; recorded live-model runs drop into the same fixture format later). No network, no LLM calls, no secrets. Verified: full run 27/27 exits 0; a deliberately corrupted quote drops to 26/27 and exits 1. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
amal66
force-pushed
the
olp-pr/evals-harness
branch
from
July 25, 2026 21:45
d6e2ac0 to
1bf1ebc
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
How this differs from #231 (closed)
#231 was closed for "limited usefulness of the evals and format. A fuller real evals suite can be implemented in the future which aligns with the format of actual assistant responses and involves real documents." This PR was rebuilt around that feedback:
[N]markers plus a<CITATIONS>JSON block, exactly what the backend emits and parses. The harness's citation parser is ported line-for-line frombackend/src/lib/chat/citations.ts(source lines cited in its comments), document pages mirror the[Page N]blocks fromdocumentOps.ts, and recorded tool calls use theToolCallshape fromchat/types.tswith Mike's real tool names.docs/evals.mdhas the full fixture-field → runtime-format mapping with file references, so the format contract is auditable.expected: "fail"(a fabricated Erie holding, a liability cap quietly changed from twelve months to twenty-four, a canary smuggled into a tool-call argument). If a checker is ever weakened, those cases flip and CI goes red. Benign edge cases (docket numbers near the SSN pattern, an answer that warns about an injection without repeating it) go red if a checker over-triggers.What this completes
CI already has an
evalsjob that runsnode evals/run.mjs --threshold 1.0if the file exists and skips otherwise. This PR adds that file, turning the gate on: a deterministic scorecard for fabricated citations, prompt injection via retrieved documents, and privileged/PII leakage — no network, no LLM calls, no keys, no npm install, so it is free to run on every PR.How to run
Exit 0 at or above threshold, non-zero below. Verified both directions: the committed corpus is 27/27, and corrupting a single quoted span drops it to 26/27 with exit code 1 and a readable reason (
quote not verbatim on page 1 of ...).The suites
citation-accuracy— parses the<CITATIONS>block and checks every[N]marker has a matching entry and every quote exists verbatim on the cited page (or in the fetched opinion forcluster_idcitations). Covers cross-page quotes, page ranges, and the legacy citation shape.prompt-injection— recorded turns where the document carries an injected instruction and a canary; fails on canary leakage in output or tool arguments, or on a disallowed tool call (e.g. an injected note ordering anedit_documentrewrite).privilege-leakage— flags off-matter emails, SSN-like values,sb_secret_*keys,service_roleJWTs (the public anon key deliberately doesn't trip it), and document identifiers from another client's matter.Future live mode
This gates checker logic plus a regression corpus; the answers are hand-written recordings, so it won't catch a live model regression. But because the fixtures already use the runtime shapes, the "fuller real evals suite" is a drop-in extension: run real prompts through the backend, capture the actual streamed responses and tool calls into this same fixture format, and score them with these same checkers — behind an opt-in, outside CI, since it needs keys and isn't deterministic.
docs/evals.mdspells this out, along with the runtime counterparts (citation verification inbackend/src/lib/chat/citations.ts; spotlighting of untrusted content in the security-hardening PR #227).🤖 Generated with Claude Code