LlamaIndex クエリ パイプライン エージェントを開発する

このページでは、LlamaIndex Query Pipelines テンプレート(Vertex AI SDK for Python の LlamaIndexQueryPipelineAgent クラス)を使用してエージェントを開発する方法について説明します。このエージェントは、検索拡張生成(RAG)を使用して質問に回答するように設計されています。たとえば、「ポール・グレアムの大学生活はどのようなものでしたか?」というクエリに回答します。

LlamaIndex Query Pipelines を使用してエージェントを開発する手順は次のとおりです。

  1. モデルを定義して構成する
  2. リトリーバーを定義して使用する
  3. レスポンス シンセサイザーを定義して使用する
  4. (省略可)プロンプト テンプレートをカスタマイズする
  5. (省略可)オーケストレーションをカスタマイズする

始める前に

環境の設定の手順に沿って環境が設定されていることを確認します。

モデルを定義して構成する

LlamaIndex Query Pipeline エージェントが使用するモデルを定義して構成します。

  1. モデル バージョンを定義します。

    model = "gemini-2.0-flash"
    
  2. (省略可)モデル パラメータを指定します。

    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. 次のモデル構成を使用して LlamaIndexQueryPipelineAgent を作成します。

    from vertexai.preview import reasoning_engines
    
    agent = reasoning_engines.LlamaIndexQueryPipelineAgent(
        model=model,                # Required.
        model_kwargs=model_kwargs,  # Optional.
    )
    
  4. インタラクティブな環境(ターミナルや Colab ノートブックなど)で実行している場合は、エージェントにクエリを実行できます。

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

    次のようなレスポンスが返されます。

    {'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': {}}
    

(省略可)モデルをカスタマイズする

LlamaIndexQueryPipelineAgent テンプレートは、 Google Cloudで利用可能なすべての基盤モデルにアクセスできるように、デフォルトで Google GenAI を使用します。Google GenAI で使用できないモデルを使用するには、次のように model_builder= を定義します。

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.
):

LlamaIndexQueryPipeline でサポートされているチャットモデルとその機能の一覧については、使用可能な LLM インテグレーションをご覧ください。各チャットモデルは、model=model_kwargs= に独自のサポート対象値のセットを使用します。

Google GenAI

Google GenAI は、環境を設定するとデフォルトでインストールされます。model_builder を省略すると、LlamaIndexQueryPipelineAgent テンプレートで自動的に使用されます。

from vertexai.preview import reasoning_engines

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

Anthropic

  1. Anthropic のドキュメントに沿って、アカウントを設定し、llama-index-llms-anthropic パッケージをインストールします。

  2. Anthropic モデルを返す model_builder を定義します。

    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. LlamaIndexQueryPipelineAgent テンプレートで Anthropic モデルを使用します。

    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

OpenAILike は、Gemini の ChatCompletions API と組み合わせて使用できます。

  1. OpenAILike ドキュメントに沿ってパッケージをインストールします。

    pip install llama-index-llms-openai-like
    
  2. OpenAILike モデルを返す model_builder を定義します。

    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. 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.
        },
    )
    

リトリーバーを定義して使用する

モデルを定義したら、モデルが推論に使用するリトリーバーを定義します。retrieverインデックスの上に構築できますが、包括的に定義することもできます。ローカルでリトリーバーをテストする必要があります。

  1. 関連するドキュメントと類似性スコアを返すリトリーバーを定義します。

    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. リトリーバーをテストします。

    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?")
    

    取得したレスポンスは次のようになります。

    [
      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. LlamaIndexQueryPipelineAgent テンプレートでリトリーバーを使用するには、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. テストクエリを実行して、エージェントをローカルでテストします。

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

    レスポンスは、スコア付きのノードの 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'}
    ]
    

レスポンス シンセサイザーを定義して使用する

モデルとリトリーバーを定義したら、ユーザークエリと特定のテキスト チャンクのセットを使用して LLM からレスポンスを生成するレスポンス シンセサイザーを定義します。デフォルトの get_response_synthesizer を使用するか、レスポンス モードを構成できます。

  1. 回答を返すレスポンス シンセサイザーを定義します。

    def response_synthesizer_builder(model, response_synthesizer_kwargs=None):
        from llama_index.core.response_synthesizers import SimpleSummarize
    
        return SimpleSummarize(llm=model)
    
  2. 関数をテストします。

    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],
    )
    

    レスポンスの例を以下に示します。

    "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. LlamaIndexQueryPipeline テンプレートでレスポンス シンセサイザーを使用するには、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. テストクエリを実行して、完全な RAG クエリ パイプラインをローカルでテストします。

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

    レスポンスは、次のような辞書形式になります。

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

(省略可)プロンプト テンプレートをカスタマイズする

プロンプト テンプレートは、ユーザー入力をモデルの指示に変換し、コンテキストに関連性があり一貫性のある出力のレスポンスをガイドします。詳しくは、プロンプトをご覧ください。

デフォルトのプロンプト テンプレートは、次のセクションに順番に編成されています。

セクション 説明
(省略可)システム指示 すべてのクエリに適用されるエージェント向けの手順。
ユーザー入力 ユーザーからのエージェントに渡すクエリ。

独自のプロンプト テンプレートを指定せずにエージェントを作成すると、デフォルトのプロンプト テンプレートが生成されます。このテンプレートは、次のようになります。

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)

次の例では、エージェントをインスタンス化すると、プロンプト テンプレートの全体が使用されます。

  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,
  )

デフォルトのプロンプト テンプレートを独自のプロンプト テンプレートでオーバーライドし、エージェントの作成時に使用できます。

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?",
    }
)

(省略可)オーケストレーションをカスタマイズする

すべての LlamaIndexQueryPipeline コンポーネントは、オーケストレーション用の入力スキーマと出力スキーマを提供する Query Component インターフェースを実装します。LlamaIndexQueryPipelineAgent では、クエリに応答するために実行可能ファイルをビルドする必要があります。デフォルトでは、LlamaIndexQueryPipelineAgentQuery Pipeline を使用してシーケンシャル チェーンまたは有向非巡回グラフ(DAG)を構築します。

次のいずれかを行う場合は、オーケストレーションをカスタマイズすることをおすすめします。

  • RAG パイプラインを拡張するエージェントを実装します(既存のプロンプト、モデル、リトリーバー、レスポンス シンセサイザー モジュールを Query EngineQuery TransformerOutput ParsersPostprocessor/RerankersCustom Query Component に拡張するなど)。

  • ReAct を使用してエージェントにプロンプトを送り、ツールを実行して、各ステップにそのステップを実行した理由をコメントでアノテーションします。そのためには、runnable_builder= 引数を指定して LlamaIndexQueryPipelineAgent を作成するときに、デフォルトの実行可能ファイルをオーバーライドします。

    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,
    ):
    

    ここで

カスタム パイプラインまたは ReAct を使用して、オーケストレーション ロジックをカスタマイズできます。

カスタム パイプライン

エージェントに追加のモジュール(Postprocessor など)を提供するには、LlamaIndexQueryPipelineAgentrunnable_builder をオーバーライドします。

  1. Postprocessor を定義します。

    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. エージェントにクエリを実行します。

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

    出力例を以下に示します。

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

ReAct エージェント

独自の ReAct エージェントでツール呼び出し動作を提供するには、LlamaIndexQueryPipelineAgentrunnable_builder をオーバーライドします。

  1. 為替レートを返す関数を定義します。

    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. ツールを使用してカスタム ReAct エージェントを作成します。

    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. エージェントにクエリを実行します。

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

    出力は次のようになります。

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

次のステップ