Read the story

Architecture

Answers staff questions with citations.On Cloud Run, paused since October.

Staff sign in with a company Google account and ask in a chat, and the API searches Postgres by meaning and by keyword inside their group permissions, reranks to 8 passages and has Gemini answer from them with citations. Uploaders add documents through Dropoff, where each file is hashed, kept, carded, chunked and embedded before anyone can find it.

55 minutes

for the read path on June 27, from the permission filter to the eval test

10 minutes

the life of a link to a full source file, handed out only after the group check

24 days

from the first commit to real models in production on July 16

8 passages

reach Gemini per question, cut from 50 vector and 50 keyword matches

New York --:-- simulated

A question
Hybridge Enterprise Search Staff sign in with a company Google account and ask in a chat, and the API searches Postgres by meaning and by keyword inside their group permissions, reranks to 8 passages and has Gemini answer from them with citations. Uploaders add documents through Dropoff, where each file is hashed, kept, carded, chunked and embedded before anyone can find it. Identity Platform Google sign in, 1 domain Vertex Gemini embed, cards, answers Vertex Ranking reranks to the top 8 Hybridge staff company Google accounts Ask Hybridge chat and Dropoff, React search-api Cloud Run, FastAPI Postgres 16 pgvector, BM25, groups Cloud Storage original files migrate job Alembic, every deploy use ID token question, SSE ACL filter, hybrid embed, answer top 8 originals schema GCP hybridge-search, us-central1

Hover a part to trace what it touches. Click it for its notes.

  • Built by me
  • Managed by Google
  • Outside system
  • Main data path
  • On demand
  • Credentials cross

When it breaks

What each failure does, and what it does not do.

  1. The reranker fails

    Search falls back to the fused order, cut to 8, and logs the error, so questions still get answers.

    api/app/serving/rerank.py:11

  2. Vertex cannot be reached

    The chat streams a plain message that search is unavailable for now, and nothing broken is saved to the conversation.

    api/app/routers/conversations.py:139

  3. Ingestion stops halfway

    The document is marked failed with its error, or stays processing if the process died. Its owner or an admin can retry it from Dropoff.

    api/app/routers/dropoff.py:248

What I tuned

Small choices that keep it fast and cheap.

  1. Fifty and fifty, then eight

    Top 50 by vector and top 50 by keyword, merged with reciprocal rank fusion at k 60, reranked down to 8 passages for the model.

    api/app/serving/pipeline.py:31

  2. Chunks of 800 tokens

    Layout blocks stay whole when they fit. Longer ones split into 800 token windows with 12 percent overlap, keeping exact character offsets for citations.

    api/app/ingestion/chunking.py:11

  3. One Postgres, both searches

    Vectors and the keyword index sit in the same table under the same filter, so there is no second store to keep in sync.

    api/app/serving/retrieval.py

Guards

Rules the code enforces, not rules people remember.

  1. Permissions inside the query

    Each chunk carries its document's groups. Both searches run with that filter and a current documents only clause in SQL, before any ranking or model call.

    api/app/serving/acl.py:13

  2. No evidence, no model call

    If no passage is visible to the asker, the API returns the fixed abstain line and never calls Gemini. Otherwise Gemini may only use the 8 passages it is given.

    api/app/serving/pipeline.py:38

  3. Links cannot reach inside

    A link added in Dropoff must be http or https, must resolve to a public address, and is fetched without following redirects.

    api/app/ingestion/url_guard.py:11