220 lines
6.2 KiB
Markdown
220 lines
6.2 KiB
Markdown
# Basic RAG Cookbook
|
|
|
|
Build basic RAG functionality with Weaviate.
|
|
For advanced strategies, [see here](./advanced_rag.md).
|
|
|
|
|
|
Docs to reference if needed:
|
|
- Search patterns and basics in Weaviate: https://docs.weaviate.io/weaviate/search/basics
|
|
- Filters in Weaviate: https://docs.weaviate.io/weaviate/search/filters
|
|
- Vector search: https://docs.weaviate.io/weaviate/search/similarity
|
|
- Keyword search: https://docs.weaviate.io/weaviate/search/bm25
|
|
- Hybrid search: https://docs.weaviate.io/weaviate/search/hybrid
|
|
- Image search: https://docs.weaviate.io/weaviate/search/image
|
|
|
|
## Core Rules
|
|
|
|
- Use a virtual environment via `venv`
|
|
- Use `uv` for Python project/dependency management.
|
|
- Do not manually author `pyproject.toml` or `uv.lock`; let `uv` generate/update them.
|
|
- Use this install set: `uv add weaviate-client python-dotenv dspy`
|
|
- Customise this cookbook to the users specification, ask them for details if not given.
|
|
|
|
Assume the user has data already to be used, do not create data unless asked to.
|
|
|
|
## Env Rules
|
|
|
|
Mandatory:
|
|
- `WEAVIATE_URL`
|
|
- `WEAVIATE_API_KEY`
|
|
|
|
External provider keys:
|
|
- Fill only keys actually used by the target Weaviate collection setup.
|
|
|
|
|
|
## Weaviate Client
|
|
|
|
```python
|
|
import os
|
|
from weaviate import connect_to_weaviate_cloud
|
|
|
|
client = connect_to_weaviate_cloud(
|
|
cluster_url=os.getenv("WEAVIATE_URL", ""),
|
|
auth_credentials=os.getenv("WEAVIATE_API_KEY", ""),
|
|
headers={
|
|
"X-OpenAI-Api-Key": os.getenv("OPENAI_API_KEY")
|
|
},
|
|
)
|
|
```
|
|
|
|
If the user's collections require vectorizer provider keys, set the matching keys listed in `environment_requirements.md`.
|
|
|
|
Clients must be closed after completion. Wrap in `try/finally` blocks with `client.close()` (and `client.connect()` to reconnect if needed).
|
|
|
|
|
|
## Multi-tenancy
|
|
|
|
Multi-tenancy should be checked via
|
|
|
|
```python
|
|
config = await collection.config.get()
|
|
config.multi_tenancy_config.enabled # bool
|
|
```
|
|
|
|
e.g.
|
|
|
|
```python
|
|
|
|
base_collection = client.collections.use(collection_name)
|
|
|
|
config = collection.config.get()
|
|
if config.multi_tenancy_config.enabled:
|
|
collection = base_collection.with_tenant("<tenant_name>")
|
|
else:
|
|
collection = base_collection
|
|
```
|
|
|
|
Tenant names can be obtained via
|
|
```python
|
|
all_tenants = list(collection.tenants.get().keys())
|
|
```
|
|
|
|
## Basic Retrieval
|
|
|
|
Use collections via
|
|
|
|
```python
|
|
collection = client.collections.use("<collection_name>")
|
|
```
|
|
|
|
Weaviate can use vector, keyword or hybrid search.
|
|
|
|
```python
|
|
collection.query.near_text # semantic (text)
|
|
collection.query.bm25 # keyword
|
|
collection.query.hybrid # blend of keyword and semantic
|
|
```
|
|
|
|
It can also do image search
|
|
|
|
```python
|
|
collection.query.near_image(
|
|
near_image = ... # base 64 representation of image or Path object to image
|
|
)
|
|
```
|
|
|
|
## Key Code Blocks
|
|
|
|
RAG should have 4 pieces of core functionality:
|
|
|
|
1. Pre-retrieval
|
|
2. Retrieval
|
|
3. Post-retrieval
|
|
4. Generation
|
|
|
|
These should all be separate functions and combined into a single function, leaving scope for later editing or for the user themselves to modify it, to keep it understandable.
|
|
|
|
## Pre-retrieval
|
|
|
|
Transform the user question into a vector-database style (list of) query(ies). Basic RAG will provide no extra query transformations.
|
|
|
|
```python
|
|
def query_transformation(query: str) -> list[str]:
|
|
return [query]
|
|
```
|
|
|
|
## Retrieval
|
|
|
|
```python
|
|
def retrieve(
|
|
query: str,
|
|
limit: int = 10, # optional
|
|
filters = [] # optional
|
|
# additional arguments if required can go here and passed down to the search strategy
|
|
) -> list[dict]:
|
|
|
|
# import client logic here
|
|
|
|
collection = client.collections.use("<collection_name>")
|
|
|
|
response = collection.query.near_text( # or hybrid, bm25, near_image
|
|
query=query,
|
|
limit=limit,
|
|
filters=filters if filters else None
|
|
)
|
|
|
|
return [
|
|
{
|
|
**obj.properties,
|
|
"uuid": obj.uuid
|
|
}
|
|
for obj in response.objects
|
|
]
|
|
```
|
|
|
|
## Post-Retrieval
|
|
|
|
Modify the output of `retrieve`. Basic RAG will provide no extra post-processing. But you can consider adding uniqueness checks, formatting to remove properties, or more.
|
|
|
|
```python
|
|
def process_retrieval_results(objects: list[dict]) -> list[dict]:
|
|
return objects
|
|
```
|
|
|
|
|
|
## Generation
|
|
|
|
This step depends on your LLM framework, [see below](#user-specific-customisations). Using DSPy:
|
|
|
|
```python
|
|
import dspy
|
|
def generate(query: str, context: list[dict]) -> str:
|
|
lm = dspy.LM("<model_name>") # e.g. gpt-5.2, gpt-5-mini, claude-sonnet-4-5, etc.
|
|
answer = dspy.Predict("context, query -> answer") # inputs: context, query. outputs: answer
|
|
pred = answer(context=context, query=query, lm=lm)
|
|
return pred.answer # answer is then an attribute of pred
|
|
```
|
|
|
|
## User-specific Customisations
|
|
|
|
If not specified ask the user about these points before implementing their respective strategies:
|
|
|
|
**LLM Framework**
|
|
|
|
You can use DSPy (works with all LiteLLM providers) or LiteLLM itself.
|
|
|
|
- DSPy: https://dspy.ai/learn/programming/language_models/
|
|
- LiteLLM: https://docs.litellm.ai/docs/
|
|
|
|
Alternatively, users can use a single model provider. What model provider will they use?
|
|
|
|
- OpenAI (https://platform.openai.com/docs/libraries)
|
|
- Anthropic (https://platform.claude.com/docs/)
|
|
- Google GenAI (https://ai.google.dev/gemini-api/docs/libraries)
|
|
- Other (such as locally hosted models), use best judgement
|
|
|
|
These may require additional installs.
|
|
|
|
**Collections**
|
|
|
|
Do collections already exist and what are they called? Does the user want to query multiple collections or just a single one? Does it need to be customisable?
|
|
|
|
What format is the data, images or text or something else? What vectoriser is the collection set up as? What API keys are needed?
|
|
|
|
**Search strategy**
|
|
|
|
Does the user want semantic, keyword or hybrid search?
|
|
|
|
Hybrid search has an `alpha` parameter, controlling tradeoff between keyword and semantic weights. `alpha=1` is pure semantic, `alpha=0` is pure keyword.
|
|
|
|
|
|
## Troubleshooting
|
|
|
|
- Weaviate startup host errors: ensure `WEAVIATE_URL` is full `https://...` URL.
|
|
- For any other issues, refer to the official library/package documentation and use web search extensively for troubleshooting.
|
|
|
|
## Done Criteria
|
|
|
|
- Create test scripts to check each function works independently with test data. Tear down tests after completion, or create a proper test suite with pytest (requires install)
|
|
- User has completed specification of the app.
|