07
Search and answers over the document archive
Embeddings in PostgreSQL, an Azure AI Search index, and a RAG layer on top: ask in plain English, get an answer that cites its source.
- Role
- Full Stack Developer · Mavericks Tech Solutions
- When
- Sep 2025 – present
- Relevant for
- AI & AgentsSoftware
- 01 Embed question
- 02 SearchORDER BY embedding <=> $question LIMIT 3
- 03 Answer from chunks
property-policy-wording.pdf
- Covered causes of loss include fire, lightning, windstorm and hail.
- Sudden and accidental discharge of water from a plumbing system is covered.#1 cosine 0.89
- Excluded: seepage or leakage continuing over 14 days or more.#2 cosine 0.83
property-schedule-guide.pdf
- Form PC-1 lists each location with its construction, occupancy, protection and exposure.
- Every building on Form PC-1 needs a replacement value and the year built.
- Sprinkler and alarm details go in the protection section of the form.
underwriting-guidelines.pdf
- Buildings valued up to $5M may be quoted without referral.
- Buildings older than 1950 are referred to a senior underwriter.
- Sprinklered risks receive a 10% rate credit.
claims-handbook.pdf
- Claims should be reported within 30 days of the date of loss.
- Photos and a repair estimate are required for water claims.#3 cosine 0.76
synthetic documents · illustrative scores · cut-off 0.60
answer · PostgreSQL + pgvector
Yes, a burst pipe is a sudden and accidental discharge, which is covered. Slow leaks that went on for 14 days or more are excluded. To claim, send photos and a repair estimate.
The question never says "plumbing"; vectors match it by meaning.
My part
I built the embeddings in PostgreSQL (pgvector), the Azure AI Search index, and the retrieval and answer layer on top.
Problem
Answers lived in years of archived policy wordings, guidelines and claim files. Finding one meant searching folders and reading PDFs end to end, and keyword search missed anything phrased differently.
Approach
Documents are chunked and embedded. Vectors live beside the records in PostgreSQL with pgvector, so similarity search is plain SQL. The same chunks are indexed in Azure AI Search for hybrid retrieval: keyword and vector scores fused, then a semantic reranker. An LLM answers from the top chunks only, citing each one, and says so when nothing scores high enough.
Outcome
Questions over the archive can be asked in plain English, and each answer cites the passage it came from so it can be checked.
What I did
Vectors beside the data
pgvector keeps similarity search in plain SQL; filters by account, date or document type are ordinary WHERE clauses.
PostgreSQLpgvector
Hybrid search
Azure AI Search fuses keyword and vector scores, then reranks, catching exact terms like form numbers.
Azure AI Search
Cited answers
An LLM answers from the retrieved chunks only, citing each one.
Honest "not found"
A score threshold turns weak matches into "not in these documents".
Bedrock prototype
An earlier prototype: documents in an S3 bucket, synced into a Bedrock Knowledge Base (Titan embeddings, OpenSearch Serverless).
AWS BedrockAmazon S3
Stack
- PostgreSQL
- pgvector
- Azure AI Search
- Embeddings
- RAG
- Python
- FastAPI
- AWS Bedrock
- Amazon S3