How to Integrate Anthropic for lending with pgvector for production AI

By Cyprian AaronsUpdated 2026-04-21
anthropic-for-lendingpgvectorproduction-ai

Combining Anthropic for lending with pgvector gives you a practical pattern for production AI agents: use Anthropic to reason over lending workflows, then use pgvector to retrieve policy, borrower history, and document embeddings from your own database. That gives you grounded answers, better auditability, and a clean way to keep sensitive lending context inside your infrastructure.

For lending teams, this is the difference between a chat demo and an agent that can actually support underwriting, collections, and loan servicing with retrieval-backed responses.

Prerequisites

  • Python 3.10+
  • PostgreSQL 14+ with the pgvector extension enabled
  • An Anthropic API key
  • Access to your lending knowledge base:
    • policy documents
    • underwriting rules
    • loan product docs
    • customer support playbooks
  • Python packages:
    • anthropic
    • psycopg[binary]
    • pgvector
    • openai or another embedding provider if you are not using Anthropic for embeddings
  • A network path to your database from the application runtime

Install the dependencies:

pip install anthropic psycopg[binary] pgvector numpy

Integration Steps

1) Set up pgvector in PostgreSQL

Create the extension and a table for embedded lending documents. Use a vector dimension that matches your embedding model.

import psycopg

conn = psycopg.connect("postgresql://postgres:postgres@localhost:5432/lending")
conn.execute("CREATE EXTENSION IF NOT EXISTS vector")

conn.execute("""
CREATE TABLE IF NOT EXISTS lending_docs (
    id SERIAL PRIMARY KEY,
    doc_type TEXT NOT NULL,
    title TEXT NOT NULL,
    content TEXT NOT NULL,
    embedding vector(1536)
)
""")

conn.commit()
conn.close()

If you are using a different embedding model dimension, change vector(1536) accordingly.

2) Generate embeddings and store them in pgvector

Anthropic’s API is used for reasoning and generation; for embeddings, use a dedicated embedding model. The pattern stays the same: convert lending text into vectors, then persist them in PostgreSQL.

import psycopg
from pgvector.psycopg import register_vector
from openai import OpenAI

client = OpenAI()

docs = [
    {
        "doc_type": "policy",
        "title": "Debt-to-income rule",
        "content": "Applicants with DTI above 43% require manual review unless compensating factors exist."
    },
    {
        "doc_type": "servicing",
        "title": "Late payment handling",
        "content": "If payment is more than 15 days late, notify the borrower and create a follow-up task."
    }
]

def embed(text: str):
    resp = client.embeddings.create(
        model="text-embedding-3-small",
        input=text
    )
    return resp.data[0].embedding

conn = psycopg.connect("postgresql://postgres:postgres@localhost:5432/lending")
register_vector(conn)

for doc in docs:
    vec = embed(f"{doc['title']}\n{doc['content']}")
    conn.execute(
        """
        INSERT INTO lending_docs (doc_type, title, content, embedding)
        VALUES (%s, %s, %s, %s)
        """,
        (doc["doc_type"], doc["title"], doc["content"], vec)
    )

conn.commit()
conn.close()

This gives you a searchable knowledge base for retrieval augmented generation.

3) Retrieve relevant lending context with pgvector

When a user asks a question about lending policy or loan handling, pull the closest matches first. That keeps Anthropic grounded in your internal data.

import psycopg
from pgvector.psycopg import register_vector
from openai import OpenAI

client = OpenAI()

def embed(text: str):
    resp = client.embeddings.create(
        model="text-embedding-3-small",
        input=text
    )
    return resp.data[0].embedding

query = "Can we approve an applicant with high DTI if they have strong reserves?"

conn = psycopg.connect("postgresql://postgres:postgres@localhost:5432/lending")
register_vector(conn)

qvec = embed(query)

rows = conn.execute(
    """
    SELECT title, content
    FROM lending_docs
    ORDER BY embedding <-> %s
    LIMIT 3
    """,
    (qvec,)
).fetchall()

context = "\n\n".join([f"Title: {r[0]}\nContent: {r[1]}" for r in rows])
conn.close()

The <-> operator is the standard pgvector distance operator used for nearest-neighbor search.

4) Send retrieved context to Anthropic for a grounded answer

Now pass the retrieved lending context into Anthropic’s Messages API. This is where the agent reasons over policy and produces an answer suitable for an operations workflow.

from anthropic import Anthropic

anthropic_client = Anthropic(api_key="YOUR_ANTHROPIC_API_KEY")

response = anthropic_client.messages.create(
    model="claude-3-5-sonnet-latest",
    max_tokens=300,
    messages=[
        {
            "role": "user",
            "content": f"""
You are assisting with lending operations.
Use only the provided context to answer.

Context:
{context}

Question:
{query}
"""
        }
    ]
)

print(response.content[0].text)

In production, keep the system prompt strict:

  • cite source documents when possible
  • refuse to invent policy not present in retrieved context
  • route ambiguous cases to manual review

5) Wrap it into a reusable agent function

Put retrieval + generation behind one function so your application can call it from an API endpoint or workflow engine.

import psycopg
from pgvector.psycopg import register_vector
from openai import OpenAI
from anthropic import Anthropic

embed_client = OpenAI()
anthropic_client = Anthropic(api_key="YOUR_ANTHROPIC_API_KEY")

def embed(text: str):
    resp = embed_client.embeddings.create(
        model="text-embedding-3-small",
        input=text
    )
    return resp.data[0].embedding

def answer_lending_question(question: str) -> str:
    conn = psycopg.connect("postgresql://postgres:postgres@localhost:5432/lending")
    register_vector(conn)

    qvec = embed(question)
    rows = conn.execute(
        """
        SELECT title, content
        FROM lending_docs
        ORDER BY embedding <-> %s
        LIMIT 5
        """,
        (qvec,)
    ).fetchall()

    conn.close()

    context = "\n\n".join([f"Title: {t}\nContent: {c}" for t, c in rows])

    resp = anthropic_client.messages.create(
        model="claude-3-5-sonnet-latest",
        max_tokens=300,
        messages=[
            {
                "role": "user",
                "content": f"Answer using only this context:\n\n{context}\n\nQuestion: {question}"
            }
        ]
    )
    return resp.content[0].text.strip()

print(answer_lending_question("What happens when DTI is above threshold?"))

Testing the Integration

Run one end-to-end query and verify that retrieval returns relevant docs before Claude answers.

result = answer_lending_question("Can an applicant over our DTI threshold still be reviewed?")
print(result)

Expected output:

Applicants with DTI above 43% require manual review unless compensating factors exist.

If you get an empty or generic answer:

  • check that embeddings were inserted into pgvector correctly
  • confirm your vector dimension matches the model output size
  • verify your prompt restricts Claude to retrieved context only

Real-World Use Cases

  • Underwriting copilot
    Pull policy docs and borrower notes from pgvector, then have Anthropic draft a recommendation with rationale and escalation flags.

  • Loan servicing assistant
    Retrieve servicing procedures, late-fee rules, and communication templates so agents can respond consistently to borrower requests.

  • Compliance Q&A bot
    Search internal lending policies by semantic meaning instead of keyword matching, then generate answers tied to source material for audit trails.


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