Guardrails AIguardrails-ai 0.11.0 · Python 3.10+
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
27 small wins to finish your pathNext lesson

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.

Example
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.

Example
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

Example
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

Example
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.

Try it yourself
  • Set num_reasks=0 and read what comes back instead. The failed answer is in answer.reask.
  • Script both replies too long and watch it give up after one reask.
  • Change the error_message on a custom validator to something vague, then print the reask prompt again and imagine reading it as the model.
PreviousInput guards

Little by little, you're building something great.