Hands‑On Java Function Calling with Spring AI: Build an Intelligent Customer Service Bot

This article explains how Function Calling lets large language models invoke Java methods via Spring AI, walks through the four‑step workflow, shows declarative and programmatic tool definitions, and demonstrates a complete customer‑service chatbot with code examples and best‑practice guidelines.

Coder Trainee
Coder Trainee
Coder Trainee
Hands‑On Java Function Calling with Spring AI: Build an Intelligent Customer Service Bot

RAG enables large models to read private documents ("knowing"), but the next challenge is enabling them to "do"; Function Calling serves as the bridge that lets a model request execution of Java code.

1. What is Function Calling?

Function Calling allows a large model to generate a JSON payload containing a function name and parameters, which Spring AI then parses and invokes the corresponding Java method.

User query → model analyzes intent.

Model decision → decides which tool to call, produces JSON with function name and arguments.

Application execution → Spring AI parses the JSON and calls the Java method.

Result return → execution result is sent back to the model, which generates the final reply.

Key insight: the model can only request a tool; the actual execution is performed by your Java code, which is a crucial security design.

2. Application Scenarios

Information retrieval : fetch real‑time data such as weather, database queries, or external APIs.

Operation execution : perform actions like sending emails, creating orders, or setting alarms.

3. Defining Tools in Spring AI

3.1 Declarative way – @Tool annotation

Recommended for its simplicity. Example:

import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;

@Component
public class DateTimeTools {
    @Tool(description = "获取用户时区的当前日期和时间")
    String getCurrentDateTime() {
        return LocalDateTime.now().toString();
    }

    @Tool(description = "为指定时间设置闹钟,时间格式为 ISO-8601")
    void setAlarm(@ToolParam(description = "ISO-8601 格式的时间") String time) {
        System.out.println("⏰ 闹钟已设置为 " + time);
        // 实际可调用闹钟服务
    }
}

Key parameters of @Tool:

name : tool name, defaults to the method name and must be unique per request.

description : detailed description that the model uses to decide whether to call the tool.

returnDirect : whether the result is returned directly to the client without passing back to the model. @ToolParam annotates method parameters to help the model extract values.

3.2 Programmatic way – FunctionToolCallback

Use when finer control is needed:

import org.springframework.ai.model.function.FunctionToolCallback;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FunctionCallbackConfig {
    @Bean
    public FunctionToolCallback weatherFunctionCallback() {
        return FunctionToolCallback.builder()
                .description("获取指定城市的天气信息")
                .function("getWeather", (WeatherRequest req) -> {
                    // 业务逻辑
                    return new WeatherResponse(req.city(), "晴", "25°C");
                })
                .inputType(WeatherRequest.class)
                .build();
    }
}

3.3 Registering Tools with ChatClient

Runtime (single‑request) tool:

String response = ChatClient.create(chatModel)
        .prompt("明天是星期几?")
        .tools(new DateTimeTools()) // 仅本次可用
        .call()
        .content();

Default (global) tool:

ChatClient chatClient = ChatClient.builder(chatModel)
        .defaultTools(new DateTimeTools()) // 默认启用
        .build();

3.4 Bean‑name registration

If a tool is defined as a Spring Bean, reference it by name:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Description;
import java.util.function.Function;

@Description("Get the weather in location")
@Bean
public Function<WeatherRequest, WeatherResponse> weatherFunction() {
    return request -> {
        // 业务逻辑
        return new WeatherResponse(request.location(), "25°C", "晴朗");
    };
}

4. Full Hands‑On: Intelligent Customer Service Assistant

4.1 Tool definitions

import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Component;
import java.util.Map;

@Component
public class CustomerServiceTools {
    private static final Map<String, String> ORDERS = Map.of(
        "1001", "已发货,预计明天送达",
        "1002", "处理中",
        "1003", "已签收"
    );

    @Tool(description = "查询订单状态,参数为订单号")
    public String queryOrder(@ToolParam(description = "订单号,如 1001") String orderId) {
        if (orderId == null || orderId.trim().isEmpty()) {
            return "请提供有效的订单号";
        }
        String result = ORDERS.get(orderId);
        return result != null ? "订单 " + orderId + ":" + result : "未找到订单 " + orderId;
    }

    @Tool(description = "获取退款政策信息")
    public String getRefundPolicy() {
        return """
            1. 7天无理由退货(不影响二次销售)
            2. 15天质量问题换货
            3. 1年质保期内免费维修
            """;
    }

    @Tool(description = "转接人工客服")
    public String transferToHuman() {
        return "已为您转接人工客服,预计等待 2-3 分钟";
    }
}

4.2 Agent service

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.stereotype.Service;

@Service
public class CustomerServiceAgent {
    private final ChatClient chatClient;

    public CustomerServiceAgent(ChatClient.Builder builder, CustomerServiceTools tools) {
        this.chatClient = builder
            .defaultSystem("""
                你是一个专业的客服助手,可以调用工具来处理用户问题。
                可用工具:
                - queryOrder:查询订单状态
                - getRefundPolicy:获取退款政策
                - transferToHuman:转接人工客服
                请友好、专业地帮助用户。
                """)
            .defaultTools(tools)
            .build();
    }

    public String chat(String userInput) {
        return chatClient.prompt(userInput).call().content();
    }
}

4.3 Controller

import org.springframework.web.bind.annotation.*;
import java.util.Map;

@RestController
@RequestMapping("/api/customer")
public class CustomerController {
    private final CustomerServiceAgent agent;

    public CustomerController(CustomerServiceAgent agent) {
        this.agent = agent;
    }

    @PostMapping("/chat")
    public Map<String, String> chat(@RequestBody Map<String, String> request) {
        String response = agent.chat(request.get("message"));
        return Map.of("response", response);
    }
}

4.4 Testing with curl

# Test order query
curl -X POST http://localhost:8080/api/customer/chat \
  -H "Content-Type: application/json" \
  -d '{"message":"查询订单1001"}'

# Test refund policy
curl -X POST http://localhost:8080/api/customer/chat \
  -H "Content-Type: application/json" \
  -d '{"message":"怎么退货?"}'

# Test transfer to human
curl -X POST http://localhost:8080/api/customer/chat \
  -H "Content-Type: application/json" \
  -d '{"message":"我要找人工客服"}'

5. Best Practices

5.1 Write detailed tool descriptions

The model’s decision relies entirely on the description; use specific wording such as "获取指定城市的当前天气信息" instead of a vague "获取数据".

5.2 Annotate parameters with @ToolParam

This helps the model correctly extract argument values.

5.3 Implement robust error handling

Tools should catch exceptions and return friendly error messages rather than letting the dialogue break.

5.4 Keep tool responsibilities single‑purpose

Design fine‑grained tools so the model can combine them flexibly.

5.5 Consider security

Since the model can only request tool execution, enforce permission checks and audit logs in the Java code.

💡代码地址在第一期获取
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

backendJavaFunction CallingSpring AIChatbotAI integration
Coder Trainee
Written by

Coder Trainee

Experienced in Java and Python, we share and learn together. For submissions or collaborations, DM us.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.