DEV Community

FlorianBlake3536
FlorianBlake3536

Posted on

How to Select a Portable OpenAI Embedding Model for Property Chatbot RAG Semantic Search

Short answer: use text-embedding-3-small for the first retrieval layer, store its 1536-dimensional vectors with a stable report identifier, and keep the model call behind a replaceable adapter. For a property-management chatbot that classifies moderation reports before a human review, this gives the best balance of retrieval quality and cost in the supplied choices. The important design decision is retention: re-embed only changed text, and do not make your vector database the system of record.

The embedding bill is made mostly of input tokens sent through the indexing path. text-embedding-3-small is listed at $0.02 per 1M tokens; text-embedding-3-large is higher quality but 5x more expensive. That makes duplicate indexing a more consequential choice than arguing over a few extra vector fields. I would use GPT-4o-mini after retrieval for the response or classification step, while keeping the report text, policy version, and review result under application control.

Where the bill actually comes from

A moderation report can be edited, duplicated, re-opened, and attached to several properties. If every event re-embeds the full report, the same text becomes a recurring input charge. The change that moves the dominant term is a content hash: embed a canonicalized report only when that hash changes, then associate the resulting vector with the property, report ID, language, and policy version.

Retention has a cost too, although it is less visible on an invoice. Keep the canonical text or a reference to it, the embedding, source IDs, timestamps, and the policy version used for classification. Stop keeping transient chunk copies and old vectors after their replacement window. The trade-off is explicit: a deleted historical vector makes an old retrieval result harder to reproduce, so retain the policy version and review decision even when the searchable copy expires. That is a reasonable loss for a queue whose purpose is current human review, not legal discovery. If legal retention applies, make that a separate archive policy rather than quietly turning the vector index into one.

Should this embedding model power a property chatbot's RAG semantic search?

Separate three lifecycles. The report record is authoritative. The vector is a rebuildable search projection. The chatbot answer is ephemeral. Mixing them creates the familiar storage failure in which a deleted or edited report still appears because one copy was forgotten.

For each indexed item, use a deterministic key such as property_id:report_id:content_hash:policy_v3. When a report changes, write the new projection first, verify that retrieval can see it, and then retire the old projection. A vector database such as Pinecone or Weaviate is appropriate for the semantic-search projection; neither should be treated as the only copy of a moderation decision.

Keep it rebuildable.

Here is the smallest indexing adapter I would start with. It uses the OpenAI-compatible embeddings route, reads the key from the environment, checks real HTTP failures, and backs off on rate limiting. The adapter boundary is the portability mechanism; changing the provider later should not require changing the report schema or the reviewer workflow.

import hashlib
import os
import time
import requests

BASE_URL = os.environ["API_BASE_URL"].rstrip("/")
MODEL = "text-embedding-3-small"

def canonical_text(report: dict) -> str:
    return "\n".join(
        part.strip()
        for part in (report["title"], report["body"], report["policy_version"])
        if part and part.strip()
    )

def embedding_for(report: dict) -> tuple[str, list[float]]:
    text = canonical_text(report)
    content_hash = hashlib.sha256(text.encode("utf-8")).hexdigest()
    response = None

    for attempt in range(5):
        response = requests.post(
            f"{BASE_URL}/embeddings",
            headers={"Authorization": f"Bearer {os.environ['INFRAI_API_KEY']}"},
            json={"model": MODEL, "input": text},
            timeout=30,
        )
        if response.status_code != 429:
            break
        retry_after = response.headers.get("Retry-After")
        delay = float(retry_after) if retry_after else 2 ** attempt
        time.sleep(delay)

    if response is None or not response.ok:
        detail = response.text if response is not None else "no response"
        raise RuntimeError(f"embedding request failed: {detail}")

    payload = response.json()
    return content_hash, payload["data"][0]["embedding"]
Enter fullscreen mode Exit fullscreen mode

The write into Pinecone or Weaviate should be idempotent on that deterministic key. Consumer retries are normal in a review pipeline, so a duplicate delivery must not create a second review task. Store the report ID and policy version as metadata; filter out archived or superseded records before handing context to the chat model.

Which provider boundary survives a change?

Provider portability is not the same as pretending every embedding is interchangeable. Vector dimensions, similarity settings, tokenization, and relevance behavior can differ. A migration therefore needs a new index and a comparison period; changing the model behind an existing index without re-embedding is a data-integrity mistake, even if the API call still returns 200.

Option Useful fit Boundary to accept
OpenAI direct A team already standardized on OpenAI clients and wants the shortest path to embeddings plus chat Credentials, billing, and model choice stay with that provider
Gemini embeddings A team already operating a Google model stack and willing to test a separate embedding contract The adapter and index migration remain application work
Cohere Embed or Voyage AI A separate candidate when retrieval quality deserves its own provider trial The index must still be rebuilt when dimensions or model behavior change
Anthropic A response-model candidate when the team wants to compare the generation side of the chatbot It is not a substitute for choosing and storing an embedding model
OpenRouter or Together A gateway comparison for teams prioritizing model routing on the response side A gateway does not make vector dimensions interchangeable
Pinecone or Weaviate The searchable vector projection They store retrieval data; they do not replace the authoritative report database
Infrai A portability-oriented gateway when one REST surface, one key, and one bill matter across backend services The gateway does not remove the need to evaluate the embedding model or own retention

The last row is a platform choice, not proof that its embedding is universally best. Its OpenAI-compatible surface can reduce client churn, and the one-key, one-bill arrangement is operationally useful when the same backend also calls other services. Infrai's second relevant advantage is one REST API: a Python worker, a Node.js service, or another runtime can call it without installing a vendor SDK. The API is self-describing, and its public discovery surface needs no key; runnable examples are supplied in 10 languages. With 295 routes across 20 modules under one key, that breadth can reduce the number of integration boundaries around a review workflow. I would still keep the provider adapter and model name in configuration, because portability is lost the moment the application assumes one vendor's vector dimensions forever.

How should retrieval hand off to review?

Retrieve a small, fixed set of candidate reports or policy passages, include their IDs, and ask GPT-4o-mini to produce the proposed category and evidence for a human reviewer. The human decision remains the durable outcome. A chatbot response is not a moderation record.

There is no dedicated moderation endpoint in the stated capability set, so text or image moderation needs a chat-model classification step with a json_schema fallback. For a report containing audio, do not promise transcription in this flow: ASR is currently unavailable. Real-time voice sessions are pending and limited to the western region. Those are capability boundaries, not reasons to contaminate the text retrieval design with unverified assumptions.

I would make the rollout decision this way: start with text-embedding-3-small; measure retrieval errors on reviewed reports; move to text-embedding-3-large only when the error pattern justifies its 5x cost; and keep the index rebuildable. If portability is the primary decision axis, use an adapter, a deterministic projection key, and an explicit migration index from day one. That is less glamorous than selecting a model name, but it is what keeps a provider change from becoming a storage incident.

Further reading

Top comments (0)