← Back to DevBytes

AI Agents for Accounting Firms: Use Cases and Setup

What Are AI Agents for Accounting Firms?

AI agents are autonomous software programs that use large language models (LLMs) to reason, plan, and execute multi-step tasks with minimal human intervention. In the context of accounting firms, these agents go far beyond simple chatbots or document classifiers — they can ingest financial documents, classify transactions, reconcile accounts, generate draft financial statements, and even prepare audit workpapers by orchestrating multiple tools and data sources in a coordinated workflow.

Unlike traditional rule-based automation, AI agents exhibit agency: they decompose a complex objective (e.g., "Reconcile the Q3 general ledger with the bank statements") into discrete steps, decide which tool to invoke for each step, handle errors gracefully, and produce a structured, auditable output. They combine the reasoning capabilities of models like GPT-4 or Claude with deterministic tools such as SQL query engines, spreadsheet parsers, OCR pipelines, and accounting rule engines to create a reliable digital workforce for accounting operations.

Why AI Agents Matter for Accounting Firms

Accounting firms face mounting pressure on multiple fronts: a persistent talent shortage, increasing regulatory complexity, compressed margins on compliance work, and client expectations for real-time insights. AI agents address these challenges directly:

Core Use Cases with Practical Implementations

1. Intelligent Transaction Classification

One of the most time-consuming tasks in bookkeeping is assigning general ledger account codes to raw transaction data from bank feeds, credit card statements, and invoices. An AI agent can read the transaction description, amount, and context, then map it to the correct chart of accounts code with high accuracy. The agent can also flag ambiguous transactions for human review rather than guessing.

Below is a complete Python implementation of a transaction classification agent using LangChain and OpenAI. The agent retrieves the firm's chart of accounts from a vector store, reasons about each transaction, and outputs a structured classification.

# transaction_classifier_agent.py
# Complete AI Agent for classifying accounting transactions
# Requires: pip install langchain openai chromadb pydantic

import os
import json
from typing import List, Optional
from pydantic import BaseModel, Field
from langchain_openai import ChatOpenAI
from langchain.tools import tool
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.agents import AgentExecutor, create_openai_tools_agent
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings

# ---- Data Models ----
class Transaction(BaseModel):
    id: str
    date: str
    description: str
    amount: float
    source: str  # e.g., 'bank_feed', 'credit_card', 'invoice'

class ClassificationResult(BaseModel):
    transaction_id: str
    account_code: str
    account_name: str
    confidence: float  # 0.0 to 1.0
    reasoning: str
    needs_human_review: bool

# ---- Chart of Accounts Store (simulated with Chroma) ----
# In production, load your actual chart of accounts here.
SAMPLE_CHART_OF_ACCOUNTS = [
    {"code": "5100", "name": "Office Supplies Expense", "keywords": "office supplies, stationery, paper, pens, printer ink, toner"},
    {"code": "5200", "name": "Rent Expense", "keywords": "rent, lease, office space, landlord, property payment"},
    {"code": "5300", "name": "Utilities Expense", "keywords": "electricity, water, internet, gas bill, utility, power"},
    {"code": "5400", "name": "Professional Fees", "keywords": "legal fees, lawyer, consultant, accounting services, audit"},
    {"code": "5500", "name": "Travel Expense", "keywords": "airfare, flight, hotel, lodging, mileage, taxi, uber, travel"},
    {"code": "5600", "name": "Meals & Entertainment", "keywords": "restaurant, dinner, lunch, catering, client meal, coffee"},
    {"code": "4100", "name": "Service Revenue", "keywords": "consulting revenue, services income, professional fees earned, client payment"},
    {"code": "4200", "name": "Product Revenue", "keywords": "product sales, software license, subscription revenue, goods sold"},
    {"code": "1200", "name": "Accounts Receivable", "keywords": "invoice, customer owes, receivable, unpaid, AR"},
    {"code": "2100", "name": "Accounts Payable", "keywords": "vendor bill, payable, supplier invoice, owed to vendor, AP"},
]

def build_chart_of_accounts_store():
    """Build a vector store from chart of accounts entries."""
    texts = []
    metadatas = []
    for entry in SAMPLE_CHART_OF_ACCOUNTS:
        text = f"Account {entry['code']}: {entry['name']}. Keywords: {entry['keywords']}"
        texts.append(text)
        metadatas.append({"code": entry['code'], "name": entry['name']})
    
    embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
    vectorstore = Chroma.from_texts(
        texts=texts,
        metadatas=metadatas,
        embedding=embeddings,
        collection_name="chart_of_accounts"
    )
    return vectorstore

# ---- Tool Definition ----
@tool
def lookup_accounts(query: str) -> str:
    """Search the chart of accounts for matching account codes based on a description query.
    Returns top 3 matching accounts with codes and names."""
    vectorstore = build_chart_of_accounts_store()
    results = vectorstore.similarity_search_with_score(query, k=3)
    
    output_lines = []
    for doc, score in results:
        similarity = 1.0 - (score / 2.0)  # Normalize Chroma distance to similarity
        code = doc.metadata.get("code", "unknown")
        name = doc.metadata.get("name", "unknown")
        output_lines.append(f"  - Code {code} ({name}) | relevance: {similarity:.2f}")
    
    return "Potential account matches:\n" + "\n".join(output_lines)

@tool
def flag_for_human_review(transaction_id: str, reason: str) -> str:
    """Flag a transaction for human review when the agent cannot confidently classify it."""
    # In production, this would write to a database or trigger a notification.
    return json.dumps({
        "action": "flagged_for_review",
        "transaction_id": transaction_id,
        "reason": reason,
        "status": "pending_review"
    })

# ---- Agent Setup ----
def create_classification_agent():
    """Create and return an agent executor for transaction classification."""
    
    llm = ChatOpenAI(
        model="gpt-4o",
        temperature=0.1,  # Low temperature for consistent classification
    )
    
    tools = [lookup_accounts, flag_for_human_review]
    
    system_prompt = """You are an expert accounting AI agent specializing in transaction classification.
    
Your task: Given a transaction, determine the correct general ledger account code.

Process:
1. Read the transaction description, amount, date, and source carefully.
2. Use the `lookup_accounts` tool to search the chart of accounts using keywords from the description.
3. Reason about which account is most appropriate based on the transaction nature, amount context, and source.
4. If you are highly confident (confidence >= 0.85), output the classification.
5. If you are uncertain (confidence < 0.85), use `flag_for_human_review` and explain what additional information would help.

Important accounting rules:
- Revenue accounts (4000-4999) are for incoming money from clients/customers.
- Expense accounts (5000-5999) are for outgoing payments for business operations.
- Asset accounts (1000-1999) are for things the business owns.
- Liability accounts (2000-2999) are for obligations the business owes.
- For negative amounts in bank feeds, consider them as expenses or contra-revenue.
- For deposits in bank feeds, consider them as revenue or receivable payments.

Output your final answer as a JSON object matching the ClassificationResult schema."""

    prompt = ChatPromptTemplate.from_messages([
        ("system", system_prompt),
        ("human", """Please classify the following transaction:

Transaction ID: {transaction_id}
Date: {date}
Description: {description}
Amount: ${amount}
Source: {source}

Provide your classification as a structured JSON response."""),
        MessagesPlaceholder(variable_name="agent_scratchpad"),
    ])
    
    agent = create_openai_tools_agent(llm, tools, prompt)
    
    executor = AgentExecutor(
        agent=agent,
        tools=tools,
        verbose=True,
        max_iterations=5,
        handle_parsing_errors=True,
    )
    
    return executor

# ---- Usage Example ----
if __name__ == "__main__":
    # Set your API key
    os.environ["OPENAI_API_KEY"] = "your-api-key-here"
    
    agent_executor = create_classification_agent()
    
    # Example transactions from a bank feed
    sample_transactions = [
        {
            "transaction_id": "TXN-1001",
            "date": "2025-01-15",
            "description": "AMAZON WEB SERVICES AWS MONTHLY CHARGE REF 84738291",
            "amount": -295.00,
            "source": "bank_feed"
        },
        {
            "transaction_id": "TXN-1002",
            "date": "2025-01-16",
            "description": "DEPOSIT FROM ACME CORP INVOICE #INV-4521",
            "amount": 15000.00,
            "source": "bank_feed"
        },
        {
            "transaction_id": "TXN-1003",
            "date": "2025-01-17",
            "description": "UBER TRIP TO CLIENT OFFICE JAN 17 DOWNTOWN",
            "amount": -34.50,
            "source": "credit_card"
        },
    ]
    
    for txn in sample_transactions:
        result = agent_executor.invoke({
            "transaction_id": txn["transaction_id"],
            "date": txn["date"],
            "description": txn["description"],
            "amount": txn["amount"],
            "source": txn["source"],
        })
        print(f"\nResult for {txn['transaction_id']}:")
        print(result["output"])

2. Automated Bank Reconciliation Agent

Bank reconciliation — matching internal ledger entries against bank statement lines — is a cornerstone accounting control that remains stubbornly manual in many firms. An AI agent can ingest both datasets, identify matches using fuzzy logic on dates, amounts, and descriptions, propose reconciling entries for discrepancies, and produce a reconciliation report ready for reviewer sign-off.

The following implementation demonstrates a reconciliation agent that handles common matching scenarios including exact matches, near matches (within a tolerance), and unmatched items requiring adjustment entries.

# reconciliation_agent.py
# AI Agent for automated bank reconciliation
# Requires: pip install langchain openai pydantic thefuzz

import os
import json
from typing import List, Optional
from pydantic import BaseModel
from datetime import datetime, timedelta
from thefuzz import fuzz  # Fuzzy string matching
from langchain_openai import ChatOpenAI
from langchain.tools import tool
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.agents import AgentExecutor, create_openai_tools_agent

# ---- Data Models ----
class LedgerEntry(BaseModel):
    entry_id: str
    date: str
    description: str
    amount: float
    account_code: str

class BankLine(BaseModel):
    line_id: str
    posting_date: str
    description: str
    amount: float
    transaction_type: str  # 'credit' or 'debit'

class MatchResult(BaseModel):
    ledger_entry_id: str
    bank_line_id: str
    match_type: str  # 'exact', 'fuzzy', 'amount_only', 'unmatched'
    confidence: float
    difference: float
    notes: str

class ReconciliationReport(BaseModel):
    period_start: str
    period_end: str
    total_ledger_items: int
    total_bank_items: int
    matched_count: int
    unmatched_ledger_count: int
    unmatched_bank_count: int
    total_difference: float
    matches: List[MatchResult]
    suggested_adjustments: List[dict]

# ---- Simulated Data Access ----
def fetch_ledger_entries(account_code: str, start_date: str, end_date: str) -> List[dict]:
    """Simulate fetching ledger entries from an accounting system.
    In production, replace with QuickBooks API, Xero API, or SQL query."""
    return [
        {"entry_id": "LE-001", "date": "2025-01-10", "description": "Office Depot - Printer Paper", "amount": -89.50, "account_code": "5100"},
        {"entry_id": "LE-002", "date": "2025-01-12", "description": "Client Payment - ABC Inc INV-4501", "amount": 5000.00, "account_code": "4100"},
        {"entry_id": "LE-003", "date": "2025-01-15", "description": "Electric Bill - ConEdison Jan", "amount": -425.30, "account_code": "5300"},
        {"entry_id": "LE-004", "date": "2025-01-18", "description": "Starbucks - Client Meeting Coffee", "amount": -12.75, "account_code": "5600"},
        {"entry_id": "LE-005", "date": "2025-01-20", "description": "Transfer to Savings Account", "amount": -10000.00, "account_code": "1050"},
        {"entry_id": "LE-006", "date": "2025-01-22", "description": "Unknown Deposit - PENDING REVIEW", "amount": 250.00, "account_code": "9999"},
    ]

def fetch_bank_lines(account_number: str, start_date: str, end_date: str) -> List[dict]:
    """Simulate fetching bank statement lines via Plaid, bank API, or CSV import."""
    return [
        {"line_id": "BL-100", "posting_date": "2025-01-10", "description": "OFFICE DEPOT #4832 PRINTER PAPER PURCHASE", "amount": -89.50, "transaction_type": "debit"},
        {"line_id": "BL-101", "posting_date": "2025-01-12", "description": "WIRE TRANSFER CREDIT FROM ABC INC", "amount": 5000.00, "transaction_type": "credit"},
        {"line_id": "BL-102", "posting_date": "2025-01-15", "description": "CONEDISON ELECTRIC BILL PAYMENT", "amount": -425.30, "transaction_type": "debit"},
        {"line_id": "BL-103", "posting_date": "2025-01-18", "description": "STARBUCKS STORE #9021 COFFEE", "amount": -12.75, "transaction_type": "debit"},
        {"line_id": "BL-104", "posting_date": "2025-01-20", "description": "INTERNAL TRANSFER TO SAVINGS ACCT XXXXXX4521", "amount": -10000.00, "transaction_type": "debit"},
        {"line_id": "BL-105", "posting_date": "2025-01-25", "description": "ATM WITHDRAWAL BROADWAY BRANCH", "amount": -200.00, "transaction_type": "debit"},
        {"line_id": "BL-106", "posting_date": "2025-01-26", "description": "BANK SERVICE CHARGE MONTHLY MAINTENANCE FEE", "amount": -15.00, "transaction_type": "debit"},
    ]

# ---- Fuzzy Matching Engine ----
def compute_match_score(ledger_desc: str, bank_desc: str, ledger_amount: float, bank_amount: float, ledger_date: str, bank_date: str) -> dict:
    """Compute a comprehensive match score between a ledger entry and a bank line."""
    
    # Tokenize and normalize descriptions
    ledger_tokens = ledger_desc.lower().strip()
    bank_tokens = bank_desc.lower().strip()
    
    # Fuzzy string similarity (0-100)
    string_similarity = fuzz.token_sort_ratio(ledger_tokens, bank_tokens)
    
    # Amount match (exact or within tolerance)
    amount_difference = abs(abs(ledger_amount) - abs(bank_amount))
    amount_exact = amount_difference < 0.01
    
    # Date proximity (within 3 days)
    try:
        ld = datetime.strptime(ledger_date, "%Y-%m-%d")
        bd = datetime.strptime(bank_date, "%Y-%m-%d")
        days_diff = abs((ld - bd).days)
    except ValueError:
        days_diff = 999
    
    date_close = days_diff <= 3
    
    # Composite score
    if amount_exact and string_similarity > 80 and date_close:
        match_type = "exact"
        confidence = 0.95 + (string_similarity / 200)  # 0.95-1.0
    elif amount_exact and string_similarity > 50:
        match_type = "fuzzy"
        confidence = 0.70 + (string_similarity / 400)
    elif amount_exact:
        match_type = "amount_only"
        confidence = 0.50
    else:
        match_type = "unmatched"
        confidence = 0.10
    
    return {
        "match_type": match_type,
        "confidence": min(confidence, 1.0),
        "string_similarity": string_similarity,
        "amount_difference": amount_difference,
        "days_difference": days_diff,
    }

# ---- Tool Definitions ----
@tool
def get_ledger_data(account_code: str, start_date: str, end_date: str) -> str:
    """Fetch ledger entries for a specific account code and date range.
    Returns JSON array of ledger entries."""
    entries = fetch_ledger_entries(account_code, start_date, end_date)
    return json.dumps(entries, indent=2)

@tool
def get_bank_data(account_number: str, start_date: str, end_date: str) -> str:
    """Fetch bank statement lines for an account number and date range.
    Returns JSON array of bank statement lines."""
    lines = fetch_bank_lines(account_number, start_date, end_date)
    return json.dumps(lines, indent=2)

@tool
def compute_matches(ledger_json: str, bank_json: str) -> str:
    """Run the matching engine between ledger entries and bank lines.
    Input: JSON strings of ledger array and bank array.
    Returns JSON array of match results with confidence scores."""
    
    ledger_data = json.loads(ledger_json)
    bank_data = json.loads(bank_json)
    
    matches = []
    matched_bank_ids = set()
    matched_ledger_ids = set()
    
    # First pass: find best matches for each ledger entry
    for le in ledger_data:
        best_match = None
        best_score = -1
        
        for bl in bank_data:
            if bl["line_id"] in matched_bank_ids:
                continue
            
            score_data = compute_match_score(
                le["description"], bl["description"],
                le["amount"], bl["amount"],
                le["date"], bl["posting_date"]
            )
            
            if score_data["confidence"] > best_score:
                best_score = score_data["confidence"]
                best_match = {
                    "ledger_entry_id": le["entry_id"],
                    "bank_line_id": bl["line_id"],
                    "match_type": score_data["match_type"],
                    "confidence": score_data["confidence"],
                    "difference": score_data["amount_difference"],
                    "notes": f"String similarity: {score_data['string_similarity']}%, Days diff: {score_data['days_difference']}"
                }
        
        if best_match and best_score >= 0.50:
            matches.append(best_match)
            matched_bank_ids.add(best_match["bank_line_id"])
            matched_ledger_ids.add(best_match["ledger_entry_id"])
    
    # Flag unmatched items
    for le in ledger_data:
        if le["entry_id"] not in matched_ledger_ids:
            matches.append({
                "ledger_entry_id": le["entry_id"],
                "bank_line_id": None,
                "match_type": "unmatched",
                "confidence": 0.0,
                "difference": abs(le["amount"]),
                "notes": "No matching bank line found. Possible outstanding check or timing difference."
            })
    
    for bl in bank_data:
        if bl["line_id"] not in matched_bank_ids:
            matches.append({
                "ledger_entry_id": None,
                "bank_line_id": bl["line_id"],
                "match_type": "unmatched",
                "confidence": 0.0,
                "difference": abs(bl["amount"]),
                "notes": "No matching ledger entry found. May require a journal entry to record."
            })
    
    return json.dumps(matches, indent=2)

@tool
def generate_adjustment_suggestion(unmatched_item: str, reason: str) -> str:
    """Generate a suggested adjusting journal entry for an unmatched item.
    Returns a structured journal entry suggestion for human review."""
    suggestion = {
        "entry_type": "suggested_adjustment",
        "status": "pending_approval",
        "unmatched_item": unmatched_item,
        "reason": reason,
        "suggested_action": "Review and potentially record a journal entry to reconcile this item.",
        "created_at": datetime.now().isoformat(),
    }
    return json.dumps(suggestion, indent=2)

# ---- Agent Setup ----
def create_reconciliation_agent():
    """Create the reconciliation agent executor."""
    
    llm = ChatOpenAI(
        model="gpt-4o",
        temperature=0.1,
    )
    
    tools = [get_ledger_data, get_bank_data, compute_matches, generate_adjustment_suggestion]
    
    system_prompt = """You are an expert reconciliation agent for accounting firms.

Your task: Perform a complete bank reconciliation between the general ledger cash account and the bank statement for a given period.

Process:
1. Use `get_ledger_data` to fetch all ledger entries for the cash account in the date range.
2. Use `get_bank_data` to fetch all bank statement lines for the same period.
3. Use `compute_matches` with the JSON outputs from steps 1 and 2 to run the matching engine.
4. Analyze the results carefully:
   - Exact matches: Confirm these are reconciled.
   - Fuzzy matches: Note any description discrepancies but confirm amounts match.
   - Unmatched ledger items: These may be outstanding checks, deposits in transit, or errors.
   - Unmatched bank items: These may be bank fees, interest, or items not yet recorded in the ledger.
5. For significant unmatched items (>$10), use `generate_adjustment_suggestion` to propose journal entries.
6. Compile a final ReconciliationReport as a JSON object summarizing all findings.

Be thorough. Every unmatched item must have a clear note explaining the likely reason (timing difference, bank fee, error, etc.)."""

    prompt = ChatPromptTemplate.from_messages([
        ("system", system_prompt),
        ("human", """Please perform a bank reconciliation with the following parameters:

Bank Account Number: {account_number}
GL Cash Account Code: {account_code}
Period: {start_date} to {end_date}

Produce a complete ReconciliationReport JSON object at the end."""),
        MessagesPlaceholder(variable_name="agent_scratchpad"),
    ])
    
    agent = create_openai_tools_agent(llm, tools, prompt)
    
    return AgentExecutor(
        agent=agent,
        tools=tools,
        verbose=True,
        max_iterations=8,
        handle_parsing_errors=True,
    )

# ---- Usage Example ----
if __name__ == "__main__":
    os.environ["OPENAI_API_KEY"] = "your-api-key-here"
    
    agent = create_reconciliation_agent()
    
    result = agent.invoke({
        "account_number": "1234567890",
        "account_code": "1000",  # Cash in Bank GL account
        "start_date": "2025-01-01",
        "end_date": "2025-01-31",
    })
    
    print("\n===== RECONCILIATION REPORT =====")
    print(result["output"])

3. Financial Statement Drafting Agent

After transactions are classified and accounts reconciled, the next logical step is generating draft financial statements. An AI agent can pull trial balance data, apply accounting standards (GAAP/IFRS), structure the balance sheet, income statement, and cash flow statement, and produce formatted outputs ready for partner review. The agent can also compute common financial ratios and flag unusual fluctuations for investigation.

# financial_statement_agent.py
# AI Agent that drafts financial statements from trial balance data
# Requires: pip install langchain openai pydantic

import os
import json
from typing import List, Dict, Optional
from pydantic import BaseModel
from datetime import datetime
from langchain_openai import ChatOpenAI
from langchain.tools import tool
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.agents import AgentExecutor, create_openai_tools_agent

# ---- Data Models ----
class TrialBalanceLine(BaseModel):
    account_code: str
    account_name: str
    category: str  # 'asset', 'liability', 'equity', 'revenue', 'expense'
    debit_balance: float
    credit_balance: float
    net_balance: float  # debit - credit; positive = debit balance, negative = credit balance

class FinancialStatements(BaseModel):
    period: str
    balance_sheet: Dict
    income_statement: Dict
    cash_flow_statement: Dict
    key_ratios: Dict[str, float]
    anomalies_detected: List[str]
    prepared_at: str

# ---- Simulated Trial Balance Data ----
def fetch_trial_balance(period: str) -> List[dict]:
    """Simulate fetching trial balance from accounting software.
    Replace with actual API call to QuickBooks, Xero, Sage, etc."""
    return [
        # Assets (debit balances)
        {"account_code": "1000", "account_name": "Cash - Operating Account", "category": "asset", "debit_balance": 85000.00, "credit_balance": 0.00, "net_balance": 85000.00},
        {"account_code": "1020", "account_name": "Cash - Savings Account", "category": "asset", "debit_balance": 120000.00, "credit_balance": 0.00, "net_balance": 120000.00},
        {"account_code": "1100", "account_name": "Accounts Receivable", "category": "asset", "debit_balance": 45000.00, "credit_balance": 0.00, "net_balance": 45000.00},
        {"account_code": "1150", "account_name": "Allowance for Doubtful Accounts", "category": "asset", "debit_balance": 0.00, "credit_balance": 2500.00, "net_balance": -2500.00},
        {"account_code": "1300", "account_name": "Fixed Assets - Net", "category": "asset", "debit_balance": 200000.00, "credit_balance": 0.00, "net_balance": 200000.00},
        {"account_code": "1350", "account_name": "Accumulated Depreciation", "category": "asset", "debit_balance": 0.00, "credit_balance": 45000.00, "net_balance": -45000.00},
        {"account_code": "1400", "account_name": "Prepaid Expenses", "category": "asset", "debit_balance": 8500.00, "credit_balance": 0.00, "net_balance": 8500.00},
        # Liabilities (credit balances)
        {"account_code": "2000", "account_name": "Accounts Payable", "category": "liability", "debit_balance": 0.00, "credit_balance": 32000.00, "net_balance": -32000.00},
        {"account_code": "2100", "account_name": "Accrued Expenses", "category": "liability", "debit_balance": 0.00, "credit_balance": 8500.00, "net_balance": -8500.00},
        {"account_code": "2200", "account_name": "Deferred Revenue", "category": "liability", "debit_balance": 0.00, "credit_balance": 18000.00, "net_balance": -18000.00},
        {"account_code": "2500", "account_name": "Long-Term Debt", "category": "liability", "debit_balance": 0.00, "credit_balance": 150000.00, "net_balance": -150000.00},
        # Equity (credit balances)
        {"account_code": "3000", "account_name": "Common Stock", "category": "equity", "debit_balance": 0.00, "credit_balance": 100000.00, "net_balance": -100000.00},
        {"account_code": "3100", "account_name": "Retained Earnings - Beginning", "category": "equity", "debit_balance": 0.00, "credit_balance": 95000.00, "net_balance": -95000.00},
        {"account_code": "3200", "account_name": "Dividends Paid", "category": "equity", "debit_balance": 25000.00, "credit_balance": 0.00, "net_balance": 25000.00},
        # Revenue (credit balances)
        {"account_code": "4000", "account_name": "Service Revenue", "category": "revenue", "debit_balance": 0.00, "credit_balance": 280000.00, "net_balance": -280000.00},
        {"account_code": "4100", "account_name": "Product Revenue", "category": "revenue", "debit_balance": 0.00, "credit_balance": 75000.00, "net_balance": -75000.00},
        # Expenses (debit balances)
        {"account_code": "5000", "account_name": "Salaries & Wages", "category": "expense", "debit_balance": 165000.00, "credit_balance": 0.00, "net_balance": 165000.00},
        {"account_code": "5100", "account_name": "Rent Expense", "category": "expense", "debit_balance": 36000.00, "credit_balance": 0.00, "net_balance": 36000.00},
        {"account_code": "5200", "account_name": "Office Supplies Expense", "category": "expense", "debit_balance": 4200.00, "credit_balance": 0.00, "net_balance": 4200.00},
        {"account_code": "5300", "account_name": "Utilities Expense", "category": "expense", "debit_balance": 5100.00, "credit_balance": 0.00, "net_balance": 5100.00},
        {"account_code": "5400", "account_name": "Professional Fees", "category": "expense", "debit_balance": 12000.00, "credit_balance": 0.00, "net_balance": 12000.00},
        {"account_code": "5500", "account_name": "Depreciation Expense", "category": "expense", "debit_balance": 8500.00, "credit_balance": 0.00, "net_balance": 8500.00},
        {"account_code": "5600", "account_name": "Interest Expense", "category": "expense", "debit_balance": 6200.00, "credit_balance": 0.00, "net_balance": 6200.00},
        {"account_code": "5700", "account_name": "Income Tax Expense", "category": "expense", "debit_balance": 28000.00, "credit_balance": 0.00, "net_balance": 28000.00},
    ]

# ---- Tool Definitions ----
@tool
def get_trial_balance(period: str) -> str:
    """Fetch the complete trial balance for a given accounting period.
    Returns JSON array of all GL accounts with debit/credit/net balances."""
    tb_data = fetch_trial_balance(period)
    return json.dumps(tb_data, indent=2, default=str)

@tool
def compute_ratio(metric_name: str, numerator: float, denominator: float) -> str:
    """Compute a financial ratio given numerator and denominator values.
    Returns the ratio value and a brief interpretation."""
    if denominator == 0:
        return json.dumps({"ratio": "N/A", "interpretation": "Denominator is zero; ratio cannot be computed."})
    
    ratio_value = numerator / denominator
    
    interpretations = {
        "current_ratio": ("healthy (>2.0)" if ratio_value > 2.0 else "moderate (1.0-2.0)" if ratio_value >= 1.0 else "concerning (<1.0)"),
        "debt_to_equity": ("high leverage (>2.0)" if ratio_value > 2.0 else "moderate leverage (1.0-2.0)" if ratio_value >= 1.0 else "low leverage (<1.0)"),
        "profit_margin": ("strong (>20%)" if ratio_value > 0.20 else "moderate (10-20%)" if ratio_value >= 0.10 else "thin (<10%)"),
        "gross_margin": ("healthy (>50%)" if ratio_value > 0.50 else "moderate (30-50%)" if ratio_value >= 0.

— Ad —

Google AdSense will appear here after approval

← Back to all articles