const-tommy.dev
기록을 불러오는 중입니다
AI 프로젝트를 시작하면서 가장 거슬렸던 건 용어였다. LLM API, RAG, Tool Calling, Function Calling, Agent... 다들 무슨 대단한 기술처럼 말하는데, 막상 들여다보니 본질은 하나였다. "남이 만든 모델한테 프롬프트 보내고 답 받기." 그게 전부다.
이 글은 그 거품을 한 번 걷어내고, 각 용어가 실제로 가리키는 기술적 차이가 뭔지 코드로 정리한 기록이다.

먼저 짚고 갈 것. LLM API, RAG, Tool Calling, Agent — 이것들의 밑바닥은 전부 동일하다. 모델에 텍스트를 보내고 텍스트를 받는다. 차이는 그 호출을 어떻게 구성하고, 몇 번 하고, 중간에 뭘 끼우느냐일 뿐이다.
요리에 비유하면 다 "불로 가열하기"인데, 굽기·볶기·삶기로 이름이 나뉜 것과 같다. 불을 쓰는 건 똑같다.
프롬프트 보내고 답 받기. 1회 호출로 끝.
import { generateText } from "ai";
import { google } from "@ai-sdk/google";
const { text } = await generateText({
model: google("gemini-2.5-flash"),
prompt: "여름에 시원한 셋업 추천해줘",
});
// 모델이 학습한 일반 지식으로만 답한다나머지는 전부 여기에 살을 붙인 것이다.
거창한 이름(Retrieval Augmented Generation)이지만, 실체는 호출하기 전에 관련 데이터를 찾아서 프롬프트에 넣는 것이다.
// (1) 먼저 DB에서 상품을 가져온다 (Retrieval)
const { data: products } = await supabase
.from("products")
.select("name, category, price, stock");
const productList = products
.map((p) => `- ${p.name} (${
추가된 건 딱 하나. 호출 전에 데이터를 찾아 프롬프트에 넣는 단계. 이게 RAG의 전부다.
여기서 한 가지 깨달음. 모델은 "똑똑하지만 우리 가게는 처음인 점원"이다. 한국어도 잘하고 응대도 잘하지만, 우리 가게에 뭐가 있는지는 모른다. 그래서 매번 "오늘 재고는 이거야"라고 목록을 쥐여주는 게 RAG다. 사실(fact)은 DB가 주고, 이해와 표현은 모델이 한다.
이건 살짝 진짜 다른 게 있다. 위의 1, 2는 모델이 텍스트만 뱉었다. Tool Calling은 모델이 "이 함수 좀 실행해줘"라고 요청할 수 있게 한다.
import { tool } from "ai";
import { z } from "zod";
const { text } = await generateText({
model: google("gemini-2.5-flash"),
prompt: "린넨 셋업 재고 있어?",
tools: {
checkInventory: tool({
description
흐름은 이렇다.
손님 "린넨 셋업 재고 있어?"
│
▼
모델 (텍스트 대신) checkInventory("린넨 셋업") 호출 요청
│
▼
서버 DB 조회 → 재고 0 → 결과를 모델에 전달
│
▼
모델 "린넨 셋업은 지금 품절이에요"왜 필요한가? RAG처럼 모든 상품을 프롬프트에 다 넣을 수 없을 때(상품이 1만 개라면?), 모델이 필요한 것만 그때그때 조회하게 한다. 그리고 모델이 멋대로 지어내는 대신 실제 함수로 사실을 확인하니 환각이 줄어든다.
Function Calling과 Tool Calling은 회사마다 부르는 이름만 다를 뿐 같은 것이다. (Gemini는 Function Calling, Claude는 Tool Use)
Agent도 마법이 아니다. 호출을 한 번이 아니라, 결과를 보고 다음 행동을 정하며 여러 번 반복하는 것이다.
// 1단계: 대화 로그에서 손님 의도 분류
const intents = await generateText({
model: google("gemini-2.5-flash"),
system: "대화 로그에서 손님들의 구매 의도를 분류해 JSON으로 반환해라.",
prompt: conversationLogs,
});
// 2단계: 1단계 결과로 → 미충족 수요 추출
const unmetDemands = await generateText({
model: google("gemini-2.5-flash"),
4번의 호출이 '판단 → 추출 → 검증 → 제안'으로 이어진다. 단발 호출이 아니라 결과를 보고 다음 단계를 밟는 흐름. 이게 Agent다.
여기서 중요한 점. "스스로 판단한다"고 하지만, 실제론 내가 단계를 설계하고 각 단계의 출력을 다음 입력으로 넘기는 루프를 코드로 짠다. 모델은 각 단계의 판단을 맡고, 흐름의 뼈대는 개발자가 만든다.
| 용어 | 코드상 추가되는 것 |
|---|---|
| LLM API | generateText({ prompt }) — 이게 전부 |
| RAG | 호출 전에 DB 조회 → 프롬프트에 데이터 삽입 |
| Tool Calling | tools: {} 등록 + 함수 실행 왕복 |
| Agent | 위 호출들을 여러 번, 앞 결과를 뒤 입력으로 연결 |
밑바닥은 전부 generateText 한 줄. 살을 어떻게 붙이느냐의 차이일 뿐이다.
"전부 그냥 API 호출 아니냐"는 직관은 맞다. 거품을 안 믿는다는 점에서 오히려 건강하다.
다만 누군가 "Agent 만들어봤냐"고 물을 때, 그건 "API 호출할 줄 아냐"가 아니다. "단발 호출을 넘어, 결과를 보고 다음을 판단하며 여러 단계를 엮는 흐름을 설계할 줄 아냐" 를 묻는 것이다. 진짜 어려운 건 단계를 어떻게 나누고, 중간 결과를 어떻게 검증하고, 환각을 어떻게 막느냐다.
용어의 거품은 걷어내되, "단발 호출 vs 흐름 설계"의 차이는 분명히 알고 말하는 것. 그게 핵심이라고 생각한다.