reask, and the prompt it sends
Lesson 15 stopped a bad question. This lesson deals with a bad answer by asking again, and prints the words Guardrails uses to ask.
from guardrails import Guard
from guardrails_ai.valid_length import ValidLength
from pretend_guardrails import PretendModel
model = PretendModel(replies=["Order 8821 is running late and should arrive on Friday."])
desk = Guard().use(ValidLength(min=1, max=30, on_fail="reask"))
answer = desk(model, messages=[{"role": "user", "content": "Where is order 8821?"}], num_reasks=1)
print(answer.validation_passed)
print(answer.validated_output)
print("model calls:", len(model.prompts))The first reply was scripted to be too long. The Guard noticed, called the model a second time, and the second answer passed. num_reasks is the ceiling on how many extra attempts you are willing to pay for, and it defaults to one.
What the model was told
Three courses describe reasking. None of them shows the text. It is worth reading once, because it explains why lesson 7 cared so much about the wording of error_message.
for message in model.prompts[1]:
print(message["role"], "|", message["content"])The original question is gone. What the model gets is its own previous answer, the error_message the validator produced, and the validator's registered name with its arguments. Then a closing instruction that Guardrails writes for you.
So a validator whose error_message reads invalid is asking the model to guess. One that reads shorter than 30 characters is asking it to do something. The stand-in model in lesson 13 recognises a reask by looking for that first line, which is exactly what a real model does, only better.
The record of a reask
call = desk.history.last
print(len(call.iterations))
print([iteration.status for iteration in call.iterations])
print(len(call.reask_messages))This is the call with two iterations that lesson 4 promised. One Call, one thing the customer asked for; two Iteration entries, two attempts at it.
fix_reask, the cheaper cousin
model = PretendModel(replies=["Order 8821 is running late and should arrive on Friday."])
desk = Guard().use(ValidLength(min=1, max=30, on_fail="fix_reask"))
answer = desk(model, messages=[{"role": "user", "content": "Where is order 8821?"}], num_reasks=1)
print(repr(answer.validated_output))
print("model calls:", len(model.prompts))fix_reask repairs first and revalidates. The repaired value passed, so no second call happened. It only reasks when the repair is not good enough, which makes it the one to reach for when your validator has a decent fix_value and you would rather not pay for another round trip.
These two actions are the only ones that cost a second model call, and the Error and Remediation table is where the full comparison lives.
- Set
num_reasks=0and read what comes back instead. The failed answer is inanswer.reask. - Script both replies too long and watch it give up after one reask.
- Change the
error_messageon a custom validator to something vague, then print the reask prompt again and imagine reading it as the model.
Little by little, you're building something great.