Inicio / Artículos / Notas prácticas: Receta de Spring AI: Filtrado de resultados RAG con metadatos

Notas prácticas: Receta de Spring AI: Filtrado de resultados RAG con metadatos

Guía paso a paso operativa de las notas prácticas: Receta de Spring AI: Filtrado de resultados RAG con metadatos; contratos, verificaciones y espacios para código listo para uso destinados a los equipos que implementan este patrón.

2112 palabras

Esta guía reconstruye el proceso desde las materias primas hasta un sistema funcional para: Spring AI Recipe: Filtrado de resultados RAG con metadatos. El enfoque está en pasos operativos, verificaciones explícitas y código que se puede incorporar directamente a un repositorio sin necesidad de adivinar la intención. En la etapa de visión general, defina las entradas, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Registre los tiempos de ejecución y el costo en tokens o consultas junto con los resultados funcionales. La visibilidad temprana del costo evita facturas inesperadas cuando el proceso pasa de una demostración a entornos compartidos.

On each player's turn, they place a dog, food bowl, or toy into
one of the three play yards. The game ends when a player has
placed their last dog into a yard. Then each player who hasn't
played in that round gets to place one more item into a yard
until the last player so that all players have an equal number
of turns.
On their turn, a player starts by moving any flies on the board
to the same space as an adjacent frog (effectively the frog has
eaten that fly). Then the player can either place 1 fly adjacent
to a frog on the board as a distraction so that the frog won't
jump or they may place a frog of their player color onto any
empty space. If the frog is adjacent to another frog (even if it
is their own), the adjacent frog will jump two spaces in any
direction away from the frog that was just placed. The direction
of the jump is decided by the current player. If the frog jumps
off the board, then that frog is out of play.

The game ends when all players have placed all of their frogs
(not counting frogs that have jumped off the board). The player
with the most frogs remaining on the board wins.
When it is a player's turn, they will draw a card from the
ingredients deck and add it to their hand. Then they may play
any ingredient card from their hand to add that ingredient to
one of up to three tacos in progress. Or, if they have a crunch
card in their hand, then they may play it to remove one
ingredient from any one opponent's taco. The first player to
completely build 3 tacos wins.
@Configuration
public class RagIngestionConfig {

  private static final Logger logger =
      LoggerFactory.getLogger(RagIngestionConfig.class);

  @Value("${rag.documents}")
  Resource[] documentResources;

  @Bean
  @Order(-1)
  ApplicationRunner load(VectorStore vectorStore) {
    return args -> {
      for (Resource documentResource : documentResources) {
        var filename = documentResource.getFilename();
        logger.info("Loading document from {}.", filename);
        var reader = new TikaDocumentReader(documentResource);
        var splitter = TokenTextSplitter.builder().build();
        vectorStore.accept(
            splitter.apply(
                reader.get()));
      }
      logger.info("Document loading complete.");
    };
  }
}
How can I help?

> What can I do on my turn?

 - Draw a card from the ingredients deck and add it to your hand.
 - Then you may either:
    - Play any ingredient card from your hand to add that ingredient
      to one of up to three tacos in progress, or
    - Play a crunch card (if you have one) to remove one ingredient
      from any one opponent's taco.
 - On your turn you place a dog, a food bowl, or a toy into one of
   the three play yards.
 - The game ends when a player has placed their last dog; then players
   who haven't yet played in that round get one more placement so all
   players have equal turns.

Agregar metadatos a los documentos

Al trabajar en la sección de Agregar metadatos al escenario, anote primero el contrato: los datos de entrada requeridos, la señal de éxito y qué ocurre en caso de un fallo parcial. Esa lista de verificación ayuda a mantener honestas las futuras modificaciones del código. Guarde la configuración fuera del código de la aplicación. Los archivos de entorno, los almacenes de secretos y las banderas de funcionalidad deben estar en un lugar donde los operadores puedan auditarlos sin tener que leer todo el sistema. Mida la tasa de recuperación con un conjunto fijo de preguntas antes de ajustar los prompts. El cambio constante de prompts rara vez soluciona un sistema de recuperación deficiente.

@Bean
@Order(-1)
ApplicationRunner load(VectorStore vectorStore) {
  return args -> {
    for (Resource documentResource : documentResources) {
      var filename = documentResource.getFilename();

  logger.info("Loading document from {}.", filename);
      var reader = new TikaDocumentReader(documentResource);
      var splitter = TokenTextSplitter.builder().build();

      var titleTag =
          filename.substring(0, filename.lastIndexOf('.'));

      vectorStore.accept(
          splitter.apply(
              reader.get().stream()
                  .peek(document ->
                      document.getMetadata()
                          .put("title", titleTag))
                  .toList()));
    }
    logger.info("Document loading complete.");
  };
}
title = camp-bowwow

Determinar de qué juego pregunta el usuario

Al trabajar en la fase de determinar qué juego corresponde, anote primero el contrato: los datos necesarios, la señal de éxito y qué ocurre en caso de fallo parcial. Esa lista de verificación ayuda a mantener honestas las futuras modificaciones del código. Documente tanto el camino óptimo como el de recuperación. Las reintentos, los controles humanos y el manejo de mensajes no entregados forman parte del producto, no son mejoras posteriores. Mida la capacidad de recuperación con un conjunto fijo de preguntas antes de ajustar los prompts. El cambio constante de prompts rara vez soluciona un sistema de recuperación deficiente.

@Service
public class TitleHelper {

  private final ChatClient chatClient;

  public TitleHelper(ChatModel chatModel) {
    this.chatClient = ChatClient.builder(chatModel).build();
  }

  public String determineGameTitle(String question) {
    var title = chatClient.prompt()
        .user(userSpec -> userSpec
            .text("""
                Your job is to try to determine the title of a game from
                the question asked.
                The game choices are:
                - camp-bowwow
                - frog-panic
                - taco-truck
                - unknown
                If the game's title isn't explicitly mentioned in the
                question, or you don't recognize the game's title, then
                say "unknown".
                The question is:
                {question}
                """)
            .param("question", question))
        .call()
        .content();
    return title.equals("unknown") ? null : title;
  }
}

Filtrado de la búsqueda vectorial

Al trabajar en la etapa de Filtrado de la Búsqueda Vectorial, anote primero el contrato: entradas requeridas, señal de éxito y qué ocurre en caso de fallo parcial. Esa lista de verificación mantiene honestas las futuras modificaciones del código. Prefiera unidades pequeñas y probables a scripts extensos. Cuando un paso falla, el fallo debe apuntar a una única responsabilidad y no a un proceso complicado. Mida el recuerdo en un conjunto fijo de preguntas antes de ajustar los prompts. El cambio constante de prompts rara vez soluciona un sistema de recuperación deficiente. Al trabajar en la etapa de Filtrado de la Búsqueda Vectorial, anote primero el contrato: entradas requeridas, señal de éxito y qué ocurre en caso de fallo parcial. Esa lista de verificación mantiene honestas las futuras modificaciones del código. Registre los tiempos y el costo en tokens o consultas junto con los resultados funcionales. La visibilidad temprana del costo evita facturas inesperadas cuando el proceso pasa de la versión de demostración a entornos compartidos.

@Bean
ApplicationRunner go(
    ChatClient chatClient,
    TitleHelper titleHelper) {

  return args -> {
    System.out.println("How can I help?\n");

    try (Scanner scanner = new Scanner(System.in)) {
      while (true) {
        System.out.print("> ");
        if (!scanner.hasNextLine()) break;
        var input = scanner.nextLine();
        if (input.isBlank()) continue;

        var requestSpec = chatClient.prompt(input);

        var gameTitle =
            titleHelper.determineGameTitle(input);

        var answer = requestSpec
            .advisors(spec -> {
              spec.param(
                  ChatMemory.CONVERSATION_ID,
                  "DEMO");
              if (gameTitle != null) {
                spec.param(
                    QuestionAnswerAdvisor.FILTER_EXPRESSION,
                    String.format(
                        "title == '%s'",
                        gameTitle));
              }
            })
            .call()
            .content();

        System.out.println("\n - " + answer);
      }
    }
  };
}
title == 'camp-bowwow'

Prueba en práctica

La etapa de prueba en práctica funciona mejor cuando se trata como una superficie medible. Capture una transcripción exitosa, un caso de fallo y la nota de reversión antes de ampliar el alcance. Mantenga la configuración fuera del código de la aplicación. Los archivos de entorno, los almacenes de datos confidenciales y las banderas de funcionalidad deben estar en un lugar donde los operadores puedan auditarlos sin tener que leer todo el sistema. Separe la política de fragmentación de la política de recuperación. Cambiar una no debe obligar a reescribir la otra cuando cambian las métricas de calidad.

How can I help?

> I'm playing Camp Bowwow. What do I do when it's my turn?

 - On your turn you place one item - either a dog, a food bowl,
   or a toy - into one of the three play yards.

(For reference: the game ends when a player has placed their last
dog; then any players who haven't yet played in that round each get
one more item turn in order until the last player, so all players
have equal turns.)
> I'm playing Frog Panic. What do I do when it's my turn?

- On your turn:
1. First, move any flies on the board onto the same space as an
   adjacent frog (the frog eats that fly).
2. Then choose one action:
   - Place 1 fly adjacent to a frog on the board as a distraction
     (so that frog won't jump), or
   - Place a frog of your color onto any empty space.
If the frog you place is adjacent to another frog (even your own),
that adjacent frog jumps two spaces away in any direction you choose.
If a frog jumps off the board it is out of play.

(For reference: the game ends when all players have placed all their
frogs; the player with the most frogs remaining on the board wins.)

Más que similitud

La etapa de “Más que similitud” funciona mejor cuando se trata como una superficie medible. Capture un transcripto ideal, un caso de fallo y la nota de reversión antes de ampliar el alcance.

Lista de verificación operativa

La etapa de la lista de verificación operativa funciona mejor cuando se trata como una superficie medible. Capture un transcripto ideal, un caso de fallo y la nota de reversión antes de ampliar el alcance.

Trate esta etapa como un contrato entre las entradas y las salidas validadas. Asigne nombres a los artefactos, defina comprobaciones de éxito y rechace las completaciones parciales silenciosas.

Separar la política de fragmentación de la política de recuperación. Cambiar una no debería obligar a reescribir la otra cuando cambian las métricas de calidad.

Añadir una prueba de funcionamiento que ejerza la ruta crítica en el proceso de integración continua utilizando configuraciones fijas, y no APIs pagadas en tiempo real, siempre que lo permitan los presupuestos.

Registrar los tiempos de ejecución y el costo de tokens o consultas junto con los resultados funcionales. Tener visibilidad del costo desde el principio evita facturas inesperadas cuando la ruta pasa de una versión de demostración a entornos compartidos.

Separar la política de fragmentación de la política de recuperación. Cambiar una no debería obligar a reescribir la otra cuando cambian las métricas de calidad.

Antes de promocionar la solución, congelar las versiones, capturar un registro completo de la ruta crítica y confirmar los pasos para revertir cambios. Los entornos compartidos necesitan límites de uso, verificaciones de asignación y un responsable claro para la rotación de credenciales secretas. Es mejor priorizar una fiabilidad sólida que demostraciones ingeniosas pero puntuales.

Nota por lotes para bef2f8a7cb72: mantener las claves del proveedor fuera del repositorio, establecer un límite para los tokens por sesión y almacenar las transcripciones junto a los archivos de evaluación para que los cambios posteriores en el modelo sigan siendo comparables.

Para la nota de reforzamiento de la etapa 0, defina las entradas, el responsable de la tarea y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la tarea a partir de un punto de control conocido sin tener que adivinar el estado oculto. Documente tanto la ruta óptima como la ruta de recuperación. Las intentonas, los controles humanos y el manejo de mensajes no entregados forman parte del producto, no son mejoras posteriores.

Detalle de reforzamiento 0/963: mida el tiempo de ejecución, la clase del error y el consumo de tokens para esta nota, y luego decida si mantener el cambio basándose en un conjunto fijo de preguntas en lugar de en anécdotas.

Al trabajar en la fase 1 de las notas de fortalecimiento, anote primero el contrato: los datos de entrada requeridos, la señal de éxito y qué ocurre en caso de fallo parcial. Esa lista de verificación mantiene honestas las futuras modificaciones del código. Considere esta fase como un contrato entre los datos de entrada y las salidas validadas. Asigne nombres a los artefactos, defina comprobaciones de éxito y rechace las completaciones parciales silenciosas.

Detalle de fortalecimiento 1/963: mida el tiempo de ejecución, la clase del error y el consumo de tokens para esta nota, y luego decida si mantener el cambio basándose en un conjunto fijo de preguntas en lugar de en anécdotas.