By Sagar Shankaran, Founder of CallSphere
Build an AI financial planning agent that integrates bank data, analyzes spending patterns, tracks savings goals, and generates personalized financial recommendations.
Key takeaways
Budgeting apps show you what happened. A financial planning agent tells you what to do next. By combining transaction data analysis, goal tracking, and LLM-powered reasoning, an agent can provide the kind of personalized advice that previously required a human financial advisor. In this tutorial, you will build an agent that analyzes spending patterns, monitors progress toward financial goals, and generates actionable recommendations.
We start by modeling transactions and building a spending analyzer.
flowchart LR
INPUT(["User intent"])
PARSE["Parse plus<br/>classify"]
PLAN["Plan and tool<br/>selection"]
AGENT["Agent loop<br/>LLM plus tools"]
GUARD{"Guardrails<br/>and policy"}
EXEC["Execute and<br/>verify result"]
OBS[("Trace and metrics")]
OUT(["Outcome plus<br/>next action"])
INPUT --> PARSE --> PLAN --> AGENT --> GUARD
GUARD -->|Pass| EXEC --> OUT
GUARD -->|Fail| AGENT
AGENT --> OBS
style AGENT fill:#4f46e5,stroke:#4338ca,color:#fff
style GUARD fill:#f59e0b,stroke:#d97706,color:#1f2937
style OBS fill:#ede9fe,stroke:#7c3aed,color:#1e1b4b
style OUT fill:#059669,stroke:#047857,color:#fff
from pydantic import BaseModel
from datetime import date, datetime
from collections import defaultdict
class Transaction(BaseModel):
date: date
description: str
amount: float # negative = expense, positive = income
category: str | None = None
account: str
class SpendingSummary(BaseModel):
period: str
total_income: float
total_expenses: float
net_savings: float
savings_rate: float
category_breakdown: dict[str, float]
top_merchants: list[dict]
def analyze_spending(
transactions: list[Transaction], period_start: date, period_end: date
) -> SpendingSummary:
"""Analyze spending patterns for a given period."""
filtered = [
t for t in transactions
if period_start <= t.date <= period_end
]
income = sum(t.amount for t in filtered if t.amount > 0)
expenses = sum(abs(t.amount) for t in filtered if t.amount < 0)
net = income - expenses
savings_rate = (net / income * 100) if income > 0 else 0
# Category breakdown
by_category = defaultdict(float)
by_merchant = defaultdict(float)
for t in filtered:
if t.amount < 0:
cat = t.category or "Uncategorized"
by_category[cat] += abs(t.amount)
by_merchant[t.description] += abs(t.amount)
top_merchants = sorted(
[{"name": k, "total": v} for k, v in by_merchant.items()],
key=lambda x: x["total"],
reverse=True,
)[:10]
return SpendingSummary(
period=f"{period_start} to {period_end}",
total_income=income,
total_expenses=expenses,
net_savings=net,
savings_rate=round(savings_rate, 1),
category_breakdown=dict(by_category),
top_merchants=top_merchants,
)
Bank transactions often have cryptic descriptions. The agent uses an LLM to categorize them.
Hear it before you finish reading
Talk to a live CallSphere AI voice agent for financial services in your browser — 60 seconds, no signup.
from openai import OpenAI
client = OpenAI()
class CategorizedTransaction(BaseModel):
description: str
category: str
subcategory: str
is_recurring: bool
is_essential: bool
class BatchCategorization(BaseModel):
transactions: list[CategorizedTransaction]
CATEGORIES = [
"Housing", "Transportation", "Food & Dining",
"Utilities", "Healthcare", "Insurance",
"Entertainment", "Shopping", "Personal Care",
"Education", "Savings & Investments",
"Debt Payments", "Gifts & Donations", "Other",
]
def categorize_transactions(
descriptions: list[str],
) -> list[CategorizedTransaction]:
"""Categorize transactions using an LLM."""
desc_list = "\n".join(
f"{i+1}. {d}" for i, d in enumerate(descriptions)
)
response = client.beta.chat.completions.parse(
model="gpt-4o",
messages=[
{
"role": "system",
"content": (
"Categorize each transaction into one of these "
f"categories: {', '.join(CATEGORIES)}. "
"Also identify if it is recurring and essential."
),
},
{"role": "user", "content": desc_list},
],
response_format=BatchCategorization,
)
return response.choices[0].message.parsed.transactions
Financial goals need monitoring with progress calculations and projections.
from dataclasses import dataclass
@dataclass
class FinancialGoal:
name: str
target_amount: float
current_amount: float
target_date: date
monthly_contribution: float
priority: int # 1 = highest
@property
def progress_pct(self) -> float:
if self.target_amount == 0:
return 100.0
return (self.current_amount / self.target_amount) * 100
@property
def remaining(self) -> float:
return max(0, self.target_amount - self.current_amount)
@property
def months_to_goal(self) -> float | None:
if self.monthly_contribution <= 0:
return None
return self.remaining / self.monthly_contribution
@property
def on_track(self) -> bool:
if self.months_to_goal is None:
return False
today = date.today()
months_left = (
(self.target_date.year - today.year) * 12
+ self.target_date.month - today.month
)
return self.months_to_goal <= months_left
def track_goals(goals: list[FinancialGoal]) -> list[dict]:
"""Generate status report for all financial goals."""
report = []
for goal in sorted(goals, key=lambda g: g.priority):
status = {
"name": goal.name,
"progress": f"{goal.progress_pct:.1f}%",
"remaining": goal.remaining,
"monthly_contribution": goal.monthly_contribution,
"on_track": goal.on_track,
}
if goal.months_to_goal is not None:
status["months_to_goal"] = round(goal.months_to_goal, 1)
report.append(status)
return report
The agent combines spending analysis and goal tracking to generate advice.
def generate_recommendations(
spending: SpendingSummary,
goals: list[dict],
monthly_income: float,
) -> str:
"""Generate personalized financial recommendations."""
context = (
f"Monthly Income: ${monthly_income:,.2f}\n"
f"Monthly Expenses: ${spending.total_expenses:,.2f}\n"
f"Savings Rate: {spending.savings_rate}%\n\n"
f"Spending Breakdown:\n"
)
for cat, amount in sorted(
spending.category_breakdown.items(),
key=lambda x: x[1],
reverse=True,
):
pct = (amount / spending.total_expenses) * 100
context += f" {cat}: ${amount:,.2f} ({pct:.1f}%)\n"
context += "\nFinancial Goals:\n"
for goal in goals:
track = "ON TRACK" if goal["on_track"] else "BEHIND"
context += (
f" {goal['name']}: {goal['progress']} "
f"[{track}]\n"
)
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{
"role": "system",
"content": (
"You are a certified financial planner. Analyze the "
"spending data and goals, then provide 5 specific, "
"actionable recommendations. Be concrete with dollar "
"amounts. Prioritize high-impact changes."
),
},
{"role": "user", "content": context},
],
)
return response.choices[0].message.content
# Load and analyze transactions
spending = analyze_spending(transactions, date(2026, 2, 1), date(2026, 2, 28))
# Track goals
goals = track_goals([
FinancialGoal("Emergency Fund", 15000, 8500, date(2026, 12, 31), 800, 1),
FinancialGoal("Vacation", 3000, 1200, date(2026, 8, 1), 400, 2),
])
# Get recommendations
advice = generate_recommendations(spending, goals, 6500.0)
print(advice)
Use a service like Plaid, Yodlee, or MX to access transaction data via API. Plaid provides a Python SDK that handles bank authentication and returns standardized transaction objects. Always store tokens securely and never cache raw bank credentials.
Still reading? Stop comparing — try CallSphere live.
See the financial services AI agent handle a real call — complete, industry-specific, and live in your browser. No signup.
Anonymize transactions before sending them to the LLM — replace merchant names with categories where possible and never include account numbers or personal identifiers. For maximum privacy, use a local model for transaction categorization and only send aggregated summaries to cloud APIs.
Yes. Add context about life events (new job, marriage, home purchase) to the recommendation prompt. The LLM can adjust advice accordingly — for example, recommending higher emergency fund targets after a job change or suggesting tax-advantaged accounts after marriage.
#FinancialPlanning #BudgetAnalysis #GoalTracking #PersonalFinance #AIAgent #AgenticAI #LearnAI #AIEngineering

Written by
Sagar Shankaran· Founder, CallSphere
LinkedInSagar Shankaran is the founder of CallSphere, where he builds production AI voice and chat agents deployed across healthcare, hospitality, real estate, and home services. He writes about agentic AI, LLM engineering, and shipping voice agents that handle real calls in production.
See how AI voice agents work for your industry. Live demo available -- no signup required.
Enterprise CIO Guide perspective on Klarna's AI agent pioneered the resolution-equivalent metric and is now in its third year of production data.
SMB Founder Playbook perspective on Klarna's AI agent pioneered the resolution-equivalent metric and is now in its third year of production data.
Healthcare Practice Use Case perspective on Klarna's AI agent pioneered the resolution-equivalent metric and is now in its third year of production data.
Real Estate and Property Management Lens perspective on Klarna's AI agent pioneered the resolution-equivalent metric and is now in its third year of production data.
Sales and RevOps Lens perspective on Klarna's AI agent pioneered the resolution-equivalent metric and is now in its third year of production data.
Adoption Across San Francisco, New York, Boston, and Austin perspective on Klarna's AI agent pioneered the resolution-equivalent metric and is now in its third year of production data.
© 2026 CallSphere Inc. All rights reserved.
Made within San Francisco
Watch how CallSphere handles real customer calls, schedules appointments, and processes payments — live.
Try Live DemoBook a DemoCalculate Your ROI