왜 액션 정의에서 자꾸 막히는가
Joule Studio로 커스텀 스킬을 만들 때 가장 많은 시간을 잡아먹는 구간은 화면 디자인도, 대화 흐름도 아닌 액션(Action) 정의입니다. 액션은 Joule이 실제 백엔드 API를 호출하는 통로인데, 여기서 파라미터 스키마·매핑·인증·설명문 중 하나라도 어긋나면 "Joule이 아예 액션을 호출하지 않거나, 엉뚱한 값으로 호출하는" 증상이 나타납니다. 이 글을 끝까지 읽으면 아래 항목을 스스로 점검할 수 있게 됩니다.
- OpenAPI 스펙을 액션으로 등록할 때 스키마를 어떻게 다듬어야 하는지
- Destination 인증 설정에서 빠뜨리기 쉬운 속성이 무엇인지
- LLM이 액션 호출 시점을 판단하게 만드는 description 작성법
- 입력/출력 매핑 오류와 에러 핸들링 부재를 미리 잡는 방법
시작 전에 갖춰두면 좋은 배경
이 글은 초보자 눈높이로 쓰였지만, REST/OData API의 기본 개념(GET/POST, 요청 바디, 응답 코드)과 JSON 문법은 알고 있다는 전제로 진행합니다. SAP BTP 서브어카운트에서 Destination을 만들어 본 경험이 있다면 훨씬 수월하고, 없더라도 이 글의 예제 설정을 따라오면 됩니다. LLM의 함수 호출(function calling) 개념을 들어봤다면 액션 동작 원리를 이해하는 데 도움이 됩니다.
어떤 환경에서 따라할 수 있나
Joule Studio는 SAP Build 로비 안에서 제공되는 기능으로, 일반적으로 다음 조건이 필요합니다.
- SAP BTP 서브어카운트 + SAP Build(Joule Studio 사용 가능 에디션) 구독
- Joule 프로비저닝이 완료된 테넌트 (Joule Studio는 Joule 활성화가 선행되어야 합니다)
- 액션이 호출할 백엔드 API — 이 글에서는 가상의 사내 설비 관리 시스템
FacilityCare의 REST API를 사용합니다 - BTP 콕핏의 Destination 편집 권한
기능 범위는 에디션과 릴리스 시점에 따라 달라질 수 있으므로, 실제 프로젝트 전에는 자신의 테넌트에서 사용 가능한 기능을 확인하는 것이 권장됩니다.
액션이 동작하는 원리부터 잡고 가기
액션을 "Joule에게 쥐여주는 리모컨 버튼"이라고 생각하면 이해가 빠릅니다. 사용자가 "3층 공조기 점검 요청 넣어줘"라고 말하면, Joule의 LLM은 자신이 가진 액션 목록(버튼들)의 이름과 설명문을 읽고 어떤 버튼을 누를지 결정합니다. 즉 흐름은 이렇습니다.
- 사용자 발화 → LLM이 의도 파악
- 등록된 액션들의 name/description/파라미터 스키마와 대조
- 가장 적합한 액션 선택 + 발화에서 파라미터 값 추출
- Destination을 통해 실제 API 호출
- 응답(출력 스키마)을 받아 자연어로 요약해 답변
여기서 핵심은 LLM이 코드를 읽는 게 아니라 메타데이터(설명문과 스키마)를 읽는다는 점입니다. API가 완벽해도 description이 부실하면 2번 단계에서 탈락하고, 스키마가 모호하면 3번 단계에서 잘못된 값이 들어갑니다. 초보자가 겪는 실수의 8할이 이 두 지점에서 발생합니다. 반대로 말하면, 액션 정의는 "API 등록 작업"이 아니라 "LLM에게 사용설명서를 써주는 작업"이라고 접근해야 합니다.
실전 예제: 설비 점검 요청 액션을 3단계로 완성하기
1단계 — 기본: OpenAPI 스펙을 액션으로 등록
가상의 FacilityCare 시스템에 점검 요청을 생성하는 API를 액션으로 만들어 봅니다. Skill Builder에서 액션을 추가할 때 OpenAPI 스펙을 가져오는 방식이 일반적입니다.
openapi: 3.0.0
info:
title: FacilityCare Inspection API
version: 1.0.0
paths:
/inspection-requests:
post:
operationId: createInspectionRequest
summary: 설비 점검 요청 생성
description: >
특정 설비에 대한 점검 요청을 생성한다.
사용자가 점검, 정비, 고장 확인을 요청할 때 사용한다.
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [equipmentTag, urgencyLevel]
properties:
equipmentTag:
type: string
description: 설비 태그 번호 (예 AHU-3F-02)
urgencyLevel:
type: string
enum: [LOW, NORMAL, HIGH]
description: 긴급도. 사용자가 말하지 않으면 NORMAL
requestNote:
type: string
description: 요청 사유 요약
포인트는 세 가지입니다. 첫째, operationId는 동사+명사로 명확하게. 둘째, 모든 파라미터에 description을 채우기. 셋째, 선택값은 enum으로 제한해 LLM이 임의의 문자열("급함", "빨리요")을 넣지 못하게 막는 것입니다.
2단계 — 실무: Destination 연결과 에러 응답 정의
액션이 실제 시스템을 호출하려면 BTP Destination이 필요합니다. 초보자가 가장 많이 빠뜨리는 부분이 인증 방식과 부가 속성입니다.
{
"Name": "FACILITYCARE_API",
"Type": "HTTP",
"URL": "https://facilitycare.example.internal/api/v1",
"ProxyType": "Internet",
"Authentication": "OAuth2ClientCredentials",
"clientId": "fc-joule-client",
"tokenServiceURL": "https://auth.example.internal/oauth/token"
}
그리고 스펙에 에러 응답을 반드시 정의해 두어야 Joule이 실패 상황을 사용자에게 자연스럽게 설명할 수 있습니다.
responses:
'201':
description: 요청 생성 성공. 요청 번호 반환
'404':
description: 해당 설비 태그가 존재하지 않음
'409':
description: 동일 설비에 미완료 점검 요청이 이미 존재함
404와 409의 description이 다르게 적혀 있으면, Joule은 "설비 번호를 다시 확인해 달라"와 "이미 접수된 요청이 있다"를 구분해서 답할 수 있습니다. 에러 스키마가 없으면 어떤 실패든 뭉뚱그린 오류 안내만 나옵니다.
3단계 — 프로덕션: 최소 권한, 출력 다이어트, 테스트 시나리오
운영 배포 전 점검 포인트입니다.
- 최소 권한 원칙: Destination의 클라이언트에는 점검 요청 생성/조회 스코프만 부여합니다. 액션 하나 때문에 전체 API 권한을 열어주는 사례가 의외로 많습니다.
- 출력 스키마 다이어트: 응답에 50개 필드가 있어도 액션 출력에는 요청 번호, 상태, 담당 팀 정도만 노출하는 것이 좋습니다. 불필요한 필드는 토큰을 낭비하고 답변 품질을 떨어뜨립니다.
- 테스트 시나리오 문서화: "정상 생성", "존재하지 않는 설비", "긴급도 미언급", "중복 요청" 등 최소 5개 발화 패턴을 만들어 Joule Studio의 미리보기/테스트 기능으로 반복 검증합니다. 발화를 바꿔가며 파라미터 추출이 흔들리지 않는지 보는 것이 핵심입니다.
자주 막히는 지점 문답으로 풀기
Q1. 액션을 등록했는데 Joule이 아예 호출을 안 합니다.
십중팔구 description 문제입니다. "점검 요청 API"처럼 한 줄짜리 설명은 LLM 입장에서 판단 근거가 없습니다. "언제 이 액션을 써야 하는지"를 사용자 발화 관점으로 서술하세요. 스킬의 트리거 문구와 액션 설명이 서로 충돌하는 경우도 확인해야 합니다.
Q2. 호출은 되는데 401/403 오류가 납니다.
Destination의 Authentication 유형이 NoAuthentication으로 남아 있거나, OAuth 토큰 URL·스코프가 누락된 경우가 대부분입니다. Joule Studio에 연결하기 전에 Destination 자체를 콕핏의 연결 확인 기능이나 별도 REST 클라이언트로 먼저 검증하는 습관이 필요합니다. 온프레미스 시스템이면 ProxyType을 OnPremise로 두고 Cloud Connector 경로를 확인하세요.
Q3. 파라미터에 자꾸 이상한 값이 들어갑니다.
스키마가 type: string만 있고 형식 제약이 없기 때문입니다. enum, pattern, 예시 값을 넣어 추출 범위를 좁히세요. 날짜는 "다음 주 화요일" 같은 상대 표현이 들어올 수 있으므로 description에 기대 형식(ISO 8601 등)을 명시하는 것이 일반적으로 안전합니다.
Q4. 출력이 그대로 JSON 덩어리로 노출됩니다.
출력 스키마와 필드 description이 비어 있으면 Joule이 응답을 해석하지 못합니다. 출력 필드에도 "사람에게 보여줄 이름"을 설명으로 달아 주세요.
여기까지 왔다면, 그 다음은
단일 액션이 안정화되면 자연스럽게 다음 주제로 확장됩니다. 여러 액션을 조합해 판단·실행하는 Agent Builder 기반 에이전트 설계, 조회 계열 답변 품질을 끌어올리는 그라운딩(문서 기반 지식) 연동, 그리고 스킬 배포 후 사용 로그를 보며 description을 다듬는 반복 개선 사이클입니다. 또한 OData 서비스를 액션화할 때의 메타데이터 변환 규칙, SAP Build의 액션 프로젝트 재사용 전략도 이어서 살펴볼 만한 주제입니다.
더 깊이 파고들 때 볼 자료
- SAP Help Portal — Joule 제품 문서
- SAP Help Portal — SAP Build(액션 프로젝트 및 Destination 연동)
- SAP Help Portal — BTP Connectivity와 Destination 설정
- SAP Community — Joule 토픽 (실무자 사례와 Q&A)
- SAP — Joule 제품 소개 페이지
- OpenAPI Specification — 스키마 작성 레퍼런스
액션 정의는 결국 "LLM이 읽을 문서를 쓰는 일"입니다. API를 붙이는 데 성공했다면, 이제 설명문을 다듬는 데 시간의 절반을 쓰세요. 그것이 Joule 스킬 품질을 가르는 가장 확실한 투자입니다.
댓글 0
아직 댓글이 없습니다.