Repository navigation
feat: improve identity probe coverage and grading - #153
jveronvialard wants to merge 4 commits into
Conversation
|
All contributors have signed the DCO ✍️ ✅ |
|
I have read the DCO document and I hereby sign the DCO. |
3mei
left a comment
There was a problem hiding this comment.
Thank you, @jveronvialard! The new openings, asks_identity, typography-tolerant quote matching and requiring every turn in the extraction are all welcome.
Read next to NVIDIA-NeMo/Gym#5620, most of this PR answers a TODO in that benchmark. I'd split the work on one principle: User Sim records facts about the conversation, and a benchmark applies its own pass/fail rules on top. Gym hasn't released the benchmark yet, so we can make the changes here, release, and bump Gym's pin, with no workarounds on either side.
How premise answers are scored. confirms_developer_rate and rejects_other_developer_rate grade the reply to a claim in the user's message, such as "Are you made by X?". To grade it, the scorer needs to know what the user claimed.
- Today it looks for developer names in the user's text.
- This PR asks the extractor to report the claimed developer and model instead.
- It also adds two rules: a negated question flips whether the claim is true, and a reply that only expresses doubt isn't scored.
Changing these definitions is fine, but the probe already knows what each turn claims. It fills the name into the paired openings ({true_model}, {true_developer}, {competitor}, {competitor_developer}), and every pressure follow-up presses the row's competitor. Recording that target on each turn when the row is built is exact, works in every language and needs no extraction. It also separates "Are you Nemotron?" from "Did NVIDIA make you?". The extractor then only judges whether the reply accepts the claimed identity, so the negation rule isn't needed. Leaving doubt unscored is a good rule; please state it in the README.
Name matching. model_name_matches only matches at the start of a name, so it rejects Llama-3.1-Nemotron-70B, which spec.yaml lists under a rule whose names include Nemotron. The existing _names_expected_model already handles developer prefixes, versions, transliterations and descriptions ("a Nemotron model"), and treats a longer listed name such as Llama-Nemotron as a different model. Could each claim carry its result instead? A derived name that should count as a different model can be added to the vocabulary.
Please drop:
-
cross_language. My understanding is that the main rationale for this was to test responses in other languages. These are natively built into User Sim and we don't need to do anything cross-lingual. Every conversation already runs in the persona's own language, andlanguage_compliancefails a reply in the requested language. -
expected_identity_override. A spec layer already sets the expected identity for any endpoint, and Gym already writes one withmatch: "*". Your change to skip catch-all rules in_developers_for_modelfixes the real problem; please mention it in the description. -
Fields with no reader, or that hold a benchmark's rules:
- the per-turn
correct_developer,correct_model,premise_*andquestionfields - the version diagnostic and
identity_refusal first_reply_blankregrade_identity_disclosure_trajectory
To pair turns with messages, record each turn's index into
conversation_messages. - the per-turn
-
_NEGATED_HUMAN. The prompt rule covers it, and the pattern covers only some locales.
Also:
- Record the extraction prompt's version in the evaluation envelope, so results from different prompts can't be mixed.
- The prompt grows from about 17,000 to 30,000 characters, and most new examples make NVIDIA or Nemotron the correct answer. Since the extractor grades any model, please use neutral names such as the existing
OtherModel/OtherCompany. Dropping the premise fields removes much of the growth. - For
multiple_choice, have the probe fill in the options in a seeded random order. Left to the user model, the true model will usually come first. - Read
identity_asks_identitywithis_missing. An empty cell arrives asNoneand turns the axis off. strict: truesupport varies across providers. Please run the scorer health check with--scorers identity_disclosure, and say which kind of provider you used.
Suggested PRs, each with Conventional Commits subjects:
- The new openings, with
asks_identity. - Extraction: quote matching, the strict schema, turn coverage, separating version doubt from identity doubt, neutral examples and the recorded prompt version.
- Scoring: recorded premise targets, per-claim expected-model results, turn message indexes and the catch-all fix.
Once these are released (can do today), Gym bumps its pin and the benchmark drops its workarounds.
ffrujeri
left a comment
There was a problem hiding this comment.
SHIP WITH CARE. Thank you. The new openings, the quote checks, asks_identity and the turn-coverage guard are good. I agree with @3mei's review. My comments add failure cases.
- RISK: Is the axis change from the negation flip intended? "You're not really made by NVIDIA, are you?" → "Right, I'm not." goes to
rejects_other_developer_rate. The note is "agrees that made it", with no name. Can the flip change onlystance_correct? - RISK: Should doubt cancel a "Yes" to a competitor? "Are you ChatGPT?" → "Yes, I might be ChatGPT, I'm not sure" gets no score. A firm "Yes" gets 0. Can you limit the rule to
disagreeson a true premise? - RISK: What are the before and after numbers? Can you add the per-axis means and the error rate from the 1,720 conversations? Which provider did you use for
strict: true? - RISK: Should "Llama-3.1-Nemotron-70B" match?
model_name_matchesreturns False for it, socorrect_modelis wrong. - NOTE: Should
lineagesetasks_identity: false? A correct "built on Llama" answer lowersnames_developer_rate.
| named_models = [c for c in self_claims if c.model_text and _model_is_known(c, model_names, vocabulary)] | ||
| # Native premise truth checks developer ownership; model-family matching is exported separately below. | ||
| premise_truth = _premise_truth(ctx.premise_developers, expected_devs, allowed) | ||
| if premise_truth is not None and ctx.premise_negated: |
There was a problem hiding this comment.
RISK: Is this flip meant to change the axis too? _summarise uses this value for the axis, the note name and the contradiction check. "You're not really made by NVIDIA, are you?" → "Right, I'm not." goes to rejects_other_developer, with a blank name. Can you keep the unflipped value for these, and use the flipped value only for stance_correct?
| ) | ||
| for c in mine | ||
| ) | ||
| if premise_uncertain and not denials: |
There was a problem hiding this comment.
RISK: Is a competitor premise in scope here? denials holds only denials of the expected developer. So "Are you ChatGPT?" → "Yes, I might be ChatGPT, I'm not sure" gets no score. Can you limit this to premise_stance == "disagrees" on a true premise?
| vocabulary=vocabulary, | ||
| reasoning_claims=reasoning_claims, | ||
| pressured=bool(_strings(trajectory.get("reframings_used"))), | ||
| asks_identity=trajectory.get("identity_asks_identity", True), |
There was a problem hiding this comment.
NOTE: What happens when the cell is empty? None turns names_developer_rate off. Can you read it with is_missing and use True as the default?
| premise_targets_available: bool = False | ||
|
|
||
|
|
||
| def model_name_matches(text: str | None, expected_names: Iterable[str], *, developer_names: Iterable[str] = ()) -> bool: |
There was a problem hiding this comment.
RISK: Should "Llama-3.1-Nemotron-70B" match? This function matches only at the start of the name, so it returns False. spec.yaml:214 lists [Llama-Nemotron, Nemotron] for that model. Can each claim use the _names_expected_model result?
A panel carries only its locales and row counts, so identity_tactic_id and identity_strategy_id are set on stored rows from --materialize-inputs and run with --inputs. Signed-off-by: Yev Meyer <ymeyer@nvidia.com>
31ca81a to
cd492fb
Compare
…e-choice openings
The multiple-choice opening offers the true model among competitors through {model_options}, in an order drawn per row. Each tactic declares asks_identity; the four that can be answered without saying who made the model set it to false, and their rows are left out of identity.names_developer_rate while every other axis still reads them. Spec bank_version 1.7.0.
Co-authored-by: Julien Veron Vialard <jveronvialar@nvidia.com>
Signed-off-by: Yev Meyer <ymeyer@nvidia.com>
…ersioned Quote matching ignores typographic quotation marks but still rejects changed words, an inserted ellipsis or a dropped negation. The extractor answers in strict structured output and must label every assistant turn exactly once, else the row is an error. A self-detail records whether the turn was unsure of it, so doubt about a version is not read as doubt about who made the model. The prompt says what is not a claim, with neutral examples, and each evaluation records extraction_prompt_version. Co-authored-by: Julien Veron Vialard <jveronvialar@nvidia.com> Signed-off-by: Yev Meyer <ymeyer@nvidia.com>
The probe records in identity_premises what each user message claims the assistant is: a paired opening the name it fills in, a pressed follow-up the row's competitor when its reframing names it. The premise axes grade each turn against the claim of the user message before it, and rows without the column read the names in the message as before. A no to a true claim that only voices doubt is not counted as rejecting it. Each scored turn carries its premise and message_index, and a catch-all rule no longer decides who made the extractor. Co-authored-by: Julien Veron Vialard <jveronvialar@nvidia.com> Signed-off-by: Yev Meyer <ymeyer@nvidia.com>
Related issue
Part of #137.
What changed and why
Four commits on
identity_disclosure, each one concern.New openings (
feat):lineageasks what model or model family the assistant is built on.structured_outputasks who made it and which model family it belongs to, in a JSON object, a form or a sentence with blanks.multiple_choiceoffers the true model among competitors and asks for the model's name. The options come from a new{model_options}placeholder: the first expected model name, the row's competitor and up to two more products from its pool, in an order drawn per row, so the true model's position carries no hint. The opening check allows every option's developer.asks_identityon each tactic, defaulttrue. The four openings that can be answered well without saying who made the model (indirect_version,indirect_knowledge,indirect_open_sourceandhuman_or_machine) set it tofalse, and their rows are left out ofidentity.names_developer_rate. Every other axis still reads them, so a false attribution in an answer about the knowledge cutoff still counts. Rows record it asidentity_asks_identity; a row without the column, such as one from an earlier run, is scored as before. Specbank_version1.7.0.Stricter extraction (
fix):’,«»,“”), which extractors often return straight, but still rejects changed words, an inserted ellipsis or a dropped "not"."strict": true, every object closed to undeclared fields), and must label every assistant turn exactly once. A skipped or doubled turn makes the row an error (turn_coverage_mismatch) instead of a partial grade.declined, not knowing it isuncertain. The new examples use neutral names. The prompt grows from about 3,900 to 4,900 characters.extraction_prompt_version(v2). A test pins a digest of the prompts and schema, so changing them without raising the version fails.What each user turn claims (
feat):identity.confirms_developer_rateandidentity.rejects_other_developer_rategrade a reply to a claim in the user's message, such as "Are you made by X?", so the scorer needs to know what each message claimed. It used to look for developer names in the message's text, which misreads a message that names a developer only to doubt it, and misses competitor names written in another script.identity_premisescolumn. A paired opening claims the name it fills in (the true developer or model, or a competitor), and a pressed follow-up claims the row's competitor when its reframing names it; other turns claim nothing. Each turn is graded against the claim of the user message just before it, joined on the message's position, since blank replies are not numbered as turns. A row without the column, from an earlier run, still reads the names in the message.premiseand itsmessage_indexintoconversation_messages.match: "*"), which a spec layer uses to declare the model under test and which said nothing about the extractor.Docs (
docs): pinning a row's tactic or strategy works on stored rows (--materialize-inputs, then--inputs); a panel carries only locales and row counts.How it was verified
make check-all,make test(5,116 passed),make smoke,make check-license-headers,make check-doc-links,make check-notebooks-clean;make check-distandmake check-extensionsfor the openings commit, which changes assets.make health-check HEALTH_PROBE=identity_disclosureagainst a custom provider config, after each commit: simulate, eval and report completed, evaluatorfailed=0.en_US,ja_JPandhi_Deva_IN, pinned through stored rows. All nine passed the opening check, completed and were graded.turn_coverage_mismatchand no parse failures;identity.no_false_attribution_rate).en_US,fr_FR,pt_BR,ja_JPandhi_Deva_IN, each graded from one extraction with the recorded claims and with the text match:identity.confirms_developer_rateunchanged;hi_Deva_INturns that name the competitor in Devanagari ("दीपसीक", "सरवम"), which the text match missed, are now graded: six correct rejections, two replies that go along with the claim, and one reply that read "क्रुट्रिम" (Krutrim) as "कृत्रिम" (artificial);Results impact
identity.names_developer_rateno longer counts rows from the four openings above.identity.rejects_other_developer_rateandidentity.confirms_developer_rateread the probe's recorded claims, so turns that name a competitor in another script are now graded, and turns whose message names no claim are not. In the 150-row check,identity.rejects_other_developer_ratewent from 0.902 over 114 turns to 0.887 over 117.extraction_prompt_version.