Repository navigation
Conversation
Quote extraction can now run on a System One model that answers extract questions (Inception Mercury Decide does; Clef, Jev and the pplx deciders answer only choice, noul and score). It is configured apart from the decision model with QUOTE_ENDPOINT_TYPE, QUOTE_MODEL, QUOTE_MODEL_HOST and QUOTE_MODEL_API_KEY, which share the DECISION_* factory, so judgments can stay on local Clef while quotes come from Mercury. - SystemOneClient gains ExtractQuestion and ExtractAnswer. Every returned span is checked against the request state (code-point offsets) and a mismatch is a parse error. - For each topic one extract question marks the sentences where the participant gives a view on it. Spans are widened to whole sentences and non-adjacent sentences are joined with " ... ", so each piece of a quote is verbatim text of the response. A topic with nothing marked gets the whole response; host errors fail the stage. - With QUOTE_ENDPOINT_TYPE unset the generative path is unchanged, and --skip_quote_extraction still takes precedence.
Adds src/models/extraction.py: an Extractor marks exact spans of a text for
named fields. Two backends implement it:
- SystemOneExtractor asks extract questions through a System One host
(Inception Mercury Decide), one question per field.
- ClairicExtractor calls Cloudflare's Clairic on Workers AI, asking a field
that allows several spans as a list of text and a single-span field as
text. List items without offsets are dropped.
Both check every span against the text before returning, and both use the
System One transport's bearer auth and retry policy (the retry loop moves
into systemone.post_with_retry). model_factory.get_extractor(prefix) picks
the backend from {prefix}_ENDPOINT_TYPE=systemone|clairic; Clairic's host
defaults to the Workers AI URL for CLOUDFLARE_ACCOUNT_ID. The quote stage
now takes any extractor via QUOTE_*.
Adds src/participant_context: for each configured attribute, an extraction model (CONTEXT_*, Clairic or a System One extract host such as Mercury Decide) marks the participant's own words as evidence, and the decision model (DECISION_*), when set, picks one of the attribute's labels or "none" for that evidence. A participant who says nothing for an attribute gets no evidence and the label "none" without a question, so nothing is inferred from silence. Without a decision model only evidence is written. The runner reads participant_id and survey_text from a CSV and adds <name>_evidence, <name>_not_stated, <name>_label and <name>_label_probability columns. The default attribute is role (business_owner, parent_or_caregiver, shift_worker, remote_worker); --attributes_file takes a JSON list of others.
Review of the participant-context stage found that an evidence-only attribute kept whatever the extraction model marked: asked how long participants had lived in the city, Clairic marked "older people", "last year" and "suburban" in responses that never say. Neither span confidence nor not_stated separated these from real evidence, so a threshold would not fix it. When a decision model is configured, evidence for an attribute without labels is now checked with one yes/no question (whether the evidence states what the attribute asks, at DECISION_THRESHOLD), and rejected evidence is dropped. Attributes with labels were already checked, as the decision model can answer "none". The README and docstrings no longer claim that silence always yields no evidence: they say evidence is a candidate, checked when a decision model is set and unchecked otherwise.
The gate review of 91f4d80 found the docs still promised the label "none" whenever nothing is marked, which only holds for attributes with labels and a decision model, and an AttributeResult docstring still said evidence is None "when they said nothing". Documentation only.
Gate re-review: AttributeResult.evidence said the decision model keeps or drops evidence whenever one is configured. For an attribute with labels the evidence stays even when the label is "none". Documentation only.
Review found that ClairicExtractor read a field missing from the response as an empty extraction and silently dropped offsets that were not a [start, end] pair, so a malformed response could look like "nothing found". Both are now parse errors. Answers whose offsets are null are still dropped, as before and as documented, since Clairic returns those for text it cannot place.
A blank survey_text reached the extraction model, and Mercury rejects an empty state, so one blank row failed the whole run on Mercury but not on Clairic. Blank responses, including ones made only of empty <response> segments, now reach no model and are treated as saying nothing. Inserting the participant-context section had also split the quote paragraph of the README, leaving its last sentences (sentence widening, the whole-response fallback, --skip_quote_extraction) under the wrong heading. They are back where Jigsaw-Code#124 has them.
This branch has not been deployed
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.
Stacked on #124 (which is stacked on #120). The change here is the commits after #124's (69fcfb2, 91f4d80, a02055b, f5223e7, the merge of #124 ca6f899, and 2849036). Please review #120 and #124 first.
Summary
Surveys often collect few demographics, yet responses carry context such as "I run a bakery" or "my elderly mother relies on paratransit". This adds
src/participant_context, a stage that turns that context into columns, so opinions can be compared across groups.For each configured attribute:
CONTEXT_*, eitherclairicorsystemone, through theExtractorinterface from Extract quotes as exact spans with Mercury or Clairic (stacked on #120) #124) marks the participant's own words as evidence. Every span is checked against the response.DECISION_*), when set, checks that evidence, seeing the whole response too. For an attribute with labels it picks one of them ornone. For an attribute without labels it answers one yes/no question, whether the evidence states what the attribute asks (atDECISION_THRESHOLD), and rejected evidence is dropped.When the extraction model marks nothing, the attribute gets no evidence and no further question is asked; an attribute with labels then gets the label
noneif a decision model is set. Marked evidence is only a candidate, as extraction models sometimes mark unrelated words, which is why the decision model checks it. Without a decision model, evidence is written unchecked and should be reviewed. A blank response reaches no model and is treated as one where nothing is marked, so a blank row no longer fails a run on hosts such as Mercury that reject empty text.The default attribute is
role(business_owner,parent_or_caregiver,shift_worker,remote_worker).--attributes_filetakes a JSON list of others, each with aname, theinstructionsfor what to mark, and optionallabels; the file is validated (unique identifier names, label keys without spaces,nonereserved). The output adds<name>_evidence,<name>_not_stated,<name>_labeland<name>_label_probability.Evaluation
The 28-response transit survey from #124, with role labels written down before the first run (9 participants state a role, 19 do not). Labels came from local Clef in both runs; only the extraction backend differs.
shift_worker(probability 0.80, against 0.98 or more for the correct labels).With 28 responses this is a sanity check, not a benchmark; on this set Clairic is the better default for this stage.
A review run also tried an evidence-only attribute,
tenure("how long they have lived in the city"), which only one participant states ("30 years"). Without a check, Clairic also marked "older people", "last year", "suburban" and two other phrases in responses that never say. Neither span confidence (up to 0.94 on those) nornot_statedseparated them from real evidence, so a threshold would not have fixed it. With the yes/no check (91f4d80) and local Clef, only "30 years" remains, in two Clairic runs and one Mercury run, and the role results are unchanged.Test plan
noneoption, single-span fields,<response>joining, attribute-file validation and the runner's CSV columns; each of these was checked to fail when the behavior is removedCONTEXT_ENDPOINT_TYPE=clairicand with=systemone(Mercury), labels from local Clef: exit 0, results above<response>segments), on Mercury and on Clairic with Clef: exit 0, both blank rowsnone, role results unchangedtenureattribute, with and without a decision model: results above