Skip to content

Add LLM-as-a-judge to eval suite - #2324

Draft
sarahyurick wants to merge 10 commits into
NVIDIA-NeMo:mainfrom
sarahyurick:llm_judge
Draft

Add LLM-as-a-judge to eval suite#2324
sarahyurick wants to merge 10 commits into
NVIDIA-NeMo:mainfrom
sarahyurick:llm_judge

Conversation

@sarahyurick

@sarahyurick sarahyurick commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Things to consider:

  • Add an agent skills file for how to write the YAML and Jinja files?
  • Right now I made it text specific, with JSONL/Parquet input/output. It should not be difficult to expand to other modalities.
  • This can be combined with Curator's Slurm array partitioning feature. It should be easy to get an agent to do it but we can also just add it to the eval/llm_judge directory directly.
  • Resumability and retry support too. Like with my small test run, there were 192 input records but 189 output records. Likely an NDD refusal or error for some reason. Do we care about getting every single judgement?
  • List of LLMs to recommend as judges
  • Multiple LLMs doing the same judging task. If the LLMs agree about a record then it is "easier"/more confident, but if the LLMs disagree then more analysis/user insight is needed.
  • How to handle empty examples
  • How to truncate according to token limits. It depends on the judging pipeline that the user wants to run but can we provide good inputs/recommendations?
  • vLLM shutdown issue when I Ctrl+C a run. Not sure if it is machine dependent.

Signed-off-by: Sarah Yurick <sarahyurick@gmail.com>
@copy-pr-bot

copy-pr-bot Bot commented Aug 20, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

Comment thread eval/llm_judge/generic_text_judge.py Outdated

DataFormat = Literal["jsonl", "parquet"]
FilterOperator = Literal["eq", "ne", "gt", "gte", "lt", "lte", "in", "not_in"]
_FILTER_OPERATORS = {"eq", "ne", "gt", "gte", "lt", "lte", "in", "not_in"}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we have some operator typing in audio somewhere... if yes, we should make it a generic util (if it is not already).

Comment thread eval/llm_judge/generic_text_judge.py Outdated
Comment thread eval/llm_judge/generic_text_judge.py Outdated
Comment thread eval/llm_judge/generic_text_judge.py Outdated
if not isinstance(execution, dict):
raise ValueError("'execution' must be a mapping when provided.")
mode = execution.get("mode", "single_stage")
if mode not in {"single_stage", "multi_stage"}:

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure if splitting up each judge task into its own NDD stage is worth it, or if it should all stay within the same NDD stage. Both are possible. Not sure it matters though.

files_per_partition: int | None,
) -> Pipeline:
"""Build one streaming Curator reader → optional language gate → NDD stages → filters → writer pipeline."""
# TODO: Add an optional TokenLengthFilter stage before NDD stages so prompts

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Big TODO. Right now I just do string truncation in the jinja file. But we should decide the best course of action.

Also, since some extractors might return null while others return content, we should decide whether those examples are evaluated or not? Right now I force the empty string and still give it to the judge.

Comment thread eval/llm_judge/README.md Outdated

Use this example when you have JSONL or Parquet records and want an LLM to evaluate fields in each record with your own prompt and rubric. You supply the input data, a YAML configuration, and one or more small Jinja prompt files.

The runner starts a local Curator Dynamo/vLLM `InferenceServer`, runs NeMo Data Designer (NDD) judge columns, and writes the original records plus judge output. The bundled example compares `raw_text`, `justext_text`, and `trafilatura_text`. Copy and adapt it to judge a PDF parser result against raw PDF text, assess whether two documents are semantic duplicates, or evaluate another text-processing result.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very specific example to the data generated by cc_extract.py. But everything is supposed to be generic as long as the user provides the input path, YAML, and Jinja file(s).

Comment thread pyproject.toml
# Synthetic Data Generation (SDG) Dependencies
sdg_cpu = [
"data-designer==0.5.5",
"data-designer==0.9.1",

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using this version for my tests. It is only like 2 commits behind NDD main right now.

@@ -0,0 +1,94 @@
# User-owned evaluation semantics: replace prompts, rubrics, models, and execution mode for another task.
models:
- alias: judge

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can handle as many models as we want to serve (assuming the GPU math works out). Each should have a unique alias.

Comment thread eval/llm_judge/examples/text_extraction_judge.yaml Outdated
extract_reasoning_content: false
scores:
- name: best_extraction
description: Select the candidate that is most useful as clean document text.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had Codex generate all the Jinja, scoring, etc. prompts. Just a POC for now.

@VibhuJawa
VibhuJawa self-requested a review August 20, 2026 04:11
Comment thread eval/llm_judge/generic_text_judge.py Outdated
Comment thread eval/llm_judge/generic_text_judge.py Outdated
Comment thread eval/llm_judge/generic_text_judge.py Outdated
sarahyurick and others added 9 commits August 20, 2026 10:38
Signed-off-by: Sarah Yurick <sarahyurick@gmail.com>
Signed-off-by: Sarah Yurick <sarahyurick@gmail.com>
Signed-off-by: Sarah Yurick <sarahyurick@gmail.com>
Signed-off-by: Sarah Yurick <sarahyurick@gmail.com>
Signed-off-by: Sarah Yurick <sarahyurick@gmail.com>
Signed-off-by: Sarah Yurick <sarahyurick@gmail.com>
Signed-off-by: Sarah Yurick <sarahyurick@gmail.com>
Signed-off-by: Sarah Yurick <sarahyurick@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant