Enhance README and setup_weaviate.py to clarify tokenization settings for the ProductProfile collection. Added warnings for tokenization drift and updated usage instructions for collection recreation after embedding or tokenization changes.

This commit is contained in:
Arsham Mirehvandi 2026-09-20 17:05:38 +02:00
parent 68f39cfe95
commit 4eec89aa9d
2 changed files with 39 additions and 4 deletions

View File

@ -50,7 +50,9 @@ The pipeline is **idempotent**: a hash manifest (`.processed_manifest.json`) tra
python setup_weaviate.py python setup_weaviate.py
``` ```
The `ProductProfile` collection is configured to embed `situation_fit_summary` with `gemini-embedding-001` and the `RETRIEVAL_DOCUMENT` task type. If you change embedding settings on an existing database, recreate the collection: The `ProductProfile` collection is configured to embed `situation_fit_summary` with `gemini-embedding-001` and the `RETRIEVAL_DOCUMENT` task type. `product_id` uses `tokenization: field` so filters such as `contains_any` match whole IDs (underscores included) rather than ID fragments.
Tokenization and embedding settings cannot be changed in place. After changing them, recreate the collection **and** re-ingest with `--force`. Recreating without `--force` leaves Weaviate empty because the hash manifest would skip unchanged files:
```bash ```bash
python setup_weaviate.py --recreate python setup_weaviate.py --recreate

View File

@ -7,7 +7,7 @@ Uses Google AI Studio (Gemini API) with:
Usage: Usage:
python setup_weaviate.py # create collection if it does not exist python setup_weaviate.py # create collection if it does not exist
python setup_weaviate.py --recreate # delete and recreate (required after embedding config changes) python setup_weaviate.py --recreate # delete and recreate (required after embedding or tokenization changes)
""" """
from __future__ import annotations from __future__ import annotations
@ -30,7 +30,7 @@ except ModuleNotFoundError:
sys.exit(1) sys.exit(1)
from dotenv import load_dotenv from dotenv import load_dotenv
from weaviate.classes.config import Configure, DataType, Property from weaviate.classes.config import Configure, DataType, Property, Tokenization
COLLECTION_NAME = "ProductProfile" COLLECTION_NAME = "ProductProfile"
EMBEDDING_MODEL = "gemini-embedding-001" EMBEDDING_MODEL = "gemini-embedding-001"
@ -62,6 +62,37 @@ def connect_weaviate(cfg: Dict[str, str]) -> weaviate.WeaviateClient:
) )
def _tokenization_name(value: object) -> str:
"""Normalize a Weaviate tokenization enum or string to a lowercase name."""
raw = getattr(value, "value", value)
return str(raw).split(".")[-1].lower()
def _warn_if_product_id_tokenization_drift(client: weaviate.WeaviateClient) -> None:
"""Warn when an existing collection still tokenizes product_id as words."""
collection = client.collections.get(COLLECTION_NAME)
config = collection.config.get()
actual = None
for prop in config.properties:
if prop.name == "product_id":
actual = getattr(prop, "tokenization", None)
break
if actual is not None and _tokenization_name(actual) == _tokenization_name(Tokenization.FIELD):
return
actual_label = _tokenization_name(actual) if actual is not None else "missing"
print(
f"[WARN] Collection '{COLLECTION_NAME}' product_id tokenization is '{actual_label}'; "
f"expected '{_tokenization_name(Tokenization.FIELD)}'. "
"Word tokenization splits underscored IDs, so contains_any filters can match unrelated products. "
"Tokenization cannot be changed in place. Recreate and re-ingest:\n"
" python setup_weaviate.py --recreate\n"
" python ingest.py --force",
file=sys.stderr,
)
def ensure_product_profile_collection( def ensure_product_profile_collection(
client: weaviate.WeaviateClient, client: weaviate.WeaviateClient,
*, *,
@ -76,6 +107,7 @@ def ensure_product_profile_collection(
if recreate: if recreate:
client.collections.delete(COLLECTION_NAME) client.collections.delete(COLLECTION_NAME)
else: else:
_warn_if_product_id_tokenization_drift(client)
return False return False
client.collections.create( client.collections.create(
@ -86,6 +118,7 @@ def ensure_product_profile_collection(
data_type=DataType.TEXT, data_type=DataType.TEXT,
skip_vectorization=True, skip_vectorization=True,
index_filterable=True, index_filterable=True,
tokenization=Tokenization.FIELD,
), ),
Property( Property(
name="product_name", name="product_name",
@ -115,7 +148,7 @@ def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
parser.add_argument( parser.add_argument(
"--recreate", "--recreate",
action="store_true", action="store_true",
help="Delete and recreate the collection (use after changing embedding settings).", help="Delete and recreate the collection (use after changing embedding or tokenization settings).",
) )
return parser.parse_args(argv) return parser.parse_args(argv)