Skip to content
Learn Agentic AI14 min read0 views

Building a Financial Planning Agent: Budget Analysis, Goal Tracking, and Recommendations

Build an AI financial planning agent that integrates bank data, analyzes spending patterns, tracks savings goals, and generates personalized financial recommendations.

Why Personal Finance Needs Intelligent Agents

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.

System Components

  1. Transaction Analyzer — categorize and analyze spending data
  2. Goal Tracker — monitor progress toward financial targets
  3. Projection Engine — forecast future financial state
  4. Recommendation Generator — produce personalized advice

Step 1: Transaction Analysis

We start by modeling transactions and building a spending analyzer.

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,
    )

Step 2: Transaction Categorization with LLM

Bank transactions often have cryptic descriptions. The agent uses an LLM to categorize them.

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

Step 3: Goal Tracking

Financial goals need monitoring with progress calculations and projections.

See AI Voice Agents Handle Real Calls

Book a free demo or calculate how much you can save with AI voice automation.

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

Step 4: Personalized Recommendations

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

Running the Agent

# 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)

FAQ

How do you connect to real bank data?

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.

How do you handle privacy when sending financial data to an LLM?

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.

Can the agent adapt its advice based on life events?

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

Share this article
C

CallSphere Team

Expert insights on AI voice agents and customer communication automation.

Try CallSphere AI Voice Agents

See how AI voice agents work for your industry. Live demo available -- no signup required.