연동 가이드
Java / Kotlin (JVM)
Java와 Kotlin 서버에서 공식 OpenAI Chat Completions API, Spring AI, LangChain4j를 ABTO Gateway에 연결합니다.
ABTO를 연결하면 AI 호출의 비용과 응답 속도를 확인하고, 대시보드에서 모델과 프롬프트를 바꿀 수 있습니다. 공식 OpenAI Java SDK를 직접 사용한다면 아래 순서로 첫 호출을 보내세요. Spring AI나 LangChain4j를 사용한다면 프레임워크 연결로 이동하세요.
이 페이지의 예제는 OpenAI Chat Completions용입니다. 동기 호출, 비동기 호출, tool calling과 structured output을 지원합니다. Streaming과 Responses API는 지원하지 않습니다. RAG의 임베딩 호출은 기존 제공사 연결을 유지하세요.
1. 설치하기
섹션 제목: “1. 설치하기”Java 17 이상인 서버 프로젝트에 사용 경로에 맞는 패키지 하나를 설치하세요. Java와 Kotlin에서 같은 라이브러리를 사용합니다. 아래는 공식 OpenAI 클라이언트용 Gradle 의존성입니다.
서버 SDK백엔드에서 실행 (Calling Key)
다섯 패키지는 같은 버전을 사용하며, 공통 패키지인 abto-calling-core는 함께 설치됩니다.
| Maven 좌표 | 사용 경로 |
|---|---|
app.abto:abto-calling | 공식 OpenAI Java 클라이언트 |
app.abto:abto-calling-core | 순수 JDK HTTP |
app.abto:abto-calling-spring-ai-1 | Spring AI 1.1 / Boot 3 |
app.abto:abto-calling-spring-ai-2 | Spring AI 2.0 / Boot 4 |
app.abto:abto-calling-langchain4j | LangChain4j / LangGraph4j |
기본 abto-calling에는 Spring과 LangChain 의존성이 없습니다. Spring AI 1과 2 어댑터는 별도 애플리케이션에서 사용하세요.
2. API 키 준비하기
섹션 제목: “2. API 키 준비하기”ABTO Calling Key와 OpenAI API 키를 서버 환경변수에 설정하세요. 브라우저용 Event Key 대신 Calling Key를 사용해야 합니다.
export ABTO_CALLING_KEY=your-abto-calling-keyexport OPENAI_API_KEY=your-openai-api-key다른 제공사로도 라우팅한다면 아래 providerKey에 해당 제공사의 키를 추가하세요.
3. 첫 호출 보내기
섹션 제목: “3. 첫 호출 보내기”AbtoOpenAI.create(config)로 연결을 준비하고 openai()로 공식 OpenAI 클라이언트를 만드세요.
AbtoContext.builder(...)에는 호출을 구분할 기능 ID를 넣습니다. 예제에서는 review.summary를 사용합니다. 기능 ID만 보낼 때는 AbtoContext.of("review.summary")로 만듭니다.
import app.abto.calling.AbtoConfig;import app.abto.calling.AbtoContext;import app.abto.calling.openai.AbtoOpenAI;import com.openai.models.chat.completions.ChatCompletionCreateParams;
var config = AbtoConfig.builder() .apiKey(() -> System.getenv("ABTO_CALLING_KEY")) .gatewayBaseUrl("https://gateway.abto.app/v1") .providerKey("openai", () -> System.getenv("OPENAI_API_KEY")) .build();try (var abto = AbtoOpenAI.create(config)) { var client = abto.forContext(AbtoContext.builder("review.summary").deviceId("device-id").build()).openai(); var params = ChatCompletionCreateParams.builder() .model("gpt-4o-mini") .addUserMessage("이 상품의 리뷰를 세 줄로 요약해 줘.") .build(); try (var raw = client.chat().completions().withRawResponse().create(params)) { var requestId = raw.headers().values("x-abto-request-id").stream().findFirst().orElse(null); var completion = raw.parse(); }}completion: 평소와 같은 OpenAI 응답 본문requestId: Gateway가 발급한x-abto-request-id
실제 서버에서는 abto를 한 번 만들고 재사용한 뒤, 서버 종료 시 닫으세요.
요청마다 forContext(...)로 기능과 사용자 정보를 전달하세요. 각 요청은 컨텍스트를 구분하면서 연결 풀과 장애 복구 상태를 공유합니다.
deviceId에는 브라우저나 모바일 앱에서 받은 기기 식별자를 그대로 전달하세요. 비동기 작업에도 이 컨텍스트를 함께 전달해야 합니다.
한 기능이 LLM을 여러 번 부르면 호출 지점마다 .nodeId("review.draft")처럼 노드를 넣으세요.
x-abto-node-id로 전송되며, 라우팅에서 노드마다 에이전트 설정을 따로 고를 수 있습니다.
넣지 않으면 기능 ID가 노드 이름이 됩니다.
AbtoContext는 하나가 에이전트나 워크플로 실행 한 번입니다.
만들 때 traceId를 넣지 않으면 새 실행 ID가 생기고 x-abto-trace-id로 전송됩니다.
실행 하나 동안 같은 AbtoContext를 이어 써야 그 안의 모든 호출이 같은 실행으로 묶입니다.
노드를 바꿀 때는 새로 만들지 말고 context.withNodeId("review.polish")를 쓰세요. 실행 ID를 비롯한 나머지 값은 그대로 두고 노드만 바꿉니다.
호출마다 AbtoContext를 새로 만들면 호출마다 실행이 나뉩니다.
다른 서비스가 넘겨준 실행을 이어 갈 때만 .traceId(...)로 그 값을 넣으세요.
200자 이하의 출력 가능한 ASCII만 받으며, 비어 있거나 규칙을 벗어난 값은 Gateway가 오류 없이 무시합니다.
요청의 traceparent는 실행 ID로 쓰지 않습니다.
4. 호출 확인하기
섹션 제목: “4. 호출 확인하기”대시보드의 요청 목록에서 review.summary 호출을 찾아 응답, 비용, 응답 속도를 확인하세요.
호출이 실패하면 Calling Key와 OpenAI API 키가 서버에 설정되어 있는지 먼저 확인하세요.
응답은 왔지만 기록이 없다면 프로젝트, 조회 기간과 직접 폴백 실행 여부를 확인하세요.
연결이 완료되면 모델이나 프롬프트 바꿔 보기를 따라 라우팅도 확인해 보세요.
기존 OpenAI 클라이언트에 연결하기
섹션 제목: “기존 OpenAI 클라이언트에 연결하기”이미 공식 OpenAI 클라이언트를 사용하고 있다면 withOptions에 ABTO의 연결 설정을 전달하세요.
openai()로 새 클라이언트를 만드는 경우와 같은 인증과 폴백 정책을 적용합니다.
var transport = java.net.http.HttpClient.newBuilder() .followRedirects(java.net.http.HttpClient.Redirect.NEVER) .build();var scope = abto.forContext(AbtoContext.builder("review.summary").deviceId("device-id").build());var configured = existingClient.withOptions(scope.openaiOptions(transport));var completion = configured.chat().completions().create(params);HTTP 전송에는 위에서 전달한 JDK 클라이언트를 사용하며, 해당 클라이언트의 연결 풀, 프록시, TLS와 executor 설정을 유지합니다.
기존 OpenAI 클라이언트의 재시도 횟수, 전체 요청 timeout과 사용자 정의 헤더는 유지됩니다.
OkHttp 전용 interceptor는 자동으로 옮겨지지 않습니다. JDK 클라이언트는 Redirect.NEVER를 사용하고 CookieHandler와 Authenticator를 설정하지 않아야 합니다.
연결 timeout은 JDK 클라이언트를 만드는 builder에서 설정하세요. 전체 요청 timeout보다 짧은 읽기나 쓰기 timeout이나 JDK 설정과 충돌하는 연결 timeout을 지정하면 설정 오류가 발생합니다.
ABTO는 전달받은 기존 클라이언트를 닫지 않습니다. 애플리케이션의 원래 종료 처리를 유지하세요.
새 ABTO 연결에서도 기존 옵션을 재사용하려면 AbtoOpenAI.create(config, existingClient, transport)를 사용하세요.
Kotlin에서 비동기로 호출하기
섹션 제목: “Kotlin에서 비동기로 호출하기”import app.abto.calling.AbtoCancellationimport app.abto.calling.AbtoContextimport kotlinx.coroutines.future.await
val cancellation = AbtoCancellation()val scope = abto.forContext(AbtoContext.builder("review.summary").deviceId("device-id").build()) .withCancellation(cancellation)val completion = cancellation.bind( scope.openai().async().chat().completions().create(params)).await()
val configured = existingClient.withOptions( abto.forContext(AbtoContext.builder("review.summary").deviceId("device-id").build()) .openaiOptions(safeJdkTransport))비동기 요청마다 새 AbtoCancellation을 만들고 반환된 Future를 bind에 전달하세요.
코루틴이 취소되거나 cancellation.cancel()을 호출하면 실제 HTTP 호출도 취소되고, 진행 중인 Gateway 복구 확인 상태도 해제됩니다.
공식 SDK의 Future만 취소하면 HTTP 호출까지 취소가 전달되지 않습니다.
동기 코드에서는 withContext(context, callback)으로 컨텍스트를 전달할 수도 있습니다.
코루틴과 그래프 실행에서는 위 예제처럼 forContext를 사용하세요.
Spring AI와 LangChain4j에 연결하기
섹션 제목: “Spring AI와 LangChain4j에 연결하기”기존 모델 builder에 ABTO의 연결 설정을 적용하세요. 모델 옵션, 도구 설정, listeners와 추적 설정은 기존 builder에서 그대로 사용합니다.
| 프레임워크 | 설정 적용 경로 | 실행 가능한 Java / Kotlin 예제 |
|---|---|---|
| Spring AI 1.1 | openAiApiOptions(restBuilder).accept(apiBuilder), chatModelOptions(api).accept(modelBuilder) | Boot 3 예제 |
| Spring AI 2.0 | chatModelOptions().accept(modelBuilder) | Boot 4 예제 |
| LangChain4j | openAiOptions().accept(modelBuilder) | 모델과 그래프 예제 |
Spring AI 1에서는 기존 RestClient builder를 복제하고 HTTP 전송을 담당하는 request factory를 교체합니다.
기존 request factory에서 설정한 프록시나 TLS 옵션은 전달할 JDK 클라이언트에도 적용하세요.
Spring AI 2에서는 ABTO가 설정한 공식 동기와 비동기 OpenAI 클라이언트를 사용합니다.
LangChain4j 연결은 JDK HTTP 클라이언트를 사용합니다. 다른 사용자 정의 HTTP 구현과의 호환성은 검증하지 않았습니다. LangGraph4j에서는 ABTO를 연결한 모델과 요청별 컨텍스트를 그래프 노드에 전달하세요. 그래프 상태와 체크포인트는 기존 설정을 유지합니다.
HTTP로 직접 연결하기
섹션 제목: “HTTP로 직접 연결하기”OpenAI SDK 없이 호출하려면 AbtoHttpClient.wrap(safeJdkTransport, gateway)로 JDK HTTP 클라이언트에 ABTO를 연결하세요.
Java는 send, Kotlin은 sendAsync(...).await()를 사용할 수 있습니다.
폴백을 켰다면 BodyPublishers.ofString이나 ofByteArray처럼 다시 전송할 수 있는 요청 본문을 사용하세요.
Java HTTP 예제와 Kotlin HTTP 예제는 OpenAI 클라이언트 생성과 기존 클라이언트 설정도 함께 보여 줍니다. 예제는 SDK 소스를 참조하지 않고 Maven 좌표로 빌드됩니다.
Gateway 장애 시 직접 폴백하기
섹션 제목: “Gateway 장애 시 직접 폴백하기”직접 폴백은 Gateway 장애 시 ABTO를 도입하기 전에 쓰던 주소로 요청을 보내는 기능입니다.
기본값은 꺼짐입니다. 사용하려면 AbtoFallback.builder().baseUrl(existingEndpoint).build()를 AbtoConfig의 fallback에 전달하고 OpenAI API 키도 설정하세요.
요청이 Gateway에 도달하지 못했거나, Gateway가 제공사를 호출하기 전에 거절한 것이 확실할 때만 직접 호출합니다.
전송 여부가 불명확한 연결 단절, 호출자 취소, 제공사 오류에서는 현재 요청을 다시 보내지 않습니다.
timeout이 난 요청까지 다시 보내려면 onTimeout(true)를 설정하세요. 이미 처리된 요청이 다시 실행되어 중복 과금될 수 있습니다.
직접 호출에는 원래 Chat Completions 본문을 사용하며, ABTO 키와 제공사 후보 키, 쿠키는 제거합니다.
직접 호출은 Gateway를 거치지 않으므로 대시보드의 모델과 프롬프트 정책과 요청 기록, x-abto-request-id가 적용되지 않습니다.
재시도 설정
섹션 제목: “재시도 설정”공식 OpenAI SDK와 프레임워크의 기존 재시도 설정을 그대로 사용하세요. ABTO는 이 값을 덮어쓰거나 별도의 재시도 횟수를 추가하지 않습니다.
Langfuse와 기존 추적 유지하기
섹션 제목: “Langfuse와 기존 추적 유지하기”Micrometer, OpenTelemetry, LangChain4j listeners와 Langfuse exporter는 기존 설정을 유지하세요.
ABTO는 추적 exporter를 추가하거나 전역 설정을 바꾸지 않습니다.
기존 traceparent와 tracestate는 유지되며, AbtoContext.builder(feature).deviceId(device).traceparent(traceparent).tracestate(tracestate).build()로 직접 전달할 수도 있습니다.
추적 도구에는 앱이 보낸 입력이 기록됩니다. 대시보드에서 바꾼 모델과 프롬프트가 반영된 실제 실행 내용과 비용은 ABTO 요청 기록에서 확인하세요. ABTO가 추적 도구를 자동으로 설정하지는 않습니다. 추적 데이터가 도착하는지는 사용 중인 Langfuse나 수집기에서 확인하세요.
호환성을 확인한 버전
섹션 제목: “호환성을 확인한 버전”| Java | OpenAI Java | Spring AI 1.1.8 / Boot | Spring AI 2.0.1 / Boot |
|---|---|---|---|
| 17 | 4.49.0 | 3.4.13 | 4.0.8 |
| 21 | 4.49.0 | 3.5.16 | 4.1.1 |
| 25 | 4.81.0 | 3.5.16 | 4.1.1 |
위 표의 조합과 함께 Kotlin 2.3.21, 코루틴 1.10.2, LangChain4j 1.22.0, LangGraph4j 1.9.5를 검증했습니다. Maven 3.9.11과 Gradle 9.3.0 프로젝트에서 패키지를 설치하고 예제를 실행했습니다. 표에 없는 버전까지 호환성을 보장하지는 않습니다. 추가 설정은 공개 소스와 API별 예제를 참고하세요.