Desarrolla un agente de canalización de consultas de LlamaIndex

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

Sigue estos pasos para desarrollar un agente con las canalizaciones de consultas 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 canalización de consultas de LlamaIndex.

  1. Define la versión del modelo:

    model = "gemini-2.0-flash" 
  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 los siguientes parámetros de configuración del modelo:

    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 un 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 Anthropic para configurar una cuenta e instalar el paquete llama-index-llms-anthropic.

  2. Define model_builder para que devuelva 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 de 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-2.0-flash",  # Or "meta/llama3-405b-instruct-maas"     model_builder=model_builder,        # Required.     model_kwargs={         "temperature": 0,               # Optional.         "max_retries": 2,               # Optional.     }, ) 

Cómo definir y usar un recuperador

Después de definir el modelo, define el recuperador que usa el modelo para el razonamiento. Un retriever se puede compilar sobre índices, pero también se puede definir de forma integral. Debes probar tu recuperador de forma local.

  1. Define un recuperador que devuelva documentos pertinentes 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="text-embedding-005", 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 en el 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. Prueba el agente de forma local realizando consultas de prueba:

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

    La respuesta es una lista de nodos con puntuaciones que se puede serializar en JSON.

    [{'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'} ] 

Cómo definir y usar un sintetizador de respuestas

Después de definir tu modelo y tu recuperador, define el sintetizador de respuestas que genera una respuesta a partir de un LLM usando una búsqueda del usuario y un conjunto determinado de fragmentos de texto. Puedes usar el get_response_synthesizer predeterminado 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 en el 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 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 para el modelo, lo que guía las respuestas para generar resultados coherentes y pertinentes según el contexto. 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 ve 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, como se muestra 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) Personaliza la organización

Todos los componentes LlamaIndexQueryPipeline implementan la interfaz 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.

Es posible que desees personalizar la organización si planeas realizar alguna de las siguientes acciones:

  • Implementa un agente que extienda la canalización de RAG (por ejemplo, extiende un módulo existente de Prompt, Model, Retriever o Response Synthesizer a Query Engine, Query Transformer, Output Parsers, Postprocessor/Rerankers o Custom Query Component).

  • Solicítale al agente que use ReAct para ejecutar herramientas y anotar 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 Postprocessor) al agente, anula runnable_builder para LlamaIndexQueryPipelineAgent.

  1. Define un posprocesador:

    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. Realiza una 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 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-2.0-flash",   runnable_kwargs={"tools": [get_exchange_rate]},   runnable_builder=runnable_with_tools_builder, ) 
  3. Realiza una 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?