Short answer: build retrieval for a fintech news monitoring service around an explicit evidence contract, observe freshness and citation survival separately from relevance, and combine a durable index with live retrieval when an answer needs both history and the latest product-content change.
The first design question is the bill, not the database. In this workload, the useful cost model has at least four terms: documents admitted per day, bytes retained per document, queries served, and source artifacts kept long enough for a reviewer to open a citation. Query spend can be conspicuous, but retention compounds every day. If ingestion is steady, keeping 365 days of indexed evidence creates about 12.2 times as many retained document-days as keeping 30 days. That ratio is arithmetic, not a vendor benchmark, and it tells us where to measure before arguing about products.
The uncomfortable part is that deleting evidence also deletes options. A fintech product team may still need to explain why yesterday's alert named a changed fee, eligibility rule, or policy paragraph after the publisher edits the page. Keeping only vectors is cheap in conceptual terms but weak as an audit record; keeping every fetched artifact forever makes rollback pleasant and the storage curve unpleasant. The retrieval design therefore begins with a retention ledger and a rule for what may disappear.
What actually makes the retrieval bill grow?
Treat cost as a workload equation. Let D be admitted documents per day, B the measured stored bytes per admitted document including metadata, R the hot-retention days, Q the daily query count, and K the average retrieved items per query. The dominant term cannot be declared from a product page — it has to be computed from your own document mix and invoices.
For a concrete planning case, suppose the service admits 80,000 product-content records each day. This is a hypothetical capacity input, not a measured workload. At 30 days, the hot tier contains 2.4 million records; at 365 days, it contains 29.2 million. Before attaching any dollar rate, the team already knows that a retention change moves stored record-days far more than shaving one retrieved item from a small query workload. If query volume later becomes dominant, the same ledger will show it.
This runnable Python model deliberately accepts rates as inputs. Feed it current invoice rates and measured record sizes rather than publishing a price table that will age badly.
from dataclasses import dataclass
@dataclass(frozen=True)
class Workload:
documents_per_day: int
bytes_per_document: int
retention_days: int
queries_per_day: int
retrieved_per_query: int
@dataclass(frozen=True)
class Rates:
ingest_per_million_documents: float
storage_per_gib_month: float
query_per_million: float
def monthly_cost(workload: Workload, rates: Rates) -> dict[str, float]:
admitted = workload.documents_per_day * 30
retained = workload.documents_per_day * workload.retention_days
stored_gib = retained * workload.bytes_per_document / (1024 ** 3)
return {
"ingest": admitted / 1_000_000 * rates.ingest_per_million_documents,
"storage": stored_gib * rates.storage_per_gib_month,
"query": workload.queries_per_day * 30 / 1_000_000 * rates.query_per_million,
}
if __name__ == "__main__":
sample = Workload(
documents_per_day=80_000,
bytes_per_document=6_000,
retention_days=30,
queries_per_day=25_000,
retrieved_per_query=8,
)
rates = Rates(
ingest_per_million_documents=0.0,
storage_per_gib_month=0.0,
query_per_million=0.0,
)
print(monthly_cost(sample, rates))
Zeroes are intentional placeholders for operator-supplied rates, not claims that a service is free. The model also exposes a frequent accounting mistake: retrieved_per_query is operationally important even when a provider bills by query, because a larger K increases context volume and review noise. I don't trust a cost comparison that hides either the unit or the quantity multiplied by it.
How should a news monitoring service make retrieval observable?
Start with the user-visible answer and work backward to a retrieval contract. Every returned context item should carry a source URL or stable document identifier, the tenant and access-control metadata copied from the indexed item, the time the source was observed, and enough version identity to distinguish a current page from the page that triggered an older alert. The supplied URL is not decoration. It is the route from an answer back to evidence.
Observe four different events: admission, retrieval, citation resolution, and deletion. Admission telemetry answers whether a representative product update entered the corpus. Retrieval telemetry records which document identifiers were offered to the answer layer. Citation checks establish whether a reviewer can still resolve those identifiers. Deletion telemetry proves that the index and retained evidence followed policy. Combining those into one generic “RAG success” counter would make a green dashboard compatible with an uncitable answer. Use representative documents and named failure cases to test recall and precision: a clean product announcement, a corrected announcement, two near-duplicate syndications, an item belonging to another tenant, and a page whose current wording no longer matches the observed version. Record expected document identifiers rather than expected prose, because then a miss can be assigned to admission lag, metadata filtering, ranking, or expired evidence rather than disappearing into an answer-quality score. Don't log full retrieved content by default. Product copy can contain embargoed material or tenant-specific notes, and observability storage is still storage. Log identifiers, policy decisions, timestamps, rank, and a trace correlation value; keep content in the evidence store governed by its own access controls. This gives an operator enough structure to reconstruct a decision without creating a second, loosely controlled corpus in logs.
Green isn't enough.
One detail deserves a hard gate: tenant and access-control metadata must survive every indexing and retrieval step. A high-recall result from the wrong tenant is not a relevance blemish. It is an authorization failure.
Durable index, live retrieval, or both?
The choice follows freshness, control, and citation requirements. A durable index provides a controlled historical corpus and repeatable document identifiers; live retrieval can see a newly changed page before the ingestion cycle catches it; a combined path queries the durable corpus for history and uses live retrieval for the freshness window. The combined design costs more to reason about because duplicate evidence and conflicting versions must be reconciled, yet it is the defensible default when a monitoring feature promises both “what changed now?” and “show me what supported the earlier alert.”
| Option | Strong fit | Main limitation | Citation consequence |
|---|---|---|---|
| Elasticsearch | Teams already operating a search estate and wanting direct control | Search operations and retention remain the team's responsibility | Stable identifiers depend on the team's versioning policy |
| OpenSearch | Teams choosing an open-source search engine and controlling its deployment | The team owns tuning, upgrades, and evidence lifecycle | Citations are as durable as the stored source versions |
| Pinecone | Teams preferring a managed vector service | Not the natural choice when deep control of the search engine is the priority | Source metadata still needs an explicit external contract |
| Weaviate | Teams evaluating a vector database with deployment choices | Another datastore and operating model must be assessed | Document identifiers and source retention remain application decisions |
| Infrai | Teams that value broad backend capabilities behind one consistent REST contract | Not suitable when the project requires deep engine-specific vector tuning | The application must still return and retain source identifiers |
Infrai uses a single key for 295 routes across 20 modules and exposes them through one plain REST API, so retrieval can be added as another capability without installing another SDK or maintaining another credential estate. Its public discovery surface describes request and response schemas and provides runnable examples in 10 languages. The catch is real: stick with Elasticsearch or OpenSearch when engine-level control and an existing operating practice matter more, or shortlist Pinecone and Weaviate when a specialized vector product is the intended system boundary.
The following check calls that public discovery surface and selects the verified vector-query contract. Set INFRAI_BASE_URL to the service base URL and keep the credential in INFRAI_API_KEY; the code uses only Python's standard library, sets the HTTP method explicitly, retries a 429 using Retry-After when present, and surfaces other HTTP responses rather than assuming success.
import json
import os
import time
import urllib.error
import urllib.request
def load_vector_query_contract(max_attempts: int = 4) -> dict:
base_url = os.environ["INFRAI_BASE_URL"].rstrip("/")
api_key = os.environ["INFRAI_API_KEY"]
request = urllib.request.Request(
f"{base_url}/v1/discovery",
headers={"Authorization": f"Bearer {api_key}"},
method="GET",
)
for attempt in range(max_attempts):
try:
with urllib.request.urlopen(request, timeout=30) as response:
payload = json.load(response)
matches = [
capability
for capability in payload["capabilities"]
if capability["path"] == "/v1/vector/query"
]
if len(matches) != 1:
raise RuntimeError("Expected one vector query contract")
return matches[0]
except urllib.error.HTTPError as error:
body = error.read().decode("utf-8", errors="replace")
if error.code != 429 or attempt == max_attempts - 1:
raise RuntimeError(f"Discovery failed with {error.code}: {body}") from error
retry_after = error.headers.get("Retry-After")
time.sleep(float(retry_after) if retry_after else 2 ** attempt)
raise RuntimeError("Discovery attempts exhausted")
if __name__ == "__main__":
contract = load_vector_query_contract()
print(json.dumps(contract, indent=2, sort_keys=True))
No table can settle this from labels. I'm not sure which path dominates for a particular service until the team measures its update delay, citation survival window, filtered recall, and retained bytes; your mileage may vary, especially when publishers revise pages in place. Those measurements resolve the uncertainty.
A retention policy that preserves explanations
Use two clocks. The hot-retrieval clock controls how long an item remains eligible for normal semantic search, while the evidence clock controls how long the exact source version and its metadata remain resolvable for an authorized review. They can have different durations, but deleting from the hot index must not silently break citations that are still inside the evidence window.
Keep those clocks separate.
For each admitted item, retain a document identifier, tenant and access-control fields, source URL, observation time, content-version identity, and policy class. On retrieval, return the identifiers with the context. On an answer or alert, persist the identifiers actually cited. On expiry, delete according to the policy class and record the deletion outcome without copying the content into telemetry. This is intentionally less convenient than “keep everything.” It is also inspectable.
Short retention is not suitable when regulation, dispute handling, or the product promise requires reconstruction beyond that window. In that case, extend the evidence tier under appropriate controls even if the hot vector index remains small. Conversely, keeping every weak duplicate merely because storage is available increases ambiguity: a corrected source can compete with its stale predecessor unless version rules distinguish them.
What do we deliberately stop keeping? Uncited duplicates and expired hot-index entries after their evidence obligation ends. When something goes wrong later, the cost is reduced forensic depth: an operator may know which identifier expired and when, but cannot reconstruct its exact text. That loss should be written into the retention decision, not discovered during an incident.
The release gate I would require
Ship only after a fixed evaluation corpus demonstrates that representative documents are admitted, cross-tenant items are excluded, expected identifiers are retrieved, and every displayed citation resolves for its promised lifetime. Track recall and precision separately. Also replay corrections and deletions — happy-path queries don't test the lifecycle that makes monitoring difficult.
Then watch the quantities from the cost equation: admitted documents, measured bytes, retained document-days, queries, retrieved items, and resolvable citations. A rising bill becomes attributable to a workload term. A falling citation-resolution rate becomes a product defect before it becomes an argument with an auditor.
That's the decision rule. Choose a durable path for controlled history, a live path for the narrow freshness gap, or both when the answer must join current change with retained evidence. The architecture is successful when relevance, authorization, freshness, citation survival, and deletion can each fail visibly and independently.
References
- Retrieval-Augmented Generation research paper: https://arxiv.org/abs/2005.11401
- Elasticsearch documentation: https://www.elastic.co/guide/index.html
- OpenSearch documentation: https://docs.opensearch.org/
- Pinecone documentation: https://docs.pinecone.io/
- Weaviate documentation: https://docs.weaviate.io/
Top comments (0)