Google File Search Basic

Create a Gemini File Search store, upload a document, and query it with citations.

The source snapshot treats a completed upload operation as success without checking its error, and cleanup is not guaranteed on failures. Use the current adaptation below. It validates local inputs before creating stores, checks indexing errors, and attempts cleanup of its own stores in finally.

file_search_basic.py
"""
Google File Search Basic
========================

Cookbook example for `google/gemini/file_search_basic.py`.
"""

from pathlib import Path

from agno.agent import Agent
from agno.models.google import Gemini

# ---------------------------------------------------------------------------
# Create Agent
# ---------------------------------------------------------------------------

# Create Gemini model
model = Gemini(id="gemini-3.7-flash")

# Create agent with the model
agent = Agent(model=model, markdown=True)

print("Creating File Search store...")
store = model.create_file_search_store(display_name="Basic Demo Store")
print(f"[OK] Created store: {store.name}")

print("\nUploading file to store...")
# Upload a file directly to the File Search store
operation = model.upload_to_file_search_store(
    file_path=Path(__file__).parent / "documents" / "sample.txt",
    store_name=store.name,
    display_name="Sample Document",
)

# Wait for upload to complete
print("Waiting for upload to complete...")
completed_op = model.wait_for_operation(operation)
print("[OK] Upload completed")

# Configure model to use File Search
model.file_search_store_names = [store.name]

# Query the documents
print("\nQuerying documents...")
run = agent.run(
    "Can you tell me about the content in the uploaded document? Specifically, what are the main safety guidelines mentioned?"
)
print(f"\nResponse:\n{run.content}")

# Extract and display citations
print("\n" + "=" * 50)
if run.citations and run.citations.raw:
    print("Citations:")
    print("=" * 50)

    # Access grounding metadata directly from citations
    grounding_metadata = run.citations.raw.get("grounding_metadata", {})
    chunks = grounding_metadata.get("grounding_chunks", []) or []

    sources = set()
    for chunk in chunks:
        if isinstance(chunk, dict):
            retrieved_context = chunk.get("retrieved_context")
            if isinstance(retrieved_context, dict):
                title = retrieved_context.get("title", "Unknown")
                sources.add(title)

    if sources:
        print(f"\nSources ({len(sources)}):")
        for i, source in enumerate(sorted(sources), 1):
            print(f"  [{i}] {source}")

        print(f"\nDetailed Citations ({len(chunks)}):")
        for i, chunk in enumerate(chunks, 1):
            if isinstance(chunk, dict):
                retrieved_context = chunk.get("retrieved_context")
                if isinstance(retrieved_context, dict):
                    print(f"\n  [{i}] {retrieved_context.get('title', 'Unknown')}")
                    if retrieved_context.get("uri"):
                        print(f"      URI: {retrieved_context['uri']}")
                    print("      Type: file_search")
                    if retrieved_context.get("text"):
                        text = retrieved_context["text"]
                        if len(text) > 200:
                            text = text[:200] + "..."
                        print(f"      Text: {text}")
    else:
        print("Citations metadata found but no File Search sources detected")
else:
    print("No citations found in response")

# Cleanup
print("\n" + "=" * 50)
print("Cleaning up...")
model.delete_file_search_store(store.name)
print("[OK] Store deleted")

# ---------------------------------------------------------------------------
# Run Agent
# ---------------------------------------------------------------------------

if __name__ == "__main__":
    pass

Current adaptation

File Search stores persist until deleted, subject to embedding-model lifecycle limits. They are separate from the Files API's expiring uploads. These demo scripts create their own stores and use force=True to remove their documents during cleanup. Do not substitute a shared production store name. If cleanup fails, use the reported store name to delete the demo resource after resolving the error.

The operation's done state is not proof of successful indexing: inspect completed.error before querying. Citations depend on the generated answer; an empty citation field is possible.

Save this adaptation as file_search_basic_current.py. The original block above remains the pinned cookbook source.

file_search_basic_current.py
from pathlib import Path

from agno.agent import Agent
from agno.models.google import Gemini

path = Path("cookbook/90_models/google/gemini/documents/sample.txt")
if not path.is_file():
    raise FileNotFoundError(path)
model = Gemini(id="gemini-3.7-flash")
store = model.create_file_search_store(display_name="Basic Demo Store")
try:
    operation = model.upload_to_file_search_store(file_path=path, store_name=store.name)
    completed = model.wait_for_operation(operation)
    if completed.error:
        raise RuntimeError(f"Indexing failed: {completed.error}")
    model.file_search_store_names = [store.name]
    result = Agent(model=model, markdown=True).run("What safety guidelines are documented?")
    print(result.content)
    print(result.citations)
finally:
    model.delete_file_search_store(store.name, force=True)

Run the Example

Set up your virtual environment

uv venv --python 3.12
source .venv/bin/activate

Install dependencies

uv pip install -U agno google-genai

Export your Google API key

export GOOGLE_API_KEY="your_google_api_key_here"

Clone Agno

Clone the pinned Agno source and run the remaining commands from its root:

git clone https://github.com/agno-agi/agno.git
cd agno
git checkout 8f36eaf2d18e91afa7b327eec66a3cd3685dcb87

Prepare demo documents

The required text files are not supplied by the repository. Save this as prepare_documents.py at its root and run python prepare_documents.py. It leaves existing files intact.

prepare_documents.py
from pathlib import Path

folder = Path("cookbook/90_models/google/gemini/documents")
folder.mkdir(parents=True, exist_ok=True)
path = folder / "sample.txt"
if not path.exists():
    path.write_text('Safety: Disconnect power before maintenance. Wear eye protection. Keep emergency exits clear.', encoding="utf-8")

Run the example

Save the current adaptation as file_search_basic_current.py at the repository root, then run:

python file_search_basic_current.py

Full source: cookbook/90_models/google/gemini/file_search_basic.py