Volver al inicio

Ajedrez con LLM en Python sin alucinaciones

El artículo describe la implementación de una IA de ajedrez basada en LLM usando python-chess para validación. FEN/PGN/UCI se usan en prompts y fallback para estabilidad. Acceso gratuito vía OpenRouter.

Crea una IA de ajedrez en Llama sin errores
Advertisement 728x90

Construyendo una IA de ajedrez con LLM y Python — Sin coste, sin alucinaciones

Los desarrolladores a menudo necesitan integrar modelos de lenguaje (LLM) en aplicaciones de juegos, pero la API de OpenAI puede ser cara. Una alternativa asequible es OpenRouter, que ofrece modelos de código abierto gratuitos etiquetados como :free. Esto permite ejecutar consultas a Llama 4 sin costo alguno, con límites en el volumen de mensajes.

La inicialización del cliente utiliza la interfaz estándar de openai:

class LLMAI:
    def __init__(self, model_name="meta-llama/llama-4-maverick-17b-128e-instruct:free"):
        self.client = openai.OpenAI(
            base_url="https://openrouter.ai/api/v1",
            api_key=OPENROUTER_API_KEY,
        )
        self.model_name = model_name
        self.move_count = 0

Establecer base_url garantiza compatibilidad con el código existente. El modelo recibe un prompt por cada movimiento, minimizando la carga computacional.

Google AdInline article slot

Pasando el estado del tablero al prompt

Los LLM no procesan tableros visuales, por lo que el contexto debe construirse en formato de texto. Los formatos estándar FEN y PGN se mejoran con una lista de movimientos legales desde python-chess para evitar alucinaciones.

Código para construir el contexto:

# Obtener posición actual en formato FEN
fen = board.fen()

# Historial de movimientos en formato PGN
pgn_moves = []
temp_board = chess.Board()

# Historia de movimientos
for move in board.move_stack:
    pgn_moves.append(temp_board.san(move))
    temp_board.push(move)

pgn_history = " ".join(pgn_moves) if pgn_moves else "Posición inicial"

# Obtener lista de movimientos legales
legal_moves = [move.uci() for move in board.legal_moves]
legal_moves_str = ", ".join(legal_moves)

El sistema de prompts limita las respuestas a notación UCI (4 caracteres, por ejemplo, e2e4):

Google AdInline article slot
prompt = f"""Eres un jugador de ajedrez como {'blancas' if board.turn == chess.WHITE else 'negras'}.

Posición actual (FEN): {fen}
Historial de movimientos: {pgn_history}
Número de movimiento: {self.move_count}

Movimientos disponibles en formato UCI: {legal_moves_str}
Elige el MEJOR movimiento entre las opciones disponibles y devuelve SOLO el código UCI (por ejemplo, e2e4, g1f3, e7e8q).
No añadas explicaciones, análisis ni texto adicional.
La respuesta debe contener únicamente el código UCI."""

Combinar FEN (estado actual), PGN (historia) y lista UCI (restricciones) aumenta la precisión entre un 80 % y un 90 % frente a prompts básicos.

Validación de movimientos y mecanismo de respaldo

Los LLM pueden ignorar instrucciones, devolviendo texto o movimientos ilegales. La solución? Usar el patrón "Árbitro de IA": python-chess valida los movimientos UCI antes de aplicarlos al tablero.

Lógica del bucle de interfaz:

Google AdInline article slot
if move_uci:
    try:
        move = chess.Move.from_uci(move_uci)
        if move in self.board.legal_moves:
            # Animación antes de ejecutar el movimiento
            self.animate_move(move)
            self.board.push(move)
            self.move_history.append(move.uci())
            self.last_ai_response = f"IA generó el movimiento: {move.uci()}"
            print(self.last_ai_response)
        else:
            # Respuesta de emergencia con movimiento aleatorio
            move = random.choice(list(self.board.legal_moves))
            self.animate_move(move)
            self.board.push(move)
            self.move_history.append(move.uci())
            self.last_ai_response = f"IA falló, movimiento aleatorio: {move.uci()}"
            print(self.last_ai_response)
    except ValueError:
        # Respuesta de emergencia con movimiento aleatorio
        move = random.choice(list(self.board.legal_moves))

Ejemplo de registro:

  • Solicitud #1: IA respondió con 'e7e5' → movimiento válido.
  • Solicitud #2: IA respondió con 'Bien, creo que el mejor movimiento es d7d5' → fallback a a7a6.

Esto asegura un juego continuo sin fallos.

Componentes clave de la arquitectura

El proyecto se basa en:

  • python-chess: gestión del tablero, validación de movimientos, manejo de FEN/PGN/UCI.
  • pygame: renderizado del tablero, animaciones de movimientos.
  • openai-client + OpenRouter: llamadas de inferencia del LLM.
  • Lógica de respaldo: random.choice para resiliencia.

Diseño modular: la clase LLMAI aísla la interacción con el LLM de la interfaz, pasando solo movimientos validados. Fácilmente escalable a otros juegos basados en reglas como Go o damas.

| Componente | Rol | Biblioteca |

|-----------|------|---------|

| Tablero | Estado y validación | python-chess |

| LLM | Selección de movimiento | OpenRouter Llama 4 |

| Interfaz | Renderizado e input | pygame |

| Árbitro | Validación | try/except |

Lo que más importa

  • OpenRouter ofrece acceso gratuito a LLM potentes para prototipado, con límites de volumen de solicitudes.
  • Un prompt estricto usando FEN, PGN y listas UCI reduce las alucinaciones al 10–20 %.
  • El patrón "Árbitro de IA" con respaldo garantiza estabilidad: el LLM sugiere, la biblioteca verifica y aplica.
  • python-chess es el estándar de facto para lógica de ajedrez en Python, cubriendo todas las reglas incluyendo enroque y jaque en passant.
  • Este proyecto demuestra cómo integrar modelos no deterministas en aplicaciones deterministas.

— Editorial Team

Advertisement 728x90

Leer después