_
Spring AI란 (feat. 개발자에게 필요한 AI 기본 개념)

Spring AI란 (feat. 개발자에게 필요한 AI 기본 개념)

2024-02-21

대 AI 시대 Java 개발자는 이대로 죽을 것인가.

TL;DR

  • Spring에서 AI에 관한 새로운 패러다임의 대단한 것을 내놓은 것이 아님
  • GPT와 같은 여러 모델들을 Spring에 통합하기 위한 추상화, 구현 제공 (LangChain, LlamaIndex으로 가능한 것들, 하지만 이들의 직접적인 포팅은 아님)
  • 여러 서비스 중인 모델들에 대한 Client 구현을 탑재함 (Convention over Configuration 에 맞춰 특정 모델에 대한 Starter 의존성 추가 후 properties로 API key 세팅, aiClient에 대한 구현 빈 주입 후 바로 사용)
  • 임베딩 및 벡터 영속화, RAG 까지 추상화되어 있음

누구에게 필요한가

찍먹할 주니어, 취미 개발자들은 크게 와닿지 않을 수도 있지만 프로젝트를 설계하고 운영하는 사람 입장에서는 기술의 파편화가 생각보다 많은 비용을 초래한다는 것을 알고 있다. AI를 발라야 하는데 파이썬이라는 다른 기술을 기술 풀에 포함시키는 것은 단순히 프로젝트 하나가 다른 언어로 들어온 것 이상의 많은 끔찍한 비용을 초래한다는 것을 알고있기 때문에 본인 같은 꼰대 시니어들은 웬만하면 같은 기술 풀 내에서 요구사항의 구현이 가능하다면 그렇게 하려고 한다. 이런 사람에게는 앞으로 염두에 둘만한 프로젝트가 아닌가 싶다. 사실 간단한 요구사항은 AWS Rekognition이나 Bedrock등을 이용해서 건건이 해결이 가능하다.
하지만 점점 요구사항의 깊이가 깊어지거나 개별 트레이닝을 시켜야할 대상이 많아지면 쌩 Saas API 호출만 옵션에 두기에는 무리가 올 시점이 얼마 남지 않은 것 같다. 이런 요구사항에 대해 파이썬을 도입하고 LangChain 등 잘 알려진 수단을 사용하면 해결 가능하다.

파이썬이 싫어요

파이썬을 좋아하는 사람들도 많이 있지만 본인은 파이썬에는 왠지 정이 가지 않는다. 왜일까...
아무튼 본인은 자바(Java) 개발자면서 몇 안 되는 자바라는 언어를 그럭저럭 만족하면서 사용하는 사람이다.
하지만 Java, Spring 원툴로는 커버가 안 되는 부분들이 꽤나 존재한다. 상대적 자바 성애자로서 자바 개발자들의 설움을 달래고자 본인이 개발해서 사용 중인 여러 도구들을 공개하기도 하고 팁들을 공유하고자 하는데 몸이 한 개라서 쉽지가 않다.
Python Hunters – Season 3, Episode 4출처 : rottentomatoes.com

Python Hater && 폐쇄적 Java, Spring 애호가를 위한 Spring AI

Spring AI 프로젝트가 무엇인지 궁금해서 들어온 당신은 본인이 무슨 쓸데없는 말을 하고 있나 싶겠지만 이 프로젝트의 기저에는 위에서 말한 본인의 생각과 비슷한 결이 있다.

대 AI시대에 자바 개발자 서러워서 살겠나. 찍먹하고 싶긴 하지만 억지로 근본 없는 파이썬을 익히느니 차라리 하스켈을 공부하겠소.

위의 표현은 웃자고 한 표현이고 각 언어는 각각이 가지는 모토가 있다. 안타까운 점은 Java 페티시가 있는 개발자들의 성향은 파이썬의 지향점에 공감하지 못하는 사람들이 많다는 것이다.

각설하고 Spring AI를 한마디로 정의하면

AI... 생각보다 메인스트림으로 오고 있네? 걍 두면 X 되겠다. Python LangChain, LlamaIndex 와 같은 기능을 Spring에서 사용할 수 있도록 추상화 시키자

정도가 될 것이다.

그래서 할 수 있는 게 뭔데?

그래서 이걸 해볼까 말까 하는 고민에서는 이걸로 뭘 할 수 있는지를 알아야 선택을 할 텐데 글을 작성하고 있는 이 시점에서 한글로 정리된 게 없어서 직접 알아보는 와중에 정리를 해놓을까 한다. 찍먹 여부를 판단할 때 도움이 되길 바란다.

Spring에서도 LangChain, LlamaIndex가 하는 일을 할 수 있게 하자.
Spring 특 : Echo System에 강결합 시켜서 Spring에서 못 도망가게 하는 요소를 하나 더 만들자.

AI가 유난이니까 찍먹 해보려고 들어온 당신이기 때문에 기본 용어들에 대한 사전지식이 부족할 것이기 때문에 좀 더 부연해 보겠다.

LangChain?

휴먼이 브라우저를 통해서 타이핑을 하고 그 결과를 받아서 그림을 결과로 획득했다고 하면 그 그림을 다시 그림 관련 AI서비스에 업로드를 하고 그 결과를 다시 글로 정리하려면 다시 관련 서비스에 업로드를 하는 과정들이 필요할텐데 이를 프로그래밍으로 해결할 수 있게 한 것이 LangChain이다.
역시 파이썬유저는 LangChain을 사용하면 되지만 이 글을 읽는 당신은 Spring 페티시가 매우 강력해서 이미 Spring이 아니면 무엇도 할수 없는 사람이 되어버렸지 않은가? Spring AI는 LangChain,LlamaIndex을 참고했지만 직접적인 포팅버전은 아니다.

개발 시 알아야 할 AI 기본 개념

더해서 찍먹을 하기 위해서 알아야 될 사전 지식들을 맛보기로 떠먹여 주겠다. 앞으로 이야기할 내용은 Spring AI 레퍼런스 에서 소개하고 정의하고 있는 AI Concept의 내용이다. 물론 "AI 기본개념"이라고 한다면 너무나도 광범위하지만 Spring AI에서 다루고 있는 컨셉을 기준으로 설명하며 이는 Spring에만 국한된 게 아니라 LLM을 이용한 개발 시 알아야 할 기본 소양과 크게 다르지 않을 것이다. 정확하고 자세한 내용은 생략하고 직관적인 표현들로 시냅스에 때려 박아주기 위해서 불가피하게 정확한 정의 기술과는 동떨어진 표현들이 있을 수 있으니 자세한 내용은 레퍼런스를 직접 읽어보면 될 것이다.

모델

미리 설계된 알고리즘
GPT4, GPT3.5, Stable Diffusion 같은 것들을 의미하며 각 모델은 특화된 영역들이 있다. (더해서 벡터 개념이 있는데 아래의 임베딩의 내용이다.)

프롬프트 (Prompts)

AI 모델에게 목표하는 Output을 내도록 안내하는 Input
Chat GPT만 사용해 봤다면 단순하게 휴먼의 언어로 내리는 명령 정도로 인식하게 되겠지만 LLM이 발벚하면서 Placeholder, 역할 부여 등 특정 목표를 달성하도록 하는 일반적인 패턴들이 반영되게 되었고 이런 것들을 현실화시키는 것이 프롬프트 엔지니어링이다.

프롬프트 템플릿 (Prompt Templates)

입력하게 될 텍스트의 템플릿
PathVariable을 사용했을 때처럼 요구사항에 따라서 적절하게 Placeholder를 설정할 수 있다.

PromptTemplate promptTemplate = new PromptTemplate("Tell me a {adjective} joke about {topic}");

Prompt prompt = promptTemplate.create(Map.of("adjective", adjective, "topic", topic));

return chatClient.call(prompt).getResult();

프롬프트 엔지니어링 분야 또한 도메인별로 여러 가지 노하우가 있겠지만 개념 자체를 이해하기 위해서 단적인 예를 들어 보겠다.
ChatGPT에 리눅스 명령어를 물어보면 명령어만 알면 되는데 여러 미사여구로 글을 시작한다.
바로 명령어만 획득하고 싶다면 프롬프트에 미리 "설명 하지 말고 명령어만 보여줘"라고 하면 이것 또한 프롬프트 엔지니어링이다. 이는 한 휴먼이 필요에 의해 행동한 것이지만 특정 요구사항에 맞는 소프트웨어를 설계하는 상황이라면 항상 같은 값을 반환하지는 않는 LLM의 한계를 어느 정도 프롬프트 엔지니어링을 통해서 정제해서 극복할 수 있다. 이렇게 정제하지 않으면 필요한 건 명령어 결과이지만 매번 미사여구가 들어오면 다 돈 낭비인 것이다.
프롬프트 엔지니어링에 대한 간단한 예시를 보고 싶다면 openai에서 작성한 Best Practice 문서를 확인해 보자.

임베딩 (Embeddings)

텍스트를 벡터로 변환해서 모델이 언어를 처리할 수 있도록 하는 행위

LLM의 동작 방식을 억지로 간단히 표현하면

다음에 올 개연성이 높은 순서대로 단어를 나열한다.

정도가 될 텐데 사람이야 뒤에 따라올 말을 어떻게 도출하는지는 직접 고찰해 보기를 바라고 LLM 모델은 각 단어별로 어떤 단어가 뒤에 와야 하는지를 계산기다 보니까 수학적으로 도출하는데 이때 이용되는 것이 벡터 저장 공간이다. 이미 학습된 모델을 사용해서 커스텀 훈련을 시킨 내용들을 반영하고자 할 때 PostgreSQL을 이용한 pgvector 같은 벡터 저장공간에 모델이 사용할 수 있도록 저장을 시킬 수 있다.
pgvector는 영속화 수단이라고 생각하면 되고 추상화된 저장 로직을 사용한 뒤 지원되는 여러 벡터 저장 수단을 선택적으로 사용할 수 있다.

토큰 (Tokens)

모델이 문장을 처리하기 위해 쪼갠 단위
token = $
실제 구현 시 모델이 한 번에 처리할 수 있는 토큰의 최댓값이 정해져 있기 때문에 이를 고려해서 적절히 설계할 필요가 있다.
물론 처리해야 할 토큰의 개수는 곧 비용이기 때문에 학문적 연구가 아닌 현생 개발자들에게 토큰은 항상 우선적으로 고려해야 할 사항이다.

출력 파싱 (Output Parsing)

JSON 상하차 전문 노가다 꾼들에게 AI 모델이 반환하는 텍스트는 당혹스럽기 그지없다.

그래서 Spring AI는 몇 가지 패턴의 OutputParser를 제공하는데 LLM이 반환하는 텍스트의 형태는 천차만별일 텐데 어떻게 그 많은 패턴을 전부 JSON 포맷으로 파싱을 할 수 있을까. 전문가의 노하우를, 코드를 직접 보면 알 수 있다.

다음은 BeanOutputParser의 실제 코드 일부이다.

	@Override
	public String getFormat() {
		String template = """
				Your response should be in JSON format.
				Do not include any explanations, only provide a RFC8259 compliant JSON response following this format without deviation.
				Do not include markdown code blocks in your response.
				Here is the JSON Schema instance your output must adhere to:
				```%s```
				""";
		return String.format(template, this.jsonSchema);
	}

말로만 듣던 감성 코딩이다. 커밋 로그를 보면 전문가의 향기를 볼 수 있는데 단호한 어조가 필요함을 알 수 있다.
감성 코딩은 단호한 어조로 해야 한다.
이게 뭔가 싶은 사람도 있겠지만 다시 생각해 보면 Input이 text고 그에 맞게 input을 준 것이다.

AI 모델에 데이터 가져오기 (Bringing Your Data to the AI model)

GPT의 'P'는 pre로 Pre-trained 즉 이미 학습된 모델이라는 뜻이다. 다들 알다시피 OpenAI의 Chat GPT가 학습한 내용은 모델에 따라서 시점이 정해져 있다. 최근의 내용은 학습을 못한 것인데 이를 극복하기 위한 여러 방법들이 있다.
파인튜닝과 프롬프트 스터핑(Prompt Stuffing) 인데 파인튜닝은 고전적인 방법으로 직접 모델을 돌릴 ML 전문가 양반들에게도 까다로운 방법이고 모델별 토큰 제약을 고려해서 잘라서 프롬프트에 끼워 넘기는 방식을 프롬프트 스터핑이라고 하고 Spring AI에서는 이를 위한 여러 솔루션을 사용할 수 있도록 추상화 해 놓았다.

검색 증강 생성(Retrieval Augmented Generation, RAG)

PDF 100개 정도 분량의 개념이 있다고 가정해 보자 앞서 이야기한 파인튜닝을 거치면 모델 자체를 조정하니까 PDF의 정보를 직접 반영하면 될 것인데 이걸 학습 레벨이 아닌 수준에서 적용한다고 하면 이 글을 보고 있을 필요는 없을 것 같다.
다른 방법으로는 프롬프트에 PDF 100개의 text를 추출해서 붙여 넣은 뒤 질문을 뒤에 이어서 써서 보내면 된다.
문제는 어마어마한 토큰이 소모되는 것도 있지만 모델별로 한 번에 소화 가능한 토큰의 최댓값이 있다는 것이다.
앞서 이야기한 임베딩을 통하면 PDF 100개를 OpenAI API로 임베딩 생성 요청을 보내면(토큰 사이즈 제약등을 극복하기 위한 spliter, loader등을 SpringAI에서 추상화된 형태로 제공한다.) 반환 값으로 벡터가 올 것이고 이를 벡터 저장소에 저장하면 로컬에 임베딩된 결과를 가지고 있을 수 있게 된다.
이 과정을 거치고 나면 이제 질문을 던질 때 바로 OpenAI API로 쏘는 게 아니고 로컬 벡터 저장소에 먼저 질의를 한 뒤에 반환된 값 + 질문 내용을 쏘게 된다. 이렇게 되면 PDF 100개의 TEXT 전체를 보내는 게 아니라 정제된 결괏값을 보내는 것이기 때문에 토큰이 훨씬 줄어들며 이런 방식이 프롬프트 스터핑이라고 보면 될 것 같다.

AI 응답 평가 (Evaluating AI responses)

앞서 언급한 내용들로 구현을 했는데, 어느 정도 Input에 대한 Output이 예상 가능한 범주 내에서 반환되어야 소프트웨어로서의 기능을 한다고 볼 수 있을 것이다. LLM이 아니라면 항상 기대한 값이 반환되겠지만 그렇지 않기 때문에 이를 더 신경 써야 한다. Spring AI에서는 Junit에서 사용될 수 있도록 프롬프트 형태로 응답을 평가하는 기본적인 검증 예제를 몇 가지 제공한다.

Tag


Category


Related Posts

Nginx 무료 SSL/TLS 인증서 적용, 갱신 퀵가이드

Nginx 무료 SSL/TLS 인증서 적용, 갱신 퀵가이드

Nginx SSL/TLS 적용하기 퀵 가이드 이 글에서는 Cerbot, Let's Encrypt를 사용해서 인증서를 발급받고 nginx에 적용, 지속적 자동 갱신을 시키는 과정을 확인한다. 시간이 없는 상황이거나 한국인이면 급할테니 깊이보다 주요개념만 핥아보자. 설정만 보려면 Nginx conf를 확인 해봅시다. Let's En

2023-04-17 Development Nginx
let's encrypt. certbot 오류로 인해 발급제한이 걸려 인증서 갱신을 못할 때 (내 사이트를 살)

let's encrypt. certbot 오류로 인해 발급제한이 걸려 인증서 갱신을 못할 때 (내 사이트를 살)

let's encrypt. certbot 오류로 인해 발급제한이 걸려 인증서 갱신을 못할 때 로그파일과 임시 파일로 해결 할 수도 있다.

2024-07-04 Development
현장에서 꼭 필요한 애자일 핵심 개념 (K-애자일은 그만! 대표님, 팀장님 제발 이것 좀 보세요ㅠㅠ)

현장에서 꼭 필요한 애자일 핵심 개념 (K-애자일은 그만! 대표님, 팀장님 제발 이것 좀 보세요ㅠㅠ)

현장에서 꼭 필요한 애자일 핵심 개념, 한국에서 자행되는 K-애자일의 폭력성과 문제점을 진단하고 해결책을 제시한다.