
로컬 LLM을 웹 검색 데이터와 연결하는 방법
요약
로컬 LLM에 실시간 웹 검색 기능을 통합하여 최신 데이터에 기반한 답변을 생성하는 방법을 설명합니다. TypeScript를 사용하여 검색 쿼리를 생성하고, 검색 결과를 컨텍스트 윈도우에 맞게 관리하는 채팅 앱 구축 과정을 다룹니다.
핵심 포인트
- 로컬 LLM의 학습 데이터 차단 시점 한계 극복 방법 제시
- TypeScript를 활용한 실시간 웹 검색 연동 채팅 앱 구현
- 검색 결과가 컨텍스트 윈도우를 초과하지 않도록 관리하는 기술
- 모델이 직접 검색 쿼리를 생성하고 실행하는 에이전트 흐름 이해
이 블로그에서는 로컬 LLM (Large Language Model)에 실시간 웹 데이터에 대한 접근 권한을 부여하는 방법을 보여드립니다. 우리는 TypeScript로 작은 채팅 앱을 구축할 것입니다. 모델이 무엇을 검색할지 결정하면, 여러분의 코드가 검색을 실행하고, 모델은 최신 데이터를 바탕으로 답변을 작성하게 됩니다. 추론 (Inference)은 여러분의 하드웨어에서 실행되며, 로컬 환경을 벗어나는 유일한 것은 모델이 선택하여 발행한 검색 쿼리 (Search Query)뿐입니다.
이 블로그를 마칠 때쯤이면, 여러분은 전체 요청 흐름을 이해하게 될 것이고, 방대한 검색 응답이 컨텍스트 윈도우 (Context Window)를 초과하지 않도록 유지하는 방법을 알게 될 것이며, 자신의 로컬 모델을 대상으로 클론하여 실행할 수 있는 공개 저장소(Public Repository)를 갖게 될 것입니다.
LLM이 실시간 데이터에 접근할 수 없는 이유
모든 대규모 언어 모델 (LLM)은 학습 데이터 차단 시점 (Training Data Cutoff)에 의해 제한됩니다. 그 날짜 이후에 발생한 모든 일은 모델에게 보이지 않습니다. 이는 모델을 로컬에서 실행하든 호스팅된 API를 호출하든 마찬가지입니다.
클라우드 채팅 제품들은 대부분의 사용자에게 이 사실을 숨깁니다. ChatGPT, Claude, Gemini는 모델이 제품에 포함된 형태로 출시되며, 해당 제품에는 내장된 검색 도구 (Built-in Search Tool)가 포함되어 있습니다. 여러분이 ChatGPT에게 "지금 Nvidia의 주가는 얼마인가요?"라고 물으면, 모델이 스스로 답변하는 것이 아닙니다. 제품이 검색 도구를 호출하고, 그 결과를 모델에 다시 전달하면, 모델이 최신 정보를 바탕으로 답변을 작성하는 것입니다.
해당 제품을 건너뛰고 모델을 로컬에서 실행하는 순간, 그 어떤 것도 연결되어 있지 않은 상태가 됩니다. 도구도, 검색도, 실시간 데이터도 없이 모델 단독으로만 작동하게 됩니다. 해결책은 대형 AI 연구소들이 이미 내부적으로 사용하고 있는 방식입니다. 모델에게 도구를 부여하고, 모델이 스스로 언제 그 도구를 호출할지 결정하게 만드는 것입니다.
우리의 경우, 도구는 웹 검색 API (web search API)입니다. 단 한 번의 API 호출로 가공되지 않은 HTML 대신 구조화된 검색 결과를 반환받을 수 있습니다. 우리는 Google Search, Finance, News, Maps, Flights 및 100개 이상의 다른 검색 엔진에 대해 깔끔한 JSON을 반환하는 SerpApi를 사용할 것입니다.
LM Studio로 로컬 모델 실행하기
LLM을 로컬에서 사용하는 방법은 여러 가지가 있지만, 이 가이드에서는 LM Studio를 사용할 것입니다. 이는 자신의 하드웨어에서 오픈 웨이트 (open-weight) 모델을 다운로드하고 실행할 수 있는 데스크톱 앱입니다. 이 프로젝트에서 LM Studio가 중요한 이유는 한 가지입니다. 로컬호스트 (localhost)에서 OpenAI 및 Anthropic 표준을 통해 모델을 노출한다는 점입니다. 이미 사용 중인 SDK의 엔드포인트 (endpoint)를 해당 주소로 지정하기만 하면, GPT나 Claude와 통신하던 동일한 코드가 이제 여러분의 로컬 모델과 통신하게 됩니다.
몇 분 안에 로컬에서 모델을 실행하는 방법은 다음과 같습니다:
- lmstudio.ai에서 LM Studio를 다운로드하여 설치합니다.
- 모델 브라우저를 열고 네이티브 도구 호출 (native tool calling) 기능이 있는 모델을 다운로드합니다. 저는 36GB 통합 메모리를 갖춘 M4 Max에서 원활하게 실행되는 Qwen3.5-9B를 사용하고 있습니다.
- Developer 탭을 열고, 모델을 로드한 뒤 Start Server를 클릭합니다.
- 이제 LM Studio는
http://localhost:1234/v1에서 OpenAI 호환 엔드포인트를 제공합니다.
이 가이드가 진행되는 동안 서버는 계속 실행 상태를 유지해야 합니다. 해당 로컬 엔드포인트(endpoint)가 바로 우리 코드가 통신하는 대상이기 때문입니다. 앱이 필요할 때마다 모델을 요청하므로 모델을 미리 로드해 둘 필요는 없으며, LM Studio는 실제로 호출이 들어올 때만 모델을 로드합니다.
도구 호출 (tool calling) 기능이 있는 모델이라면 무엇이든 여기서 작동하므로, 에이전트 워크플로 (agentic workflows)를 위해 구축된 모델을 권장합니다. 전체 루프(loop)가 바로 그 신뢰성에 의존하기 때문입니다. Google의 Gemma 4 또한 강력한 선택지 중 하나입니다. 어떤 모드와 크기를 선택할지는 사용자의 하드웨어에 달려 있습니다. 모델은 검색 결과(search result)를 담을 수 있을 만큼 충분히 큰 컨텍스트 윈도우 (context window)와 함께 메모리에 적재될 수 있어야 합니다.
사용자의 하드웨어가 무엇을 처리할 수 있는지 확실하지 않다면, LM Studio의 모델 검색 기능이 하드웨어에 가장 적합한 모델을 표시해주고, 모델이 실행하기에 너무 클 경우 경고를 보내줍니다. 저는 속도와 응답 품질 사이의 균형이 좋은 Qwen3.5-9B를 선택했습니다.
채팅 앱 구축하기
우리는 클라우드 제품들이 사용하는 것과 동일한 도구 호출 루프 (tool-calling loop) 내에 로컬 모델을 포함하는 채팅 앱을 구축할 것입니다. 이 루프는 모델과 코드 사이의 상호작용입니다. 모델이 검색을 요청하면, 코드가 이를 실행하고, 모델이 답변을 작성하는 방식입니다.
전체 프로젝트는 다음에서 클론(clone)할 수 있는 TypeScript 앱입니다:
현재 이 앱은 5개의 SerpApi 엔진(Search, Finance, News, Maps, Flights)을 지원하며, 더 많은 엔진을 추가할 수 있습니다. 아키텍처는 세 가지 구성 요소로 이루어져 있습니다:
세 번째 부분은 대부분의 튜토리얼에서 생략되지만, 로컬 설정이 실제로 작동하게 만드는 핵심적인 부분입니다. 검색 결과(search response)는 컨텍스트 창(context window)이 작은 모델에 담기에는 너무 크기 때문입니다. 우리는 JSON Restrictor라고 불리는 단 하나의 SerpApi 파라미터를 사용하여 이 문제를 해결할 것입니다.
프로젝트 설정
저장소(repo)를 클론하고, 의존성(dependencies)을 설치한 뒤, SerpApi 대시보드에서 API 키를 가져오세요:
git clone https://github.com/serpapi/local-llm-web-search.git
cd local-llm-web-search
bun install
...
LM Studio에서 서버가 실행 중인지 확인한 다음, 앱을 시작하세요.
bun dev
로컬 서버가 실행 중이고 키가 설정되었다면, 루프(loop)가 어떻게 작동하는지 살펴볼 준비가 되었습니다.
루프의 작동 방식
이 패턴은 OpenAI가 함수 호출(function calling)에 대해 문서화한 방식 및 Anthropic이 도구 사용(tool use)에 대해 문서화한 방식과 동일합니다. 총 다섯 단계로 순차적으로 진행됩니다. 아래 코드는 앱의 agent.ts와 restrictors.ts에서 핵심적인 부분만 발췌한 것입니다.
1단계. 모델에 도구(Tools) 선언하기
모델에게 호출할 수 있는 도구 목록을 전달합니다. 각 도구는 이름, 설명, 그리고 파라미터를 위한 JSON 스키마(JSON Schema)를 가집니다. 다음은 모델이 사용할 수 있는 웹 검색 도구로 SerpApi를 선언하는 예시입니다:
import OpenAI from "openai"
const TOOLS: OpenAI.Chat.Completions.ChatCompletionTool[] = [
...
몇 가지 주목할 사항이 있습니다:
-
설명(descriptions)은 보기보다 중요합니다. 모델은 설명을 읽고 도구(tools)를 선택하므로, 각 설명을 당신의 코드를 한 번도 본 적 없는 개발자를 위한 문서처럼 작성하세요.
-
각 도구 이름은 하나의 SerpApi 엔진에 매핑됩니다. 이 앱은
google_search,google_finance_search,google_news_search,google_maps_search, 그리고google_flights_search를 지원합니다. -
이러한 정의는 매 요청마다 모델로 전달되므로 간결하게 유지하세요. 이들은 다른 모든 요소와 마찬가지로 동일한 컨텍스트 윈도우 (context window) 예산에 포함됩니다.
2단계. 모델에게 필요한 것이 무엇인지 묻기
사용자의 프롬프트(prompt)를 도구 목록과 함께 모델로 보냅니다. 모델은 다음 두 가지 중 하나를 반환합니다:
-
이미 알고 있는 내용일 경우 직접적인 답변
-
웹 데이터가 필요한 경우 도구 호출 (tool calls)
OpenAI 클라이언트를 이전에 시작한 LM Studio 서버로 지정하세요:
const client = new OpenAI({
baseURL: "http://localhost:1234/v1", // LM Studio의 OpenAI 호환 엔드포인트 (endpoint)
apiKey: "lm-studio", // 로컬에서는 비어 있지 않은 어떤 문자열이든 작동합니다
...
주목할 만한 몇 가지 사항:
-
이 호출이 연결되려면 LM Studio 서버가 백그라운드에서 실행 중이어야 합니다. 모델 자체는 요청이 도착할 때 필요에 따라 로드됩니다. 만약 연결 거부 (connection refused) 에러가 발생한다면, 가장 먼저 확인해야 할 사항입니다.
-
tool_choice: "auto"를 사용하면 모델이 직접 답변할지 도구를 호출할지 결정할 수 있습니다. 낮은 온도 (temperature, 0.1) 설정은 도구 선택을 결정론적 (deterministic)으로 유지합니다. 모델이 답변을 작성하는 두 번째 호출에서는 온도를 높일 것입니다. -
만약
first.choices[0].message.tool_calls가 비어 있지 않다면, 모델이 웹을 원한다는 뜻이며 당신이 수행해야 할 작업이 있다는 의미입니다. -
이 호출을 하기 전에, 앱은 슬라이딩 윈도우 (sliding window) 방식으로 대화 기록을 다듬어(trim) 오래된 대화 내용을 삭제함으로써 긴 채팅이 예산을 초과하지 않도록 합니다.
json_restrictor는 각 응답을 작게 유지하며, 윈도우는 누적된 기록을 작게 유지합니다.
3단계. 도구 호출 실행
각 도구 호출(tool call)에 대해 인자(arguments)를 검증하고 일치하는 SerpApi 엔진을 실행합니다. 모델은 동일한 턴에서 하나 이상의 도구를 선택할 수 있습니다. 예를 들어, "Apple과 Tesla 주식을 비교해줘"라는 요청은 두 개의 금융 조회(finance lookup)를 트리거하므로, 이를 동시에 실행합니다:
const toolCalls = first.choices[0].message.tool_calls ?? []
const results = await Promise.all(
...
주의할 점 몇 가지:
-
validate는 네트워크 호출을 수행하기 전에 Zod 스키마를 통해 인자를 검증합니다. 크기가 작은 모델은 가끔arguments에서 잘못된 형식의 JSON을 생성할 수 있는데, 이 검증 단계는 쓰레기 값(garbage input)에 SerpApi 검색 비용을 낭비하기 전에 이를 잡아냅니다. -
executeTool은 응답이 다듬어지는(trimmed) 곳입니다. 이것이 다음 단계이며, 전체 프로세스를 로컬 모델에서 실행 가능하게 만드는 핵심 부분입니다.
4단계. JSON Restrictor를 사용하여 응답 다듬기
SerpApi는 엔진이 알고 있는 모든 정보를 반환합니다. 단일 Google Flights 응답은 공항 메타데이터, 모든 가격대, 경유 세부 정보, 탄소 배출 추정치, 수하물 규정 등이 포함되면 14,000 토큰(tokens) 이상을 반환할 수 있습니다. Google Finance 응답은 46,000 토큰을 넘을 수도 있습니다. 32K 컨텍스트 윈도우(context window)로 실행되는 Qwen3.5-9B 모델은 시스템 프롬프트(system prompt), 대화 기록, 그리고 도구 정의 자체를 추가하고 나면 이를 감당할 수 없습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기



