To build a basic retrieval-augmented generation (RAG) app, split your documents into chunks, embed each chunk with Gemini, store the text and vectors in ChromaDB, and retrieve relevant chunks for each question. Then give those passages and the question to Gemini to generate an answer. This tutorial uses explicit Gemini embeddings and a persistent local Chroma database, so the index survives after the Python process ends.
What this RAG app does
RAG combines two distinct operations: retrieval finds passages from your own documents, and generation uses a language model to answer with those passages as context. Chroma stores each chunk alongside its embedding and metadata; Gemini creates embeddings and generates the final response. Retrieval does not guarantee that the answer is correct, so the app should retain source information and be tested against questions whose answers are known and unknown.
As an Amazon Associate I earn from qualifying purchases.
Google’s Gemini embeddings guide describes embeddings as a way to retrieve relevant information for model context. Chroma’s getting-started guide explains collections, documents, metadata, and similarity queries.
Choose how Chroma receives embeddings
| Approach | What happens | Trade-off |
|---|---|---|
| Let Chroma embed text | Provide documents and query text; Chroma uses the collection’s embedding function. | Simpler when a compatible embedding function is configured, but it does not by itself give the explicit Gemini model and task-format control used here. See Chroma’s guide and query documentation. |
| Generate Gemini embeddings explicitly | Call Gemini for document and query vectors, then pass those vectors to Chroma. | Gives direct control of the Gemini model and input formatting. You must keep model, vector dimensions, and task formatting compatible for both corpus and queries. See Google’s guide and Chroma’s add-data documentation. |
The code below uses the second approach. Do not mix vectors from different embedding models or dimensions in one collection. Chroma requires query-vector dimensions to match the collection’s vectors, and Google says the embedding spaces for gemini-embedding-001 and gemini-embedding-2 are incompatible.
#1 Best Overall
- Dual-Brain Hybrid Power: Combines the Qualcomm Dragonwing QRB2210 MPU (Quad-core Arm Cortex-A53 @ 2.0 GHz CPU, Adreno GPU, AI acceleration) and the real-time, low-power STM32U585 MCU for advanced applications like object recognition, voice commands, and motion detection.
- AI & Linux Capabilities: Unlocks AI-powered vision and sound solutions; runs Linux Debian OS for coding in Python and supports the Arduino ecosystem with libraries and Sketches; quick start with Arduino App Lab.
- Advanced Features: Equipped with 4 GB LPDDR4 RAM, 32 GB eMMC built-in storage, ideal for single-board computer (SBC) mode, running multiple simultaneous high-level processes, more complex AI or ML models, extensive logs. Dual-band Wi-Fi 5 (2.4/5 GHz), Bluetooth 5.1, and high-speed headers for vision, audio, and display peripherals.
- Seamless Expansion & Connectivity: Features the classic UNO form factor for shields compatibility, an 8x13 LED matrix, and a Qwiic connector for easy expansion with Modulino nodes; power and connect via the USB-C connector.
- Intended Use & Development: The perfect platform for prototyping robotics or IoT projects, empowering innovators with a unified development experience to mix Arduino Sketches, Python scripts, and containerized AI models in a single interface.
Set up Python, packages, and your API key
Use a virtual environment and install the ChromaDB and Google Gen AI Python packages. Keep your Gemini API key outside source control; the SDK reads it from the GEMINI_API_KEY environment variable when creating a client without an explicit key.
python -m venv .venv
# Activate the environment for your operating system, then:
pip install chromadb google-genai
# Set GEMINI_API_KEY in your shell or development environment.
# Do not commit the key to your repository.
The package installation command is shown in Chroma’s getting-started guide; the Google SDK’s embedding and generation calls are documented in the embedding guide and generate-content API reference.
Prepare and embed your document chunks
Start with a small corpus of text you are permitted to use. Clean extraction artifacts, split long files into manageable passages, and retain a source identifier plus useful location data such as a filename, page, or section. Assign every chunk a stable, unique string ID. Stable IDs let you rerun ingestion with upsert rather than create duplicate records.
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →The following example assumes you have already extracted and chunked text. Each item includes a unique ID, its text, and metadata. Adapt the loader to your file formats; PDF parsing and chunk-size selection are intentionally outside this minimal example.
Rank #2
- Dual-Brain Hybrid Power: Combines the Qualcomm Dragonwing QRB2210 MPU (Quad-core Arm Cortex-A53 @ 2.0 GHz CPU, Adreno GPU, AI acceleration) and the real-time, low-power STM32U585 MCU for advanced applications like object recognition, voice commands, and motion detection.
- AI & Linux Capabilities: Unlocks AI-powered vision and sound solutions; runs Linux Debian OS for coding in Python and supports the Arduino ecosystem with libraries and Sketches; quick start with Arduino App Lab.
- Advanced Features: Equipped with 2 GB LPDDR4 RAM, 16 GB eMMC built-in storage, ideal to develop in PC-connected mode, running the OS, Python scripts, and basic network services (SSH) without a demanding GUI or heavy multitasking; great for lightweight AI and memory-optimized TinyML applications, needing local storage for basic OS and core libraries. Dual-band Wi-Fi 5 (2.4/5 GHz), Bluetooth 5.1, and high-speed headers for vision, audio, and display peripherals.
- Seamless Expansion & Connectivity: Features the classic UNO form factor for shields compatibility, an 8x13 LED matrix, and a Qwiic connector for easy expansion with Modulino nodes; power and connect via the USB-C connector.
- Intended Use & Development: The perfect platform for prototyping robotics or IoT projects, empowering innovators with a unified development experience to mix Arduino Sketches, Python scripts, and containerized AI models in a single interface.
from google import genai
import chromadb
ai = genai.Client()
chroma = chromadb.PersistentClient(path="./chroma_db")
collection = chroma.get_or_create_collection(name="knowledge")
MODEL = "gemini-embedding-2"
DIMENSIONS = 768
chunks = [
{
"id": "handbook-p3-safety",
"text": "Wear eye protection when operating the machine.",
"metadata": {"file": "handbook.pdf", "page": 3, "section": "Safety"},
},
{
"id": "handbook-p4-cleaning",
"text": "Disconnect the machine before cleaning it.",
"metadata": {"file": "handbook.pdf", "page": 4, "section": "Cleaning"},
},
]
def embed_document(text):
result = ai.models.embed_content(
model=MODEL,
contents=f"title: Handbook | text: {text}",
config={"output_dimensionality": DIMENSIONS},
)
return result.embeddings[0].values
vectors = [embed_document(chunk["text"]) for chunk in chunks]
collection.upsert(
ids=[chunk["id"] for chunk in chunks],
documents=[chunk["text"] for chunk in chunks],
embeddings=vectors,
metadatas=[chunk["metadata"] for chunk in chunks],
)
Google’s documentation lists gemini-embedding-2 as stable and latest updated in April 2026. It gives an 8,192-token input limit and output dimensions from 128 to 3,072, with 768, 1,536, and 3,072 recommended. These are model limits and options, not recommended chunk sizes. The example chooses 768 for illustration; use the same dimension for every document and query vector.
For text-only asymmetric retrieval with Embedding 2, Google recommends task instructions in the text. The example formats documents as title: ... | text: ...; format queries for the task as well, for example task: question answering | query: .... Choose the task appropriate to your application and apply it consistently. The current Embedding 2 format uses text instructions rather than Embedding 1’s task_type parameter. If you use gemini-embedding-001 instead, its documented input limit is 2,048 tokens; its dimension range is also 128–3,072. Check the current embeddings documentation before choosing a model or SDK configuration.
When using Embedding 2, do not pass a list of distinct inputs expecting one vector per item: Google notes that multiple inputs can be aggregated into one embedding. This example calls the API separately for each chunk so each stored record receives its own vector. For larger ingestion jobs, Google also documents a Batch API.
Retrieve passages and ask Gemini
At question time, embed the query with the same model and output dimension used for the documents, then call Chroma with query_embeddings. The example requests four results; that is a starting choice, not a universal optimum. Chroma’s query API otherwise returns ten matches per query by default. Inspect the returned documents and metadata before constructing the generation request.
Rank #3
- Single core ARM Cortex-A7 32-bit core, integrated with NEON and FPU
- Built in Micro's self-developed 4th generation NPU, with high computational accuracy and support for mixed quantization of int4, int8, and int16. Among them, int8 has a computing power of 0.5 TOPS and int4 has a computing power of up to 1.0 TOPS
- Built in self-developed 3rd generation ISP3.2, supports 4 million pixels, and supports various image enhancement and correction algorithms such as HDR, WDR, and multi-level denoisin
- It has powerful encoding performance, supports intelligent encoding, adapts to save bit rates according to the scene, and saves more than 50% of the bit rate compared to conventional CBR mode, making the captured images high-definition, smaller in size, and doubling the storage space
- The design with built-in RISC-V MCU supports low-power fast startup, 250ms fast capture, and simultaneous loading of AI model library, enabling facial recognition to be completed within 1 second
def embed_query(question):
result = ai.models.embed_content(
model=MODEL,
contents=f"task: question answering | query: {question}",
config={"output_dimensionality": DIMENSIONS},
)
return result.embeddings[0].values
def answer_question(question):
matches = collection.query(
query_embeddings=[embed_query(question)],
n_results=4,
include=["documents", "metadatas", "distances"],
)
passages = matches["documents"][0]
sources = matches["metadatas"][0]
context = "nn".join(
f"Source: {source}nPassage: {passage}"
for source, passage in zip(sources, passages)
)
response = ai.models.generate_content(
model="gemini-2.5-flash",
contents=(
"Answer the question using only the supplied context. "
"If the context does not contain the answer, say you do not know. "
"Do not invent sources.nn"
f"Context:n{context}nnQuestion: {question}"
),
)
return response.text, sources
text, sources = answer_question("What should I do before cleaning the machine?")
print(text)
print(sources)
The generation call follows Google’s API reference pattern of calling client.models.generate_content with a model and contents. Replace the illustrative generation model identifier with one available to your Gemini API project, and verify current model names in Google’s documentation. The returned source metadata can be rendered alongside an answer so readers can trace the supporting passages.
Chroma supports metadata filters with where and document-content filters with where_document when querying. Use them when questions should be limited to a specific file, section, or other metadata value; consult Chroma’s query documentation for the filter syntax.
Choose storage that matches how you will run the app
| Storage mode | Good fit | What to know |
|---|---|---|
| In-memory client | A disposable demo or short-lived experiment. | Chroma’s getting-started example uses it for simplicity; ingested data is lost when the process terminates. See Chroma’s guide. |
| Persistent local client | A tutorial or single-machine application that should retain its index between runs. | PersistentClient(path="./chroma_db") stores local database files in the specified directory. Back up or exclude that directory according to your project’s data-handling needs. |
| Client-server or hosted deployment | An application that needs shared access or a deployment architecture beyond one local process. | Choose this when deployment requirements justify operating a service; setup and operational details depend on the selected deployment mode. |
Test retrieval separately from generation
A fluent answer can conceal a retrieval failure. Evaluate the retrieved passages before judging the generated response, using representative questions from the corpus and questions that should not be answerable.
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallCrashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minute- For questions with known answers, check whether the retrieved passages actually contain the needed evidence.
- For unrelated questions, check that retrieval does not surface misleading near-matches.
- For questions whose answers are absent, check that Gemini says the context is insufficient instead of filling gaps from general knowledge.
- Adjust chunk boundaries, the number of retrieved results, and prompt instructions based on observed failures; do not label the system accurate without an evaluation.
Common consistency and migration mistakes
Query vectors do not match the collection
Chroma raises an error when supplied vectors do not match the collection’s dimensionality. Ensure documents and queries use the same model and output dimension, and do not attach vectors from a different embedding setup to the collection. See Chroma’s add-data documentation and query documentation.
Rank #4
- 【POWERFUL ESP32‑S3 CONTROLLER】Built‑in Xtensa 32‑bit LX7 dual‑core processor, 512KB SRAM, 8MB PSRAM, 16MB Flash for stable AI voice computing and multitask processing.
- 【Preloaded Dual AI Platforms】Comespre-installed with complete Deepseek and OpenAI voice dialogue projects.Experience intelligent voice interaction instantly. (Note: OpenAI functionality requires your own API key.)
- 【STABLE WIRELESS & CLEAR AUDIO】Integrated 2.4GHz Wi‑Fi + Bluetooth 5 (LE); dedicated audio decoding module for natural, responsive voice interaction.
- 【USER‑FRIENDLY VISUAL & PLUG‑AND‑PLAY】2” TFT‑SPI color screen shows real‑time chat; modular design, no extra wiring, ready to use after setup.
- 【FULL LEARNING SUPPORT】45 programmable GPIOs, rich interfaces, online web tutorials, free technical support for beginners & developers.
Changing embedding models without rebuilding
Google says Embedding 1 and Embedding 2 use incompatible vector spaces. If you migrate an existing index from gemini-embedding-001 to gemini-embedding-2, re-embed the indexed corpus and rebuild the collection rather than querying old vectors with new-model embeddings. See Google’s embedding-model notes.
Repeated ingestion creates unexpected records
Use the same stable chunk IDs on each ingestion run and upsert those records. If IDs change on every run, the database cannot recognize that a chunk is an update to an existing record.
Answers omit evidence provenance
Keep the Chroma IDs and metadata returned with passages, then display useful source details such as file and page. The model’s answer is generated text; retaining retrieval metadata lets the application show which stored passages informed it.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Fix the driver behind crashes, sound loss and screen glitches3Repair Windows errors before they cause bigger problemsQuick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




