들어가며
Spring AI를 공부하면서 가장 인상 깊었던 기능이 바로 Tool Calling(Function Calling)입니다.
그동안은 프롬프트와 RAG를 통해서만 LLM에 외부 정보를 전달할 수 있었지만, 이제는 LLM이 직접 외부에 접근할 수 있는 시대가 열렸습니다.
물론 모든 모델이 이 기능을 지원하는 것은 아닙니다. 예를 들어 Ollama에서는 해당 기능이 가능한 모델에 Tools 라벨이 표시됩니다.
Spring AI 공식 문서에서는 Tool Calling의 활용 범위를 크게 두 가지로 설명하고 있습니다.
- 정보 검색: 데이터베이스, 웹 서비스, 파일 시스템 또는 웹 검색 엔진과 같은 외부 소스에서 정보를 검색 (예: RAG 구현)
- 조치 수행: 이메일 전송, 데이터베이스에 새 레코드 생성, 양식 제출, 워크플로 트리거 등 특정 작업을 수행(예: 챗봇으로 항공편 예약하기)
이 기능을 확장·고도화하면 AI Agent 수준의 기능 구현까지 가능하다고 생각합니다.
동작순서
Spring AI Tool Calling의 동작 과정입니다.
출처: Spring AI
- 도구 정의 포함
- 모델이 도구를 사용할 수 있도록, 채팅 요청에 해당 도구의 정의를 포함해야 합니다.
- 각 도구 정의는 이름, 설명, 입력 매개변수 스키마로 구성됩니다.
- 도구 호출 요청
- 모델이 특정 도구를 사용하기로 결정하면, 도구 이름과 입력 매개변수를 포함한 호출 요청을 응답으로 보냅니다.
- 애플리케이션 실행
- 애플리케이션은 도구 이름을 기반으로 해당 도구를 식별하고, 전달받은 매개변수를 사용해 실행합니다.
- 결과 처리
- 실행된 도구의 결과는 애플리케이션에서 후처리 과정을 거칩니다.
- 결과 반환
- 애플리케이션은 처리된 도구 호출 결과를 다시 모델로 전달합니다.
- 최종 응답 생성
- 모델은 도구 호출 결과를 추가 컨텍스트로 활용하여 최종 사용자 응답을 생성합니다.
@Tool / @ToolParam 어노테이션
첫번째로 도구로 설정하는 방법으로 2가지 어노테이션이 있습니다.
- @Tool: 도구로 설정할 메소드에 대한 이름, 설명등 정의
- name: 도구 이름으로 고유해야함(default 메소드명)
- description: 도구 설명
- returnDirect: 결과를 직접 받을지 설정(default false AI에 전달)
- resultConverter: AI에 전달할 때 DTO로 변환
- @ToolParam: 도구로 설정된 메소드에 넣을 파라미터 설명, 필수여부 정의
- required: 필수여부(default true)
- description: 도구 설명
💡 팁: description을 구체적으로 작성할수록 LLM이 도구를 올바르게 호출합니다.
예제 코드
public class DateTools {
// 외부에 정보를 받음
@Tool(description = "현재 날짜와 시간을 알려주는 도구")
String getCurrentDateTime() {
System.out.println(LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
return LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
}
// 외부에 동작 수행
@Tool(description = "알람을 설정하는 도구")
String setAlarm(@ToolParam(description = "yyyy-mm-dd hh:mi:ss") String time) {
System.out.println("Alarm set for " + time);
return time;
}
}
@AllArgsConstructor
@RestController
public class Tools {
private final ChatModel chatModel;
@GetMapping("/v1/tools")
public ChatResponse tools(@RequestParam("text") String text){
Prompt prompt = new Prompt(
List.of(
new SystemMessage("너는 도구를 유용하게 잘 사용하는 친절한 AI 비서이야"),
new UserMessage( text )
));
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultTools(new DateTools()) // 기본설정
.build();
ChatResponse response = chatClient
.prompt(prompt)
//.tools(new DateTools()) // 요청마다 옵션으로 설정
.call()
.chatResponse();
return response;
}
}
위 예제에서 getCurrentDateTime 메서드 대신 DB 조회, Web Search API 호출 등으로 확장할 수 있습니다.
또한 setAlarm 메서드를 이메일 발송, 슬랙 알림, 결제 승인 처리 등으로 변경해 조치 수행 기능을 구현할 수 있습니다.
ToolContext
ToolContext는 LLM이 아니라 외부에서 직접 값을 전달할 수 있는 파라미터입니다.
이 객체에는 LLM의 도구 호출 이력, 시스템 메시지, 사용자 메시지 등 컨텍스트 정보가 포함됩니다.
이를 활용하면 DB 조회 시 요청한 유저 정보를 함께 전달해 권한 기반의 결과 필터링이 가능합니다.
출처: Spring AI
@Tool(description = "고객 정보 검색")
Customer getCustomerInfo(Long id, ToolContext toolContext) {
return customerRepository.findById(id, toolContext.getContext().get("tenantId"));
}
String response = ChatClient.create(chatModel)
.prompt("ID 42의 고객에 대해 자세히 알려주세요.")
.tools(new CustomerTools())
.toolContext(Map.of("tenantId", "aaa")) // Tool에 외부 값 전달
.call()
.content();
마무리하며
Tool Calling과 Advisor의 가장 큰 차이점은 호출 여부를 결정하는 주체입니다.
Tool Calling은 LLM의 판단에 따라 호출 여부가 결정되지만, Advisor는 항상 호출됩니다.
두 기능의 역할과 성격이 다른 부분이 있습니다.
다만 Tool Calling을 사용할 때는 주의해야 할 점이 있습니다.
성능이 낮은 모델을 사용할 경우 LLM이 필요한 도구를 호출하지 않거나, 잘못된 파라미터를 전달할 수 있습니다.
예를 들어, qwen3:1.7b 모델로 "현재 시간의 10분 뒤에 알람을 설정해" 라고 요청했을 때 예외적인 상황이 발생했습니다.
- 현재 시간을 가져오는 메서드는 정상적으로 호출했지만, 알람 설정 메서드는 호출하지 않고도 "설정 완료"라고 응답한 경우
- 혹은 파라미터에 null 값을 여러 번 전달한 뒤, 마지막에야 정상적인 시간을 넣어 호출한 경우
실무에 적용할 때는 반드시 이런 케이스를 감지하고 대응할 로직을 포함하는 것이 좋습니다.
Spring AI를 학습하며 느낀 점은, 사용법이 생각보다 간단하고,
멀티모달, MCP, Chat Memory 등 다양한 기능들을 제공하여 AI 활용이 훨씬 가까워졌습니다.
아마 앞으로 3년 내에는 대부분의 웹 서비스에 직·간접적으로 적용될 것으로 보입니다.
마치 프로메테우스가 불을 인간에게 가져다준 것처럼, 파이썬 중심이던 AI 활용 환경이 이제 Java 진영에서도 가능해졌습니다.
이전 시리즈
해당 예시는 github에 있습니다.
url: https://github.com/Zero-Human/springAi
참고자료
- Spring AI 공식 문서
'Spring' 카테고리의 다른 글
Advisor로 LLM에 추가 정보 제공 및 응답 가공하기 (feat. RAG) (3) | 2025.08.03 |
---|---|
Spring AI로 LLM 연결하기(Feat: ollama) (8) | 2025.07.27 |
[Spring] Factory Method Pattern 사용하기 (Feat: injecting Collections) (0) | 2025.03.23 |