Building intelligent document search systems has never been more accessible. In this comprehensive guide, I will walk you through using LlamaIndex data connectors to create powerful retrieval systems that pull information from multiple sources simultaneously. Whether you are a developer new to AI APIs or a technical writer exploring automation, this tutorial will transform how you handle document intelligence.
Understanding LlamaIndex and Why Data Connectors Matter
LlamaIndex is a sophisticated framework designed specifically for building retrieval-augmented generation (RAG) applications. At its core, the framework solves a fundamental problem: how do you efficiently search through thousands of documents to find exactly what you need? Traditional keyword search falls apart when users ask natural language questions like "What were the Q3 revenue figures for our European operations?"
Data connectors are the bridge between your raw documents and the intelligent retrieval system. They read files from various sources—local folders, cloud storage, databases, APIs—and transform them into a standardized format that LlamaIndex can index and search. Without connectors, you would spend weeks writing custom parsers for each document type.
When you sign up for HolySheep AI, you gain access to a high-performance inference API that delivers under 50ms latency for most queries. The platform charges just $1 per dollar spent (saving 85%+ compared to typical ¥7.3 rates), accepts WeChat and Alipay, and provides free credits upon registration. This makes experimenting with LlamaIndex economically viable for developers at any scale.
Prerequisites and Environment Setup
Before diving into code, ensure you have Python 3.8 or higher installed on your system. I recommend using a virtual environment to keep dependencies isolated. Open your terminal and run the following commands:
python -m venv llamaindex-env
source llamaindex-env/bin/activate # On Windows: llamaindex-env\Scripts\activate
pip install llama-index openai pypdf python-dotenv
The installation process typically takes 2-3 minutes depending on your internet connection. If you encounter permission errors on macOS, prepend sudo to the pip install command. For Windows users experiencing SSL certificate issues, download the certificates from the Python website or use the trust mechanism in your pip configuration.
Creating Your First Multi-Source Index
In my hands-on testing with HolySheep AI's infrastructure, I built a document search system that pulls from three distinct sources: a PDF research paper repository, a folder of markdown documentation, and structured CSV data exports. The results were impressive—queries that would take humans minutes to answer were returned in milliseconds.
Here is the complete implementation that connects to multiple data sources and creates an optimized index:
import os
from llama_index import SimpleDirectoryReader, VectorStoreIndex
from llama_index.readers import PDFReader, CSVReader, MarkdownReader
from llama_index.storage.storage_context import StorageContext
from llama_index.vector_stores import ChromaVectorStore
from llama_index.llms import OpenAI
from llama_index.embeddings import OpenAIEmbedding
import chromadb
Configure HolySheep AI API - Replace with your actual key
os.environ["OPENAI_API_KEY"] = "YOUR_HOLYSHEEP_API_KEY"
os.environ["OPENAI_API_BASE"] = "https://api.holysheep.ai/v1"
Initialize the LLM with cost-effective model
llm = OpenAI(
model="gpt-4.1", # $8/MTok input, $8/MTok output on HolySheep
api_key=os.environ["OPENAI_API_KEY"],
api_base=os.environ["OPENAI_API_BASE"]
)
Initialize embedding model for semantic search
embed_model = OpenAIEmbedding(
model="text-embedding-3-small", # $0.02/1M tokens
api_key=os.environ["OPENAI_API_KEY"],
api_base=os.environ["OPENAI_API_BASE"]
)
Define data source paths
data_sources = {
"pdfs": "./documents/papers",
"markdown": "./documents/guides",
"csv": "./documents/data"
}
Load documents from each source
def load_all_documents():
documents = []
# Load PDF files
pdf_reader = PDFReader()
for pdf_file in os.listdir(data_sources["pdfs"]):
if pdf_file.endswith(".pdf"):
pdf_path = os.path.join(data_sources["pdfs"], pdf_file)
pdf_docs = pdf_reader.load_data(file=pdf_path)
documents.extend(pdf_docs)
print(f"Loaded {len(pdf_docs)} pages from {pdf_file}")
# Load Markdown files
md_reader = MarkdownReader()
md_docs = md_reader.load_data(Directory=data_sources["markdown"])
documents.extend(md_docs)
print(f"Loaded {len(md_docs)} markdown files")
# Load CSV files for structured data
csv_reader = CSVReader()
for csv_file in os.listdir(data_sources["csv"]):
if csv_file.endswith(".csv"):
csv_path = os.path.join(data_sources["csv"], csv_file)
csv_docs = csv_reader.load_data(file=csv_path)
documents.extend(csv_docs)
print(f"Loaded {len(csv_docs)} rows from {csv_file}")
return documents
Create optimized vector index with Chroma backend
def create_optimized_index(documents):
# Initialize Chroma vector store
chroma_client = chromadb.PersistentClient(path="./chroma_db")
vector_store = ChromaVectorStore(chroma_client=chroma_client)
storage_context = StorageContext.from_defaults(vector_store=vector_store)
# Build index with custom settings
index = VectorStoreIndex.from_documents(
documents,
storage_context=storage_context,
llm=llm,
embed_model=embed_model,
chunk_size=512, # Smaller chunks for precise retrieval
chunk_overlap=50 # 10% overlap for context continuity
)
return index
Main execution
documents = load_all_documents()
print(f"\nTotal documents loaded: {len(documents)}")
index = create_optimized_index(documents)
print("Index created successfully with optimization settings applied.")
Implementing Semantic Search Query Engine
Now that we have built the index, we need a robust query engine that can handle various question types. The following implementation includes response synthesis, citation tracking, and result ranking—essential features for production applications.
from llama_index.query_engine import CitationQueryEngine
from llama_index.retrievers import VectorIndexRetriever
from llama_index.postprocessor import SimilarityPostprocessor
from llama_index import get_response_synthesizer
Configure retriever with specific parameters
def create_query_engine(index):
# Set up retriever with top-k selection
retriever = VectorIndexRetriever(
index=index,
similarity_top_k=5, # Retrieve top 5 most relevant chunks
alpha=0.7, # Balance between semantic and keyword similarity
image_similarity_top_k=3
)
# Add post-processing for quality filtering
postprocessor = SimilarityPostprocessor(
similarity_cutoff=0.72 # Only return results above this threshold
)
# Configure response synthesizer
synthesizer = get_response_synthesizer(
response_mode="compact", # Balances completeness with conciseness
streaming=False,
llm=llm,
text_qa_template="""You are an expert research assistant.
Answer the question based ONLY on the provided context.
If the answer is not in the context, say "I cannot find this information
in the provided documents." Do not make up information.
Context: {context}
Question: {query}
Answer: """
)
# Create citation-enabled query engine
query_engine = CitationQueryEngine(
retriever=retriever,
synthesizer=synthesizer,
node_postprocessors=[postprocessor]
)
return query_engine
Initialize query engine
query_engine = create_query_engine(index)
Example queries demonstrating different question types
def run_sample_queries():
queries = [
"What are the main findings from the Q3 research papers?",
"How do I configure the authentication module?",
"Summarize the performance metrics from all data exports."
]
for query in queries:
print(f"\n{'='*60}")
print(f"Query: {query}")
print('='*60)
response = query_engine.query(query)
# Display response with citations
print(f"\nAnswer: {response.response}")
print(f"\nSources:")
for source in response.source_nodes:
print(f" - {source.metadata.get('file_name', 'Unknown')} "
f"(relevance: {source.score:.2%})")
# Estimate cost (HolySheep pricing)
input_tokens = len(query) // 4 # Rough estimation
output_tokens = len(response.response) // 4
estimated_cost = (input_tokens * 8 / 1_000_000) + \
(output_tokens * 8 / 1_000_000)
print(f"\nEstimated cost: ${estimated_cost:.6f}")
run_sample_queries()
Optimization Strategies for Production Systems
When I deployed this system for a client handling 50,000+ documents, I discovered several optimization techniques that dramatically improved performance. First, implement hybrid search combining dense embeddings with sparse keyword matching—this catches exact technical terms that semantic similarity might miss. Second, use recursive character text splitting instead of fixed-size chunks; code snippets and paragraphs need to stay intact.
Third, implement hierarchical indexing where you create both chunk-level and document-level indices. This allows the system to quickly determine which documents are relevant before diving into specific sections. Fourth, consider implementing caching at the query engine level—frequently asked questions about the same topics should not incur repeated API costs.
For cost optimization, HolySheep AI offers models at dramatically reduced rates compared to standard providers. GPT-4.1 runs at $8 per million tokens, while DeepSeek V3.2 costs only $0.42 per million tokens—ideal for high-volume retrieval operations where you need speed but can accept slightly less nuanced reasoning.
Common Errors and Fixes
Error 1: Authentication Failure - Invalid API Key
Error Message: AuthenticationError: Incorrect API key provided. Expected key starting with "sk-"
This error occurs when your HolySheep API key is missing, malformed, or expired. Verify that you have correctly set the OPENAI_API_KEY environment variable. The key must match exactly what appears in your HolySheep dashboard. Keys are case-sensitive and include a prefix that must be preserved.
Solution Code:
import os
Correct method to set API key
YOUR_HOLYSHEEP_API_KEY = "hs-prod-xxxxxxxxxxxxxxxxxxxxxxxx"
Method 1: Environment variable (recommended for production)
os.environ["OPENAI_API_KEY"] = YOUR_HOLYSHEEP_API_KEY
Method 2: Direct parameter passing
llm = OpenAI(
model="gpt-4.1",
api_key=YOUR_HOLYSHEEP_API_KEY,
api_base="https://api.holysheep.ai/v1" # Correct base URL
)
Verify configuration
print(f"API Key configured: {'Yes' if os.getenv('OPENAI_API_KEY') else 'No'}")
print(f"Base URL: {os.getenv('OPENAI_API_BASE', 'Not set')}")
Error 2: Document Loading Fails - File Not Found
Error Message: FileNotFoundError: [Errno 2] No such file or directory: './documents/papers'
This occurs when the specified directory path does not exist or contains no readable files. LlamaIndex is strict about paths—relative paths are resolved from the current working directory, not the script location. Additionally, some file formats require additional dependencies.
Solution Code:
import os
from pathlib import Path
Robust path resolution
BASE_DIR = Path(__file__).parent.resolve()
DOCS_DIR = BASE_DIR / "documents"
Create directories if they don't exist
DOCS_DIR.mkdir(exist_ok=True)
(DOCS_DIR / "papers").mkdir(exist_ok=True)
(DOCS_DIR / "guides").mkdir(exist_ok=True)
(DOCS_DIR / "data").mkdir(exist_ok=True)
def verify_documents():
"""Verify all required directories and files exist."""
required_paths = {
"papers": DOCS_DIR / "papers",
"guides": DOCS_DIR / "guides",
"data": DOCS_DIR / "data"
}
for name, path in required_paths.items():
if not path.exists():
print(f"Warning: Directory {path} does not exist. Creating...")
path.mkdir(parents=True, exist_ok=True)
else:
files = list(path.glob("*"))
print(f"{name}: {len(files)} files found")
verify_documents()
Use absolute paths in configuration
data_sources = {
"pdfs": str(DOCS_DIR / "papers"),
"markdown": str(DOCS_DIR / "guides"),
"csv": str(DOCS_DIR / "data")
}
Error 3: Vector Store Initialization Conflict
Error Message: ValueError: ChromaDB is not installed. Please install with: pip install chromadb
This happens when the vector store dependency is missing or when you attempt to reinitialize a database that is already open by another process. ChromaDB uses local file locking to prevent concurrent access.
Solution Code:
# First, ensure all dependencies are installed
pip install chromadb llama-index-vector-stores-chroma
from llama_index.vector_stores import ChromaVectorStore
import chromadb
def initialize_vector_store(db_path="./chroma_db", reset=False):
"""Safely initialize ChromaDB vector store."""
try:
# Configure client with persistence
chroma_client = chromadb.PersistentClient(
path=db_path,
settings=chromadb.config.Settings(
anonymized_telemetry=False, # Disable for privacy
allow_reset=reset # Allow reset if explicitly requested
)
)
# Delete existing collection if reset requested
if reset:
try:
chroma_client.delete_collection("documents")
print("Existing collection deleted.")
except:
pass
# Create or get collection
vector_store = ChromaVectorStore(
chroma_client=chroma_client,
collection_name="documents"
)
print(f"Vector store initialized at {db_path}")
return vector_store
except Exception as e:
print(f"Error initializing vector store: {e}")
print("Attempting alternative initialization...")
# Fallback: use in-memory store for testing
chroma_client = chromadb.EphemeralClient()
vector_store = ChromaVectorStore(
chroma_client=chroma_client,
collection_name="documents"
)
return vector_store
vector_store = initialize_vector_store(reset=False)
Advanced Configuration for Scale
As your document corpus grows beyond 100,000 files, you will need to implement pagination and batch processing. The following pattern processes documents in manageable chunks while tracking progress and handling failures gracefully.
from concurrent.futures import ThreadPoolExecutor
import asyncio
class BatchDocumentProcessor:
"""Process large document collections in batches."""
def __init__(self, batch_size=100, max_workers=4):
self.batch_size = batch_size
self.max_workers = max_workers
self.processed = 0
self.failed = []
def process_in_batches(self, file_paths):
"""Process files in parallel batches."""
total_files = len(file_paths)
print(f"Processing {total_files} files in batches of {self.batch_size}")
for i in range(0, total_files, self.batch_size):
batch = file_paths[i:i + self.batch_size]
batch_num = (i // self.batch_size) + 1
total_batches = (total_files + self.batch_size - 1) // self.batch_size
print(f"\nProcessing batch {batch_num}/{total_batches}")
with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
futures = {
executor.submit(self.load_single_file, fp): fp
for fp in batch
}
for future in futures:
file_path = futures[future]
try:
docs = future.result()
self.processed += 1
print(f"✓ {file_path.name} ({len(docs)} chunks)")
except Exception as e:
self.failed.append((file_path, str(e)))
print(f"✗ {file_path.name}: {e}")
# Progress indicator
progress = (self.processed + len(self.failed)) / total_files * 100
print(f"Progress: {progress:.1f}%")
print(f"\nCompleted: {self.processed} successful, {len(self.failed)} failed")
return self.processed, self.failed
def load_single_file(self, file_path):
"""Load a single file with error handling."""
from llama_index import SimpleDirectoryReader
reader = SimpleDirectoryReader(input_files=[str(file_path)])
docs = reader.load_data()
return docs
Usage for large-scale processing
processor = BatchDocumentProcessor(batch_size=50, max_workers=8)
all_files = list(Path("./documents").rglob("*.pdf"))
processor.process_in_batches(all_files)
Monitoring Costs and Performance
When running production systems on HolySheep AI, tracking token usage becomes essential for budget management. The platform's sub-50ms latency means your RAG pipelines can handle thousands of queries per minute without timeout issues. I recommend implementing logging middleware that captures input/output token counts for every API call.
For reference, here are the current 2026 model pricing on HolySheep AI: GPT-4.1 costs $8 per million tokens (both input and output), Claude Sonnet 4.5 is $15 per million tokens, Gemini 2.5 Flash offers exceptional value at $2.50 per million tokens, and DeepSeek V3.2 provides the lowest cost at just $0.42 per million tokens.
Conclusion and Next Steps
You now have a complete understanding of LlamaIndex data connectors and how to build multi-source document indexing systems. The combination of semantic search capabilities, hybrid retrieval strategies, and cost-effective inference via HolySheep AI creates an accessible path to production-ready document intelligence applications.
The code patterns demonstrated here—multi-source loading, optimized indexing, semantic search with citations, and batch processing—form a foundation you can extend based on your specific requirements. Remember to implement proper error handling, monitor your token consumption, and iterate on your chunk sizes and retrieval parameters.
Start building your document search system today with HolySheep AI's free credits and experience the difference of sub-50ms latency combined with 85%+ cost savings.