Playing Valid Chess Moves
Note
To download this example as a Jupyter notebook, click here.
Warning
This example is currently under development (it cannot be used to play a full chess game yet).
In this example, we will use Guardrails to play chess with an LLM and ensure that it makes valid moves.
Objective
We want to generate a valid chess moves for a given board state.
Step 1: Create the RAIL Spec
Ordinarily, we would create an RAIL spec in a separate file. For the purposes of this example, we will create the spec in this notebook as a string following the RAIL syntax. For more information on RAIL, see the RAIL documentation. We will also show the same RAIL spec in a code-first format using a Pydantic model.
First we define a custom Validator:
from guardrails.validators import Validator, register_validator, ValidationResult, PassResult, FailResult
from typing import Dict, Any
import chess
BOARD = chess.Board()
@register_validator(name="is-valid-chess-move", data_type="string")
class IsValidChessMove(Validator):
board = BOARD
def validate(self, value: Any, metadata: Dict) -> ValidationResult:
global BOARD
try:
# Push the move onto the board.
BOARD.push_san(value)
except Exception as e:
# If the move is invalid, raise an error.
return FailResult(
error_message=f"Value {value} is not a valid chess move. {e}"
)
return PassResult()
Then we can define our RAIL spec either as XML:
rail_str = """
<rail version="0.1">
<output>
<string description="A move in standard algebraic notation." format="is-valid-chess-move" name="move" on-fail-is-valid-chess-move="reask" required="true"></string>
</output>
<prompt>
Generate a move for the chess board. The board is currently in the following state:
${board_state}
${gr.complete_json_suffix}
</prompt>
</rail>
"""
Or as a Pydantic model:
from pydantic import BaseModel, Field
prompt = """
Generate a move for the chess board. The board is currently in the following state:
${board_state}
${gr.complete_json_suffix}
"""
class ChessMove(BaseModel):
move: str = Field(
description="A move in standard algebraic notation.",
validators=[IsValidChessMove(on_fail="reask")]
)
Step 2: Create a Guard
object with the RAIL Spec
We create a gd.Guard
object that will check, validate and correct the output of the LLM. This object:
- Enforces the quality criteria specified in the RAIL spec.
- Takes corrective action when the quality criteria are not met.
- Compiles the schema and type info from the RAIL spec and adds it to the prompt.
From XML:
From a Pydantic model:
We see the prompt that will be sent to the LLM. The {board_state}
is substituted with the current state of the board.
Let's get the reference to the board.
Step 3: Wrap the LLM API call with Guard
The guard
wrapper returns the raw_llm_respose (which is a simple string), and the validated and corrected output (which is a dictionary).
We can see that the output is a dictionary with the correct schema and types.
Let's make a move.
Ask for another move from the model.