Skip to main content
Scoring generated text by hand does not scale, and a score computed outside the table drifts from the row it describes. Declare the judge as columns on the same table: the answer, the judge prompt, the verdict, and the numeric score each become a computed column, so inserting a test case runs the whole evaluation and the score is stored beside the answer it grades.

The table

Each row is one test case: a prompt, and the criteria the answer is judged against.

The answer under test

The judge prompt

Build it with pxtf.string.format, not Python’s str.format.
template.format(prompt=runs.prompt) does not do what it looks like. Python formats the expression object itself, so every row gets the same string containing the column’s name. pxtf.string.format returns an expression, which is evaluated per row.

The verdict and the score

The judge returns prose. A UDF pulls the number out so you can sort and aggregate on it.
Returning None rather than 0.0 on a malformed verdict matters: a zero is indistinguishable from a genuinely bad answer, and it drags any average you compute.

Run it

Both openai.chat_completions() calls need OPENAI_API_KEY. Inserting a row runs the answer, the judge prompt, the verdict, and the score in order, because each column depends on the one above it.
A UDF cannot be defined in the global namespace of a plain Python script. In a notebook or a REPL the definition above works as written; in a script, put extract_score in an importable module and import it.

Notes

  • Add a criterion and only the affected columns recompute; the answers already generated are not called again.
  • To grade the same answers under two judges, add a second judge_response column with a different model rather than a second table.
  • To grade answers your application already produced, put these columns on that table instead of a separate one. The judge does not need its own copy of the data.
  • pxt.create_table() here is the notebook and test form. An application declares the same columns on a TableModel in app.py and creates them with pxt schema update.
Last modified on September 9, 2026