Mastering Spring AI Tool Calling: Build a Finance Assistant Using Ollama

Mastering Spring AI Tool Calling: Build a Finance Assistant Using Ollama thumbnail

Large Language Models are great at reasoning and generating natural language, but they do not have access to your private business data. If a user asks:

  • What is my monthly salary?
  • How much did I spend on Food last month?
  • Give me a summary of my finances.

The model cannot answer these questions accurately without accessing external systems.

This is where Spring AI Tool Calling becomes extremely powerful. Instead of allowing the model to hallucinate, Spring AI enables the LLM to invoke Java methods as tools. These tools can fetch live data from databases, APIs, or enterprise systems and return grounded responses.

In this tutorial, we'll build a Finance Assistant powered by:

  • Spring AI 2.0
  • Ollama (Qwen3:8B)
  • PostgreSQL
  • JDBC Chat Memory
  • Streaming Responses
  • Tool Calling

What is Spring AI Tool Calling?

Tool Calling allows an LLM to invoke Java methods when additional information is required to answer a question. Instead of generating a response immediately, the model can:

  1. Analyze the user request.
  2. Identify a suitable tool.
  3. Extract parameters.
  4. Execute the Java method.
  5. Use the returned result to generate the final answer.

For example:

User:
How much did I spend on Food last month?

The model cannot know the answer itself.

Instead it:

1. Determines a financial lookup is required.
2. Extracts category=Food.
3. Resolves "last month" into actual dates.
4. Calls a tool.
5. Uses tool output to answer.

This transforms an LLM from a text generator into an intelligent application orchestrator.


Benefits include:

  • Eliminates hallucinations
  • Provides real-time data access
  • Enables enterprise integrations
  • Makes AI applications deterministic
  • Allows multi-step reasoning workflows

For business applications, tool calling is often more important than prompt engineering.


Project Architecture

Architecture Diagram:

spring-ai-tool-calling-architecture

The model decides when a tool should be executed. Spring AI automatically performs the tool orchestration.


Spring AI + Ollama Setup

The application uses Ollama running locally.

application.yaml

spring:
  application:
    name: finance-assistant

  ai:
    ollama:
      base-url: http://localhost:11434
      connection-timeout: 60s
      read-timeout: 300s
      chat:
        options:
          model: qwen3:8b
          keep_alive: 30m

    chat:
      memory:
        repository:
          jdbc:
            initialize-schema: always

Key observations:

  • Qwen3:8B is used as the local model.
  • JDBC Chat Memory schema is auto-created.
  • PostgreSQL stores both finance data and conversation history.

  datasource:
    url: jdbc:postgresql://localhost:5432/fin-assist
    username: postgres
    password: postgres
    hikari:
      maximum-pool-size: 10
      minimum-idle: 2
      connection-timeout: 30000
      idle-timeout: 600000
      max-lifetime: 1800000

Creating Finance Tools

The heart of tool calling is the tool implementation. Spring AI exposes tools using the @Tool annotation.

When you register tools: Spring AI scans all methods annotated with @Tool and automatically converts them into tool definitions that can be understood by the LLM. What makes this particularly powerful is that the model never directly accesses your database.

Expense Lookup Tool


@Tool(description = "Retrieve total expenses for a given category between two dates.")
public ExpenseInfo getExpenses(ExpenseQuery query) {
    BigDecimal amount = expenseRepository.findExpenses(
            query.category(),
            LocalDate.parse(query.fromDate()).atStartOfDay(),
            LocalDate.parse(query.toDate()).atStartOfDay());

    log.info("Tool Invoked: getExpenses({}, {}, {})",
        query.category(),
        query.fromDate(),
        query.toDate()
    );

    return new ExpenseInfo(amount, "INR");
}

Interesting part:

The LLM automatically generates:

{
  "category": "Food",
  "fromDate": "2026-05-01",
  "toDate": "2026-05-31"
}

from a natural language prompt such as:

How much did I spend on Food last month?

No manual parsing is required.

Salary Tool


@Tool(description = "Retrieve user's monthly salary")
public SalaryInfo getSalary(){
    Salary salary = Optional.ofNullable(salaryRepository.findTopByOrderByIdDesc()).orElse(null);

    BigDecimal monthlySalary = Objects.isNull(salary) ? BigDecimal.ZERO : salary.getMonthlySalary();
    return new SalaryInfo(monthlySalary, "INR");
}

The model learns from the description when this tool should be used.

Investment Tool


@Tool(description = "Get all investments")
public List<Investment> getInvestments() {
    return investmentRepository.findAll();
}

Financial Snapshot Tool


@Tool(description = "Retrieve a financial summary containing current salary," +
        "current month expenses, and total investments.")
public FinancialSnapshot summary(ExpenseSummaryQuery query) {

    SalaryInfo salary = getSalary();
    ExpenseInfo expenseInfo = monthlyExpenseSummary(query);

    BigDecimal investments = investmentRepository.findInvestments();

    return new FinancialSnapshot(
            salary.monthlySalary(),
            expenseInfo.expenseAmount(),
            investments);
}

This demonstrates a powerful pattern:

A tool can internally orchestrate multiple data retrieval operations and expose a higher-level business capability.


Adding PostgreSQL-backed Tool Data

Our finance assistant is grounded using PostgreSQL.

Database Schema

create table expenses (
    id bigint generated by default as identity,
    amount numeric(38,2),
    category varchar(255),
    expense_date timestamp(6),
    merchant varchar(255),
    primary key (id)
);

create table investments (
    id bigint generated by default as identity,
    amount numeric(38,2),
    date timestamp(6),
    investment_type varchar(255),
    primary key (id)
);

create table salary (
    id bigint generated by default as identity,
    monthly_salary numeric(38,2),
    primary key (id)
);

Unlike RAG systems that retrieve embeddings, tool calling retrieves structured data directly from transactional systems.

A useful comparison can be made with my Spring AI RAG implementation: Spring AI RAG Pipeline

RAG is ideal for knowledge retrieval. Tool calling is ideal for operational and transactional data.


Building the Finance Assistant

The most important configuration resides in ChatConfig.


@Bean
ChatClient financeChatClient(
        ChatClient.Builder builder,
        FinanceTools financeTools,
        ChatMemory chatMemory) {

    return builder
            .defaultTools(financeTools)
            .defaultAdvisors(
                    MessageChatMemoryAdvisor
                            .builder(chatMemory)
                            .build()
            )
            .build();
}

This single line:

.defaultTools(financeTools)

registers every @Tool method as a callable capability. When the model determines a tool is required, Spring AI automatically:

  1. Builds the tool schema.
  2. Sends it to the model.
  3. Receives tool invocation requests.
  4. Executes the Java method.
  5. Returns tool results.
  6. Continues the conversation.

Implementing JDBC Chat Memory

Conversation memory is implemented using PostgreSQL.

@Bean
ChatMemory chatMemory(ChatMemoryRepository repository) {
    return MessageWindowChatMemory.builder()
            .chatMemoryRepository(repository)
            .maxMessages(20)
            .build();
}

This gives the assistant awareness of previous messages. The backing table:


    CREATE TABLE IF NOT EXISTS SPRING_AI_CHAT_MEMORY (
    conversation_id VARCHAR(36) NOT NULL,
    content TEXT NOT NULL,
    type VARCHAR(10) NOT NULL,
    timestamp TIMESTAMP NOT NULL
);

The memory advisor is attached here:

.defaultAdvisors(
    MessageChatMemoryAdvisor.builder(chatMemory).build()
)

This allows:

User: What is my monthly salary?
Assistant: INR150,000

User: What about my expenses?

The assistant understands the context without requiring the user to repeat information.


Streaming Responses

Streaming dramatically improves perceived latency. Controller:


@PostMapping(produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ChatResponse> ask(
        @RequestBody ChatRequest chatRequest) {

    return finService.chat(chatRequest);
}

Service:


    return chatClient.prompt(chatRequest.getQuestion())
        .system(systemPrompt + "\nToday's date is "
                + LocalDate.now())
        .advisors(a -> a.param(
                ChatMemory.CONVERSATION_ID,
                chatRequest.getConversationId()))
        .stream()
        .content()
        .map(chunk -> ChatResponse.builder()
                .conversationId(chatRequest.getConversationId())
                .response(chunk)
                .build());

Benefits:

  • Faster perceived response time
  • Better UX
  • Ideal for local models
  • Natural conversational experience

Tool Invocation Demo

Query 1

What is my monthly salary?

Expected flow:

The logs clearly show:

Tool Invoked: getSalary()
tool-calling-query-1

Query 2

How much did I spend on Food last month?

This is more interesting.

The model must:

  1. Detect expense lookup intent.
  2. Extract category = Food.
  3. Resolve last month.
  4. Build ExpenseQuery.
  5. Invoke tool.

Tool execution:

Tool Invoked:
getExpenses(Food, yyyy-mm-dd, yyyy-mm-dd)

This demonstrates semantic parameter extraction.

No regex. No NLP pipeline. No custom parser.

spring-ai-tool-calling-query-2

Multi-Tool Calling Demo

Query

Give me a summary of my finances.
spring-ai-multi-tool-calling

This pattern is extremely important because real-world AI agents rarely depend on a single tool.

Most enterprise assistants require:

  • Customer lookup
  • Order lookup
  • Inventory lookup
  • Payment lookup

before producing a final answer.

Your Financial Snapshot tool is an excellent example of composite tool orchestration.


Limitations of Local Models

While local models are fantastic for privacy and cost control, they have limitations.

Common issues include:

  • Incorrect tool selection
  • Missed parameter extraction
  • Hallucinated answers
  • Reduced reasoning depth
  • Smaller context windows

Next Step: Agentic Finance Planner

Tool calling is the first step toward building agents.

Today our assistant can:

  • Retrieve salary
  • Retrieve expenses
  • Retrieve investments
  • Generate summaries

The next logical evolution is an Agentic Finance Planner. Imagine a user asking:

I want to save Rs.5 lakh in the next 18 months.
Create a plan.

An agent could:

  1. Analyze current salary.
  2. Analyze expenses.
  3. Analyze investments.
  4. Calculate savings potential.
  5. Generate monthly targets.
  6. Continuously track progress.

That will be the focus of the next article:

Spring AI Tool Calling vs MCP vs RAG

A common question is when to use Tool Calling, MCP, or RAG. Although they are often mentioned together, they solve very different problems. Below is the comparison:

Capability Tool Calling MCP (Model Context Protocol) RAG (Retrieval-Augmented Generation)
Primary Purpose Execute business logic and retrieve live data Connect models to external tools through a standard protocol Retrieve knowledge from documents
Access Live Data Yes Yes No
Invoke Actions Yes Yes No
Works with Databases Excellent Excellent Limited
Works with External APIs Excellent Excellent No
Works with PDFs & Documents Not Ideal Not Ideal Excellent
Enterprise Knowledge Search No No Yes
Typical Data Source Java Methods, Databases, APIs MCP Servers & External Services Vector Databases & Documents
Best Use Cases Customer lookups,
Banking apps,
Order tracking,
Financial assistants
GitHub integration,
Slack integration,
External tools,
Multi-system connectivity
Knowledge assistants,
Documentation search,
Policy Q&A,
PDF chat
Example Question "What is my monthly salary?" "Create a GitHub issue for this bug." "What does our leave policy say?"
Recommended for Finance Assistant Primary Choice Optional Future Integration For Financial Policies & Documents

Conclusion

Spring AI Tool Calling bridges the gap between language models and enterprise applications. In this Finance Assistant, we combined:

  • Spring AI
  • Ollama
  • PostgreSQL
  • JDBC Chat Memory
  • Streaming Responses
  • Tool Calling

to build a practical AI-powered finance assistant that can retrieve real financial data instead of hallucinating answers.

The complete source code can be found here on GitHub.

Once you understand tool calling, you unlock the foundation required for building agents, planners, copilots, and autonomous business workflows with Spring AI.

Support Us!

Buying me a coffee helps keep the project running and supports new features.

cards

Powered by paypal

Thank you for helping this blog thrive!

About The Author

author-image
I write about cryptography, web security, and secure software development. Creator of practical crypto validation tools at Devglan.

References