Tools

Tools (Function Calling · 도구 호출)

LLM 이 스스로 행동을 취하게 하려면 도구 호출(Function Calling)이 필요해요. LangChain4j 는 도구 사용을 위한 두 가지 추상화 레벨을 제공해요: 저수준 ChatModel + ToolSpecification API, 그리고 고수준 AI Services + @Tool 어노테이션 방식. 이 페이지에서는 특히 오류 처리·보상 같은 운영 관점을 함께 살펴볼게요.

출처: 공식문서

저수준 도구 API (Low Level)

ChatModel(그리고 StreamingChatModel 도)의 chat(ChatRequest) 로 도구를 쓸 수 있어요. ChatRequest 를 만들 때 하나 이상의 ToolSpecification 을 지정하면 돼요. ToolSpecification 은 도구의 모든 정보를 담아요: name, description, parameters(+각 파라미터 설명), metadata. 가능한 한 많은 정보(명확한 이름, 풍부한 설명, 파라미터 설명)를 주는 게 권장돼요.

ToolSpecification 은 수동으로 만들거나(빌더 + JsonObjectSchema), 헬퍼 메서드(ToolSpecifications.toolSpecificationsFrom(Class/Object/Method))로 만들 수 있어요.

ChatRequest request = ChatRequest.builder()
        .messages(UserMessage.from("What will the weather be like in London tomorrow?"))
        .toolSpecifications(toolSpecifications)
        .build();
ChatResponse response = model.chat(request);
AiMessage aiMessage = response.aiMessage();

고수준 도구 API (High Level · @Tool)

고수준에서는 아무 Java 메서드에 @Tool 어노테이션을 달면 그 메서드가 도구가 돼요. @Tool 메서드는 다양한 타입의 파라미터와 반환 타입(void 포함)을 받을 수 있어요. @P 로 파라미터 설명을, @Description 로 클래스·필드 설명을 줄 수 있어요. @ToolMemoryId 로 메모리 ID 를 넘기고, InvocationParameters/InvocationContext 로 부가 데이터를 전달할 수 있어요.

AI Service 에 도구를 등록하는 예시:

Assistant assistant = AiServices.builder(Assistant.class)
        .chatModel(model)
        .tools(new MyTools())
        .build();

여러 도구 동시 실행: LLM 이 한 번에 여러 도구를 호출하면(병렬 도구 호출) 기본적으로 AI Service 는 순차 실행해요. 동시에 실행하고 싶다면 executeToolsConcurrently() 또는 executeToolsConcurrently(Executor) 를 호출하면 돼요.

동적 도구 지정: ToolProvider 로 호출마다 도구를 동적으로 결정할 수도 있어요. ToolProviderRequest(UserMessage, 채팅 메모리 ID, InvocationParameters 포함)를 받아 ToolProviderResult 로 현재 호출에서 사용할 도구 집합을 반환해요.

오류 처리 (Error Handling)

:::note 아래 기본값은 동기·TokenStream 모드 기준이에요. 비동기·리액티브 모드에서는 반대예요: 도구 실행 오류는 LLM 에 보내는 대신 호출을 실패시키고, 인자 파싱 오류는 호출 실패 대신 LLM 에 보내요. 명시적으로 설정한 핸들러는 모든 모드에서 사용돼요. (참고: Non-blocking and Reactive) :::

도구 이름 오류

LLM 이 존재하지 않는 도구 이름을 환각(hallucinate)할 수 있어요. 기본적으로 LangChain4j 는 예외를 던지지만, hallucinatedToolNameStrategy 로 다른 동작을 설정할 수 있어요. 예를 들어 "그런 도구는 없다"는 메시지를 LLM 에 보내 다른 도구 호출을 하도록:

AssistantHallucinatedTool assistant = AiServices.builder(AssistantHallucinatedTool.class)
        .chatModel(chatModel)
        .tools(new HelloWorld())
        .hallucinatedToolNameStrategy(toolExecutionRequest -> ToolExecutionResultMessage.from(
                toolExecutionRequest, "Error: there is no tool called " + toolExecutionRequest.name()))
        .build();

도구 인자 오류

기본값은 예외를 던져요(LLM 이 잘못된 JSON 을 생성하거나 필수 파라미터를 빼먹은 경우). 하지만 인자 오류는 대개 LLM 에서 오고, LLM 은 명확한 오류 메시지를 받으면 스스로 고칠 수 있어요. 그래서 ToolArgumentsErrorHandler 를 구성해 오류 텍스트를 LLM 에 돌려주는 걸 권장해요:

Assistant assistant = AiServices.builder(Assistant.class)
        .chatModel(chatModel)
        .tools(tools)
        .toolArgumentsErrorHandler((error, errorContext) -> ToolErrorHandlerResult.text(error.getMessage()))
        .build();

ToolErrorHandlerResult.text(...) 를 반환하면 LLM 이 고쳐 재시도하게 하고, 예외를 던지면 AI Service 흐름을 멈춰요. 원래 예외 전체에 접근해야 한다면 errorContext.rawError() 를 써요.

도구 실행 오류

기본적으로 @Tool 메서드가 예외를 던지면 그 메시지(e.getMessage())가 도구의 실행 결과로 LLM 에 전송돼요. 프로덕션에서는 원시 예외 메시지를 LLM 에 보내지 않는 걸 권장해요 — 스택 트레이스, 파일 경로, 크레덴셜, PII 가 LLM·채팅 히스토리·관측 파이프라인·프로바이더 로그로 흘러갈 수 있어요. ToolExecutionErrorHandler 로 일반 메시지나 정제된 실패 설명을 반환하고, 상세는 로그·관측 이벤트에 맡기세요:

Assistant assistant = AiServices.builder(Assistant.class)
        .chatModel(chatModel)
        .tools(tools)
        .toolExecutionErrorHandler((error, errorContext) -> ToolErrorHandlerResult.text("Tool execution failed."))
        .build();

도구 동작 보상 (Compensating Tool Actions)

AI Service 가 여러 도구로 작업을 수행할 때 한 도구가 실패하면 시스템이 불일치 상태로 남을 수 있어요 — 일부 도구는 성공했고 일부는 그렇지 않으니까. 예를 들어 은행 송금에서 LLM 이 수취인 계좌에 먼저 입금하고, 출금이 잔액 부족으로 실패하면 수취인에게 돈이 남는 상황이 생겨요.

이를 위해 도구 오류 시 보상을 켤 수 있어요. 켜면 어떤 도구 실행이 실패했을 때, 보상 동작을 선언한 이전 성공 도구 호출들을 역순으로 자동 실행 취소해요. @CompensateFor 어노테이션으로 보상 동작을 선언하고, compensateOnToolErrors(true) 로 활성화해요:

Assistant assistant = AiServices.builder(Assistant.class)
        .chatModel(model)
        .tools(new BankAccountService())
        .compensateOnToolErrors(true)
        .build();

@Tool 을 도구로 쓰는 AI Service 는 다른 AI Service 를 도구로도 사용할 수 있어요(라우터 패턴). 그리고 LangChain4j 는 모델 컨텍스트 프로토콜(MCP)도 지원해요.

더 알아보기