How to Integrate Anthropic for investment banking with pgvector for AI agents

By Cyprian AaronsUpdated 2026-04-21
anthropic-for-investment-bankingpgvectorai-agents

Why this integration matters

If you're building an AI agent for investment banking, you need two things working together: a model that can reason over messy deal context, and a retrieval layer that can pull the right precedent, memo, or market note fast. Anthropic handles the reasoning and response generation; pgvector gives you semantic search over your internal banking corpus.

The combination is useful for workflows like deal screening, comps lookup, CIM summarization, and answering banker questions with grounded context instead of guessing.

Prerequisites

  • Python 3.10+
  • PostgreSQL 14+ with the pgvector extension installed
  • An Anthropic API key
  • Access to your banking documents, such as:
    • pitch decks
    • CIMs
    • earnings call transcripts
    • internal research notes
  • Python packages:
    • anthropic
    • psycopg[binary]
    • pgvector
    • sqlalchemy
    • openai or another embedding provider if you are not using Anthropic for embeddings

Install the dependencies:

pip install anthropic psycopg[binary] pgvector sqlalchemy openai

Create the vector extension in Postgres:

CREATE EXTENSION IF NOT EXISTS vector;

Integration Steps

1) Connect to PostgreSQL and enable vector storage

Start by defining a table that stores your document chunks and embeddings. For production systems, keep the raw text, metadata, and embedding together so retrieval stays auditable.

import os
import psycopg
from pgvector.psycopg import register_vector

DB_URL = os.environ["DATABASE_URL"]

conn = psycopg.connect(DB_URL)
register_vector(conn)

with conn.cursor() as cur:
    cur.execute("""
        CREATE TABLE IF NOT EXISTS banking_chunks (
            id BIGSERIAL PRIMARY KEY,
            doc_id TEXT NOT NULL,
            chunk ტექxt TEXT NOT NULL,
            metadata JSONB DEFAULT '{}'::jsonb,
            embedding vector(1536)
        )
    """)
    conn.commit()

If your embedding model uses a different dimension, change vector(1536) to match it exactly.

2) Generate embeddings for banking content

Use an embedding model to convert each chunk into a vector. In investment banking systems, chunk at paragraph or section level so retrieval returns precise context instead of entire documents.

import os
from openai import OpenAI

client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])

def embed_text(text: str) -> list[float]:
    response = client.embeddings.create(
        model="text-embedding-3-small",
        input=text
    )
    return response.data[0].embedding

sample_chunk = "Company XYZ reported 18% revenue growth driven by enterprise subscriptions."
embedding = embed_text(sample_chunk)

with conn.cursor() as cur:
    cur.execute(
        """
        INSERT INTO banking_chunks (doc_id, chunk_text, metadata, embedding)
        VALUES (%s, %s, %s, %s)
        """,
        ("earnings_q2_2024", sample_chunk, {"source": "earnings_call"}, embedding),
    )
    conn.commit()

For real pipelines, batch embeddings and store document lineage in metadata so compliance teams can trace where every answer came from.

3) Retrieve relevant context with pgvector similarity search

When a banker asks a question, query the database for the nearest chunks using cosine distance. This is the retrieval step that grounds Anthropic’s answer in firm-specific material.

def retrieve_context(query: str, limit: int = 5) -> list[str]:
    query_embedding = embed_text(query)

    with conn.cursor() as cur:
        cur.execute(
            """
            SELECT chunk_text
            FROM banking_chunks
            ORDER BY embedding <=> %s::vector
            LIMIT %s
            """,
            (query_embedding, limit),
        )
        rows = cur.fetchall()

    return [row[0] for row in rows]

question = "What drove revenue growth in the latest quarter?"
context_chunks = retrieve_context(question)
print(context_chunks)

The <=> operator is the pgvector cosine distance operator. It is the standard pattern for semantic search in Postgres-backed RAG systems.

4) Call Anthropic with retrieved context

Now pass the retrieved chunks into Anthropic’s Messages API. For investment banking use cases, keep the prompt structured and force answers to cite only provided context.

import anthropic

anthropic_client = anthropic.Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])

def answer_question(question: str) -> str:
    context = retrieve_context(question)

    prompt = f"""
You are an investment banking assistant.
Answer only using the provided context.
If the context is insufficient, say so clearly.

Context:
{chr(10).join(f"- {chunk}" for chunk in context)}

Question:
{question}
"""

    response = anthropic_client.messages.create(
        model="claude-3-5-sonnet-latest",
        max_tokens=400,
        temperature=0,
        messages=[
            {"role": "user", "content": prompt}
        ],
    )

    return response.content[0].text

print(answer_question("What drove revenue growth in the latest quarter?"))

Use temperature=0 for analyst-style workflows where consistency matters more than creativity.

5) Wrap it into an agent tool call pattern

In production, don’t hardwire retrieval into one function. Expose pgvector search as a tool and let Anthropic decide when to use it. That gives you a cleaner agent architecture and easier expansion later.

tools = [
    {
        "name": "search_banking_docs",
        "description": "Search internal banking documents for relevant context.",
        "input_schema": {
            "type": "object",
            "properties": {
                "query": {"type": "string"}
            },
            "required": ["query"]
        }
    }
]

response = anthropic_client.messages.create(
    model="claude-3-5-sonnet-latest",
    max_tokens=400,
    tools=tools,
    messages=[
        {"role": "user", "content": "Summarize what drove revenue growth last quarter."}
    ],
)

print(response)

Your app layer should intercept tool calls, run the pgvector query, then send the retrieved chunks back to Anthropic in a follow-up message. That keeps retrieval deterministic and model reasoning isolated from data access.

Testing the Integration

Run one end-to-end test against a known document chunk and verify that retrieval plus generation works together.

test_question = "What drove revenue growth?"
answer = answer_question(test_question)

print("QUESTION:", test_question)
print("ANSWER:", answer)

Expected output:

QUESTION: What drove revenue growth?
ANSWER: Revenue growth was driven by enterprise subscriptions according to the retrieved earnings call excerpt. The provided context does not include additional breakdowns by segment.

If you get an empty or vague answer:

  • check that embeddings were stored with the correct dimension
  • confirm pgvector is installed and registered
  • verify your chunks are actually relevant to the query
  • inspect whether your prompt is forcing answers from context only

Real-World Use Cases

  • Deal team Q&A assistant

    • Ask questions about target company financials, recent performance drivers, or prior deal notes.
    • Retrieve relevant passages from CIMs and earnings transcripts before Claude drafts an answer.
  • Comparable companies research agent

    • Store comps data points and analyst notes in pgvector.
    • Let Anthropic summarize valuation themes or explain why one peer trades at a premium.
  • Investment memo copilot

    • Index internal memos, sector notes, and IC comments.
    • Generate first-draft summaries grounded in firm-approved source material instead of free-form model output.

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