ChatGPT AKKA 연계 활용편
ChatGPT OpenAPI 연동
다양한 프롬프트를 테스트해야하기때문에 ChatGPT(OpenAI) API를 이용하는 유닛테스트기를 먼저 생성
일반적인 응답속도가 30초 이상이여서 이대로는 서비스로 활용불가
프롬프트 유형화
- 유형별 인사이트를에 필요한 프롬프트 : 트렌드/제품품질/고객만족과 관련한 것들로 작성
프롬프트 데이터
프롬프트도 중요하지만, 프롬프트에 활용된 데이터에 따라 인사이트가 달라지며 시즌별로 최신을 반영해야합니다. 실시간일 필요는 없습니다.
어떠한 인사이트인경우 랭킹이 높은것보다 낮은것이 유용할수 있으며, 항상 1위를 차지하는 고정 단어일경우 제외 처리하는것이 유용할수도 있습니다.
가령 상담시스템에서 "상담" 이란 단어는 아무런 의미가 없을수 있습니다.
Messages = new List<ChatMessage>() { ChatMessage.FromSystem(metaData.system), ChatMessage.FromAssistant(metaData.assist), ChatMessage.FromUser(prompt), }
GTP API는 System/Assist/Prompt 3가지 유형으로 구분하여 메시지 작성이 가능하며
한꺼번에 질문을 할수도 있지만 다음과 구분하여 프롬프트를 작성하면 효율적이다.
구분없이 데이터+질문을 한꺼번에해도 GPT가 구분하여 답변을 할수 있지만 GPT가 이해할수 있게 이 맥락을 더 잘 만들어야한다.
- System : 다음은 당신에게 도움되는 OOO 자료이다.
- Asisst : Data~
- User : 이 자료를 활용하여 OOO 인사이트를 분석해주세요~
성능을 위한 인덱싱 전략
ChatGPT의 응답시간이 비교적 길기때문에~ API 이용중 GTP의 결과를 검색엔진화하고
사용자는 검색엔진을 통해 빠른 결과를 획득하는 하이브리드 전략입니다. 프롬프트가 어느정도 유형화가 되었을때 활용할수 있습니다.
물론 ChatGpt와 비슷한 수준에 자연어를 통한 사용자의도 파악하는 AI기술이 있다고 하면 더 높은 수준으로 검색 기술과 연동할수 있습니다.
CRUD 전략
- GPT의 분석을 실시간 제공이아닌~ 인덱스된 최근항목을 제공하고 요청이 있을시 GPT의 최신 정보가 추가되어 다음 요청시 준비된 데이터를 제공하는 전략
- 유용한 고정 프롬프트가 100개이내이고 최근 정보를 효율적으로 전달할때 활용가능 전략
- GPT의 응답값을 검색엔진에 업데이트~
UnitTest
var elkCacheData = await _searchService.FindGptReultByFilter(gPTMetaData, 20); GPTResponse gPTResponse = new GPTResponse(); if (elkCacheData.Count > 0) { gPTResponse.FromGPTResutEntity(elkCacheData[0]); gPTResponse.isCache = true; GPTResultEntityCmd gPTResultEntityCmd = new GPTResultEntityCmd() { command = "save", prompt = promptBuild.ToString(), gPTMetaData = gPTMetaData }; _actorBridge.Tell(gPTResultEntityCmd); } else { gPTResponse = await _gPTEngine.GetChatCompletions(promptBuild.ToString(), gPTMetaData); gPTResponse.isCache = false; } return gPTResponse;
- GPT에 분석의뢰~
- 유사데이터가 최근에 있다고하면
- 유사데이터를 우선제공
- 액터를 사용하여~ 새로운 분석을 시도하고 결과를 저장 ( 다음에 이용됨 )
- 유사데이터가 없다고하면
- GPT에 의뢰하고 응답을 이용
- 모든 응답은 다음시도때 빠른 응답을 위해 분석결과를 신규로 저장
- CHATGPT를 활용하는 동일한 기능을 하는 유닛에 엘라스틱과 하이브리드로 적동하여 응답속도 1000ms 이하로 기능제공
- 응답시간이 긴경우 부분결과를 중간에 업데이트하는 Stream기능을 OpenAPI에서 지원합니다. - 사용성 추가 스트리밍 전략
API화
- A : 요청시 ELK에 있는 데이터를 조회하여 빠르게 응답 ( 1초이내 )
- B : 요청시 GPT의 새로운 프롬프트를 저장 이벤트를 발생하여, GPT를 호출하고 그 결과를 저장하여 다음에 이용할수 있게 저장
이용된 Actor 기술및 소개
여기서 응답결과가 늦은 GPT를 호출하고 결과를 저장하기 위해 API 레벨에서 스레드를 생성하고 저장하는 방식을 이용하면 안되며
API의 라이프 사이클은 응답이 빠르게 완료된 시점 자원을 해제해야하기때문에 여기서 생성한 스레드에 대한 라이프를 책임질수 없으며 다양한 사이드 이펙트가 발생할수 있습니다.
액터는 AkkaSystem에 의해 백그라운드에서 ActorDispatch에 의해 이벤트가 발생할때 조건이 맞을때 작동을 하며 독립적으로 Task를 수행할수 있습니다.
namespace MorphemeApi.Actors { public class GptEntityActor : ReceiveActor { private readonly ILoggingAdapter logger = Context.GetLogger(); private readonly GPTEngine _gPTEngine; public GptEntityActor(GPTEngine gPTEngine) { logger.Info($"Create GraphEventActor:{Context.Self.Path.Name}"); _gPTEngine = gPTEngine; ReceiveAsync<GPTResultEntityCmd>(async message => { try { await _gPTEngine.SaveChatCompletions(message.prompt, message.gPTMetaData); } catch (Exception ex) { logger.Error(ex.Message); } }); } } }
Stream 연결
var serviceScopeFactory = new Mock<IServiceScopeFactory>(); serviceScopeFactory.Setup(x => x.CreateScope()).Returns(serviceScope.Object); IActorRef throttleWork = _actorSystem.ActorOf(Props.Create(() => new ThrottleActor(500, 30000))); GPTEngine gPTEngine = new GPTEngine(lightAkkaService, configuration); var gptEntityActor = _actorSystem.ActorOf(Props.Create(() => new GptEntityActor(gPTEngine)), "gptEntityActor"); //Stream 연결 : throttleWork -> fsmActor -> batchActor throttleWork.Tell(new SetTarget(gptEntityActor)); throttleWork.Tell(new GPTResultEntityCmd(){....});
Throttle(TPS제어) 에 연결하여 GPT를 호출하는 TPS를 제어할수도 있으며, 발생한 데이터를 이벤트별 저장이아닌 실시간에 가까운 데이터를 모아서 벌크처리도 가능하게 됩니다.
Akka Stream에 관련한 작동가능 코드는 다음 저장소에서 확인할수 있으며 자바/닷넷에서 동일한 컨셉으로 이용가능합니다.
- psmon/EventListenerAPI (github.com) - 닷넷버전
- java-labs/springweb/src/test/java/com/webnori/springweb/akka at master · psmon/java-labs (github.com) - 자바버전