#
Unit
JudgeUnit(DiscreteScale((1, 5)), name="CXEmpathyJudge")
.via('gpt-4o', retries=3, temperature=1.4)
.prompt("""
Score the following customer support conversation on empathy.
<conversation>
...
</conversation>
""")
.extract(RegexExtractor(pattern=RegexExtractor.FIRST_INT))
.propagate(lambda output: Schema.of(score=output.score / 5))
Verdict comes bundled with common LLM-as-a-Judge system building blocks. These wrappers, or units,
- Have well-defined inputs and outputs (see Schema)
- Support arbitrary dependency structures with other units using the
>>
operator - Are configured using chainable directives for
- Model selection with
.via
- Prompt specification (templatable with dependency outputs) with
.prompt
- Structured extraction (regex, uncertainty quantification, etc.) with
.extract
- Arbitrary transformation with
.propagate
- Model selection with
- Are executed in parallel with client-side rate-limiting
- Output
validation and post-processing
#
Anatomy of a Unit
At its heart, a Unit
is simply a wrapper around an LLM inference call with a well-specified input and output. We break this requirement into three components.
- (optional)
InputSchema
, which dependency units must provide (we perform append-only name-casting) - (required)
ResponseSchema
: the raw response from the LLM inference call - (optional)
OutputSchema
: by default, this is the unprocessedResponseSchema
Refer to the Unit Execution Lifecycle section for specific details.
#
Unit Registry
Reusing the same Unit name across different files is not permitted. This is to avoid ambiguity when using the previous
context variable.
#
Built-Ins
#
Defining a Custom Unit
Subclass Unit
to define your own units. We'll walk through the implementation of the PairwiseJudgeUnit
as a case study.
from verdict import Unit
from verdict.schema import Schema
class PairwiseJudgeUnit(Unit):
_char: str = "PairwiseJudge"
class InputSchema(Schema):
A: str
B: str
# This Unit's default Prompt template
_prompt: Prompt = Prompt.from_template("""
You must choose the better option between the following two options based on how well they satisfy the following single criteria:
A:
{input.A}
B:
{input.B}
""")
# This Unit's response produced from LLM inference
# * validate(ResponseSchema) must pass
# * process(ResponseSchema, InputSchema) -> OutputSchema
class ResponseSchema(Schema):
winner: DiscreteScale = DiscreteScale(['A', 'B'])
# This Unit's output
class OutputSchema(Schema):
winner: str
# Validate the ResponseSchema. This is called after the LLM inference call.
def validate(self, response: ResponseSchema, input: InputSchema) -> None:
pass
# Post-process the ResponseSchema into the OutputSchema.
def process(self, response: ResponseSchema, input: InputSchema) -> OutputSchema:
return self.OutputSchema(winner=input.A if response.winner == 'A' else input.B)
#
Subclass Checklist
You can override the following components when creating a custom Unit
.