Industry

Pixeltable for document-heavy insurance

A claims desk files PDFs: FNOL forms, photos, estimates, medical notes. Retrieval has to cite a passage, not only a vector. Pixeltable is the document table, the splitter, the index, and the LLM column. Keep S3, a policy admin system, and Qdrant or pgvector when those are already the contract.

Updated · Matching docs cookbook

First notice of loss arrives as a PDF, then photos, then a repair estimate, then a medical note. A desk asks: what did the insured say about prior damage? The answer has to point at a passage. A chat model that is handed the whole file does not scale across a claim file that grows every day, and it does not leave a row you can re-run when the model changes.

This is multimodal RAG with a claim_id. Photos use the image index on the same backend when the question is visual. Unstructured or LlamaParse stay in front when layout-heavy ACORD forms need a parser Pixeltable does not replace. Pixeltable is not a system of record for policies, and this page does not claim a compliance certification.

FNOL and claim-file pipeline

  1. Insert claim_id, doc_kind (fnol, estimate, medical, photo), and the PDF or image.
  2. Split PDFs with document_splitter (sentence,token_limit).
  3. Index passage text. Index photos with CLIP when the file is an image.
  4. Retrieve passages for a desk question, filtered by claim_id.
  5. Generate a summary from those passages only. Keep the prompt so a reviewer can re-run it.

Worked example: retrieve FNOL passages for one claim, then summarize

The prompt tells the model to use only retrieved passages. That is a grounding instruction, not a guarantee against hallucination. Keep the claim file in S3 if that bucket is the archive. Keep pgvector if Postgres is already the claims store and you only need ANN. Lineage of which model produced which summary is the computed column on that row; Pixeltable versions the table when you apply a new expression.

import pixeltable as pxt
from pixeltable.functions.document import document_splitter
from pixeltable.functions.huggingface import sentence_transformer
from pixeltable.functions.openai import chat_completions
TableModel = pxt.model_base()
text_embed = sentence_transformer.using(model_id='all-MiniLM-L6-v2')
class ClaimDocs(TableModel, name='claim_docs'):
claim_id: pxt.String
doc_kind: pxt.String
document: pxt.Document
class Passages(
TableModel,
name='passages',
base=ClaimDocs,
iterator=document_splitter(
document=ClaimDocs.document, separators='sentence,token_limit', limit=300
),
):
__indexes__ = [
pxt.EmbeddingIndex(text, string_embed=text_embed),
]
@pxt.query
def retrieve_claim(question: str, claim_id: str, limit: int = 8):
sim = Passages.text.similarity(string=question)
return (
Passages.where(ClaimDocs.claim_id == claim_id)
.order_by(sim, asc=False)
.limit(limit)
.select(
doc_kind=ClaimDocs.doc_kind,
text=Passages.text,
score=sim,
)
)
@pxt.udf
def build_claim_prompt(question: str, passages: list[dict]) -> str:
lines = '\n'.join(f"[{row['doc_kind']}]: {row['text']}" for row in passages)
return (
'Answer using only the retrieved passages. If they do not contain the answer, say so.\n\n'
f'{lines}\n\n'
f'Question: {question}'
)
class DeskQuestions(TableModel, name='desk_questions'):
claim_id: pxt.String
question: pxt.String
passages = retrieve_claim(question, claim_id, limit=8)
prompt = build_claim_prompt(question, passages)
answer = chat_completions(
messages=[{'role': 'user', 'content': prompt}],
model='gpt-4o-mini',
).choices[0].message.content
# pxt init
# pxt schema update app.py claims -f

To run a sketch, install pixeltable[serve] and the packages its model functions import (such as sentence-transformers, openai, or openai-whisper), export the provider key a model call reads (such as OPENAI_API_KEY), save the file as app.py, and run the commands in its last lines. The tables start empty: insert a row, or POST to an insert route, then select the computed columns. Get started covers the CLI.

Honest alternatives per slot

Named alternatives for Pixeltable for document-heavy insurance
SlotRolePixeltableNamed alternatives
System of recordPolicies, billing, FNOL numbersNot the policy admin system. Join on claim_id
  • Guidewire, Duck Creek, or a custom PAS. That system already issues claim_id
Object storeClaim PDFs and photosDocument and Image columns, or insert S3 URLs
  • Amazon S3. The claim file archive already lives there
ParserLayout-heavy ACORD and medical PDFspxt.Document plus document_splitter
  • Unstructured. You need layout-aware parsing before rows exist
  • LlamaParse. Complex tables a splitter misreads
Vector indexPassage retrievalEmbeddingIndex on the passage view
  • pgvector. Postgres already holds the claim file metadata
  • Qdrant. A dedicated ANN service is already the contract

Where Pixeltable fits

Pixeltable is the claim-file table: PDFs, a splitter view, an embedding index, retrieval filtered by claim_id, and an LLM column whose prompt is the retrieved passages. Keep the policy admin system. Keep S3 as the archive. Keep Unstructured or LlamaParse in front of insert when layout needs a specialist parser. Keep pgvector or Qdrant when that engine is already the index. This page does not claim a compliance certification. The architecture is /reference-architecture/multimodal-rag. The how-tos are multimodal RAG and chunk PDFs for RAG.

Questions

Is Pixeltable a claims system of record?

No. Policy admin, billing, and FNOL numbers stay where they are. This table holds the files, the passages, and the grounded summary for a claim_id those systems already issued.

Does the prompt stop the model from inventing a prior-damage statement?

It instructs the model to use only retrieved passages. That is not a proof. Keep the prompt and the passage rows so a reviewer can re-run the generation. Do not treat the answer column as a legal finding.