Skip to content

eval: per-client pipeline validation workflow (onboarding step) #127

Closed

Description

@arunjoyt

Why

Pipeline tuning is not a one-time activity — this is a multi-client product, and
each client brings their own contract corpus, their own ERPNext, their own
deployment. For every onboarding we need to arrive at knob values that work for
that client's data.

But we can't just re-run #113 per client:

The model: tune once, validate per client

Tune the pipeline once (#113) on the reference corpus (#112) → produces the
shipped defaults in config.py / pipeline/constants.py and the baseline
thresholds. Most knobs are corpus-independent, so this is the expensive
statistical work done a single time.

Validate per client (this issue) → a small, mostly-automated check that the
shipped defaults hold for the client's corpus, with a targeted re-tune only when
a slice is genuinely weak.

Knob taxonomy

Knob Per-client?
chunk_size / chunk_overlap No — document structure (contract clauses) is ~constant across procurement clients
RRF k No — "expect no move" per docs/PIPELINE_TUNING.md
RERANKER_MODEL, OPENAI_MODEL, system prompt No — model / prompt choices
QUERY_REWRITE_STRATEGY No — intent-dependent, not corpus-dependent
RERANK_TOP_N Weakly — precision vs. context budget
RETRIEVAL_TOP_K Yes-ish — recall knee shifts with corpus size / redundancy; generous default (20) absorbs most
metadata filter vocab (_DOCTYPE_KEYWORDS, _STATUS_KEYWORDS) Per-client, but that's config, not tuning

Scope

  1. scripts/generate_eval_set.py — corpus → candidate {question, ground_truth_answer, ground_truth_contexts, case_class, split} triples, in the same schema as evaluation/test_dataset.json. LLM-generated candidates grounded in real docname:chunk_index keys pulled from the client's Qdrant collection; emits a review file for a human to spot-check a sample before use. Same tool is used to draft the reference set in eval: build the reference tuning set (~150 Q, dev/test split) + candidate generator #112.
  2. Per-client validation run — ~20–40 questions on the client's corpus, ingested into a separate {QDRANT_COLLECTION}_eval collection (populated from their ERPNext during the pre-cutover window), evaluate.py --split against it, compared to the reference baseline thresholds from eval: tune the pipeline on the reference corpus (docs/PIPELINE_TUNING.md pass) #113.
  3. Targeted re-tune — if a case_class slice is below threshold, sweep only the implicated knob (usually RETRIEVAL_TOP_K or the system prompt), not the full eval: tune the pipeline on the reference corpus (docs/PIPELINE_TUNING.md pass) #113 pass. Record the per-client override in their .env.
  4. Onboarding artifact — the per-client eval set + validation results + any knob overrides go into the client's deployment record; add the step to docs/CLIENT_DEPLOYMENT_RUNBOOK.md.

Where it runs

A separate eval collection on the client's infra during the pre-cutover window —
never the live contract collection. Our infra only if contractually
allowed to hold their contract data.

Depends on

Related

Area: Evaluation

Activity

  1. arunjoyt commented on Aug 31, 2026

    @arunjoyt
    OwnerAuthor

    Closed via fe72f01 (merge 6755e22).

    Most of #127 shipped incrementally already:

    This close-out added:

    • evaluation/evaluate.py --collection flag (mirrors generate_eval_set.py) — a per-client run targets the collection explicitly instead of via a QDRANT_COLLECTION= env prefix; the collection is recorded in results.json's config block.
    • Consolidated the per-client validation flow in CLIENT_DEPLOYMENT_RUNBOOK.md Phase 5 + PIPELINE_TUNING.md.

    Scope change — the separate {client}_eval collection is dropped. Post-#113 the only per-client knobs (RETRIEVAL_TOP_K → prompt → RERANK_TOP_N) are all query-time — nothing rebuilds the collection — and the runbook's staging RAG stack strategy already isolates validation from the client's production users. A bespoke eval collection was a third isolation layer for a risk already covered. Validation runs read-only against the staging stack's collection (or the prod collection pre-go-live).

    250 tests pass, ruff clean.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions