Jump to content

CLOVA Studio 운영자

Administrators
  • 게시글

    255
  • 첫 방문

  • 최근 방문

  • Days Won

    49

CLOVA Studio 운영자 last won the day on November 19

CLOVA Studio 운영자 had the most liked content!

Recent Profile Visitors

1,523 profile views

CLOVA Studio 운영자's Achievements

Community Regular

Community Regular (8/14)

  • One Year In Rare
  • Collaborator Rare
  • One Month Later
  • Dedicated Rare
  • Reacting Well Rare

Recent Badges

69

Reputation

  1. 안녕하세요, @morm님, 1. 네, 개인 프로젝트의 용도로도 이용하실 수 있으며, 필요에 따라 서비스 앱 신청을 진행해주시면 됩니다. 서비스앱 신청: https://guide.ncloud-docs.com/docs/clovastudio-playground01#서비스앱신청 이용량 제어 정책: https://guide.ncloud-docs.com/docs/clovastudio-ratelimiting 2. 서비스 이용약관에 기재되어 있는데요. '서비스 약관 10조 1항: 서비스 관련 정보 및 자료는 회사의 서비스 품질 및 기능 개선을 위해 사용할 수 있습니다.'를 참고하시면 될 것 같습니다. 서비스 이용약관: https://www.ncloud.com/policy/terms/stdio 감사합니다.
  2. 파인 튜닝은 언제 활용하는가? 프롬프트 엔지니어링은 AI 모델의 성능을 향상시키는 효과적인 방법이지만, 그 한계에 도달할 때가 있습니다. 이때 유용한 대안이 바로 튜닝입니다. 튜닝은 프롬프트 엔지니어링만으로는 성능 향상이 어려운 정체 구간에서 추가적인 성능 개선을 가능하게 합니다. 튜닝의 장점 중 하나는 사용자가 보유한 고유 데이터를 활용하여 특정 작업에 특화된 모델을 만들 수 있다는 것입니다. 예를 들어, 고객의 문의를 분류하는 작업에서 100건의 테스트셋으로 프롬프트 최적화를 수행했지만, 정답률이 73%에 머물러 있었습니다. 또한, AI의 출력이 불필요한 문장이나 부가적인 설명을 포함하는 경우가 여전히 존재했습니다. 이러한 상황에서 파인튜닝은 모델의 성능을 향상시키고, 출력의 일관성을 개선하는 데 효과적인 방법입니다. 튜닝 전 오답 예시 User Completion 실제 정답 이전에 계정 만들었다가 지우고 다시 네이버 밴드 새로 계정 만들려고 하는데, 계정 만들고 지웠다가 다시 가입이 안되나요? 정답은 '네이버밴드 오류해결'입니다. 네이버밴드 계정 이러한 상황에서, 고객 문의 분류 작업을 예로 들어 튜닝을 통한 성능 향상 방법을 살펴보겠습니다. 우리의 목표는 카테고리 분류의 정확도를 높이고, AI가 '카테고리명'만을 출력하도록 만드는 것입니다. 이를 위해 다음과 같은 단계로 튜닝을 진행해 보겠습니다. Step 1. 데이터셋 준비 튜닝을 효과적으로 수행하기 위해서는 충분한 양의 고품질 데이터가 필요합니다. 하이퍼클로바X의 경우, 일반적으로 400개 이상의 데이터셋이 권장됩니다. 이는 작업의 성격과 도메인(예: 의료, 법률, 경제, 마케팅 등)에 따라 다소 차이가 있을 수 있습니다. HCX-003 모델 인퍼런스 시 결과가 잘 나오지 않던 항목을 400개 이상의 데이터셋으로 튜닝했을 때 오류 발생 확률이 크게 감소하는 것을 확인했습니다. (작업에 따라 달라질 수 있습니다.) 1. 학습시킬 데이터 수집하기 튜닝 학습을 시작하기 전, 다양한 고객 문의와 그에 맞는 카테고리 분류 데이터를 모아야 합니다. 이때 데이터 전처리가 중요한데, 이는 오타 수정, 표기 교정, 데이터 정제 및 구조화 등을 포함하는 작업입니다. 효과적인 학습 시스템 구축을 위해 필수적인 단계로, 전처리가 필요한 데이터 예시를 들면 다음과 같습니다. 데이터 전처리 과정에서는 잘못된 카테고리로 라벨링된 데이터를 올바른 카테고리로 수정하고, 의도와 의미를 판별하기 어려운 데이터는 제거하는 것이 좋습니다. 모델의 성능 향상을 위해 특수 문자와 이모티콘 제거, 축약어 및 인터넷 용어의 표준어 처리 등의 추가적인 전처리 작업도 고려할 수 있습니다. 전처리 유형 text 잘못된 completion 올바른 completion 잘못된 라벨링 네이버 드라이브 사용 금액 알려주세요. 네이버플러스멤버십 오류 해결 MYBOX 가격 전처리 유형 text 판별불가 ㅇㅋㄷㅋ111 판별불가 !!bjaowjd@@ 위와 같이 의도와 의미를 판별하기 어려운 데이터는 제거해주어야합니다. 그 외에도 모델의 성능을 높이기 위해, 특수 문자 및 이모티콘 제거, 축약어 및 인터넷 용어 표준어 처리 등의 작업도 고려할 수 있을 것입니다. 이러한 전처리 과정을 거쳐, 네이버 고객센터의 고객 문의 데이터를 기반으로 1000개의 학습 데이터셋을 구성했습니다. 이 데이터셋은 다음과 같은 구조를 가집니다. Text 열: 다양한 고객 문의 예제를 포함합니다. MYBOX, 네이버플러스멤버십, 네이버 밴드 등 15개의 카테고리에 걸쳐 실제 사용자의 문의를 반영하기 위해 비문도 포함될 수 있습니다. Completion 열: 각 고객 문의에 대한 정확한 카테고리 분류를 포함합니다. 분류 태스크의 성공을 위해 이 부분의 정확성은 매우 중요합니다. 이렇게 구성된 데이터셋의 예시는 다음과 같습니다. C_ID T_ID Text Completion 1 0 네이버플러스멤버십 한달에 내는 요금이 얼마고 멤버십은 따로 어플 설치해서 이용하는건가요? 네이버플러스멤버십 가격 2 0 핸드폰 해킹 당해서 드라이브에 사진 백업해두려고 하는데 앱을 깔아야지 백업 가능한가요? 백업하는 법 좀 알려주세요 MYBOX 보안 및 해킹 3 0 네이버 밴드 계정을 찾을려면 어케해야되나요 네이버밴드 계정 4 0 최근에 멤버십이 7월26일에 4900원이 결제 된다는데 이거 무슨 소리이죠? 네이버플러스멤버십 결제 학습 데이터는 양보다도 질이 중요합니다. 수집한 데이터의 정답(completion)이 올바르게 부여되어있는지 사전에 꼼꼼히 체크하면 도움이 됩니다. 2. 데이터셋 파일 만들기 파일 형식 : .json 혹은 .csv 파일로 준비해주세요. System_Prompt(선택) : 시스템 프롬프트를 함께 학습시킨다면, 가장 첫 열에 추가해야 합니다. 저희는 최적화한 시스템 프롬프트를 함께 데이터셋에 추가해 학습시켜보았습니다. C_ID : 한 개가 대화 데이터 한 건을 의미하며, 0부터 시작해 하나씩 늘어납니다. T_ID : User & Assistant의 발화 페어 한 건을 의미하며, 0부터 시작해 하나씩 늘어납니다. 저희의 경우, '분류' 태스크이기 때문에 여러 대화 턴 예제는 필요 없습니다. User와 Assistant의 발화 페어가 필요하지 않으므로, T_ID는 모두 0으로 설정해 데이터셋을 만들었습니다. System_Prompt C_ID T_ID Text Completion <system prompt가 있는 경우 삽입> 0 0 네이버플러스멤버십 한달에 내는 요금이 얼마고 멤버십은 따로 어플 설치해서 이용하는건가요? 네이버플러스멤버십 가격 <system prompt가 있는 경우 삽입> 1 0 핸드폰 해킹 당해서 드라이브에 사진 백업해두려고 하는데 앱을 깔아야지 백업 가능한가요? 백업하는 법 좀 알려주세요 MYBOX 보안 및 해킹 <system prompt가 있는 경우 삽입> 2 0 네이버 밴드 계정을 찾을려면 어케해야되나요 네이버밴드 계정 <system prompt가 있는 경우 삽입> 3 0 최근에 멤버십이 7월26일에 4900원이 결제 된다는데 이거 무슨 소리이죠? 네이버플러스멤버십 결제 데이터셋에 대한 자세한 설명은 데이터셋 준비 가이드를 참고해보세요. Step 2. AI 모델 튜닝하기 1. 하이퍼파라미터 설정하기 epochs : 전체 데이터셋을 반복 학습하는 횟수를 지정합니다. 예를 들어, 4에서 8로 증가시키면 모델의 학습량이 늘어나 loss가 감소할 수 있습니다. 하지만, 과도한 epochs 설정은 과적합(overfitting)을 유발할 수 있으므로 주의해야 합니다. 고객 문의 분류 작업의 경우, 8 epochs가 적절합니다. model : 튜닝에 사용할 모델을 선택합니다. 분류 작업에는 HCX-DASH-001 모델이 적합합니다. tasktype : 작업 유형을 지정합니다. 분류 작업이므로 classification을 선택합니다. 2. 학습생성API 코드 작성 후 실행하기 아래와 같이 python 코드를 작성했습니다. 여러분도 태스크 작업에 따라 적절하게 파라미터를 수정해 코드를 작성해보세요. 학습 생성 코드 보기 # -*- coding: utf-8 -*- import base64 import hashlib import hmac import requests import time class CreateTaskExecutor: def __init__(self, host, uri, method, iam_access_key, secret_key, request_id): self._host = host self._uri = uri self._method = method self._api_gw_time = str(int(time.time() * 1000)) self._iam_access_key = iam_access_key self._secret_key = secret_key self._request_id = request_id def _make_signature(self) : secret_key = bytes(self._secret_key, 'UTF-8') message = self._method + " " + self._uri + "\n" + self._api_gw_time + "\n" + self._iam_access_key message = bytes(message, 'UTF-8') signing_key = base64.b64encode(hmac.new(secret_key, message, digestmod=hashlib.sha256).digest()) return signing_key def _send_request(self, create_request): headers = { 'X-NCP-APIGW-TIMESTAMP': self._api_gw_time, 'X-NCP-IAM-ACCESS-KEY': self._iam_access_key, 'X-NCP-APIGW-SIGNATURE-V2': self._make_signature(), 'X-NCP-CLOVASTUDIO-REQUEST-ID': self._request_id } result = requests.post(self._host + self._uri, json=create_request, headers=headers).json() return result def execute(self, create_request): res = self._send_request(create_request) if 'status' in res and res['status']['code'] == '20000': return res['result'] else: return res if __name__ == '__main__': create_task_executor = CreateTaskExecutor( host='https://clovastudio.apigw.ntruss.com', uri='/tuning/v2/tasks', method='POST', iam_access_key='<your_iam_access_key>', secret_key='<your_secret_key>', request_id='<unique_request_id>' ) request_data = { 'name': 'example_tuning_task', 'model': 'HCX-003', 'taskType': 'CLASSIFICATION', 'trainEpochs': 8, 'learningRate': 1e-5, 'trainingDatasetBucket': 'bucket_name', 'trainingDatasetFilePath': 'path/to/dataset/file.json', 'trainingDatasetAccessKey': '<dataset_access_key>', 'trainingDatasetSecretKey': '<dataset_secret_key>' } response = create_task_executor.execute(request_data) print(request_data) print(response) 튜닝 API 사용은 학습 생성 가이드를 참고해보세요. 3. 튜닝 완료된 모델 확인하기 '클로바 스튜디오 > 내 작업 > 튜닝' 경로에서 아래 이미지와 같이 튜닝 모델의 작업 상태와 예상 소요 시간을 확인할 수 있습니다. '튜닝 완료된 작업 선택> 코드 보기' 경로에서 아래 이미지와 같이 튜닝한 모델의 task id를 확인하세요. Step 3. 결과 인퍼런스하기 1. 플레이그라운드에서 인퍼런스하기 ① 엔진을 튜닝한 모델로 선택 '플레이그라운드 > 불러오기'를 통해 튜닝한 모델을 선택합니다. ② 테스트셋 데이터 중 하나를 입력 테스트셋 데이터 중, 튜닝하기 전에 계속 오류를 냈던 예제 하나를 선택해서 인퍼런스 테스트를 해보았습니다. 'MYBOX'를 고객이 '네이버 드라이브'로 말하는 바람에, 분류에 어려움을 겪었던 케이스입니다. ③ 결과 확인 결과를 확인하니 분류 작업을 잘 수행했고, 결과물 형식도 원하는 형식으로 나오네요! User 네이버 드라이브에 사진을 모두 삭제하고 휴지통까지 삭제하였습니다다시 복구하고 싶은데 가능한가요?가능하다면 방법을 알고 싶습니다. Assistant MYBOX 데이터 관리 2. Chat Completion API로 테스트셋 인퍼런스하기 이제 테스트셋 100건에 대해 모두 인퍼런스해보겠습니다. ① 테스트셋 준비하기 튜닝이 잘 되었는지 확인하기 위해선 테스트할 데이터를 수집해야 합니다. 주의할 점은, 이 데이터가 튜닝할 때 사용한 데이터셋과 달라야 한다는 것입니다. 저희는 아래와 같이 평가 데이터 100개를 준비하여 평가를 진행했습니다. ② 인퍼런스 코드 실행하기 아래는 테스트셋 엑셀 파일로 대량의 테스트셋을 인퍼런스 돌리는 코드입니다. API key, 테스트셋 파일 경로, 완료 후 저장할 파일 경로, 튜닝한 모델의 task id를 변경해 코드를 실행해보세요. Chat Completion 인퍼런스 코드 보기 import requests import pandas as pd import time # API Key 정보 설정 CLOVA_API_KEY = '{CLOVA Studio API Key}' # CLOVA Studio API Key APIGW_API_KEY = '{API Gateway API Key}' # API Gateway API Key REQUEST_ID = '{Request ID}' # Unique request identifier MODEL_PARAMETERS = { "topP": 0.8, "topK": 0, "maxTokens": 256, "temperature": 0.5, "repeatPenalty": 5.0, "includeAiFilters": True, "stopBefore": [], } INPUT_FILENAME = "{테스트셋 파일 경로}" # .xlsx 파일 OUTPUT_FILENAME = "{인퍼런스 완료 후 저장할 파일 경로}" # .xlsx 파일 def send_requests(system: str, user: str) : url = "https://clovastudio.stream.ntruss.com/v2/tasks/{taskId}/chat-completions" # taskId는 사용자의 작업 ID로 대체 headers = { "X-NCP-CLOVASTUDIO-API-KEY": CLOVA_API_KEY, "X-NCP-APIGW-API-KEY": APIGW_API_KEY, "X-NCP-CLOVASTUDIO-REQUEST-ID": REQUEST_ID, "Content-Type": "application/json", "Accept": "application/json", } body = MODEL_PARAMETERS.copy() body["messages"] = [ {"role": "system", "content": system}, {"role": "user", "content": user}, ] response = requests.post(url=url, json=body, headers=headers) result = response.json() if "result" in result: print(result["result"]) return result["result"]["message"] else: print("Error:", result) return {"role": "error", "content": "Error in API response"} if __name__ == '__main__': # Input 데이터 읽기 df = pd.read_excel(INPUT_FILENAME) results_role = [] results_content = [] for i in df.itertuples(): # 시스템 메시지와 사용자 메시지를 입력으로 전달 result = send_requests(i.system, i.user) results_role.append(result['role']) results_content.append(result['content']) # 결과를 DataFrame에 추가 df.insert(2, "role", results_role, True) df.insert(3, "content", results_content, True) # 결과를 엑셀 파일로 저장 df.to_excel(OUTPUT_FILENAME, index=False) Step 4. 평가하기 1. 정량 평가 평가 데이터의 정답과 모델 답변을 비교하면서, 맞으면 match 열에 1, 틀리면 0으로 점수를 매겨서 정확도를 계산합니다. 평가셋 100의 정답과 각 모델 답변이 일치하는 지에 대한 점수를 매긴 후, HCX-DASH-001과 HCX-003모델 각각의 학습 전후를 비교해보겠습니다. 튜닝 후, HCX-DASH-001과 HCX-003 모델 모두 성능이 크게 향상되었습니다. HCX-DASH-001의 경우, 초기 성능은 73점이었지만, 파인튜닝 후 97점으로 향상되었습니다. 반면, HCX-003 모델은 초기 성능이 83점이었지만, 파인튜닝 후 95점으로 개선되었습니다. 초기에는 더 큰 모델인 HCX-003이 HCX-DASH-001보다 높은 성능을 보였지만, 양질의 데이터로 학습된 후에는 더 작은 모델인 HCX-DASH-001이 HCX-003과 비슷하거나 더 나은 성능을 보였습니다. 이는 효과적인 파인튜닝이 모델 크기의 차이를 상쇄하고, 때로는 더 작은 모델이 특정 작업에 더 잘 최적화될 수 있음을 보여줍니다. 충분한 데이터를 가지고 있고 과금에 대한 고민이 있으시다면 작은 모델인 HCX-DASH-001로도 충분한 성능 개선을 기대해볼 수 있다는 점을 참고해주세요. 2. 정성 평가 이전에 오답으로 나왔던 같은 고객 문의에 대해 어떻게 답변하는지 확인해보았습니다. 튜닝 전과 달리, 원하는 형식대로 '카테고리명'만 출력했고, 카테고리 분류도 잘한 것을 확인할 수 있었습니다. user 튜닝 전 Completion 튜닝 후 Completion 정답 이전에 계정 만들었다가 지우고 다시 네이버 밴드 새로 계정 만들려고 하는데, 계정 만들고 지웠다가 다시 가입이 안되나요? 정답은 '네이버밴드 오류해결'입니다. 네이버밴드 계정 네이버밴드 계정 튜닝은 AI 모델을 사용자와 과제에 맞게 최적화 시킬 수 있는 방법입니다. 이는 정체된 성능을 한 단계 끌어올리고, 원하는 형식으로 결과를 일관되게 출력하는 데 매우 효과적입니다. 앞서 설명드린 단계와 사례를 참고하여, 여러분의 프로젝트에서도 튜닝을 적극 활용해 보세요. 적절한 데이터셋 구성과 체계적인 프로세스를 통해 최고의 결과를 도출할 수 있을 것입니다. 더 나은 성능과 높은 정확도를 향한 튜닝 여정, 지금 바로 시작해 보세요! 🚀
  3. @eytg8e님, """당신은 ~~하는 ~입니다. 등의 내용""" 이 부분이 올바른 json 포맷이 아닙니다. json 의 문자열 내 double quote를 사용하고자 하는 경우 적절한 escape 처리를 해야 할 것 같습니다. 감사합니다.
  4. 들어가며 최근 Anthropic의 Claude와 OpenAI의 기능으로 공개된 Prompt Generator가 주목받고 있습니다. 이 기능들이 의미하는 바는 무엇일까요? 프롬프트 엔지니어링은 그동안 LLM을 효과적으로 활용하기 위한 핵심 기술로 인식되어 왔습니다. 이를 위해 다양한 노하우와 방법론이 공유되고, 그 중요성이 지속적으로 강조되어 왔습니다. 이러한 상황에서 Prompt Generator 기능이 본격적으로 등장했다는 것은, 프롬프트 엔지니어링 분야가 한 단계 더 발전할 수 있는 계기가 마련되었다는 것을 의미합니다. 수작업 프롬프트를 대체할 프레임워크 DSPy는 언어 모델 기반 애플리케이션 개발을 위한 파이썬 프레임워크로, 복잡한 프롬프트 엔지니어링 없이도 프로그래밍 중심의 접근 방식을 제공합니다. DSPy 논문1)과 DSPy 문서 에서 소개된 바와 같이, DSPy는 수행할 작업과 최적화할 지표를 정의하여 모델의 동작을 제어하고, 다양한 내장 모듈(CoT, self-reflection 등)을 통해 복잡한 파이프라인을 구성할 수 있습니다. 또한, 자동으로 프롬프트나 모델 가중치를 조정해 성능을 향상시킬 수 있습니다. DSPy는 수행할 작업과 최적화할 지표를 정의하여 모델의 동작을 손쉽게 제어할 수 있으며, CoT, self-reflection 등 다양한 내장 모듈을 통해 복잡한 파이프라인을 간편하게 구성할 수 있습니다. 또한, 자동으로 프롬프트나 모델 가중치를 조정하여 성능을 향상시키는 기능도 갖추고 있습니다. 1) Khattab, Omar, et al. 2023. "DSPY: Compiling Declarative Language Model Calls into Self-Improving Pipelines. Stanford University, UC Berkeley, and collaborators. https://arxiv.org/pdf/2310.03714 2) Stanford NLP. DSPY. GitHub repository, https://github.com/stanfordnlp/dspy DSPy의 구조적 접근 DSPy는 Signature를 활용하여 입출력 정의를 명확히 하고, 이를 Chain-of-Thought와 같은 모듈을 통해 실행합니다. Signature는 복잡한 모델 로직을 간단하게 정의할 수 있도록 도와주며, 작업의 명확성을 높이는 데 큰 역할을 합니다. Signature를 통해 각 단계의 역할을 명확히 구분하고, 이를 여러 모듈과 쉽게 연계할 수 있어 파이프라인 구축이 더욱 효율적입니다. 프로그래밍 중심의 접근 방식은 LLM 기반 애플리케이션의 구현 접근성을 높이고, 반복적인 프롬프트 작성의 부담을 줄여줍니다. 또한, LlamaIndex와 같은 프레임워크에서는 PromptTemplate 클래스를 사용해 구조화된 형식을 제공합니다. LlamaIndex 코드를 통해 일관되고 최적화된 출력을 보장하여 개발자들의 시간을 절약하고 오류 발생 가능성을 줄일 수 있습니다. PromptTemplate은 복잡한 입력 조건을 간단하게 정의하고, 다양한 시나리오에서 재사용 가능하도록 만들어 줍니다. 이를 통해 반복적인 프롬프트 작성의 부담이 줄어들고, LLM의 일관된 성능을 유지할 수 있습니다. ‘Automatic Prompt Optimization…’ 논문3)에서는 Gradient Descent와 Beam Search를 활용해 프롬프트 성능을 31%까지 향상시킨 사례를 소개합니다. 이처럼 자동화된 프롬프트 최적화는 수작업보다 더 좋은 결과를 가져올 수 있으며, 전반적으로 서비스 성능을 상향 평준화하는 데 기여할 수 있습니다. 3) Pryzant, Reid, Dan Iter, Jerry Li, Yin Tat Lee, Chenguang Zhu, and Michael Zeng. 2023. "Automatic Prompt Optimization with 'Gradient Descent' and Beam Search." October 19, Microsoft Azure AI. https://arxiv.org/pdf/2305.03495 Anthropic과 OpenAI가 Prompt Generator 기능을 제공하기 시작한 것은 LLM 활용 서비스 전체의 성능을 높여준다는 점에서 큰 의미가 있습니다. 앞으로 이러한 기능들이 DSPy와 결합된다면 더욱 강력한 애플리케이션 개발 프레임워크로 발전할 가능성이 높습니다. 결합을 통해 개발자들은 복잡한 LLM 작업을 간단한 코드 조각으로 표현할 수 있을 뿐만 아니라, 그 과정에서 모델의 성능도 자동으로 최적화할 수 있게 됩니다. 이는 LLM 기술의 보편화와 더불어 그 활용성을 극대화하는 방향으로 나아가는 것이며, 점차 많은 기업과 개발자들이 이러한 흐름을 따르게 될 것이죠. 프롬프트 엔지니어링 작업 시간 단축 프롬프트의 성능 최적화 전반적인 애플리케이션 성능의 향상 하이퍼클로바X로 실험하기 현재 클로바 스튜디오에서는 Prompt Generator 기능을 제공하고 있지 않지만, 이와 유사한 방식으로 자체 실험을 진행해 보았습니다. 사용자 요청과 구조화된 출력 데이터를 약 100개 제작하여 실험해 본 결과, HyperCLOVA X 모델로도 충분히 구조화된 응답을 생성할 수 있다는 가능성을 확인했습니다. 실험 과정에서는 수행할 작업을 명확히 설명하는 '지시문', 단계별 작업을 설명하는 '단계', 원하는 출력의 형식과 스타일을 명시한 '출력 형식', 그리고 참고할 만한 '예시'를 활용했습니다. 예를 들어, 고객 리뷰에 대한 답변을 자동으로 생성하는 경우, 단순한 요청 대신 구체적인 지시문과 예시를 통해 구조화된 출력을 얻어냈습니다. 지시문 : 수행할 작업을 명확히 설명 단계 : 단계별 작업을 설명 출력 형식 : 원하는 출력의 형식과 스타일을 명시 예시 : 참고할 만한 예시 고객 리뷰에 답변을 추가하는 서비스를 구현한다고 가정했을 때, "고객 리뷰에 답변을 추가합니다. 예제의 어투를 참고합니다. 이모티콘을 포함하세요."와 같은 두서없는 요청을 구조화하여, 구체적인 지시문과 예제를 포함한 형식으로 출력할 수 있습니다. 마치며 이와 같은 접근 방식은 LLM을 보다 효율적으로 활용할 수 있는 가능성을 열어주며, 향후 클로바 스튜디오의 에이전트 구현에도 적용할 수 있을 것입니다. 클로바 스튜디오는 앞으로도 에이전트 구현을 위한 다양한 기능을 제공할 예정이니 많은 관심과 기대 부탁드립니다.
  5. 본 가이드는 클로바 스튜디오(CLOVA Studio)와 랭체인(Langchain)을 이용해 RAG(Retrieval Augmented Generation; 검색 증강 생성) 시스템을 만드는 방법을 설명합니다. NCP 가이드: https://guide.ncloud-docs.com/docs/clovastudio-dev-langchain LangChain 공식 문서: https://python.langchain.com/docs/integrations/providers/naver/ RAG는 AI가 주어진 정보를 바탕으로 더 정확하고 관련성 높은 답변을 생성하도록 돕는 기술입니다. 이 예제에서 우리는 HTML 형식의 데이터(클로바 스튜디오 가이드)를 기반으로 질문에 대한 답변을 출력하는 RAG 시스템을 랭체인을 활용해 구현해보겠습니다. 구현하고자 하는 RAG 시스템의 구조도는 아래와 같습니다. 클로바 스튜디오의 HyperCLOVA X 모델 (챗 모드), 임베딩 API, 문단 나누기 API를 사용하며, 랭체인을 통해 아래와 같은 단계로 진행해 간단한 RAG 시스템을 구현해보겠습니다. 버전 정보 아래 예제 코드는 Python 3.12.4에서 실행 확인하였으며, 최소 Python3.9를 필요로 합니다. 아래 파일을 참고해 필요한 모듈을 모두 설치해줍니다. requirements.txt 1. 사전 준비 1) Langchain 패키지 설치 langchain-community >= 0.3.4부터 langchain_community 안에 ChatClovaX, ClovaXEmbeddings가 포함되었습니다. 랭체인은 아래 명령문을 통해 설치할 수 있습니다. pip install langchain pip install langchain-community~=0.3.4 2) 참조할 문서 (HTML 데이터) 준비 본 예제에서는 네이버클라우드플랫폼(NCP)의 클로바 스튜디오 사용 가이드중 CLOVA Studio 개념, CLOVA Studio 시나리오 페이지를 HTML 데이터로 활용합니다. 참고할 웹사이트의 링크를 아래와 같이 txt 파일로 준비했으며, 해당 내용은 필요한 데이터에 맞게 수정되어도 무방합니다. (주의) txt파일을 작성할 때는 한 줄에 하나의 URL을 작성해야 합니다. 즉, 각 URL은 줄바꿈으로 구분되어야 합니다. https://guide.ncloud-docs.com/docs/clovastudio-info https://guide.ncloud-docs.com/docs/clovastudio-procedure 3) 코드에 공통적으로 들어가는 필요 모듈 import import json import getpass import os from tqdm import tqdm import requests import time import uuid from uuid import uuid4 from pathlib import Path 4) 테스트앱 및 API키 미리 발급 받기 각 모듈 호출시 필요한 테스트앱 및 API키를 미리 발급받고 환경 변수로 저장해두세요. # API 키와 Gateway API 키를 넣습니다. os.environ["NCP_CLOVASTUDIO_API_KEY"] = getpass.getpass("NCP CLOVA Studio API Key: ") os.environ["NCP_APIGW_API_KEY"] = getpass.getpass("NCP API Gateway API Key: ") NCP CLOVA Studio API Key: ········ NCP API Gateway API Key: ········ # 문단나누기 테스트앱의 앱 ID를 넣습니다. os.environ["NCP_CLOVASTUDIO_APP_ID_SEGMENTATION"] = input("NCP CLOVA Studio Segmentation App ID: ") NCP CLOVA Studio Segmentation App ID: <your Segmentation App ID> # 임베딩 테스트앱의 앱 ID를 넣습니다. os.environ["NCP_CLOVASTUDIO_APP_ID"] = input("NCP CLOVA Studio App ID: ") NCP CLOVA Studio App ID: <your App ID> 2. Load RAG의 첫번째 작업은 추후 LLM이 응답의 문맥으로 사용할 때 RAG 시스템이 참고할 HTML 데이터를 준비하는 단계입니다. txt 파일(clovastudiourl.txt)에 사용하고 싶은 사이트 URL을 작성한 후, wget 라이브러리와 LangChain의 Document Loader 컴포넌트를 활용하여 HTML 데이터를 작업에 편한 형태로 변환했습니다. 또한, 로딩한 데이터 안에 사이트 URL을 넣어 추후 답변과 함께 사이트 주소가 출력되게 하여 질문한 사용자가 답변의 내용이 있는 실제 URL을 함께 참조할 수 있게 데이터를 수정했습니다. 1) txt → html 변환 및 원본 사이트 주소 mapping import subprocess url_to_filename_map = {} with open("clovastudiourl.txt", "r") as file: urls = [url.strip() for url in file.readlines()] folder_path = "clovastudioguide" if not os.path.exists(folder_path): os.makedirs(folder_path) for url in urls: filename = url.split("/")[-1] + ".html" file_path = os.path.join(folder_path, filename) subprocess.run(["wget", "--user-agent=RAGCookbook-Crawler/1.0", "-O", file_path, url], check=True) url_to_filename_map[url] = filename with open("url_to_filename_map.json", "w") as map_file: json.dump(url_to_filename_map, map_file) #Output --2024-08-26 13:37:02-- https://guide.ncloud-docs.com/docs/clovastudio-info Resolving guide.ncloud-docs.com (guide.ncloud-docs.com)... 104.18.6.159, 104.18.7.159 Connecting to guide.ncloud-docs.com (guide.ncloud-docs.com)|104.18.6.159|:443... connected. HTTP request sent, awaiting response... 200 OK Length: unspecified [text/html] Saving to: ‘clovastudioguide_0822/clovastudio-info.html’ 0K .......... .......... .......... .......... .......... 21.4M 50K .......... .......... .......... .......... 83.3M=0.003s 2024-08-26 13:37:02 (32.1 MB/s) - ‘clovastudioguide_0822/clovastudio-info.html’ saved [92957] --2024-08-26 13:37:02-- https://guide.ncloud-docs.com/docs/clovastudio-procedure Resolving guide.ncloud-docs.com (guide.ncloud-docs.com)... 104.18.6.159, 104.18.7.159 Connecting to guide.ncloud-docs.com (guide.ncloud-docs.com)|104.18.6.159|:443... connected. HTTP request sent, awaiting response... 200 OK Length: unspecified [text/html] Saving to: ‘clovastudioguide_0822/clovastudio-procedure.html’ 0K .......... .......... .......... .......... .......... 18.0M 50K .......... .......... .......... 21.6M=0.004s 2024-08-26 13:37:03 (19.2 MB/s) - ‘clovastudioguide_0822/clovastudio-procedure.html’ saved [82265] LangChain을 활용해 로딩한 html은, 파일을 저장한 디렉토리의 주소를 metadata의 'source'로 가져오게 됩니다. 추후 답변 제공시, 디렉토리 주소가 아닌 실제 URL을 제공하기 위해, LangChain이 로딩한 데이터를 수정해야합니다. html을 로딩할 때 파일명을 URL로 할 경우, /와 : 기호로 인해 파일명이 깨지게 됩니다. 따라서 원본 URL과 로딩한 HTML 파일을 쌍으로 mapping하고, 이 정보를 json 형식으로 "url_to_filename_map"에 저장합니다. 이후, html 파일의 저장 경로가 담긴 'source'를, 이 json 파일을 참고해 실제 URL로 변환해줍니다. 위 코드는, json 파일을 저장하는 단계까지입니다. 외부 사이트 접근 허용 코드 wget 명령어를 통해 HTML 데이터를 로딩하기 전, 해당 웹사이트의 robots.txt를 확인하여 데이터로 활용할 웹페이지의 크롤러 접근 허용 여부를 확인해야합니다. NCP의 클로바스튜디오 사용 가이드의 경우 User-agent: * Allow:/ 로 접근이 가능합니다. 일부 사이트는 User-agent를 정의하는 부분을 추가해서 사용해야 웹 서버의 요청 차단이나 에러를 방지할 수 있습니다. 대부분 서버의 --user-agent는 Mozilla/5.0를 따르고 있습니다. 하지만 경우에 따라 불러오고자 하는 사이트의 --user-agent 정보를 직접 찾아서 적어야합니다. 2) LangChain 활용 HTML 로딩 2-1) BSHTMLLoader 사용 #!pip install beautifulsoup4 lxml #필요시 beautifulsoup4 설치 from langchain_community.document_loaders import BSHTMLLoader # 폴더 이름에 맞게 수정 html_files_dir = Path('clovastudioguide') html_files = list(html_files_dir.glob("*.html")) # 모든 문서 데이터를 저장할 리스트 초기화 clovastudiodatas = [] # 찾은 HTML 파일들에 대해 처리 for html_file in html_files: # 각 파일에 대해 BSHTMLLoader 인스턴스 생성 loader = BSHTMLLoader(str(html_file)) document_data = loader.load() # 로드된 문서 데이터를 리스트에 추가 clovastudiodatas.append(document_data) print(f"Processed {html_file}") #Output Processed /Users/user/langchain/libs/community/sample/clovastudioguide/clovastudio-info.html Processed /Users/user/langchain/libs/community/sample/clovastudioguide/clovastudio-procedure.html 2-2) UnstructuredHTMLLoader 사용 이전 단계에서 txt 파일을 HTML로 변환한 후, LangChain의 UnstructuredHTMLLoader를 사용하여 이후 문단 나누기와 임베딩을 위해 machine readable한 형태로 변환해 줍니다. #!pip install unstructured #필요시 unstructured 설치 from langchain_community.document_loaders import UnstructuredHTMLLoader # 폴더 이름에 맞게 수정 html_files_dir = Path('./clovastudioguide') html_files = list(html_files_dir.glob("*.html")) clovastudiodatas = [] for html_file in html_files: loader = UnstructuredHTMLLoader(str(html_file)) document_data = loader.load() clovastudiodatas.append(document_data) print(f"Processed {html_file}") #Output Processed /Users/user/langchain/libs/community/sample/clovastudioguide/clovastudio-info.html Processed /Users/user/langchain/libs/community/sample/clovastudioguide/clovastudio-procedure.html 각 HTML에는 page_content에 모든 텍스트가, metadata의 'source'에는 저장 경로가 기록되어 있습니다. 위는, 로딩된 데이터 중 하나인 clovastudio-info.html의 형태입니다. metadata의 'source'에 실제 URL이 아닌 html 파일이 저장된 디렉토리 경로가 담긴 것을 확인할 수 있습니다. Mapping을 통해 이 'source'를 실제 URL로 바꾸는 작업을 다음 단계에 진행하게 됩니다. 3) Mapping 정보를 활용해 'source'를 실제 URL로 대체 아래의 output은 앞선 예시인 clovastudio-info.html의 형태입니다. Mapping 정보를 활용해 'source'를 실제 url로 바꾼 것을 확인할 수 있습니다. with open("url_to_filename_map.json", "r") as map_file: url_to_filename_map = json.load(map_file) filename_to_url_map = {v: k for k, v in url_to_filename_map.items()} # clovastudiodatas 리스트의 각 Document 객체의 'source' 수정 for doc_list in clovastudiodatas: for doc in doc_list: extracted_filename = doc.metadata["source"].split("/")[-1] if extracted_filename in filename_to_url_map: doc.metadata["source"] = filename_to_url_map[extracted_filename] else: print(f"Warning: {extracted_filename}에 해당하는 URL을 찾을 수 없습니다.") print(doc.metadata["source"]) #Output https://guide.ncloud-docs.com/docs/clovastudio-procedure # 이중 리스트를 풀어서 하나의 리스트로 만드는 작업 clovastudiodatas_flattened = [item for sublist in clovastudiodatas for item in sublist] HTML데이터가 잘 로딩되었는지 결과물을 출력해 확인해봅니다. # 처음 3개 문서의 URL과 내용 일부 출력 for doc in clovastudiodatas_flattened[:2]: print(f"URL: {doc.metadata['source']}") print(f"{doc.page_content[:100]}...") print("-" * 50) #Output URL: https://guide.ncloud-docs.com/docs/clovastudio-info Login Korean English Ja - 日本語 Korean API 가이드 (...이하 중략) 3. Chunking 임베딩 모델이 처리할 수 있는 적당한 크기로 raw data를 나누는 것은 매우 중요합니다. 이는 임베딩 모델마다 한 번에 처리할 수 있는 토큰 수의 한계가 있기 때문입니다. CLOVA Studio의 문단 나누기 API를 활용해 앞서 만든 raw data를 나눠보겠습니다. 모델이 직접 문장들간의 의미 유사도를 찾아 최적의 청크(chunk) 개수와 사용자가 원하는 1개 청크 크기(글자 수)를 직접 설정하여 문단을 나눌 수 있습니다. 추가로, 후처리(postProcess = True)를 통해 chunk당 글자 수의 상한선과 하한선을 postProcessMaxSize와 postProcessMinSize로 조절할 수도 있습니다. import http.client from http import HTTPStatus class CLOVAStudioExecutor: def __init__(self, host, request_id=None): self._host = host self._api_key = os.environ["NCP_CLOVASTUDIO_API_KEY"] self._api_key_primary_val = os.environ["NCP_APIGW_API_KEY"] self._request_id = request_id or str(uuid.uuid4()) def _send_request(self, completion_request, endpoint): headers = { 'Content-Type': 'application/json; charset=utf-8', 'X-NCP-CLOVASTUDIO-API-KEY': self._api_key, 'X-NCP-APIGW-API-KEY': self._api_key_primary_val, 'X-NCP-CLOVASTUDIO-REQUEST-ID': self._request_id } conn = http.client.HTTPSConnection(self._host) conn.request('POST', endpoint, json.dumps(completion_request), headers) response = conn.getresponse() status = response.status result = json.loads(response.read().decode(encoding='utf-8')) conn.close() return result, status def execute(self, completion_request, endpoint): res, status = self._send_request(completion_request, endpoint) if status == HTTPStatus.OK: return res, status else: error_message = res.get("status", {}).get("message", "Unknown error") if isinstance(res, dict) else "Unknown error" raise ValueError(f"오류 발생: HTTP {status}, 메시지: {error_message}") class SegmentationExecutor(CLOVAStudioExecutor): def execute(self, completion_request): app_id = os.environ["NCP_CLOVASTUDIO_APP_ID_SEGMENTATION"] endpoint = f'/testapp/v1/api-tools/segmentation/{app_id}' res, status = super().execute(completion_request, endpoint) if status == HTTPStatus.OK and "result" in res: return res["result"]["topicSeg"] else: error_message = res.get("status", {}).get("message", "Unknown error") if isinstance(res, dict) else "Unknown error" raise ValueError(f"오류 발생: HTTP {status}, 메시지: {error_message}") if __name__ == "__main__": # 환경 변수가 설정되어 있는지 확인 required_env_vars = [ "NCP_CLOVASTUDIO_API_KEY", "NCP_APIGW_API_KEY", "NCP_CLOVASTUDIO_APP_ID_SEGMENTATION" ] missing_vars = [var for var in required_env_vars if not os.environ.get(var)] if missing_vars: raise ValueError(f"Missing required environment variables: {', '.join(missing_vars)}") segmentation_executor = SegmentationExecutor( host="clovastudio.apigw.ntruss.com" ) chunked_html = [] for htmldata in tqdm(clovastudiodatas_flattened): try: request_data = { "postProcessMaxSize": 100, "alpha": -100, "segCnt": -1, "postProcessMinSize": -1, "text": htmldata.page_content, "postProcess": True } response_data = segmentation_executor.execute(request_data) result_data = [' '.join(segment) for segment in response_data] for paragraph in result_data: chunked_document = { "metadata": htmldata.metadata["source"], "page_content": paragraph } chunked_html.append(chunked_document) except Exception as e: print(f"Error processing data from {htmldata.metadata['source']}: {e}") # 오류 발생 시 현재 반복을 건너뛰고 다음으로 진행 continue print(len(chunked_html)) #Output 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 2/2 [00:04<00:00, 2.06s/it] 108 위 코드를 실행해 108개의 chunk가 생겼습니다. 샘플 청크를 3개 출력해 결과를 확인해봅시다. # 청크 분석 및 출력 print(f"\n총 청크 수: {len(chunked_html)}") # 샘플 청크 출력 print("\n샘플 청크 (처음 3개):") for i, chunk in enumerate(chunked_html[:3], 1): print(f"\n청크 {i}:") print(f"메타데이터: {chunk['metadata']}") print(f"내용: {chunk['page_content']}") print(f"길이: {len(chunk['page_content'])} 문자") #Output 총 청크 수: 108 샘플 청크 (처음 3개): 청크 1: 메타데이터: https://guide.ncloud-docs.com/docs/clovastudio-info 내용: Login Korean English Ja - 日本語 Korean API 가이드 CLI 가이드 내용 x HOME 포털 및 콘솔 네이버 클라우드 플랫폼 사용 환경 Compute Containers Storage Networking Database Security 길이: 145 문자 청크 2: 메타데이터: https://guide.ncloud-docs.com/docs/clovastudio-info 내용: AI Services AI·NAVER API Application Services Big Data & Analytics 길이: 66 문자 청크 3: 메타데이터: https://guide.ncloud-docs.com/docs/clovastudio-info 내용: Blockchain Business Applications Content Delivery Developer Tools Digital Twin Gaming 길이: 85 문자 4. Embedding 임베딩 단계는 앞서 Chunking 단계에서 생성된 chunk들을 기계가 이해할 수 있는 수치적 형태인 벡터 데이터로 변환하는 과정입니다. 문서의 의미를 벡터(숫자의 배열) 형태로 표현함으로써, 유저의 질문에 대해 관련있는 chunk를 검색하여 가져오는 유사도 계산시 활용됩니다. 1) LangChain을 통해 CLOVA Studio Embedding 활용하기 1-1) 임베딩 모델 설정하기 문단 나누기 API를 통해 분할된 청크(chunked_html)를 CLOVA Studio의 임베딩 API를 사용해 1024차원 벡터로 변환하는 과정입니다. <1. 사전 준비> 단계에서 발급한 임베딩 V2 테스트앱을 사용했으며, 앞서 저장해둔 API키 및 app id 를 활용합니다. 임베딩 V2는 bge-m3 모델을 사용하며, 이 모델은 임베딩 과정에서 유사도 판단을 위해 코사인 거리(Cosine)를 거리 단위로 사용합니다. 모델을 설정하지 않으면 clir-emb-dolphin 모델이 기본 설정되므로, ClovaXEmbeddings의 모델을 bge-m3로 설정해주어야 합니다. from langchain_community.embeddings import ClovaXEmbeddings clovax_embeddings = ClovaXEmbeddings(model='bge-m3') # 임베딩 모델을 설정해주세요 임베딩 모델은 니즈에 따라 세가지 모델 중, 선택하여 작업을 수행할 수 있습니다. 거리 단위를 임베딩부터 인덱싱, 검색까지 일치시켜야 데이터의 품질이 향상되므로, 임베딩 과정에서 사용한 모델(emb / sts / bge-m3)이 무엇인지 기억하는 것이 중요합니다. (자세한 내용은 클로바 스튜디오 가이드 참고) 도구명 모델명 최대 토큰 수 벡터 차원 권장 거리 지표 임베딩 clir-emb-dolphin (랭체인 기본설정) 500 토큰 1024 IP (Inner/Dot/Scalar Product; 내적) 임베딩 clir-sts-dolphin 500 토큰 1024 Cosine Similarity (코사인 유사도) 임베딩 v2 bge-m3 8192 토큰 1024 Cosine Similarity (코사인 유사도) 1-2) 임베딩 결과 확인하기 다음 예제 코드는 "클로바 스튜디오"라는 글자에 대해 CLOVA Studio의 임베딩 API를 사용해 임베딩을 생성하는 과정입니다. 임베딩은 텍스트 데이터의 의미를 벡터 공간에서 표현하기 위해 사용됩니다. text = "클로바 스튜디오" clovax_embeddings.embed_query(text) 결과를 확인해보면 "클로바 스튜디오"라는 텍스트에 대한 임베딩 벡터가 생성됩니다. 이 벡터의 크기는 1024로, 실수 배열의 형태로 표현됩니다. #Output [-0.13467026, -0.54031295, -0.5842034, 1.6036854, -1.2513447, -0.8417246, -0.51857895, ...생략] 2) 리스트 형식의 documents로 만들기 임베딩 처리를 위해 앞서 만든 청크를 Document 객체들의 리스트 형식의 documents로 변환합니다. from langchain_core.documents import Document documents = [] for index, item in enumerate(chunked_html): doc = Document( page_content=str(item['page_content']), metadata={"source": item['metadata']}, id=str(uuid4()) ) documents.append(doc) 아래 코드를 통해 변환된 documents의 구조를 확인할 수 있습니다. # documents 구조 체크 for i, doc in enumerate(documents): print(f"Document {i}:") print(f" Page Content: {doc.page_content[:100]}...") # 첫 100자만 출력 print(f" Metadata: {doc.metadata}") print(f" Page Content Type: {type(doc.page_content)}") print(f" Metadata Type: {type(doc.metadata)}") print("-" * 40) #Output Document 0: Page Content: CLOVA Studio 개념 Login Korean English Ja - 日本語 Korean API 가이드 CLI 가이드내용 x HOME 포털 및 콘솔 네이버 클라우드 플랫폼 사... Metadata: {'source': 'https://guide.ncloud-docs.com/docs/clovastudio-info', 'title': 'CLOVA Studio 개념'} Page Content Type: <class 'str'> Metadata Type: <class 'dict'> ---------------------------------------- Document 1: Page Content: 의견을 보내 주셔서 감사합니다.Classic/VPC 환경에서 이용 가능합니다.CLOVA Studio를 이용하는 전체 시나리오를 학습하기에 앞서 CLOVA Studio에 대한 몇 가... Metadata: {'source': 'https://guide.ncloud-docs.com/docs/clovastudio-info', 'title': 'CLOVA Studio 개념'} Page Content Type: <class 'str'> Metadata Type: <class 'dict'> ---------------------------------------- ...(이하 중략) 5. Vector store 이 단계는 앞서 임베딩한 결과를 Vector Store에 효율적으로 저장하고 관리하는 단계입니다. 벡터 DB는 벡터 데이터를 저장하고 검색하기 위한 데이터베이스입니다. 이 예제에서는 로컬 환경에서 보다 쉽게 활용할 수 있는 Chroma와 FAISS를 사용했습니다. Langchain의 Vector DB 비교 문서를 보고, 자신의 개발 환경에 맞는 제품을 활용하세요. 1) Vector DB에 저장하기 1-1) Chroma 활용 Chroma는 오픈소스 Vector DB이며, 클라우드를 사용하지않고 클라이언트용으로 사용하면 Apache 2.0 라이센스에 무료로 이용할 수 있습니다. 본 예제에서는 chroma를 활용해 컬렉션을 생성하고, 컬렉션에 앞서 만든 문서를 추가해보겠습니다. hnsw:space의 값을 설정함으로써 임베딩 공간의 거리 방법을 사용자 정의할 수 있습니다. 임베딩에 사용할 모델은 유사도 판단을 위해 벡터의 cosine 거리 단위로 사용하므로, 컬렉션 또한 매개변수를 "cosine"으로 설정해주었습니다. *매개변수는 ip, cosine, l2(기본값) 중에 선택할 수 있습니다. 또한, 임베딩 클래스에 오류가 발생시 멈추게끔 하는 로직을 일부 추가해, 다량의 데이터를 처리할 때 오류 발생시 재실행의 부담을 줄였습니다. LangChain 공식 문서에서 Chroma를 활용하는 자세한 내용을 확인할 수 있습니다. #chroma 다운받기 %pip install -qU "langchain-chroma>=0.1.2" import chromadb from langchain_chroma import Chroma # 임베딩 모델 정의 clovax_embeddings = ClovaXEmbeddings(model='bge-m3') # 로컬 클라이언트 경로 지정 client = chromadb.PersistentClient(path="./chroma_langchain_db") #저장할 로컬 경로 # Chroma 컬렉션 생성 chroma_collection = client.get_or_create_collection( name="clovastudiodatas_docs", #collection이 바뀔때마다 이름도 꼭 변경해줘야 합니다. metadata={"hnsw:space": "cosine"} #사용하는 임베딩 모델에 따라 ‘l2’, 'ip', ‘cosine’ 중에 사용 ) # Chroma 벡터 저장소 생성 vectorstore = Chroma( client=client, collection_name="clovastudiodatas_docs", embedding_function=clovax_embeddings ) # tqdm으로 for 루프 감싸기 for doc in tqdm(documents, desc="Adding documents", total=len(documents)): embeddings = clovax_embeddings.embed_documents([doc.page_content])[0] # 문서 추가 chroma_collection.add( ids=[str(uuid.uuid4())], # 고유한 ID 생성 documents=[doc.page_content], embeddings=[embeddings], metadatas=[doc.metadata] ) time.sleep(1.1) # 이용량 제어를 고려한 1초 이상의 딜레이, 필요에 따라 조정 가능 print("All documents have been added to the vectorstore.") #Output Adding documents: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 108/108 [03:17<00:00, 2.46s/it] All documents have been added to the vectorstore. 1-2) FAISS 활용 Facebook AI Similarity Search (Faiss)는 밀집 벡터의 효율적인 유사도 검색과 클러스터링을 위한 라이브러리입니다. 본 예제에서는 FAISS를 활용해 vectorstore를 생성하고, 문서를 추가해보겠습니다. vectorstrore를 생성할 때, 임베딩 차원 크기를 1024차원으로 설정했습니다. 또한, 임베딩 클래스에 오류가 발생시 멈추게끔 하는 로직을 일부 추가해, 다량의 데이터를 처리할 때 오류 발생시 재실행의 부담을 줄였습니다. Langchain 공식 문서에서 FAISS 활용에 대한 더 자세한 내용을 확인 할 수 있습니다. #FAISS 다운로드 %pip install -qU langchain-community faiss-cpu import faiss from langchain_community.vectorstores import FAISS from langchain_community.docstore.in_memory import InMemoryDocstore # 임베딩 모델 정의 clovax_embeddings = ClovaXEmbeddings(model='bge-m3') # FAISS 벡터 저장소 생성 vectorstore_FAISS = FAISS( embedding_function=clovax_embeddings, index=faiss.IndexFlatIP(1024), # 임베딩 차원 크기 docstore=InMemoryDocstore(), index_to_docstore_id={}, ) # tqdm으로 for 루프 감싸기 for doc in tqdm(documents, desc="Adding documents", total=len(documents)): embeddings = clovax_embeddings.embed_documents([doc.page_content])[0] # 문서 추가 vectorstore_FAISS.add_documents( ids=[str(uuid.uuid4())], # 고유한 ID 생성 documents=[doc], # 문자열만 전달 embeddings=[embeddings] ) time.sleep(1.4) # 이용량 제어를 고려한 1초 이상의 딜레이, 필요에 따라 조정 가능 print("All documents have been added to the vectorstore.") #Output Adding documents: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 108/108 [03:17<00:00, 2.46s/it] All documents have been added to the vectorstore. 2) 다양한 파라미터를 활용해 Retriever 생성하기 검색기(Retriever) 단계는 저장된 벡터 DB에서 사용자의 질문과 관련된 문서를 검색하는 과정입니다. vectorstore.as_retriever()를 통해 검색기를 생성할 수 있습니다. 이 예제에서는 retriever 파라미터 설정을 통해 3개의 문서를 참고해 답변을 하도록 해보겠습니다. retriever = vectorstore.as_retriever(kwargs={"k": 3}) 만들고자 하는 RAG 시스템의 목적 및 활용에 맞게 아래의 1~4번의 항목의 다양한 파라미터를 사용해 커스텀해보세요. 파라미터를 다양하게 조절해가며 제품의 목적에 가장 적합한 답변을 하는 방식을 찾아나갈 수 있습니다. 2-1) 유사도 점수 임계값 설정 이 설정은 유사도 점수가 0.5 이상인 문서만 반환합니다. 이를 통해 질문과 관련성이 높은 문서만을 선택적으로 가져올 수 있습니다. 임계값을 높게 설정할수록 더 관련성 높은 문서만 반환되지만, 관련 정보를 놓칠 가능성도 있습니다. 반대로 임계값을 낮추면 더 많은 문서가 반환되지만, 관련성이 떨어지는 문서도 포함될 수 있습니다. Langchain 공식 문서에서 더 자세한 내용을 확인할 수 있습니다. retriever = db.as_retriever( # 검색 유형을 "유사도 점수 임계값"으로 설정합니다. search_type="similarity_score_threshold", # 검색 인자로 점수 임계값을 0.5로 지정합니다. search_kwargs={"score_threshold": 0.5}, ) 유사도 임계값은 0에서 1 사이의 값이어야 합니다. 이는 코사인 거리를 사용하는 `clir-sts-dolphin` 모델과 `bge-m3` 모델을 사용할 때 적합합니다. IP(Inner Product) 거리를 사용하는 `clir-emb-dolphin` 임베딩 모델의 경우 유사도 점수 범위가 달라 앞서 작성된 코드를 사용해 정규화가 필요합니다. 2-2) 반환 문서 수 제한 이 설정은 상위 3개의 가장 관련성 높은 문서만 반환합니다. k 값을 낮게 설정하면 처리 속도가 빨라지고 가장 관련성 높은 문서에 집중할 수 있지만, 중요한 정보를 놓칠 수 있습니다. k 값을 높게 설정하면 더 많은 맥락을 제공할 수 있지만, 처리 시간이 길어지고 관련성이 낮은 정보가 포함될 수 있습니다. search_kwargs의 k와 kwargs의 k의 차이 kwargs의 k 값의 의미는 검색기(retriever)가 답변 생성 최종적으로 참조하는 상위 문서의 개수를 의미합니다. 즉, 아래의 예제와 같이 '3'으로 설정한다면 검색하는 문서 중에서 유사도가 높은 상위 3개의 문서를 참조하여 답변을 생성합니다. search_kwargs의 k 값의 의미는 검색하는 문서의 개수를 의미합니다. 이전의 kwargs의 k 값은 검색한 문서에서 답변을 할때 참조하는 상위 문서의 수 였다면 search_kwargs의 k는 검색하려는 문서의 개수를 의미합니다. 따라서 search_kwargs의 k값이 kwargs의 k 값보다 적은 수로 설정된다면, 검색하는 문서의 수가 답변 생성 시에 참조하는 문서의 개수보다 적은 개수로 설정된 것으로, search_kwargs의 k값의 수로 문서의 개수가 제한되어 답변됩니다. 예를 들어, search_kwargs의 k 값은 도서관에서 정보를 얻으려고 빌린 전체 책의 권 수가 되고, kwargs의 k 값은 빌린 책 전체에서 실제 정보를 얻은 책의 권수라고 이해할 수 있습니다. 2-3) MMR (Maximum Marginal Relevance) 검색 대부분의 검색 알고리즘은 문서와 쿼리 간의 유사도를 계산하여 가장 높은 점수를 가진 문서들을 순서대로 반환합니다. 이 경우, 내용이 매우 유사하거나 심지어 동일한 문서들이 연속해서 검색 결과에 나타날 수 있습니다. 이 문제점을 MMR(Maximal Marginal Relevance) 방식으로 해결할 수 있습니다. 쿼리에 대한 관련 항목을 검색할 때 검색된 문서의 중복을 피하기 위해 단순히 가장 관련성 높은 항목들만을 검색하는 대신, 이미 선택된 문서들과의 차별성도 고려해 문서를 반환합니다. 따라서, 정보 검색이나 추천 시스템처럼 검색 결과의 다양성이 중요하거나 중복된 정보를 줄이고 싶을 때 이 방식을 사용하면 좋습니다. 매개변수(parameters) k: 이 매개변수는 최종적으로 반환할 문서의 총 개수입니다. (기본값: 4) fetch_k: MMR 알고리즘을 수행할 때 고려할 초기 후보 문서 집합의 크기를 의미합니다. 광범위한 후보군에서 문서를 선택하면 좋을 때 이 값을 높입니다. 반대로, 빠른 응답 시간이 우선적이라면 이 값을 낮출 수 있습니다.(기본값: 20) lambda_mult: 쿼리와의 유사성과 선택된 문서 간의 다양성 사이의 균형을 조절하는 변수입니다. 1에 가까울수록 쿼리와의 유사도 점수만 고려하고, 0에 가까워질수록 문서간의 다양성만 고려합니다. (0~1, 기본값: 0.5) 2-4) 메타 데이터 필터링 메타 데이터 필터를 이용하여 특정 키워드가 포함된 메타 데이터만 필터링할 수 있습니다. 앞서 document에 저장할 때, metadata의 title을 함께 저장했습니다. 이를 활용해 문서를 필터링해 가져올 수 있습니다. # 메타데이터 필터링 filtered_retriever = db.as_retriever( search_kwargs={"filter": {'title': 'CLOVA Studio 시나리오'}} ) 6. RAG 시스템 완성 1) LCEL을 사용해 Chain 구현하기 LCEL(LangChain Expression Language)은 랭체인에서 코드를 작성하는 새로운 방법입니다. 프롬프트와 LLM 모델을 정의해두고, | 기호로 각 요소를 연결해 한 구성 요소의 출력을 다음 구성 요소의 입력으로 전달할 수 있습니다. 이렇게 Chain을 생성해 복잡한 AI 시스템의 워크플로우를 보다 쉽고 직관적으로 구성할 수 있습니다. 1-1) 사용할 LLM 모델 정의 먼저, HyperCLOVA X 모델 (HCX-003) 을 랭체인 Chat model 컴포넌트의 ChatClovaX를 통해 불러와 정의합니다. from langchain_community.chat_models import ChatClovaX llm = ChatClovaX( model="HCX-003", ) 1-2) Prompt 정의 PromptTemplate 사용하기 쿼리에 사용할 Prompt는 StringPromptTemplates 혹은 ChatPromptTemplates을 사용해 정의할 수 있습니다. Langchain 공식 문서에서 관련 내용을 확인해보세요. PromptTemplate은 단일 문자열의 서식을 지정하는 데 사용되며, 일반적으로 간단한 입력에 사용되는 템플릿입니다. PromptTemplate은 Python의 문자열 포맷팅을 사용하여 동적으로 특정한 위치에 입력 값을 포함시킬 수 있어, 다양한 상황에 맞게 프롬프트를 구성할 수 있습니다. 아래 예제처럼 {question}, {context} 와 같은 사용자 입력 및 매개 변수를 언어 모델에 전달하게 할 수 있습니다. from langchain_core.prompts import PromptTemplate prompt1 = PromptTemplate.from_template( """당신은 질문-답변(Question-Answering)을 수행하는 친절한 AI 어시스턴트입니다. 당신의 임무는 원래 가지고있는 지식은 모두 배제하고, 주어진 문맥(context) 에서 주어진 질문(question) 에 답하는 것입니다. 검색된 다음 문맥(context) 을 사용하여 질문(question) 에 답하세요. 만약, 주어진 문맥(context) 에서 답을 찾을 수 없다면, 답을 모른다면 `주어진 정보에서 질문에 대한 정보를 찾을 수 없습니다` 라고 답하세요. #Question: {question} #Context: {context} #Answer:""" ) ChatPromptTemplate 사용하기 ChatPromptTemplate 은 대화목록을 프롬프트로 주입하고자 할 때 활용할 수 있습니다. 메시지는 튜플(tuple) 형식으로 구성하며, (role, message) 로 구성하여 리스트로 생성할 수 있습니다. "system": 시스템 설정 메시지 입니다. 주로 전역설정과 관련된 프롬프트입니다. "human" : 사용자 입력 메시지 입니다. "ai": AI 의 답변 메시지입니다. from langchain_core.prompts import ChatPromptTemplate system_prompt = ( """당신은 질문-답변(Question-Answering)을 수행하는 친절한 AI 어시스턴트입니다. 당신의 임무는 원래 가지고있는 지식은 모두 배제하고, 주어진 문맥(context) 에서 주어진 질문(question) 에 답하는 것입니다. 검색된 다음 문맥(context) 을 사용하여 질문(question) 에 답하세요. 만약, 주어진 문맥(context) 에서 답을 찾을 수 없다면, 답을 모른다면 `주어진 정보에서 질문에 대한 정보를 찾을 수 없습니다` 라고 답하세요. """ "\n\n" "{context}" ) prompt2 = ChatPromptTemplate.from_messages( [ ("system", system_prompt), ("human", "{question}"), ] ) 1-3) Chain 생성 앞서 정의한 LLM 모델과 prompt를 LCEL 표기법을 사용해 chain으로 연결합니다. 이때, Langchain의 StrOutputParser도 chain으로 연결해 모델의 출력을 문자열로 변환해보겠습니다. 아래 코드를 실행하면 연결된 chain이 연쇄적으로 실행됩니다. retriever를 통해 관련 문서를 context로 불러오고, context와 question이 프롬프트 템플릿으로 전달되고, llm 모델을 통해 프롬프트가 실행되고, StrOutputParser를 통해 답변만 파싱해 문자열로 출력합니다. from langchain_core.runnables import RunnablePassthrough from langchain_core.output_parsers import StrOutputParser # 체인을 생성합니다. rag_chain = ( {"context": retriever, "question": RunnablePassthrough()} | prompt1 | llm | StrOutputParser() ) rag_chain.invoke("토큰이 뭔가요?") #Output 토큰은 자연어 처리를 위해 하나의 단어를 세분화한 단어 조각을 의미합니다. 예를 들어, '맛있어'라는 표현은 각각 '맛'과 '있어'라는 두 개의 토큰으로 나뉠 수 있습니다. 다른 예시로는 '원숭이 엉덩이는 빨개' 라는 문장은 '원숭이', '엉덩이는', '빨개' 와 같은 세 개의 토큰으로 나뉠 수 있습니다. 1-4) 참조 문서 링크와 함께 답변 출력하기 모델이 답변을 생성할 때 참조하는 문서의 출처를 확인하고 싶으시다면 아래의 코드를 참조하세요. RunnablePassthrough.assign()을 사용하여 검색 체인에서 소스 문서를 반환해 참조 문서 링크와 함께 답변을 출력해보겠습니다. from pprint import pprint from langchain_core.runnables import RunnablePassthrough from langchain.schema.runnable import RunnableParallel from langchain_core.output_parsers import StrOutputParser # 쿼리 텍스트 query_text = "max_token을 몇으로 설정해야해?" # 1. 쿼리 임베딩 생성 clovax_embeddings = ClovaXEmbeddings(model='bge-m3') query_embedding = clovax_embeddings.embed_query(query_text) # 2. Chroma 컬렉션에 쿼리 실행하여 유사한 문서 검색 results = chroma_collection.query( query_embeddings=[query_embedding], n_results=5, include=["embeddings", "documents", "metadatas", "distances"] ) # 3. 유사도 검색 결과를 사용하여 리트리버 구성 similarity_retriever = vectorstore.as_retriever( kwargs={"k": 5}, search_type="similarity_score_threshold", search_kwargs={"score_threshold": 0.1, "k": 3} ) # 4. 검색된 문서를 RAG 체인에 연결하여 답변 생성 rag_chain_from_docs = ( RunnablePassthrough.assign(context=(lambda x: format_docs(x["context"]))) | prompt1 | llm | StrOutputParser() ) def format_docs(docs): return "\n\n".join(doc.page_content for doc in docs) rag_chain_with_source = RunnableParallel( {"context": similarity_retriever, "question": RunnablePassthrough()} ).assign(answer=rag_chain_from_docs) pprint(rag_chain_with_source) Vector DB에 저장한 문서들과 관련있는 질문에 대해서는 모델이 아래와 같이 답변을 잘하는 것을 확인할 수 있습니다. #Output {'answer': '일반 모드에서 제공하는 언어 모델인 경우에는 최대 2048 토큰까지, 챗 모드에서 제공하는 HyperCLOVA X 언어 ' '모델인 경우에는 최대 4096 토큰까지 허용됩니다. Maximum tokens는 300~500을 권장하며 작업에 따라 ' '달라질 수 있습니다.', 'context': [Document(metadata={'source': 'https://guide.ncloud-docs.com/docs/clovastudio-info'}, page_content='Maximum tokens Maximum tokens는 결괏값을 생성할 때 사용할 최대 토큰 수입니다. 토큰 수를 높게 설정할 수록 긴 결괏값을 출력합니다.'), Document(metadata={'source': 'clovastudioguide_0822/clovastudio-info.html'}, page_content='Maximum tokens Maximum tokens는 결괏값을 생성할 때 사용할 최대 토큰 수입니다. 토큰 수를 높게 설정할 수록 긴 결괏값을 출력합니다.'), Document(metadata={'source': 'clovastudioguide_0822/clovastudio-info.html'}, page_content='프롬프트와 결괏값을 포함하여 일반 모드에서 제공하는 언어 모델인 경우에는 최대 2048 토큰까지, 챗 모드에서 제공하는 HyperCLOVA X 언어 모델인 경우에는 최대 4096 토큰까지 허용됩니다. Maximum tokens는 300~500을 권장하며 작업에 따라 달라질 수 있습니다.')], 'question': 'max_token을 몇으로 설정해야해?'} 위의 결과를 보다 보기 좋게 확인할 수 있습니다. result = rag_chain_with_source.invoke(query_text) model_answer=result['answer'] urls = {doc.metadata['source'] for doc in result['context']} print("- 사용자 질문: {0}\n- 모델의 답변: {1}\n- 참조한 문서의 url: {2}".format(query_text, model_answer, list(urls))) #Output - 사용자 질문: max_token을 몇으로 설정해야해? - 모델의 답변: 일반 모드에서 제공하는 언어 모델인 경우에는 최대 2048 토큰까지, 챗 모드에서 제공하는 HyperCLOVA X 언어 모델인 경우에는 최대 4096 토큰까지 허용됩니다. Maximum tokens는 300~500을 권장하며 작업에 따라 달라질 수 있습니다. - 참조한 문서의 url: ['clovastudioguide_0822/clovastudio-info.html', 'https://guide.ncloud-docs.com/docs/clovastudio-info'] 아래와 같이 문서와 관련 없는 질문에 대해선 정보를 찾을 수 없다고 답하는 것을 확인할 수 있습니다. rag_chain_with_source.invoke("오늘 기분이 어때?") #Output {'context': [Document(metadata={'source': 'https://guide.ncloud-docs.com/docs/clovastudio-info'}, page_content='Temperature 값이 높은 경우'), Document(metadata={'source': 'https://guide.ncloud-docs.com/docs/clovastudio-info'}, page_content='Temperature 값이 높은 경우'), Document(metadata={'source': 'https://guide.ncloud-docs.com/docs/clovastudio-procedure'}, page_content='이 문서가 도움이 되었습니까?')], 'question': '오늘 기분이 어때?', 'answer': '주어진 정보에서 질문에 대한 정보를 찾을 수 없습니다.'} 2) Built in chain 활용 원하는 경우 LangChain에는 RAG 시스템을 위의 LCEL을 사용하지 않고 함수로도 구현할 수 있습니다. create_stuff_documents_chain 검색된 컨텍스트가 프롬프트와 LLM에 공급되는 방법을 지정합니다. 이 경우 프롬프트에 문서의 요약이나 기타 처리 없이 검색된 모든 컨텍스트를 포함합니다. 이 함수를 통해 {input}과 {context}에 내용이 들어가게 됩니다. 입력 키: context, input create_retrieval_chain 검색 단계를 추가하고 검색된 문서를 최종 답변과 함께 제공합니다. 입력 키: input 출력: input, context, answer from langchain.chains import create_retrieval_chain from langchain.chains.combine_documents import create_stuff_documents_chain from langchain_core.prompts import ChatPromptTemplate from langchain_community.chat_models import ChatClovaX # chat모델 정의 llm = ChatClovaX( model="HCX-003", ) # ChatPromptTemplate 사용 system_prompt = ( """당신은 질문-답변(Question-Answering)을 수행하는 친절한 AI 어시스턴트입니다. 당신의 임무는 원래 가지고있는 지식은 모두 배제하고, 주어진 문맥(context) 에서 주어진 질문(question) 에 답하는 것입니다. 검색된 다음 문맥(context) 을 사용하여 질문(question) 에 답하세요. 만약, 주어진 문맥(context) 에서 답을 찾을 수 없다면, 답을 모른다면 `주어진 정보에서 질문에 대한 정보를 찾을 수 없습니다` 라고 답하세요. """ "\n\n" "{context}" ) prompt2 = ChatPromptTemplate.from_messages( [ ("system", system_prompt), ("human", "{input}"), ] ) # Built-in chains question_answer_chain = create_stuff_documents_chain(llm, prompt2) rag_chain = create_retrieval_chain(retriever, question_answer_chain) result = rag_chain.invoke({"input": "토큰이 뭐야?"}) print(result) #Output {'input': '토큰이 뭐야?', 'context': [Document(page_content='토큰', metadata={'source': 'https://guide.ncloud-docs.com/docs/clovastudio-info'}), Document(page_content="<예시> '맛있어'라는 표현은 각각 '맛'과 있어'라는 두 개의 토큰으로 나뉠 수 있습니다.", metadata={'source': 'https://guide.ncloud-docs.com/docs/clovastudio-info'}), Document(page_content='Temperature를 낮게 설정하면 후보에 포함된 토큰의 순위는 바뀌지 않지만 확률이 높았던 토큰은 더욱 확률 값이 높아지고 낮았던 토큰은 확률 값이 더욱 낮아집니다. 가장 높은 순위의 토큰이 선택될 가능성이 크기 때문에 정형적인 결괏값을 생성합니다.', metadata={'source': 'https://guide.ncloud-docs.com/docs/clovastudio-info'})], 'answer': '토큰은 문장이나 단어를 구성하는 기본 단위로, 의미를 가지는 최소 단위입니다. 예를 들어, "맛있어"라는 표현은 각각 "맛"과 "있어"라는 두 개의 토큰으로 나뉠 수 있습니다. 또한 인공지능 분야에서는 자연어 처리나 기계학습에서 데이터를 분석하기 위해 토큰화(tokenization) 과정을 거칩니다. 이 때 각 단어나 기호 등을 토큰으로 분리하여 처리합니다.'} 위의 결과를 보다 보기 좋게 확인할 수 있습니다. print("질문:") print(result['input']) print("\n답변:") print(result['answer']) print("\n참조 문서 URL:") unique_urls = set(doc.metadata['source'] for doc in result['context']) for url in unique_urls: print(url) #Output 질문: 토큰이 뭐야? 답변: 토큰은 문장이나 단어를 구성하는 기본 단위로, 의미를 가지는 최소 단위입니다. 예를 들어, "맛있어"라는 표현은 각각 "맛"과 "있어"라는 두 개의 토큰으로 나뉠 수 있습니다. 또한 인공지능 분야에서는 자연어 처리나 기계학습에서 데이터를 분석하기 위해 토큰화(tokenization) 과정을 거칩니다. 이 때 각 단어나 기호 등을 토큰으로 분리하여 처리합니다. 참조 문서 URL: https://guide.ncloud-docs.com/docs/clovastudio-info 7. 맺음말 지금까지 NAVER Cloud Platform(NCP)에서 Langchain을 활용하여 RAG 시스템을 구현하는 방법을 살펴보았습니다. 이 가이드에서는 HCX 모델을 활용하여 간단한 RAG 파이프라인을 구축하는 과정을 다뤘습니다. 이 가이드는 RAG의 기본 구조와 흐름에 초점을 맞추었지만, RAG 기술은 계속 발전하고 있습니다. 앞으로 다양한 RAG 시스템 구현에 관한 더 깊이 있는 주제들을 다룰 예정입니다. NCP 가이드: https://guide.ncloud-docs.com/docs/clovastudio-dev-langchain LangChain 공식 문서: https://python.langchain.com/docs/integrations/providers/naver/
  6. 안녕하세요, @아이알컴퍼니님, 현재 심사 대기중이며, 순차적으로 처리될 예정이며, 신청 취소 기능은 없습니다. 서비스 앱에 대해서 추가로 문의가 있으실 경우 "고객지원 문의하기" 를 통해 문의 등록 부탁드립니다. 더욱 편리한 서비스 제공을 위하여 항상 노력하겠습니다. 감사합니다.
  7. 스킬 트레이너는 특화된 지식을 모델에 학습시키는 도구입니다. 특정 서비스에 필요한 여러 스킬을 구성하여 이를 학습시키고 모델의 성능을 높일 수 있습니다. 스킬 트레이너에서는 네이버의 지역 검색 API를 활용한 예제 데이터를 제공하고있습니다. 본 튜토리얼에서는 해당 예제를 통해 스킬 트레이너를 활용하여 서비스를 제작하는 전체 과정을 살펴보고자합니다. 스킬 기반 LLM의 강점과 서비스 예시 스킬 기반의 LLM은 최신성과 신뢰성을 갖춘 답변을 생성하며, 튜닝이 용이하다는 강점을 가지고 있습니다. 자세한 내용은 아래와 같습니다. 최신화되고 신뢰도 있는 답변 생성 스킬은 API 기반의 답변을 생성하여 사용자에게 최신 정보를 제공하며 할루시네이션을 최소화할 수 있습니다. "판교 포케집 주소 알려줘"라는 동일한 쿼리에 대해 스킬이 적용된 LLM과 그렇지 않은 LLM 답변의 차이를 살펴보겠습니다. 학습을 통한 손쉬운 튜닝 일일이 답변을 수정해야했던 전통적 챗봇 제작 방식의 한계를 넘어, 스킬 트레이너는 스킬셋 단위로 답변 말투나 포맷을 일괄적으로 설정할 수 있습니다. 또한 이렇게 수집된 데이터를 학습시켜 손쉽게 모델을 튜닝할 수 있습니다. 스킬을 통해 가치를 극대화할 수 있는 서비스 예시 스킬은 이미 존재하는 데이터베이스나 API를 활용하여 시스템에서 특정한 조건에 맞는 정보를 조회할 때 유용하게 사용됩니다. 위에서 언급한 스킬의 강점을 고려할 때 업데이트가 자주 필요한 정보를 다루는 서비스에서 더 큰 시너지를 낼 수 있습니다. 구체적 사례는 아래와 같습니다. 뉴스 에이전트 : 특정 주제나 카테고리(예: 기술, 정치, 스포츠)에 대한 최신 뉴스의 URL을 제공하고 요약할 수 있습니다. 부동산 에이전트 : 특정 지역의 부동산 매물 정보를 조회하고 시장 분석 정보를 제공할 수 있습니다. 주식 에이전트 : 특정 주식의 실시간 가격, 변동 추이, 주요 뉴스 등을 조회하고 시장 분석 정보를 제공할 수 있습니다. 스포츠 에이전트 : 스포츠 종목의 경기 일정과 실시간 순위, 종목 규칙, 선수 정보 등 스포츠와 관련된 다양한 정보를 조회할 수 있습니다. 1. 예제 활용을 위한 사전 준비 이제부터 본격적으로 스킬을 제작해보겠습니다. 예제로 제공된 지역 검색 API를 호출하기 위해서는 네이버 개발자 센터에서 애플리케이션을 등록한 후 API 호출에 필요한 인증 정보를 획득해야 합니다. 이 단계는 에서 획득한 인증 정보는 데이터 수집 단계에서 호출 옵션값으로 사용됩니다. 인증정보 획득하기 네이버 오픈 API를 사용하기 위해서는 클라이언트 아이디와 클라이언트 시크릿을 발급받아야 합니다. 이는 인증된 사용자인지를 확인하는 수단이며, 네이버 개발자 센터 내 애플리케이션이 등록되면 발급됩니다. 자세한 방법은 다음과 같습니다. 네이버 개발자 센터에서 애플리케이션을 등록해 주십시오. 애플리케이션을 등록하는 방법은 애플리케이션 등록을 참조해 주십시오. 애플리케이션 등록 시 사용 API는 검색 API를 선택해 주십시오. API 호출 시 클라이언트 아이디와 클라이언트 시크릿 정보를 확인해 주십시오. 클라이언트 아이디와 클라이언트 시크릿 확인을 참조해 주십시오. 2. 스킬셋 생성하기 스킬셋 생성하기 스킬셋은 여러개의 스킬을 묶은 하나의 단위로, 스킬셋을 기준으로 답변 형식이 적용되며 데이터 학습을 진행할 수 있습니다. 따라서 다수의 API에 일관된 답변 스타일과 학습을 적용하기 위해서는 하나의 스킬셋으로 묶어 제작하는 것을 권장합니다. 스킬셋 설명 스킬셋 생성을 클릭하고, 스킬셋 이름과 스킬셋 설명을 작성합니다. 스킬셋 설명은 모델 응답 결과에 직접적으로 영향을 주는 정보는 아니므로, 제작하고자하는 서비스 성격에 적합한 내용으로 작성합니다. 답변 형식 설정 답변에 대한 말투와 포맷 선택할 수 있습니다. 데이터 수집 과정에서 스킬셋 답변 형식에 설정한 값이 실제 최종 답변에 반영되었는지 확인합니다. 최종 답변이 설정한 답변 형식과 상이한 경우, 데이터 수집 화면에서 최종 답변을 형식에 맞게 수정해야합니다. 3. 스킬 생성하기 스킬을 생성하기 위한 정보로 API Spec와 Manifest가 있습니다. 이 데이터는 CoT 단계에서 주요하게 쓰이는데, API Spec은 Step 2에서 API 호출을 위한 액션 입력을 생성 할때 사용되며, Manifest는 쿼리를 분석하여 API 호출한 후 호출 결과를 취합(step 3 결과 정리)할 때 사용됩니다. 4. API Spec 작성하기 API Spec 항목에는 모델이 이해할 수 있는 API 스펙을 작성합니다. 본 튜토리얼에서는 지역 검색 스킬의 API Spec 작성 방법을 중심으로 설명합니다. 본 튜토리얼에서는 지역 검색 스킬의 API Spec 작성 방법을 중심으로 설명합니다. API Spec 각 필드에 대한 상세한 설명은 API Spec 작성 가이드에서 확인할 수 있습니다. Version 사용할 OpenAPI 버전 정보입니다. 3.0 버전만 지원하며, 다른 버전을 사용할 경우 오류가 발생할 수 있습니다. { "openapi": "3.0.2" } Info 제공되는 API에 관한 기본 정보입니다. { "info": { "title": "네이버 지역 검색", "version": "1.0.0", "description": "네이버 지역 서비스에 등록된 업체 및 기관을 검색하기 위한 스킬" } Servers API가 제공되는 대상 호스트 정보로 Path들의 baseURL입니다. 네이버 오픈 API의 URL을 입력합니다. { "servers": [ { "url": "https://openapi.naver.com" } ] } Paths 제공되는 API의 Path 정보로 필수 입력값입니다. Summary에는 사용할 파라미터와 해당 Path를 통해 얻을 수 있는 정보를 구체적으로 작성하는 것이 좋습니다. 예제예서는 requests get 메소드를 사용하여 작성하였습니다. 각 Path의 하위에는 Parameter Object와 Operation Object가 존재합니다. { "paths": { "/v1/search/local.json": { "get": { "summary": "국내 지역 정보 검색 결과를 나열합니다." } } } } Parameter Object 예제에서는 네이버 지역 검색 API 파라미터를 참고하여 주요 파라미터를 작성하였습니다. 파라미터 간 역할이 중복되지 않도록 작성하는 것을 권장 합니다. 만약 역할이 비슷하거나 겹치는 파라미터가 있다면, 스킬의 정확도가 떨어질 수 있습니다. 파라미터 description에는 해당 파라미터가 어떤 의미를 갖고 있는지, 어떤 상황에서 사용되는 것인지, URL 생성 시 어떤 형식으로 들어가야 하는지 등을 구체적으로 작성합니다. 예시를 추가하면 모델이 이를 기반으로 더 정확한 파라미터를 갖춘 URL(스킬호출 Step 2의 액션 입력)을 생성할 수 있습니다. 본 예제에서는 어떠한 쿼리에 대하여 해당 파라미터가 호출되어야하는지를 포함하였습니다. 그 외에도 파라미터가 가져야 하는 값이 한정되어 있다면 구체적인 예시를 수 있습니다. { "parameters": [ { "in": "query", "name": "query", "schema": { "type": "string" }, "required": true, "description": "**query**: 사용자가 찾고자 하는 상점의 설명 (e.g. 정자동 근처 맛집, 강남역 근처 분위기 좋은 카페)\\n" }, { "in": "query", "name": "display", "schema": { "type": "integer", "default": "2" }, "required": true, "description": "요청한 쿼리에 대한 검색 결과 수 입니다. 유일한 상점이나 업체일 경우에는 결과를 1개만 보여주세요." }, { "in": "query", "name": "sort", "schema": { "type": "string" }, "required": false, "description": "검색 결과 정렬 방법\\n- **random**: 정확도순으로 내림차순 정렬(기본값)\\n- **comment**:카페, 블로그의 리뷰 개수순으로 내림차순 정렬이 필요할 때 값을 사용하세요. '맛집'이라는 키워드가 포함되면 무조건 이 값을 사용하세요.(e.g. 리뷰 많은 순으로 정렬해주세요. 유명한 곳을 알려주세요. 인기 많은 곳을 알려주세요.)\\n" ] } ▼ 한정된 값을 불러오는 파라미터 작성 예시 { "parameters": [ { "name": "category", "in":"query", "description": "장소의 대분류. restaurant, cafe, attraction, accommodation 중에 하나.", "required": false, "schema": { "type": "string" } } ] } "enum" 필드를 활용하면 파라미터 값을 더 좁게 특정할 수 있습니다. { "PlaceCategory": { "enum": [ "RESTAURANT", "CAFE", "ATTRACTION", "ACCOMODATION" ], "type": "string", "title": "- RESTAURANT: 식당\n -CAFE: 카페\n - ATTRACTION : 가볼만한 곳\n - ACCOMODATION : 숙박", "default": "RESTAURANT" } } API 설계를 위한 DOs and DON'Ts API를 설계할 때는 사용자가 주로 사용하는 조건 위주로 parameter를 정의하세요. Parameter를 정의할 때는 아래의 주의사항을 고려해주세요 DOs 파라미터의 값이 특정 포맷으로 입력되어야 한다면, Parameter description에 반드시 적어주세요. 예: 날짜는 YYYY-MM-DD 형태입니다. 날짜를 2023-12-31형태로 입력하세요. 파라미터의 값이 특정 값들 중에서 선택하는 형태라면, 선택 가능한 값을 description 혹은 enum에 반드시 적어주세요. 예: 차종은 경형, 소형, 중형, 준중형, 대형 중에서 선택하세요. API 호출 결과값은 600토큰 이내가 적합합니다. API 응답이 긴 경우 토큰 수 초과 오류를 방지하기 위해 페이지네이션 파라미터(예: display, page 등)를 함께 설계해 주실 것을 권장합니다. 필수 파라미터 누락 시에도 스킬이 정상 동작하도록 API를 구성할 수 있습니다. 필수 파라미터가 비어있을 시 API에서 오류코드(400 등)를 내려주지 않고 정상 응답 코드(200)로 반환해야하며, 이때 어떠한 파라미터가 누락되었는지를 답변 내용에 포함할 수 있습니다. 이 방식을 통해 최종 답변 영역에서 모델이 누락된 파라미터를 요청하는 질문을 생성하게 되며, 원하는 형식으로 튜닝 및 학습도 진행할 수 있습니다. DON'Ts 하나의 값이 여러 개의 파라미터에 매칭 되지 않도록 주의하세요. 예: 게시판 게시글 검색 API에서 'editor(글작성자)'와 'commenter(댓글작성자)' parameter가 각각 존재한다면, '홍길동이 이번달에 쓴 글 찾아줘'라는 쿼리에 대하여 잘못된 파라미터가 매칭될 수 있습니다. 특별한 경우가 아니면, 수식어는 파라미터의 값으로 적합하지 않습니다. 수식어는 주관적인 판단 요소로 사용자에게 결과의 신뢰성을 떨어뜨릴 수 있습니다. 예시: '따뜻한 색상의 가구 추천해줘'라는 쿼리 중, color의 값으로 '따뜻한'은 적합하지 않습니다. Operation Object operationId는 API를 식별하는 고유 문자열입니다. 다수개의 스킬 작성 시 해당 값이 중복되지 않도록 API Spec을 작성해야 합니다. { "paths": { "/v1/search/local.json": { "get": { "responses": {}, "description": "지역의 업체나 기관을 검색합니다.", "operationId": "localSearch" } } } } Response Object 작업 응답에 대한 설명으로, 지역검색 API의 응답 결괏값을 참고하여 작성하였습니다. { "responses": { "200": { "content": { "application/json": { "schema": { "type": "object", "properties": { "items": { "type": "array", "items": { "type": "object", "properties": { "link": { "type": "string" }, "mapx": { "type": "string" }, "mapy": { "type": "string" }, "title": { "type": "string" }, "address": { "type": "string" }, "category": { "type": "string" }, "telephone": { "type": "string" }, "description": { "type": "string" }, "roadAddress": { "type": "string" } } } }, "start": { "type": "integer" }, "total": { "type": "integer" }, "display": { "type": "integer" }, "lastBuildDate": { "type": "string" } } } } }, "description": "- **title**: 업체명\\n- **address**: 지번 주소(e.g. 서울특별시 중구 을지로3가 229-1)\\n- **roadAddress**: 도로명 주소(e.g. 서울특별시 중구 을지로15길 6-5)\\n- **category**: 카테고리(e.g. 식당, 카페, 병원, 미용실, 기업, 공공 기관)\\n- **description**: 업체에 대한 설명(e.g. 연탄불 한우갈비 전문점, 강남역 근처 분위기 좋은 카페)\\n- **link**: 홈페이지 링크" } } 5. Manifest 작성하기 Manifest는 해당 스킬을 통해 호출할 수 있는 API의 이름, 목적, 사용 방법 등을 입력하는 영역입니다. Manifest의 주요 영역은 Description for human과 Description for model이 있습니다. Description for human API의 목적과 용도를 작성합니다. 유저 쿼리를을 바탕으로 모델이 여러 API 중에서 필요한 API를 선택하는 기준이 됩니다. 모든 API의 역할이 명확하게 작성되어야 합니다. Description for model 모델이 답변에 사용할 API를 결정하기 위해 사용자의 질문과 ‘decription_for_model’을 모두 참고하여 여러 개의 API 중 적절한 API를 선택합니다. 내용은 가능한 구체적이어야하며, 예시를 포함할 시 모델이 정확한 답변을 생성하는 데 도움이 됩니다. 포함 권장 내용 내용 예시 스킬의 용도 네이버 지역 서비스에 등록된 업체 및 기관을 검색하기 위해서는 LocalSearch 스킬을 사용하세요. 각 paths의 용도 /v1/search/local.json은 업체나 기관의 이름, 주소 (ex. 도로명 주소, 지번 주소)을 검색어로 입력할 때 동작합니다. API를 통해 얻을 수 있는 정보 업체나 기관의 이름, 도로명 주소, 지번 주소를 알 수 있습니다. 특정 지시 사항 원하는 정렬 방식 (ex.블로그의 리뷰 개수순, 정확도순 등)을 포함하여 검색 결과를 요청할 수 있습니다. 모든 호출 결과를 최종답변에 반드시 포함해주세요. 6. 데이터 수집하기 작성된 API Spec과 Manifest를 토대로 데이터를 수집합니다. 이 단계에서 모델의 사고 과정이나 최종 답변을 수정할 수 있습니다. 수정된 데이터를 모델에 학습시켜 튜닝을 진행하게됩니다. 호출 옵션값 입력 앞서 획득한 클라이언트 아이디와 클라이언트 시크릿 정보를 호출옵션에 저장하여 사용합니다. 아래 포맷을 참고하여 호출옵션 헤더에 각각의 인증값을 입력합니다. 스킬셋 내에 등록된 모든 API에 대해 호출 옵션을 일괄 적용하고자 할 때는 baseOperation 필드를 활용하고, 특정 Operation ID에 한하여 호출 옵션을 적용하고자하면 operations 필드를 활용합니다. { "baseOperation": { "header": { "X-Naver-Client-Id": "애플리케이션 등록 시 발급받은 클라이언트 아이디 값", "X-Naver-Client-Secret": "애플리케이션 등록 시 발급받은 클라이언트 시크릿 값" }, "query": null, "requestBody": null } } CoT 과정 살펴보기 데이터 수집에서 [데이터 불러오기]를 통해 미리 수집해둔 예제 데이터를 확인할 수 있습니다. 예제를 통해 상세한 CoT 과정을 살펴봅니다. CoT는 Chain of Thought의 약자로, 쉽게 말해 LLM 모델이 사용자 쿼리에 대하여 답변을 생성하는 과정에서 단계별로 생각을 정리하고 사고를 펼치는 방법입니다. 데이터 수집 인터페이스에서 각 Step별 모델의 사고 과정(CoT)을 살펴볼 수 있습니다. 지역 검색 스킬셋의 예제 쿼리 중 하나를 살펴보겠습니다. 쿼리 데이터 정자동 맛집 찾아줘. 리뷰 많은 순으로 나열해줘. Step 1. 호출 스킬 선택 입력된 쿼리를 통해 적절한 스킬을 선택하였는지 검토합니다. LocalSearch 스킬을 올바르게 선택한 것을 확인할 수 있습니다. 하나의 문장을 모델이 적절히 분리하며, 분리된 쿼리의 수만큼 스킬이 호출됩니다. 예를 들어, "네이버 1784 주소와 근처 카페 추천해 주세요"라는 쿼리에 대해서는 "네이버 1784 주소"와 "네이버 1784 근처 카페"로 쿼리를 분리하여 스킬을 두 번 호출하게 됩니다. Step 2. 액션 입력 생성 액션 입력 필드를 통해 API Spec 내 파라미터 Description 및 Manifest에 작성한 내용이 올바르게 호출되었는지 검토 합니다. 앞서 API Spec 내 display 파라미터의 default 값을 2로 설정하였고, 리뷰 순으로 정렬하라는 유저 쿼리를 받아 display=2와 sort=comment를 포함한 URL이 반환되었습니다. 만약 이 단계에서 의도한 대로 모델이 액션 입력을 생성하지 않는다면 API Spec에서 파라미터의 'description'에 예시를 추가해 주십시오. 혹은 액션입력을 의도에 맞게 수정한 뒤 데이터를 저장하면 학습을 통해 모델이 개선됩니다. 데이터를 수정할 때는 사용자 요청과 무관한 키워드를 필드에 포함해서는 안됩니다. 예를 들어, "display" 파라미터의 값을 3으로 고정하고 싶습니다. 이 때 두 가지 방법으로 튜닝을 진행할 수 있습니다. 액션입력 값 수정 액션 입력 파라미터 값을 직접 수정하고 [적용]버튼을 눌러 결과를 다시 호출합니다. 이때 액션 입력 파라미터에 맞게 API 조회 결과 내용도 반드시 수정해야합니다. 이렇게 수정한 데이터를 여러 건 수집하여 학습을 진행합니다. 예 : https://openapi.naver.com/v1/search/local.json?query=정자동 맛집&display=3&sort=comment API Spec 수정 파라미터 description에 요구사항을 명확히 작성합니다. API Spec 수정을 통하면 데이터 수집 단계에서 수정 없이 액션 입력값을 올바르게 수정할 수 있습니다. 예: "검색된 업체 목록 개수. 특정 개수를 요청하지 않을 경우, 검색된 업체는 반드시 세 곳을 보여주세요."라는 description을 추가 합니다. query 파라미터에는 간결한 값을 입력해야 잘 작동합니다. 만약 모델이 생성한 파라미터 값이 따르면 '정자동 근처 맛집'이라면 해당 검색 키워드로는 API 검색 결과를 생성하지 못할 수 있습니다. 따라서 액션 입력 항목에서 쿼리를 '정자동 맛집'으로 수정한 후 [적용] 버튼을 클릭하여 API 검색 결과를 다시 생성해 주십시오. 예 : https://openapi.naver.com/v1/search/local.json?query=정자동 근처 맛집&display=2&sort=comment Step 3. 결과 정리 호출 결과를 취합하여 생성한 결과 정리를 생성합니다 .이를 바탕으로 최종 답변이 생성됩니다. 최종 답변 수정하기 Step 3의 결과 정리를 토대로 생성된 최종 답변을 검토합니다. 최종 답변을 원하는 형식으로 수정 후 데이터를 학습 시키면 모델을 튜닝할 수 있습니다. 스킬 호출 결과를 바탕으로 최종 답변을 생성하는 과정에서 모델이 일부 정보를 누락하거나 답변의 스타일(양식, 어투 등)을 변경하고자 하는 경우, 이를 의도에 맞게 수정한 뒤 데이터를 저장하면 학습을 통해 모델을 개선할 수 있습니다. 만약 최종 답변을 수정했다면 Step 3 결과 정리도 동일한 내용으로 구성되었는지 검토 후 함께 수정해야합니다. 스킬 호출 결과에 나타나지 않은 정보를 임의로 최종 답변에 포함해서는 안됩니다. 예제 데이터의 경우 다음 형식에 맞추어 최종 답변을 수정하였습니다. {사용자 요청 내용}을 알려드리겠습니다. 1. {장소1 이름} - 주소: {장소 1의 주소} 2. {장소2 이름} - 주소: {장소 2의 주소} 더 필요하신 지역 정보가 있다면 말씀해주세요. 스킬셋의 답변 형식과 최종 답변이 일치하지 않는 경우, 답변 형식을 직접 수정하여 학습을 진행할 수 있습니다. 답변 형식: 답변 포맷에 "JSON" 형식 선택 후 추가적인 설명 란에 "key는 반드시 영문으로 작성해 주세요."라고 입력 반환된 최종 답변: "2023년 8월 9일 기준 모자 쇼핑 구매전환 이용자 수는 다음과 같습니다. 페이지 뷰 수는 1,003,120, 이용자 수는 1,222,334입니다." 수정할 답변 형식의 작성 예시: { "20230809":{ "purchase":[ { "pv":"1,003,120", "puruser":"1,222,334" } ] } } 7. 데이터 학습하기 모델이 더 정확한 답변을 생성할 수 있도록 수집한 데이터를 모델에 학습시킵니다. 수집된 데이터 중 '작업 완료' 상태의 데이터들만 학습에 사용되며, 튜닝 성능을 보장하기 위해서는 1개의 스킬셋 당 50~100개의 데이터를 수집하여 학습을 진행할 것을 권장합니다. 데이터 학습은 스킬 호출 학습과 최종 답변 학습으로 구분되며, 각 영역의 데이터를 수정하여 학습을 진행할 수 있습니다. 스킬 호출 학습 : 사용자 요청을 수행하기 위한 API URL 및 파라미터를 모델이 정확하게 생성하지 못한 경우, 이를 의도에 맞게 수정한 뒤 데이터를 저장하면 학습을 통해 모델이 개선됩니다. 데이터를 수정할 때는 사용자 요청과 무관한 키워드를 필드에 포함해서는 안됩니다. 최종 답변 학습 : 스킬 호출 결과를 바탕으로 최종 답변을 생성하는 과정에서 모델이 일부 정보를 누락하거나 답변의 스타일(양식, 어투 등)을 변경하고자 하는 경우, 이를 의도에 맞게 수정한 뒤 데이터를 저장하면 학습을 통해 모델이 개선됩니다. 이때 스킬 호출 결과에 나타나지 않은 정보를 임의로 최종 답변에 포함해서는 안됩니다. 8. 서비스 적용하기 데이터 수집 화면에서 학습된 버전에 대한 테스트를 진행해 볼 수 있습니다. 버전 관리 내 [코드 보기] 버튼을 클릭하여 API 호출 정보를 확인할 수 있으며, API를 호출하는 방법은 스킬셋 답변 생성 API 가이드를 참조해 주십시오. curl --location --request POST 'https://clovastudio.stream.ntruss.com/testapp/v1/skillsets{skillset-id}/versions/{version}/final-answer' \ --header 'X-NCP-CLOVASTUDIO-API-KEY: \ --header 'X-NCP-APIGW-API-KEY: \ --header 'X-NCP-CLOVASTUDIO-REQUEST-ID: \ --header 'Content-Type: application/json' \ --header 'Accept: text/event-stream' \ --data '{ "query": "리뷰 많은 순으로 나열해줘.", "tokenStream": true, "chatHistory": [ { "role": "user", "content": "정자동 근처 맛집 찾아줘." }, { "role": "assistant", "content": "정자동 근처 맛집을 알려드리겠습니다.\n\n1. 효원식당 분당정자점\n- 주소: 경기도 성남시 분당구 정자동 66-11 1층\n- 카테고리: 한식>육류,고기요리\n\n2. 화로양\n- 주소: 경기도 성남시 분당구 정자동 174-1 더샵스타파크 C-1호\n- 카테고리: 음식점>양갈비\n\n3. 하누비노 정자점\n- 주소: 경기도 성남시 분당구 정자동 166-2 정자역 엠코헤리츠 3단지 101호\n- 카테고리: 한식>소고기구이\n\n더 필요한 지역 정보가 있다면 말씀해주세요." } ], "requestOverride": { "baseOperation": { "header": { "X-Naver-Client-Id": "애플리케이션 등록 시 발급받은 클라이언트 아이디 값", "X-Naver-Client-Secret": "애플리케이션 등록 시 발급받은 클라이언트 시크릿 값" }, "query": null, "requestBody": null } }
  8. 안녕하세요, @VTS 님, 1. HCX 모델을 튜닝할 때 과적합이 발생하면 원래 모델의 일반적인 성능이 저하될 수 있습니다. 따라서 적절한 epoch과 learningRate 설정을 통해, 학습이 필요할 수 있습니다. 2. 여러 task를 묶어서 진행하는 것보다 개별 task로 진행하며 검증하는 것이 효율적일 것 같습니다. 3. 테스트앱은 모델과 무관하게 하나의 API 키로 이용할 수 있습니다. 과금의 경우 앱과 무관하게 모델별로 실제 처리된 입출력 토큰수에 따라 발생합니다. 서비스 앱의 경우 앱별로 API 키가 발급됩니다. 감사합니다.
  9. AI의 애플리케이션 활용 최근 ChatGPT에 추가된 'Canvas' 기능이 주목받고 있습니다. Canvas는 AI가 글의 일부를 편집하고, 문단 길이를 조절하며, 최종 윤문을 수행할 수 있습니다. 그중에서도 가장 인상적인 기능은 독해 수준을 유치원생부터 대학원생까지 선택하여 텍스트를 변형할 수 있다는 점입니다. 이 외에도 랭체인이 최근 'Open Canvas'라는 오픈 소스를 공개하며 다양한 방식의 다시쓰기(Rewrite) 기능을 제공하고 있습니다. 여러 서비스에서 AI와 인간이 협력해 글을 더욱 정교하게 다듬어가는 과정이 지원되고 있으며, 앞으로 글쓰기 방식과 AI 협업 방식에 큰 변화가 생길 것으로 기대됩니다. 가장 기본적인 생성형 AI 플랫폼의 구조는 사용자가 쿼리를 입력하면, 모델이 응답을 생성해 다시 사용자에게 제공하는 형태입니다. 그러나 더 정교한 AI 에이전트를 구축하려면, 다양한 추가 구성 요소를 통해 복잡성을 더하게 될 것입니다. Huyen Chip, Building A Generative AI Platform, https://huyenchip.com/2024/07/25/genai-platform.html 대규모 언어 모델(LLM)의 활용은 이제 단순히 모델 자체의 성능에 의존하는 것이 아니라, 이를 어떻게 활용하고 전개하느냐에 따라 그 가치가 결정됩니다. 그 중심에는 에이전트의 역할이 있습니다. 에이전트는 LLM을 더 정교하게, 그리고 사용자의 목적에 맞춰 구체화하는 중요한 개념입니다. 단순한 모델 사용을 넘어, 에이전트를 통해 사용자 경험을 어떻게 확장하고 발전시킬 수 있을까요? 이번 글에서는 하이퍼클로바X를 활용해 텍스트의 난이도를 조정하는 작업을 진행해보겠습니다. ① 대학생 수준으로 난이도 높이기 ② 중학생 수준으로 난이도 낮추기 ③ 초등학생 수준으로 매우 쉽게 조정하기 이번 작업을 위해 사용할 사용자 인풋입니다. 평이한 난이도로 작성되어 있습니다. ① 프롬프트 작성 - 대학생 수준으로 난이도 높이기 먼저 대학생 수준의 난이도 조절을 위한 프롬프트 작성 과정을 설명하겠습니다. "논문 수준으로 난이도 높이기", "고급 어휘와 복잡한 문장 구조 사용" 등의 지시를 포함했습니다. 이번 프롬프트에서 중요한 요소는 고급 어휘와 복잡한 문장 구조를 사용하도록 지시하는 것이었으며, 이를 통해 독자가 높은 수준의 이해력을 요구받도록 의도했습니다. 또한 '시작하다'를 '착수하다'로, '도움이 된다'는 '기여하다'로 변환하는 예시를 넣었습니다. ▼ 프롬프트를 통해 하이퍼클로바X에게 글의 난이도를 높이도록 지시한 결과, 원문의 내용은 유지하면서도 상당히 수준 높은 글로 변환되었습니다. '빛'이 '광학'으로, '예전에는'이 '고전 물리학에서는'로 바뀌는 등 학술적 용어의 비중이 높아졌습니다. 이는 단순한 단어 교체를 넘어 주제의 깊이와 과학적 맥락을 강화하는 효과를 가져왔습니다. 예를 들어 빛의 속도는 '30만 km/s' 대신 '299,792,458 m/s'로 더욱 정확하게 명시 되었습니다. ② 프롬프트 작성 - 중학생 수준으로 난이도 낮추기 이번에는 중학생도 쉽게 이해할 수 있도록 글의 난이도를 낮추는 프롬프트를 작성했습니다. 첫 번째로, "글의 난이도를 중학생 수준으로 쉽게 변환하세요"라는 지시를 작성했습니다. 어휘 난이도를 조정하기 위해, 어려운 단어들을 쉽게 바꾸는 예시를 포함했습니다. 예를 들어, '착수하다'를 '시작하다'로, '기여하다'를 '도움이 된다'로 변환했습니다. 이러한 어휘 변환은 복잡한 표현을 쉽게 풀어서 설명하려는 의도로, 단어의 의미를 단순화하면서도 본래의 뜻을 유지하도록 했습니다. 마지막으로, '인간의 존엄성과 자유를 보장하기 위해, 국가는 국민의 기본권을 보호하고, 사회적 약자를 배려하는 정책을 수립해야 한다.'는 문장을 '사람들이 존중받고 자유롭게 살 수 있도록, 나라에서는 사람들의 기본적인 권리를 지키고, 어려운 사람들을 도와주는 계획을 세워야 합니다.'로 비교적 쉬운 어휘를 활용한 예시를 명확히 보여주었습니다. ▼ 중학생들도 쉽게 이해할 수 있는 형태로 출력되었습니다. 예를 들어, '파동과 알갱이'라는 빛의 성질을 설명할 때는 복잡한 물리학 용어 대신, "빛은 흔들리면서 움직이기도 하고, 작은 알갱이처럼 보일 때도 있어"라는 표현을 사용했습니다.이를 통해 학생들이 빛의 복잡한 이론을 직관적으로 이해할 수 있도록 돕습니다. 또한, 반사와 굴절을 설명하는 부분에서는 복잡한 과학적 원리를 피하고 '빛이 거울에 닿거나 물웅덩이를 지나면 방향이 바뀐다'와 같은 일상적인 예시를 사용하여, 독자가 더 쉽게 개념을 잡을 수 있도록 했습니다. ③ 프롬프트 작성 - 초등학생 수준으로 매우 쉽게 조정하기 이 프롬프트는 텍스트를 초등학생도 쉽게 이해할 수 있도록 변환하는 데 중점을 두고 작성되었습니다. 먼저, 글의 난이도를 초등학생 수준으로 낮추라는 명확한 지시를 포함했습니다. 문장 구조에 대해서는 짧고 간단한 문장을 사용하고, 기본적인 주어-동사-목적어 구조를 활용하도록 안내했습니다. 또한 복잡한 문장을 초등학생이 이해할 수 있는 수준으로 변환하는 구체적인 예제를 제시하여, AI가 실제로 어떻게 텍스트를 변환해야 하는지 명확히 보여주었습니다. ▼ 복잡한 과학 용어나 어려운 개념을 배제하고, 학생들이 일상에서 접할 수 있는 친숙한 표현과 비유를 사용해 내용을 쉽게 설명했습니다. 먼저, 빛의 속도를 설명할 때, 과학적 수치나 복잡한 이론 대신 "빛은 빠르게 움직여서 1초에 지구를 7바퀴 반이나 돌 수 있어요"와 같은 구체적이고 직관적인 예시를 사용했습니다. 또한 빛의 성질을 설명할 때 "빛이 물결처럼 움직이는 파동이라고 생각했어요"와 같은 간단한 표현을 사용하여, 학생들이 물리학적 개념을 쉽게 이해할 수 있도록 돕고 있습니다. 마무리하며 지금까지 다양한 난이도 조정 프롬프트를 통해 AI가 텍스트를 어떻게 변환하는지 살펴보았습니다. 대학생 수준부터 초등학생 수준까지, 하이퍼클로바X는 각 독자의 이해 수준에 맞춘 글을 생성해낼 수 있음을 확인했습니다. 이를 통해 AI가 단순한 생성 도구를 넘어, 학습과 교육을 포함한 다양한 분야에서 효과적으로 활용될 수 있다는 가능성을 보여주었습니다. 언어 모델의 가치는 이제 단순한 성능을 넘어, 여러 비즈니스 파트너가 이를 어떻게 애플리케이션 수준으로 발전시켜 활용하는지에 달려있습니다.
  10. 안녕하세요, 클로바 스튜디오 담당자입니다. 먼저, 소중한 피드백과 제안을 보내주셔서 감사드립니다. 네이버 클라우드 플랫폼은 다양한 비즈니스 솔루션을 제공하고 있어, 콘솔을 통한 접근이 필요한 부분이 있습니다. 하지만, 현재 구글에서 clova studio playground 를 검색하시면 해당 메뉴로 바로 진입하실 수 있습니다. 또한, 플레이그라운드(https://clovastudio.ncloud.com/playground) 페이지를 즐겨찾기에 추가하시면 더욱 편리하게 이용하실 수 있습니다. 앞으로도 더 나은 경험을 제공하기 위해 지속적으로 개선해 나가겠습니다. 감사합니다.
  11. 안녕하세요, @다프레님, client의 인증서 확인 중 오류가 발생한 것으로 보입니다. 인증서 관련된 설정을 먼저 살펴보시는 것이 필요해보입니다. 이용하시다가 궁금하신 부분이 있다면, 문의하기를 통해 문의주시면 자세히 안내드릴 수 있도록 하겠습니다. 감사합니다.
  12. 안녕하세요, @pennypost님, 플레이그라운드에서 작업을 저장하시면, 테스트 앱 발행이 가능합니다. 테스트 앱 코드에서 API-KEY를 확인하실 수 있으며, 클로바 스튜디오에서 테스트 앱은 모두 동일한 키를 사용합니다. 테스트앱 생성 안내 https://guide.ncloud-docs.com/docs/clovastudio-playground01 API 가이드 https://api.ncloud-docs.com/docs/ai-naver-clovastudio-summary 감사합니다.
  13. 안녕하세요, @pennypost님, 특정 정의된 형식을 고정으로 출력하려는 것이 맞을까요? 아래와 같은 방법이 있을 것 같습니다. Temperature는 0.03 정도로 낮추는 것과, repetition penalty도 2.0 정도로 조정하는 것이 도움이 될 수 있습니다. 무엇보다 시스템 프롬프트에 정확하게 작업을 명시하는 것이 효과적입니다. 예를들어, "- 특정 범위 내에서만 출력합니다. {긍정, 부정, 중립}" 원하시는 형식에 대한 예제를 프롬프트에 입력하는 것이 좋습니다. 감사합니다.
  14. 안녕하세요, @pennypost님, 이전 문의글에 답변을 추가하였습니다. 플레이그라운드에서 프롬프트를 활용하여 손쉽게 구현이 가능하십니다. 플레이그라운드를 통한 대체 기능 구현 방법 네이버 클라우드 플랫폼 콘솔에서 Services > AI Services > CLOVA Studio 메뉴를 차례대로 클릭해 주십시오. My Product 메뉴를 클릭한 후 [CLOVA Studio 바로가기] 버튼을 클릭해 주십시오. 플레이그라운드 메뉴를 클릭해 주십시오. 화면 왼쪽의 시스템 영역에 모델이 수행할 작업에 대한 구체적인 지시문 또는 예제를 입력해주십시오. [실행] 버튼을 클릭해 주십시오. 감사합니다.
×
×
  • Create New...