Desarrolla un agente de canalización de consultas de LlamaIndex

En esta página, se muestra cómo desarrollar un agente con la plantilla de canalizaciones de consulta de LlamaIndex (la clase LlamaIndexQueryPipelineAgent en el SDK de Vertex AI para Python). Este agente está diseñado para responder preguntas con la generación de aumento de recuperación (RAG), como la siguiente consulta: "¿Cómo es la vida de Paul Graham en la universidad?".

Sigue estos pasos para desarrollar un agente con las canalizaciones de consulta de LlamaIndex:

  1. Define y configura un modelo
  2. Define y usa un recuperador
  3. Define y usa un sintetizador de respuestas
  4. (Opcional) Personaliza la plantilla de instrucciones
  5. (Opcional) Cómo personalizar la organización

Antes de comenzar

Asegúrate de que tu entorno esté configurado con los pasos que se indican en Configura tu entorno.

Define y configura un modelo

Define y configura un modelo para que lo use tu agente de LlamaIndex Query Pipelines.

  1. Define la versión del modelo:

    model = "gemini-1.5-flash-001"
    
  2. (Opcional) Especifica los parámetros del modelo:

    model_kwargs = {
        # vertexai_config (dict): By providing the region and project_id parameters,
        # you can enable model usage through Vertex AI.
        "vertexai_config": {
            "project": "PROJECT_ID",
            "location": "LOCATION"
        },
        # temperature (float): The sampling temperature controls the degree of
        # randomness in token selection.
        "temperature": 0.28,
        # context_window (int): The context window of the model.
        # If not provided, the default context window is 200000.
        "context_window": 200000,
        # max_tokens (int): Token limit determines the maximum
        # amount of text output from one prompt. If not provided,
        # the default max_tokens is 256.
        "max_tokens": 256,
    }
    
  3. Crea un LlamaIndexQueryPipelineAgent con las siguientes configuraciones de modelos:

    from vertexai.preview import reasoning_engines
    
    agent = reasoning_engines.LlamaIndexQueryPipelineAgent(
        model=model,                # Required.
        model_kwargs=model_kwargs,  # Optional.
    )
    
  4. Si ejecutas el código en un entorno interactivo (como la terminal o el notebook de Colab), puedes consultar al agente:

    response = agent.query(input="What is Paul Graham's life in college?")
    
    print(response)
    

    Deberías recibir una respuesta similar a la que figura a continuación:

    {'message': {'role': 'assistant',
      'additional_kwargs': {},
      'blocks': [{'block_type': 'text',
        'text': "Unfortunately, there's not a lot of publicly available information about Paul Graham's personal life in college. ..."}]},
      'raw': {'content': {'parts': [{'video_metadata': None,
          'thought': None,
          'code_execution_result': None,
          'executable_code': None,
          'file_data': None,
          'function_call': None,
          'function_response': None,
          'inline_data': None,
          'text': "Unfortunately, there's not a lot of publicly available information about Paul Graham's personal life in college. ..."}],
        'role': 'model'},
        'citation_metadata': None,
        'finish_message': None,
        'token_count': None,
        'avg_logprobs': -0.1468650027438327,
        'finish_reason': 'STOP',
        'grounding_metadata': None,
        'index': None,
        'logprobs_result': None,
        'safety_ratings': [{'blocked': None,
          'category': 'HARM_CATEGORY_HATE_SPEECH',
          'probability': 'NEGLIGIBLE',
          'probability_score': 0.022949219,
          'severity': 'HARM_SEVERITY_NEGLIGIBLE',
          'severity_score': 0.014038086},
        {'blocked': None,
          'category': 'HARM_CATEGORY_DANGEROUS_CONTENT',
          'probability': 'NEGLIGIBLE',
          'probability_score': 0.056640625,
          'severity': 'HARM_SEVERITY_NEGLIGIBLE',
          'severity_score': 0.029296875},
        {'blocked': None,
          'category': 'HARM_CATEGORY_HARASSMENT',
          'probability': 'NEGLIGIBLE',
          'probability_score': 0.071777344,
          'severity': 'HARM_SEVERITY_NEGLIGIBLE',
          'severity_score': 0.024047852},
        {'blocked': None,
          'category': 'HARM_CATEGORY_SEXUALLY_EXPLICIT',
          'probability': 'NEGLIGIBLE',
          'probability_score': 0.103515625,
          'severity': 'HARM_SEVERITY_NEGLIGIBLE',
          'severity_score': 0.05102539}],
        'usage_metadata': {'cached_content_token_count': None,
        'candidates_token_count': 222,
        'prompt_token_count': 10,
        'total_token_count': 232}},
      'delta': None,
      'logprobs': None,
      'additional_kwargs': {}}
    

Personaliza tu modelo (opcional)

La plantilla LlamaIndexQueryPipelineAgent usa Google GenAI de forma predeterminada para proporcionar acceso a todos los modelos fundamentales disponibles en Google Cloud. Para usar un modelo que no está disponible a través de Google GenAI, define model_builder= de la siguiente manera:

from typing import Optional

def model_builder(
    *,
    model_name: str,                      # Required. The name of the model
    model_kwargs: Optional[dict] = None,  # Optional. The model keyword arguments.
    **kwargs,                             # Optional. The remaining keyword arguments to be ignored.
):

Para obtener una lista de los modelos de chat compatibles con LlamaIndexQueryPipeline y sus capacidades, consulta Integraciones de LLM disponibles. Cada modelo de chat usa su propio conjunto de valores admitidos para model= y model_kwargs=.

Google GenAI

La IA generativa de Google se instala de forma predeterminada cuando configuras tu entorno y se usa automáticamente en la plantilla LlamaIndexQueryPipelineAgent cuando omites model_builder.

from vertexai.preview import reasoning_engines

agent = reasoning_engines.LlamaIndexQueryPipelineAgent(
    model=model,                # Required.
    model_kwargs=model_kwargs,  # Optional.
)

Anthropic

  1. Sigue la documentación de Anthroponic para configurar una cuenta e instalar el paquete llama-index-llms-anthropic.

  2. Define model_builder para mostrar el modelo Anthropic:

    def model_builder(*, model_name: str, model_kwargs = None, **kwargs):
        from llama_index.llms.anthropic import Anthropic
    
        return Anthropic(model=model_name, **model_kwargs)
    
  3. Usa el modelo Anthropic en la plantilla LlamaIndexQueryPipelineAgent:

    from vertexai.preview import reasoning_engines
    
    agent = reasoning_engines.LlamaIndexQueryPipelineAgent(
        model="claude-3-opus-20240229",           # Required.
        model_builder=model_builder,              # Required.
        model_kwargs={
            "api_key": "ANTHROPIC_API_KEY",    # Required.
            "temperature": 0.28,                  # Optional.
        },
    )
    

OpenAILike

Puedes usar OpenAILike con la API de ChatCompletions de Gemini.

  1. Sigue la documentación de OpenAILike para instalar el paquete:

    pip install llama-index-llms-openai-like
    
  2. Define un model_builder que muestre el modelo OpenAILike:

    def model_builder(
        *,
        model_name: str,
        model_kwargs = None,
        project: str,   # Specified via vertexai.init
        location: str,  # Specified via vertexai.init
        **kwargs,
    ):
        import google.auth
        from llama_index.llms.openai_like import OpenAILike
    
        # Note: the credential lives for 1 hour by default.
        # After expiration, it must be refreshed.
        creds, _ = google.auth.default(scopes=["https://www.googleapis.com/auth/cloud-platform"])
        auth_req = google.auth.transport.requests.Request()
        creds.refresh(auth_req)
    
        if model_kwargs is None:
            model_kwargs = {}
    
        endpoint = f"https://{location}-aiplatform.googleapis.com"
        api_base = f'{endpoint}/v1beta1/projects/{project}/locations/{location}/endpoints/openapi'
    
        return OpenAILike(
            model=model_name,
            api_base=api_base,
            api_key=creds.token,
            **model_kwargs,
        )
    
  3. Usa el modelo en la plantilla LlamaIndexQueryPipelineAgent:

    from vertexai.preview import reasoning_engines
    
    agent = reasoning_engines.LlamaIndexQueryPipelineAgent(
        model="google/gemini-1.5-pro-001",  # Or "meta/llama3-405b-instruct-maas"
        model_builder=model_builder,        # Required.
        model_kwargs={
            "temperature": 0,               # Optional.
            "max_retries": 2,               # Optional.
        },
    )
    

Define y usa un recuperador

Después de definir el modelo, define el recuperador que este usa para el razonamiento. Un recuperador se puede compilar sobre índices, pero también se puede definir de forma completa. Deberías probar el recuperador de forma local.

  1. Define un recuperador que devuelva documentos relevantes y puntuaciones de similitud:

    def retriever_builder(model, retriever_kwargs=None):
        import os
        import requests
        from llama_index.core import (
            StorageContext,
            VectorStoreIndex,
            load_index_from_storage,
        )
        from llama_index.core import SimpleDirectoryReader
        from llama_index.embeddings.vertex import VertexTextEmbedding
        import google.auth
    
        credentials, _ = google.auth.default()
        embed_model = VertexTextEmbedding(
            model_name="textembedding-gecko@003", project="PROJECT_ID", credentials=credentials
        )
    
        data_dir = "data/paul_graham"
        essay_file = os.path.join(data_dir, "paul_graham_essay.txt")
        storage_dir = "storage"
    
        # --- Simple Download (if needed) ---
        if not os.path.exists(essay_file):
            os.makedirs(data_dir, exist_ok=True)  # Make sure the directory exists
            essay_url = "https://raw.githubusercontent.com/run-llama/llama_index/main/docs/docs/examples/data/paul_graham/paul_graham_essay.txt"
            try:
                response = requests.get(essay_url)
                response.raise_for_status()  # Check for download errors
                with open(essay_file, "wb") as f:
                    f.write(response.content)
                print("Essay downloaded.")
            except requests.exceptions.RequestException as e:
                print(f"Download failed: {e}")
    
        # --- Build/Load Index ---
        if not os.path.exists(storage_dir):
            print("Creating new index...")
            # --- Load Data ---
            reader = SimpleDirectoryReader(data_dir)
            docs = reader.load_data()
    
            index = VectorStoreIndex.from_documents(docs, model=model, embed_model=embed_model)
            index.storage_context.persist(persist_dir=storage_dir)
        else:
            print("Loading existing index...")
            storage_context = StorageContext.from_defaults(persist_dir=storage_dir)
            index = load_index_from_storage(storage_context, embed_model=embed_model)
    
        return index.as_retriever()
    
  2. Prueba el recuperador:

    from llama_index.llms.google_genai import GoogleGenAI
    
    model = GoogleGenAI(
        model=model,
        **model_kwargs
    )
    retriever = retriever_builder(model)
    retrieved_response = retriever.retrieve("What is Paul Graham's life in College?")
    

    La respuesta recuperada debería ser similar a la siguiente:

    [
      NodeWithScore(
        node=TextNode(
          id_='692a5d5c-cd56-4ed0-8e29-ecadf6eb9933',
          embedding=None,
          metadata={'file_path': '/content/data/paul_graham/paul_graham_essay.txt', 'file_name': 'paul_graham_essay.txt', 'file_type': 'text/plain', 'file_size': 75042, 'creation_date': '2025-03-24', 'last_modified_date': '2025-03-24'},
          excluded_embed_metadata_keys=['file_name', 'file_type', 'file_size', 'creation_date', 'last_modified_date', 'last_accessed_date'],
          excluded_llm_metadata_keys=['file_name', 'file_type', 'file_size', 'creation_date', 'last_modified_date', 'last_accessed_date'],
          relationships={
            <NodeRelationship.SOURCE: '1'>: RelatedNodeInfo(node_id='3e1c4d73-1e1d-4e83-bd16-2dae24abb231', node_type='4', metadata={'file_path': '/content/data/paul_graham/paul_graham_essay.txt', 'file_name': 'paul_graham_essay.txt', 'file_type': 'text/plain', 'file_size': 75042, 'creation_date': '2025-03-24', 'last_modified_date': '2025-03-24'}, hash='0c3c3f46cac874b495d944dfc4b920f6b68817dbbb1699ecc955d1fafb2bf87b'), <NodeRelationship.PREVIOUS: '2'>: RelatedNodeInfo(node_id='782c5787-8753-4f65-85ed-c2833ea6d4d8', node_type='1', metadata={'file_path': '/content/data/paul_graham/paul_graham_essay.txt', 'file_name': 'paul_graham_essay.txt', 'file_type': 'text/plain', 'file_size': 75042, 'creation_date': '2025-03-24', 'last_modified_date': '2025-03-24'}, hash='b8e6463833887a8a2b13f1b5a623672819faedc1b725d9565ba003223628db0e'), <NodeRelationship.NEXT: '3'>: RelatedNodeInfo(node_id='f7d2cb7e-fa0c-40bf-b8e7-b888e36b87f9', node_type='1', metadata={}, hash='db7cc1a67fa3afd1e5f24c8c61583781ce6a00c444da8f25a5374468c17b7de0')
          },
          metadata_template='{key}: {value}',
          metadata_separator='\n',
          text='So I looked around to see what I could salvage from the wreckage of my plans, and there was Lisp...',
          mimetype='text/plain',
          start_char_idx=7166,
          end_char_idx=11549,
          metadata_separator='\n',
          text_template='{metadata_str}\n\n{content}'
        ),
        score=0.7403571819090398
      )
    ]
    
  3. Para usar el recuperador dentro de la plantilla LlamaIndexQueryPipelineAgent, agrégalo debajo del argumento retriever_builder=:

    from vertexai.preview import reasoning_engines
    
    agent = reasoning_engines.LlamaIndexQueryPipelineAgent(
        model=model,                          # Required.
        model_kwargs=model_kwargs,            # Optional.
        retriever_builder=retriever_builder,  # Optional.
    )
    
  4. Para probar el agente de forma local, realiza consultas de prueba:

    response = agent.query(
        input="What is Paul Graham's life in College?"
    )
    

    La respuesta es una lista serializable JSON de nodos con puntuaciones.

    [{'node': {'id_': '692a5d5c-cd56-4ed0-8e29-ecadf6eb9933',
      'embedding': None,
      'metadata': {'file_path': '/content/data/paul_graham/paul_graham_essay.txt',
        'file_name': 'paul_graham_essay.txt',
        'file_type': 'text/plain',
        'file_size': 75042,
        'creation_date': '2025-03-12',
        'last_modified_date': '2025-03-12'},
      'excluded_embed_metadata_keys': ['file_name',
        'file_type',
        'file_size',
        'creation_date',
        'last_modified_date',
        'last_accessed_date'],
      'excluded_llm_metadata_keys': ['file_name',
        'file_type',
        'file_size',
        'creation_date',
        'last_modified_date',
        'last_accessed_date'],
      'relationships': {'1': {'node_id': '07ee9574-04c8-46c7-b023-b22ba9558a1f',
        'node_type': '1',
        'metadata': {},
        'hash': '44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a',
        'class_name': 'RelatedNodeInfo'},
        '2': {'node_id': 'ac7e54aa-6fff-40b5-a15e-89c5eb234936',
        'node_type': '1',
        'metadata': {'file_path': '/content/data/paul_graham/paul_graham_essay.txt',
          'file_name': 'paul_graham_essay.txt',
          'file_type': 'text/plain',
          'file_size': 75042,
          'creation_date': '2025-03-12',
          'last_modified_date': '2025-03-12'},
        'hash': '755327a01efe7104db771e4e6f9683417884ea6895d878da882d2b21a6b66442',
        'class_name': 'RelatedNodeInfo'},
        '3': {'node_id': '3a04be27-ac46-4acd-a8c6-031689508982',
        'node_type': '1',
        'metadata': {},
        'hash': 'db7cc1a67fa3afd1e5f24c8c61583781ce6a00c444da8f25a5374468c17b7de0',
        'class_name': 'RelatedNodeInfo'}},
      'metadata_template': '{key}: {value}',
      'metadata_separator': '\n',
      'text': 'So I looked around to see what I could salvage from the wreckage of my plans, and there was Lisp...',
      'mimetype': 'text/plain',
      'start_char_idx': 7164,
      'end_char_idx': 11547,
      'metadata_separator': '\n',
      'text_template': '{metadata_str}\n\n{content}',
      'class_name': 'TextNode'},
      'score': 0.25325886336265013,
      'class_name': 'NodeWithScore'}
    ]
    

Define y usa un sintetizador de respuestas

Después de definir tu modelo y recuperador, define el sintetizador de respuestas que genera una respuesta de un LLM con una consulta del usuario y un conjunto determinado de fragmentos de texto. Puedes usar el get_response_synthesizer predeterminada o configurar el modo de respuesta.

  1. Define un sintetizador de respuestas que muestre la respuesta:

    def response_synthesizer_builder(model, response_synthesizer_kwargs=None):
        from llama_index.core.response_synthesizers import SimpleSummarize
    
        return SimpleSummarize(llm=model)
    
  2. Sigue estos pasos para probar la función:

    response_synthesizer = response_synthesizer_builder(model=model)
    response = response_synthesizer.get_response(
        "What is Paul Graham's life in College?",
        [node.model_dump_json() for node in retrieved_response],
    )
    

    La respuesta podría ser similar a la siguiente:

    "While in a PhD program for computer science, he took art classes and worked on a book about Lisp hacking. He applied to art schools, got accepted to RISD, and later got an invitation to take the entrance exam at the Accademia di Belli Arti in Florence. He was accepted to both. He attended the Accademia, but was disappointed by the lack of instruction."
    
  3. Para usar el sintetizador de respuestas dentro de la plantilla LlamaIndexQueryPipeline, agrégalo debajo del argumento response_synthesizer_builder=:

    from vertexai.preview import reasoning_engines
    
    agent = reasoning_engines.LlamaIndexQueryPipelineAgent(
        model=model,                                                    # Required.
        model_kwargs=model_kwargs,                                      # Optional.
        retriever_builder=retriever_builder,                            # Optional.
        response_synthesizer_builder=response_synthesizer_builder,      # Optional.
    )
    
  4. Ejecuta consultas de prueba para probar la canalización de consultas de RAG completa de forma local:

    response = agent.query(
        input="What is Paul Graham's life in College?"
    )
    

    La respuesta es un diccionario similar al siguiente:

    {
      'response': "While in college, he was drawn to McCarthy's 1960 Lisp, although he didn't fully grasp the reasons for his interest at the time. He also had a brief encounter with surplus Xerox Dandelions in the computer lab but found them too slow for his liking. \n",
      'source_nodes': [
        '{"node":{"id_":"95889c30-53c7-43d0-bf91-930dbb23bde6"...,"score":0.7077213268404997,"class_name":"NodeWithScore"}'
      ],
      'metadata': {
        '95889c30-53c7-43d0-bf91-930dbb23bde6': {
          'file_path': '/content/data/paul_graham/paul_graham_essay.txt',
          'file_name': 'paul_graham_essay.txt',
          'file_type': 'text/plain',
          'file_size': 75042,
          'creation_date': '2025-03-25',
          'last_modified_date': '2025-03-25'
        }
      }
    }
    

(Opcional) Personaliza la plantilla de instrucciones

Las plantillas de instrucciones traducen la entrada del usuario en instrucciones del modelo y guían las respuestas para obtener un resultado contextualmente relevante y coherente. Consulta Instrucciones para obtener más información.

La plantilla de instrucciones predeterminada se organiza de forma secuencial en las siguientes secciones:

Sección Descripción
(Opcional) Instrucción del sistema Son instrucciones para que el agente se aplique en todas las consultas.
Entrada del usuario Es la consulta del usuario que el agente debe responder.

La plantilla de instrucciones predeterminada se genera si creas el agente sin especificar tu propia plantilla de instrucciones y se verá de la siguiente manera:

from llama_index.core import prompts
from llama_index.core.base.llms import types

message_templates = [
  types.ChatMessage(role=types.MessageRole.SYSTEM, content=system_instruction),
  types.ChatMessage(role=types.MessageRole.USER, content="{input}"),
]
prompts.ChatPromptTemplate(message_templates=message_templates)

Puedes usar la plantilla de instrucciones completa cuando crees una instancia del agente en el siguiente ejemplo:

  from vertexai.preview import reasoning_engines

  system_instruction = "I help to find what is Paul Graham's life in College"

  agent = reasoning_engines.LlamaIndexQueryPipelineAgent(
      model=model,
      system_instruction=system_instruction,
  )

Puedes anular la plantilla de instrucciones predeterminada con tu propia plantilla de instrucciones y usarla cuando construyas el agente:

prompt_str = "Please answer {question} about {name}"
prompt_tmpl = PromptTemplate(prompt_str)

from vertexai.preview import reasoning_engines
agent = reasoning_engines.LlamaIndexQueryPipelineAgent(
    model = model,
    prompt = prompt_tmpl,
)

agent.query(
    input={
        "name": "Paul Graham",
        "question": "What is the life in college?",
    }
)

(Opcional) Cómo personalizar la organización

Todos los componentes LlamaIndexQueryPipeline implementan la interfaz de Query Component, que proporciona esquemas de entrada y salida para la organización. LlamaIndexQueryPipelineAgent requiere que se compile un elemento ejecutable para que responda a las consultas. De forma predeterminada, LlamaIndexQueryPipelineAgent compila una cadena secuencial o un grafo acíclico dirigido (DAG) con Query Pipeline.

Te recomendamos que personalices la orquestación si quieres hacer alguna de las siguientes acciones:

  • Implementa un agente que extienda la canalización de RAG (como extender un módulo existente de instrucción, modelo, recuperador o sintetizador de respuestas al motor de consulta, al transformador de consultas, a los analizadores de salida, a los procesadores posteriores o a los reclasificadores o al componente de consulta personalizado).

  • Solicita al agente con ReAct que ejecute herramientas y annote cada paso con comentarios sobre por qué lo realizó. Para ello, anula el ejecutable predeterminado cuando crees el LlamaIndexQueryPipelineAgent especificando el argumento runnable_builder=:

    from typing import Optional
    from llama_index.core.llms import function_calling
    
    def runnable_builder(
        model: function_calling.FunctionCallingLLM,
        *,
        system_instruction: Optional[str] = None,
        prompt: Optional[query.QUERY_COMPONENT_TYPE] = None,
        retriever: Optional[query.QUERY_COMPONENT_TYPE] = None,
        response_synthesizer: Optional[query.QUERY_COMPONENT_TYPE] = None,
        runnable_kwargs: Optional[Mapping[str, Any]] = None,
    ):
    

    Aquí:

    • model corresponde al modelo de chat que se muestra desde model_builder (consulta Define y configura un modelo).
    • retriever y retriever_kwargs corresponden al recuperador y a las configuraciones que se usarán (consulta Define un recuperador).
    • response_synthesizer y response_synthesizer_kwargs corresponden al sintetizador de respuestas y a las configuraciones que se usarán (consulta Define un sintetizador de respuestas).
    • system_instruction y prompt corresponden a la configuración de la instrucción (consulta Cómo personalizar la plantilla de instrucciones).
    • agent_executor_kwargs y runnable_kwargs son los argumentos de palabra clave que puedes usar para personalizar el elemento ejecutable.

Puedes personalizar la lógica de orquestación con una canalización personalizada o ReAct:

Canalización personalizada

Para proporcionar un módulo adicional (como un postprocesador) al agente, anula runnable_builder por LlamaIndexQueryPipelineAgent.

  1. Define un postprocesador:

    def post_processor_builder():
      from llama_index.core.postprocessor import SimilarityPostprocessor
    
      # similarity postprocessor: filter nodes below 0.7 similarity score
      return SimilarityPostprocessor(similarity_cutoff=0.7)
    
    def runnable_with_postprocessor_builder(
        model, runnable_kwargs, **kwargs
    ):
      from llama_index.core.query_pipeline import QueryPipeline
    
      pipeline = QueryPipeline(**runnable_kwargs)
      pipeline_modules = {
          "retriever": retriever_builder(model),
          "postprocessor": post_processor_builder(),
      }
      pipeline.add_modules(pipeline_modules)
      pipeline.add_link("retriever", "postprocessor")
    
      return pipeline
    
    agent = reasoning_engines.LlamaIndexQueryPipelineAgent(
      model=model,
      runnable_builder=runnable_with_postprocessor_builder,
    )
    
  2. Consulta al agente:

    result = agent.query(input="What is Paul Graham's life in College?")
    

    El resultado debería ser similar al siguiente ejemplo:

    [
      {
        'node': {'id_': 'bb7d2942-213d-4fb3-a7cb-1a664642a7ff',
        'embedding': None,
        'metadata': {
          'file_path': '/content/data/paul_graham/paul_graham_essay.txt',
          'file_name': 'paul_graham_essay.txt',
          'file_type': 'text/plain',
          'file_size': 75042,
          'creation_date': '2025-03-25',
          'last_modified_date': '2025-03-25'
        },
        'excluded_embed_metadata_keys': [
          'file_name',
          'file_type',
          'file_size',
          'creation_date',
          'last_modified_date',
          'last_accessed_date'
        ],
        'excluded_llm_metadata_keys': [
          'file_name',
          'file_type',
          'file_size',
          'creation_date',
          'last_modified_date',
          'last_accessed_date'
        ],
        'relationships': {'1': {'node_id': 'c508cee5-5ef2-4fdf-a33d-0427dcb78b5c',
          'node_type': '4',
          'metadata': {'file_path': '/content/data/paul_graham/paul_graham_essay.txt',
            'file_name': 'paul_graham_essay.txt',
            'file_type': 'text/plain',
            'file_size': 75042,
            'creation_date': '2025-03-25',
            'last_modified_date': '2025-03-25'},
          'hash': '0c3c3f46cac874b495d944dfc4b920f6b68817dbbb1699ecc955d1fafb2bf87b',
          'class_name': 'RelatedNodeInfo'},
          '2': {'node_id': '97a84b41-62bf-4959-acae-cfd4bdfbd4d9',
          'node_type': '1',
          'metadata': {'file_path': '/content/data/paul_graham/paul_graham_essay.txt',
            'file_name': 'paul_graham_essay.txt',
            'file_type': 'text/plain',
            'file_size': 75042,
            'creation_date': '2025-03-25',
            'last_modified_date': '2025-03-25'},
          'hash': 'a7dd352be97e47e8e553ceda3d2d2c9e9d5c54adb298063c94da06167938d583',
          'class_name': 'RelatedNodeInfo'},
          '3': {'node_id': 'b984eea1-f0bc-4880-812e-3f49f1e304b8',
          'node_type': '1',
          'metadata': {},
          'hash': 'db7cc1a67fa3afd1e5f24c8c61583781ce6a00c444da8f25a5374468c17b7de0',
          'class_name': 'RelatedNodeInfo'}},
        'metadata_template': '{key}: {value}',
        'metadata_separator': '\n',
        'text': 'So I looked around to see what I could salvage from the wreckage of my plans, and there was Lisp...',
        'mimetype': 'text/plain',
        'start_char_idx': 7166,
        'end_char_idx': 11549,
        'metadata_separator': '\n',
        'text_template': '{metadata_str}\n\n{content}',
        'class_name': 'TextNode'},
        'score': 0.7403571819090398,
        'class_name': 'NodeWithScore'
      },
      {
        'node': {'id_': 'b984eea1-f0bc-4880-812e-3f49f1e304b8...'}
        'score': 0.7297395567513889,
        'class_name': 'NodeWithScore'
      }
    ]
    

Agente de ReAct

Para proporcionar un comportamiento de llamada de herramientas con tu propio agente de ReAct, anula runnable_builder para LlamaIndexQueryPipelineAgent.

  1. Define una función de ejemplo que muestre un tipo de cambio:

    def get_exchange_rate(
      currency_from: str = "USD",
      currency_to: str = "EUR",
      currency_date: str = "latest",
    ):
      """Retrieves the exchange rate between two currencies on a specified date.
    
      Uses the Frankfurter API (https://api.frankfurter.app/) to obtain
      exchange rate data.
    
      Args:
          currency_from: The base currency (3-letter currency code).
              Defaults to "USD" (US Dollar).
          currency_to: The target currency (3-letter currency code).
              Defaults to "EUR" (Euro).
          currency_date: The date for which to retrieve the exchange rate.
              Defaults to "latest" for the most recent exchange rate data.
              Can be specified in YYYY-MM-DD format for historical rates.
    
      Returns:
          dict: A dictionary containing the exchange rate information.
              Example: {"amount": 1.0, "base": "USD", "date": "2023-11-24",
                  "rates": {"EUR": 0.95534}}
      """
      import requests
      response = requests.get(
          f"https://api.frankfurter.app/{currency_date}",
          params={"from": currency_from, "to": currency_to},
      )
      return response.json()
    
  2. Crea un agente de ReAct personalizado con las siguientes herramientas:

    def runnable_with_tools_builder(model, runnable_kwargs=None, **kwargs):
      from llama_index.core.query_pipeline import QueryPipeline
      from llama_index.core.tools import FunctionTool
      from llama_index.core.agent import ReActAgent
    
      llama_index_tools = []
      for tool in runnable_kwargs.get("tools"):
          llama_index_tools.append(FunctionTool.from_defaults(tool))
      agent = ReActAgent.from_tools(llama_index_tools, llm=model, verbose=True)
      return QueryPipeline(modules = {"agent": agent})
    
    agent = reasoning_engines.LlamaIndexQueryPipelineAgent(
      model="gemini-1.5-pro-001",
      runnable_kwargs={"tools": [get_exchange_rate]},
      runnable_builder=runnable_with_tools_builder,
    )
    
  3. Consulta al agente:

    result = agent.query(input="What is the exchange rate between US and EURO today?")
    

    El resultado debe verse de la siguiente manera:

    {
      'response': 'The exchange rate between US and EURO today, 2025-03-19, is 1 USD to 0.91768 EUR.',
      'source_nodes': [],
      'metadata': None
    }
    

¿Qué sigue?