Saltar al contenido principal

Prompting

Aprende cómo construir y optimizar prompts de IA generativa en Flutter usando instrucciones del sistema, parámetros dinámicos y técnicas de control de versiones.

Asumiendo que has configurado tu aplicación Flutter con el proyecto y la configuración de Firebase necesarios para usar el Firebase AI Logic SDK (sobre el cual puedes aprender en el README), ya estás listo para comenzar a usar la IA generativa. La IA generativa es la rama del Aprendizaje Automático (ML) que utiliza una red neuronal entrenada en un gran conjunto de lenguaje humano para producir un Modelo de Lenguaje Grande (LLM). En este punto, los mejores modelos (como Google Gemini) están entrenados en lo que es esencialmente todo el internet.

A esa escala, un modelo entrenado con tal cantidad de datos ha creado modelos capaces de interpretar el lenguaje humano y producir resultados útiles en lenguaje humano. A estas alturas seguro de que ya has usado la aplicación de chat de Gemini (o ChatGPT o Claude u otras aplicaciones de chat), por lo que sabes que si le hablas a un LLM usando un lenguaje impreciso, es probable que obtengas resultados imprecisos y a menudo incorrectos. Si quieres obtener buenos resultados, tendrás que usar buenos prompts.

Construcción de prompts

#

Un prompt es la entrada que le proporcionas a un LLM para obtener la salida que deseas. Incluirá texto, así como cero o más archivos, como imágenes o archivos PDF. Si estás integrando un chat en tu aplicación, entonces el usuario ingresará los prompts (y el Flutter AI Toolkit es útil para crear la interfaz de usuario del chat). Si estás usando un LLM para implementar las características de tu aplicación, como analizar una imagen para obtener los datos de un crucigrama, entonces tú mismo construirás los prompts. La forma en que los construyas importa.

Como ejemplo, al construir el Crossword Companion, el prompt original para resolver pistas se veía así:

dart
You are a crossword puzzle solver. Your goal is to solve the puzzle by filling in the grid with the correct answers. Given the current state of the crossword grid and a single clue, provide the answer for that clue. The answer should be a single word, returned in a JSON object that matches the following schema: '{"type": "object", "properties": {"answer": {"type": "string"}}}'.

# Puzzle Information
## Grid Layout
The grid is (${grid.width}x${grid.height}):
${_getGridStateAsString(grid)}

## Clue
${clue.number} ${clue.direction == ClueDirection.across ? 'Across' : 'Down'}: ${clue.text}

Este prompt no es del todo malo; tiene algunas partes útiles:

  • Persona: la frase "Eres un resolvedor de crucigramas" reduce el enfoque del modelo
  • Contexto: el estado actual del crucigrama
  • Consulta: pedir la solución a una pista
  • Formato: proporcionar la salida en JSON para que el resultado pueda analizarse mediante programación

Sin embargo, debido a la naturaleza bidimensional de los datos, este es un prompt difícil de resolver para algunos modelos. Los resultados de Gemini 2.5 Flash (el más eficiente de los modelos disponibles en ese momento) fueron inconsistentes. La calidad de los resultados de Gemini 2.5 Pro fue excelente, pero eran más lentos y costosos de obtener. La depuración reveló que Pro esencialmente resolvía todo el crucigrama cada vez que era llamado, respondiendo únicamente con la solución a una sola pista.

Lo que se necesitaba era la eficiencia de Flash con la calidad de Pro. Para lograrlo, se requirió trabajar en el prompt:

markdown
Your task is to solve the following crossword clue.

**Clue:** "${clue.text}"

**Constraints:**
- The answer is a **$length-letter** word.
- The current letter pattern is `$pattern`, where `_` represents an unknown letter.

Return your answer and confidence score in the required JSON format.

Este prompt pide resolver la pista, proporciona el contexto importante y especifica el formato de salida. En lugar de entregar todo el estado de la cuadrícula bidimensional, la entrada se limitó al requisito de longitud y a un patrón, como "_ R _ Y". Estas simplificaciones producen resultados de alta calidad de Flash que regresan lo suficientemente rápido como para que sea divertido de ver.
Interfaz de Crossword Companion que muestra una cuadrícula parcialmente resuelta y pistas
con respuestas generadas por IA y puntuaciones de confianza

Capas en tus prompts

#

El prompt utilizado para resolver las pistas no es el único que ve el modelo. También tiene la instrucción del sistema (también conocida como mensaje del sistema o prompt del sistema) que se establece como parte de la creación de la instancia del modelo. Piensa en la instrucción del sistema como "esto es lo que haces", mientras que los prompts individuales son "ahora haz esto".

Aquí está la instrucción parcial del sistema para el modelo de resolución de pistas (verás el resto más adelante):

dart
final clueSolverSystemInstruction =
'''
You are an expert crossword puzzle solver.

**Follow these rules at all times:**
1.  **Prefer Common Words:** Prioritize common English words and proper nouns. Avoid obscure, archaic, or highly technical terms unless the clue strongly implies them.
2.  **Match the Clue:** Ensure your answer strictly matches the clue's tense, plurality (singular vs. plural), and part of speech.
3.  **Verify Grammatically:** If a clue implies a specific part of speech (e.g., it's a verb, adverb, or plural), it's a good idea to use the `getWordMetadata` tool to verify your candidate answer matches. However, avoid using it for every clue.
4.  **Be Confident:** Provide a confidence score from 0.0 to 1.0 indicating your certainty.
5.  **Trust the Clue Over the Pattern:** The provided letter pattern is only a suggestion based on other potentially incorrect answers. Your primary goal is to find the best word that fits the **clue text**. If you are confident in an answer that contradicts the provided pattern, you should use that answer.
6.  **Format Correctly:** You must return your answer in the specified JSON format.
...
''';

Dado el modelo que queremos usar y la instrucción del sistema, ahora tenemos todo lo necesario para crear una instancia:

dart
// The model for solving clues.
_clueSolverModel = FirebaseAI.googleAI().generativeModel(
  model: 'gemini-2.5-flash',
  systemInstruction: Content.text(clueSolverSystemInstruction),
  ...
);

Aunque la instrucción del sistema suele ser estática, los prompts individuales suelen crearse dinámicamente basándose en los datos.

Parametrizar tus prompts

#

Cada prompt de resolución de pistas se crea utilizando el texto de la pista, la longitud objetivo de la respuesta y el patrón obtenido hasta el momento a partir de las pistas resueltas previamente, como "_R_Y":

dart
String getSolverPrompt(Clue clue, int length, String pattern) =>
'''
Your task is to solve the following crossword clue.

**Clue:** "${clue.text}"

**Constraints:**
- The answer is a **$length-letter** word.
- The current letter pattern is `$pattern`, where `_` represents an unknown letter.

Return your answer and confidence score in the required JSON format.
''';

Con el prompt a la mano, podemos pasarlo al modelo para la respuesta de nuestra pista:

dart
final result = await _clueSolverModel.generateContent(
  prompt: getSolverPrompt(clue, length, pattern),
);

Control de versiones de prompts

#

Esta aplicación básica mantiene las cadenas de prompts en el código. Esto hace que sean difíciles de localizar y actualizar. Para aplicaciones de producción, es mejor mantener tus prompts separados del código, quizás agrupados como recursos (assets) de Flutter. Una forma de organizar los archivos de prompts es usar el formato dotprompt de Google, que te permite escribir archivos .prompt que se ven así:

markdown
---
model: googleai/gemini-2.5-flash
input:
  schema:
    text: string
output:
  format: json
  schema:
    title?: string, the title of the article if it has one
    summary: string, a 3-sentence summary of the text
    tags?(array, a list of string tag category for the text): string
---

Extract the requested information from the given text. If a piece of information is not present, omit that field from the output.

Text:

Para expandir un archivo .prompt para su uso en tus proyectos Dart y Flutter, puedes usar el paquete dotprompt_dart.