How to Integrate LlamaIndex for wealth management with Supabase for production AI

By Cyprian AaronsUpdated 2026-04-22
llamaindex-for-wealth-managementsupabaseproduction-ai

Why this integration matters

Wealth management agents need two things to be useful in production: grounded financial context and durable state. LlamaIndex gives you retrieval over portfolio notes, policy docs, research, and client records; Supabase gives you Postgres-backed storage, auth, and a clean way to persist chat history, embeddings metadata, and audit trails.

Put them together and you can build an AI agent that answers advisor questions with the right documents, remembers client context across sessions, and stores every interaction in a database you can query and govern.

Prerequisites

  • Python 3.10+
  • A Supabase project with:
    • SUPABASE_URL
    • SUPABASE_SERVICE_ROLE_KEY
  • A Postgres table for chat/session state
  • OpenAI or another LLM provider configured for LlamaIndex
  • LlamaIndex installed with the core packages you need:
    • llama-index
    • llama-index-embeddings-openai
    • llama-index-llms-openai
  • Supabase Python client:
    • supabase-py
  • Your wealth management documents ready as PDFs, markdown, or text
  • Optional but recommended:
    • pgvector enabled in Supabase if you want vector search inside Postgres

Integration Steps

  1. Set up Supabase as your persistence layer.

Start by creating a client and a simple table for conversations or agent runs. In production, I keep this separate from vector storage so I can lock down access patterns cleanly.

import os
from supabase import create_client, Client

SUPABASE_URL = os.environ["SUPABASE_URL"]
SUPABASE_SERVICE_ROLE_KEY = os.environ["SUPABASE_SERVICE_ROLE_KEY"]

supabase: Client = create_client(SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY)

# Example: store conversation metadata
response = supabase.table("wealth_agent_sessions").insert({
    "session_id": "client_123",
    "advisor_id": "advisor_42",
    "status": "active"
}).execute()

print(response.data)
  1. Configure LlamaIndex for wealth management retrieval.

Use LlamaIndex to load your client policy docs, investment memos, or product sheets. For wealth management use cases, keep the source set narrow and well-tagged so responses stay grounded.

import os
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, Settings
from llama_index.embeddings.openai import OpenAIEmbedding
from llama_index.llms.openai import OpenAI

Settings.llm = OpenAI(model="gpt-4o-mini", api_key=os.environ["OPENAI_API_KEY"])
Settings.embed_model = OpenAIEmbedding(model="text-embedding-3-small", api_key=os.environ["OPENAI_API_KEY"])

docs = SimpleDirectoryReader("./wealth_docs").load_data()
index = VectorStoreIndex.from_documents(docs)

query_engine = index.as_query_engine(similarity_top_k=3)
result = query_engine.query("What is our risk tolerance policy for high-net-worth clients?")
print(result)
  1. Persist agent outputs and citations in Supabase.

The key production pattern is to store both the answer and the source references. That gives you auditability when an advisor asks why the agent recommended a specific allocation or compliance note.

query_text = "Summarize the client's portfolio concentration risk."
response = query_engine.query(query_text)

citations = []
for node in getattr(response, "source_nodes", []):
    citations.append({
        "file_name": node.node.metadata.get("file_name"),
        "score": float(node.score) if node.score is not None else None,
    })

supabase.table("wealth_agent_answers").insert({
    "session_id": "client_123",
    "question": query_text,
    "answer": str(response),
    "citations": citations
}).execute()
  1. Use Supabase to fetch client context before querying LlamaIndex.

This is where the integration becomes useful in an agent loop. Pull structured client data from Postgres first, then pass it into your retrieval prompt so the answer reflects current account state.

client_ctx = supabase.table("client_profiles") \
    .select("client_id,name,risk_profile,aum,target_allocation") \
    .eq("client_id", "client_123") \
    .single() \
    .execute()

profile = client_ctx.data

prompt = f"""
Client name: {profile['name']}
Risk profile: {profile['risk_profile']}
AUM: {profile['aum']}
Target allocation: {profile['target_allocation']}

Question: Should we rebalance based on current concentration risk?
"""

rebalance_answer = query_engine.query(prompt)
print(rebalance_answer)
  1. Add a lightweight service layer for production requests.

Don’t wire your app directly from UI to database to index. Put a small Python service in front so you can validate inputs, enforce auth rules, log latency, and handle retries cleanly.

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class WealthQuery(BaseModel):
    session_id: str
    question: str

@app.post("/agent/query")
def agent_query(payload: WealthQuery):
    ctx = supabase.table("client_profiles") \
        .select("*") \
        .eq("client_id", payload.session_id) \
        .single() \
        .execute().data

    enriched_prompt = f"""
Client context:
{ctx}

Question:
{payload.question}
"""

    answer = query_engine.query(enriched_prompt)

    supabase.table("wealth_agent_answers").insert({
        "session_id": payload.session_id,
        "question": payload.question,
        "answer": str(answer)
    }).execute()

    return {"answer": str(answer)}

Testing the Integration

Run one end-to-end check: fetch context from Supabase, query LlamaIndex, then write the result back to Postgres.

test_session_id = "client_123"

ctx = supabase.table("client_profiles") \
    .select("*") \
    .eq("client_id", test_session_id) \
    .single() \
    .execute().data

test_question = f"Given this profile {ctx}, what are the key portfolio risks?"
test_answer = query_engine.query(test_question)

write_result = supabase.table("wealth_agent_answers").insert({
    "session_id": test_session_id,
    "question": test_question,
    "answer": str(test_answer)
}).execute()

print("Context loaded:", ctx["name"])
print("Answer snippet:", str(test_answer)[:200])
print("Rows written:", len(write_result.data))

Expected output:

Context loaded: Jane Doe
Answer snippet: The main portfolio risks are concentration in tech equities...
Rows written: 1

Real-World Use Cases

  • Advisor copilot that answers questions like “What changed in this client’s risk profile since last review?” using LlamaIndex retrieval plus Supabase-stored profile data.
  • Compliance assistant that checks recommendations against policy docs and stores every response with citations for audit.
  • Client servicing bot that remembers prior interactions per household and pulls current holdings before generating a response.

Keep learning

By Cyprian Aarons, AI Consultant at Topiax.

Want the complete 8-step roadmap?

Grab the free AI Agent Starter Kit — architecture templates, compliance checklists, and a 7-email deep-dive course.

Get the Starter Kit

Related Guides