AI 개념

AI 개념 (AI Concepts)

Spring AI를 이해하려면 그 밑바탕이 되는 AI 개념부터 알아야 해요. 모델·프롬프트·임베딩·토큰·구조화된 출력처럼 이 프레임워크가 추상화하고 있는 핵심 아이디어를 먼저 잡아 두면, 뒤에서 나오는 API가 왜 그렇게 생겼는지 훨씬 자연스럽게 와 닿아요.

출처: 공식문서

모델 (Models)

AI 모델은 정보를 처리하고 생성하도록 설계된 알고리즘으로, 사람의 인지 기능을 흉내 내는 경우가 많아요. 방대한 데이터셋에서 패턴과 통찰을 학습한 모델은 예측, 텍스트·이미지 생성, 그 외 다양한 출력을 만들어내며 여러 산업의 애플리케이션을 향상시켜요.

AI 모델은 매우 다양한 종류가 있고, 각각 특정 사용 사례에 맞춰져 있어요. ChatGPT와 그 생성형 AI 기능이 텍스트 입출력으로 사용자들을 사로잡았지만, 많은 모델과 회사가 다양한 입력·출력을 제공하고 있어요. ChatGPT 이전에는 Midjourney나 Stable Diffusion 같은 텍스트-이미지 생성 모델에 관심이 쏠렸죠.

아래 표는 여러 모델을 입력·출력 유형별로 분류한 거예요:

[Model types 이미지 — spring-ai-concepts-model-types.jpg]

Spring AI는 현재 언어·이미지·오디오로 입출력되는 모델을 지원해요. 위 표의 마지막 행(텍스트를 입력받아 숫자를 출력)은 흔히 "텍스트 임베딩"이라 불리며, AI 모델 내부의 데이터 구조를 나타내요. Spring AI는 더 고급 사용 사례를 위해 임베딩을 지원해요.

GPT 같은 모델이 특별한 이유는 사전 학습(pre-trained)되어 있다는 점이에요 — GPT의 "P"가 바로 Pre-trained를 뜻하죠. 이 사전 학습 덕분에 AI는 광범위한 머신러닝·모델 훈련 배경 없이도 쓸 수 있는 범용 개발 도구가 됐어요.

프롬프트 (Prompts)

프롬프트는 AI 모델이 특정 출력을 만들도록 이끄는 언어 기반 입력의 토대예요. ChatGPT에 익숙한 사람에게 프롬프트는 그저 대화창에 입력해 API로 보내는 텍스트처럼 보일 수 있어요. 하지만 실은 그보다 훨씬 많은 걸 담고 있죠. 많은 AI 모델에서 프롬프트의 텍스트는 단순한 문자열이 아니에요.

ChatGPT의 API는 프롬프트 안에 여러 텍스트 입력을 가지며, 각 텍스트 입력에는 역할(role)이 부여돼요. 예를 들어 모델에게 어떻게 행동해야 하는지 알려주고 상호작용의 맥락을 설정하는 시스템(system) 역할이 있어요. 그리고 보통 사용자의 입력인 유저(user) 역할도 있죠.

효과적인 프롬프트를 만드는 건 예술이자 과학이에요. ChatGPT는 인간과의 대화를 위해 설계됐어요. 이는 SQL로 "질문"하는 것과는 상당히 다른 접근이죠. AI 모델과는 마치 다른 사람과 대화하듯 의사소통해야 해요.

이런 상호작용 방식이 중요하다 보니 "프롬프트 엔지니어링(Prompt Engineering)"이라는 별도 분야까지 생겼어요. 프롬프트의 효과를 높이는 기법도 계속 늘고 있죠. 프롬프트를 만드는 데 시간을 투자하면 결과물이 크게 좋아져요.

프롬프트를 공유하는 건 공동체적 관행이 됐고, 이 주제에 대한 활발한 학술 연구도 이뤄지고 있어요. 효과적인 프롬프트를 만드는 게 얼마나 비직관적일 수 있는지 보여주는 예로, https://arxiv.org/abs/2205.11916[한 최근 연구 논문]은 가장 효과적인 프롬프트 중 하나가 "Take a deep breath and work on this step by step."라는 문구로 시작한다는 걸 발견했어요. 언어가 왜 그렇게 중요한지 짐작이 가지 않나요? ChatGPT 3.5 같은 이전 세대 기술, 그보다 더한 새 버전을 어떻게 가장 효과적으로 쓸지 아직 완전히 이해하지 못하고 있는 게 현실이에요.

프롬프트 템플릿

효과적인 프롬프트를 만들려면 요청의 맥락을 세우고, 요청의 일부를 사용자 입력에 따라 달라지는 값으로 치환해야 해요.

이 과정은 전통적인 텍스트 기반 템플릿 엔진을 프롬프트 생성·관리에 사용해요. Spring AI는 이 목적으로 OSS 라이브러리인 https://www.stringtemplate.org/[StringTemplate]을 써요.

예를 들어 이런 간단한 프롬프트 템플릿을 생각해 볼게요:

Tell me a {adjective} joke about {content}.

Spring AI에서 프롬프트 템플릿은 Spring MVC 아키텍처의 "뷰(View)"에 비유할 수 있어요. 보통 java.util.Map인 모델 객체가 템플릿의 플레이스홀더를 채우는 데 제공돼요. "렌더링된" 문자열이 AI 모델에 주어지는 프롬프트의 내용이 되는 거죠.

모델로 보내는 프롬프트의 구체적인 데이터 형식은 꽤 다양해요. 처음엔 단순한 문자열로 시작했지만, 프롬프트는 여러 메시지를 포함하는 형태로 진화했고, 각 메시지의 각 문자열은 모델에게 서로 다른 역할을 나타내요.

임베딩 (Embeddings)

임베딩은 텍스트·이미지·비디오의 숫자 표현으로, 입력 간의 관계를 포착해요.

임베딩은 텍스트·이미지·비디오를 벡터라고 부르는 부동소수점 배열로 변환하는 방식으로 동작해요. 이 벡터는 텍스트·이미지·비디오의 의미를 담도록 설계됐죠. 임베딩 배열의 길이를 벡터의 차원(dimensionality)이라 해요.

두 텍스트의 벡터 표현 사이의 숫자 거리를 계산하면, 애플리케이션은 임베딩 벡터를 만든 대상 간의 유사도를 판단할 수 있어요.

[Embeddings 이미지 — spring-ai-embeddings.jpg]

AI를 탐색하는 Java 개발자라면 이런 벡터 표현 뒤의 복잡한 수학 이론이나 특정 구현까지 이해할 필요는 없어요. 특히 애플리케이션에 AI 기능을 통합할 때는 이 벡터들이 AI 시스템에서 어떤 역할을 하는지 기본적인 이해만으로 충분해요.

임베딩은 검색 증강 생성(RAG) 패턴 같은 실용 애플리케이션에서 특히 관련이 깊어요. 임베딩은 데이터를 의미 공간(semantic space)의 점으로 표현할 수 있게 해주는데, 이는 유클리드 기하의 2차원 공간과 비슷하지만 더 높은 차원이에요. 평면 위의 점들이 좌표에 따라 가깝거나 멀어지듯, 의미 공간에서 점들의 근접함은 의미의 유사성을 반영해요. 비슷한 주제의 문장들은 이 다차원 공간에서 서로 가깝게 위치하죠. 이런 근접성 덕분에 텍스트 분류, 의미 검색, 심지어 상품 추천 같은 작업에서 AI가 관련 개념을 구분하고 묶을 수 있어요. 이 의미 공간을 하나의 벡터로 생각하면 돼요.

토큰 (Tokens)

토큰은 AI 모델이 동작하는 방식의 구성 요소예요. 입력 시 모델은 단어를 토큰으로 변환하고, 출력 시 토큰을 다시 단어로 변환해요.

영어에서 한 토큰은 대략 단어의 75%에 해당해요. 참고로 셰익스피어의 전 작품(약 90만 단어)은 약 120만 토큰으로 환산돼요.

[Tokens 이미지 — spring-ai-concepts-tokens.png]

그보다 더 중요한 건, 토큰 = 돈이라는 점이에요. 호스팅 AI 모델에서는 사용한 토큰 수로 비용이 정해져요. 입력과 출력 모두 전체 토큰 수에 포함되죠.

또 모델에는 토큰 한도가 있어, 한 번의 API 호출로 처리할 수 있는 텍스트 양이 제한돼요. 이 임계값을 흔히 "컨텍스트 윈도우(context window)"라고 불러요. 모델은 이 한도를 초과하는 텍스트는 처리하지 않아요.

예를 들어 ChatGPT3는 4K 토큰 한도이고, GPT4는 8K·16K·32K처럼 다양한 옵션을 제공해요. Anthropic의 Claude AI 모델은 100K 토큰 한도이며, Meta의 최근 연구는 1M 토큰 한도 모델을 내놓았어요.

GPT4로 셰익스피어의 전 작품을 요약하려면, 데이터를 잘게 나눠 모델의 컨텍스트 윈도우 한도 안에 넣는 소프트웨어 엔지니어링 전략을 짜야 해요. Spring AI 프로젝트가 바로 이 작업을 도와줘요.

구조화된 출력 (Structured Output)

AI 모델의 출력은 JSON으로 달라고 부탁해도 전통적으로 java.lang.String로 나와요. 올바른 JSON일 수는 있어도 JSON 데이터 구조가 아니라 그냥 문자열인 거죠. 게다가 프롬프트에 "JSON으로"라고 요청하는 것도 100% 정확하지 않아요.

이런 까다로움 때문에, 의도한 출력을 만들 프롬프트를 작성한 뒤 그 결과로 나온 단순 문자열을 애플리케이션 통합에 쓸 수 있는 데이터 구조로 변환하는 전문 분야가 생겼어요.

[Structured Output Converter Architecture 이미지 — structured-output-architecture.jpg]

구조적 출력 변환은 정교하게 설계된 프롬프트를 사용하며, 원하는 형식을 얻기 위해 종종 모델과 여러 번 상호작용해야 해요.

AI 모델에 나의 데이터·API 연결하기

모델이 학습하지 않은 정보를 어떻게 AI 모델에 제공할 수 있을까요?

참고로 GPT 3.5/4.0 데이터셋은 2021년 9월까지만 확장돼 있어요. 그래서 그 이후의 지식이 필요한 질문에는 모델이 답을 모른다고 말하죠. 재미있는 사실 하나는, 이 데이터셋이 약 650GB라는 거예요.

나의 데이터를 모델에 통합하는 데는 세 가지 기법이 있어요:

  • Fine Tuning(파인튜닝): 전통적인 머신러닝 기법으로, 모델을 조정하고 내부 가중치를 바꿔요. 하지만 머신러닝 전문가에게도 까다롭고, GPT 같은 모델은 크기가 커서 자원을 극도로 많이 소모해요. 게다가 일부 모델은 이 옵션을 제공하지 않기도 해요.

  • Prompt Stuffing(프롬프트 채우기): 더 실용적인 대안으로, 나의 데이터를 모델에 제공하는 프롬프트 안에 넣는 거예요. 모델의 토큰 한도를 고려해 관련 데이터를 모델의 컨텍스트 윈도우 안에 넣는 기법이 필요하죠. 이 접근을 흔히 "프롬프트 채우기."라고 불러요. Spring AI 라이브러리는 "프롬프트 채우기" 기법, 다른 말로 검색 증강 생성(RAG)을 바탕으로 한 솔루션을 구현하도록 도와줘요.

[Prompt stuffing 이미지 — spring-ai-prompt-stuffing.jpg]

  • Tool Calling: 대규모 언어 모델을 외부 시스템의 API에 연결하는 도구(사용자 정의 서비스)를 등록할 수 있게 해주는 기법이에요. Spring AI는 tool calling을 지원하기 위해 작성할 코드를 크게 단순화해 줘요.

검색 증강 생성 (Retrieval Augmented Generation)

정확한 AI 모델 응답을 위해 관련 데이터를 프롬프트에 포함하는 문제를 해결하기 위해 RAG(Retrieval Augmented Generation)라는 기법이 등장했어요.

이 접근은 배치 처리 스타일의 프로그래밍 모델을 사용해서, 문서의 비정형 데이터를 읽고 변환한 뒤 벡터 데이터베이스에 기록해요. 높은 수준에서 보면 이건 ETL(Extract, Transform and Load, 추출·변환·적재) 파이프라인이에요. 벡터 데이터베이스는 RAG 기법의 검색 부분에 사용되죠.

비정형 데이터를 벡터 데이터베이스에 적재할 때 가장 중요한 변환 중 하나는 원본 문서를 더 작은 조각으로 나누는 거예요. 원본 문서를 더 작은 조각으로 쪼개는 절차에는 두 가지 중요한 단계가 있어요:

  1. 콘텐츠의 의미 경계를 보존하면서 문서를 부분으로 나눈다. 예를 들어 문단과 표가 있는 문서라면 문단이나 표의 중간에서 문서를 나누지 않아야 해요. 코드라면 메서드 구현의 중간에서 코드를 나누지 않아야 하죠.
  2. 문서의 부분을 다시 AI 모델의 토큰 한도의 작은 비율 크기의 조각으로 나눈다.

RAG의 다음 단계는 사용자 입력을 처리하는 거예요. 사용자의 질문을 AI 모델이 답할 때, 질문과 그와 "유사한" 문서 조각들을 AI 모델로 보내는 프롬프트에 함께 넣어요. 이것이 벡터 데이터베이스를 쓰는 이유예요. 유사한 콘텐츠를 찾는 데 아주 뛰어나기 때문이죠.

[Spring AI RAG 이미지 — spring-ai-rag.jpg]

  • ETL Pipeline은 데이터 소스에서 데이터를 추출해 구조화된 벡터 스토어에 저장하는 흐름을 오케스트레이션하는 방법을 알려줘요. 데이터가 AI 모델에 전달될 때 검색에 최적화된 형식으로 준비되도록 보장하죠.
  • ChatClient - RAGQuestionAnswerAdvisor를 사용해 애플리케이션에서 RAG 기능을 활성화하는 방법을 다뤄요.

도구 호출 (Tool Calling)

대규모 언어 모델(LLM)은 학습 후 동결돼서 지식이 낡고, 외부 데이터에 접근하거나 수정할 수 없어요.

Tool Calling 메커니즘은 이런 단점을 해결해요. 나만의 서비스를 도구로 등록해 대규모 언어 모델을 외부 시스템의 API에 연결할 수 있게 해주죠. 이 시스템은 LLM에 실시간 데이터를 제공하고 대신 데이터 처리 작업을 수행할 수 있어요.

Spring AI는 도구 호출을 지원하기 위해 작성할 코드를 크게 단순화해 줘요. 도구 호출 대화를 대신 처리해 주거든요. @Tool 애노테이션이 붙은 메서드로 도구를 제공하고, 프롬프트 옵션에 넣어 모델이 사용할 수 있게 하면 돼요. 한 프롬프트에서 여러 도구를 정의하거나 참조할 수도 있어요.

[The main sequence of actions for tool calling 이미지 — tools/tool-calling-01.jpg]

  1. 도구를 모델에 제공하고 싶으면 그 정의를 채팅 요청에 포함시켜요. 각 도구 정의는 이름, 설명, 입력 파라미터의 스키마로 구성돼요.
  2. 모델이 도구를 호출하기로 결정하면 정의된 스키마에 맞춰 도구 이름과 입력 파라미터가 담긴 응답을 보내요.
  3. 애플리케이션은 도구 이름을 이용해 도구를 식별하고, 제공된 입력 파라미터로 도구를 실행하는 책임을 져요.
  4. 도구 호출 결과는 애플리케이션이 처리해요.
  5. 애플리케이션은 도구 호출 결과를 모델로 다시 보내요.
  6. 모델은 도구 호출 결과를 추가 맥락으로 사용해 최종 응답을 생성해요.

이 기능을 다양한 AI 모델에서 어떻게 쓰는지에 대한 자세한 내용은 Tool Calling 문서를 따라가 보세요.

AI 응답 평가하기 (Evaluating AI responses)

사용자 요청에 대한 AI 시스템의 출력을 효과적으로 평가하는 것은 최종 애플리케이션의 정확성과 유용성을 보장하는 데 매우 중요해요. 이를 위해 사전 학습된 모델 자체를 사용하는 몇 가지 기법이 등장하고 있어요.

이 평가 과정은 생성된 응답이 사용자의 의도와 쿼리의 맥락에 부합하는지 분석하는 일을 포함해요. 관련성(relevance), 일관성(coherence), 사실적 정확성(factual correctness) 같은 지표로 AI 생성 응답의 품질을 가늠하죠.

한 가지 접근은 사용자의 요청과 AI 모델의 응답을 모델에 함께 보여주고, 응답이 제공된 데이터와 일치하는지 물어보는 방식이에요.

또한 벡터 데이터베이스에 저장된 정보를 보조 데이터로 활용하면 평가 과정을 강화하고 응답 관련성을 판단하는 데 도움이 돼요.

Spring AI 프로젝트는 모델 응답을 평가하는 기본 전략에 접근할 수 있는 Evaluator API를 제공해요. 자세한 내용은 Evaluation Testing 문서를 참고하면 돼요.

더 알아보기 (Learn more)