웹사이트 검색

웹 기반 LLM의 장점과 구현


소개

최근 또는 예정된 사건에 대해 질문할 때 대규모 언어 모델을 호출하면, 모델이 과거에 학습한 데이터만 접근할 수 있기 때문에 답변이 종종 부정확할 수 있습니다. 예를 들어, 마지막 대통령 선거 이전에 학습된 어떤 LLM에게 대통령이 누구인지 물으면, 웹에 접근할 수 없기 때문에 이전 대통령을 답하거나 답을 만들어낼 수도 있습니다. 아래는 학습 지식 컷오프가 2023년 12월인 Llama 3.3 70B에 대한 질의 예시입니다.

response = call_llm("Who is the current president?")
print(f"Response: {response}")
Response: As of my knowledge cutoff in 2023, Joe Biden is the President of the United States. However, please note that my information may not be up-to-date, and I recommend checking with a reliable news source for the most current information.

대부분의 대형 언어 모델(LLM) API는 귀하의 요청을 웹 검색에 "기초(granting)"함으로써 이 문제를 해결합니다. 웹 기반 기초(Web grounding)는 검색 강화 생성(Retrieval Augmented Generation)과 유사한 과정으로, 워크플로우가 사용자의 입력이 현재 웹에서 정보를 필요로 하는지 여부를 판단하고, 웹 검색을 수행하며, 검색 결과를 최종 프롬프트의 컨텍스트로 추가하고, 그 응답을 사용자에게 반환하는 과정을 의미합니다.

내장된 웹 기반 기능이 있는 LLM API 대신 오픈 소스 모델을 사용하기로 선택한 경우, 이 흐름을 직접 구현해야 할 수도 있습니다. 이 튜토리얼에서는 모델의 응답 정확성을 높이기 위해 웹 기반 기능을 사용하는 Python 워크플로를 구현할 것입니다.

핵심 내용

  • LLM은 훈련된 시점까지만 정보를 가지고 있습니다. 웹 기반 연결은 실시간 웹 검색을 이용해 LLM 프롬프트가 최신 정보에 접근할 수 있도록 합니다.

  • 웹 그라운딩은 검색 기반 생성(RAG)의 한 형태로, 워크플로우가 LLM을 호출할 때 사용자 프롬프트에 추가적인 맥락을 찾아 제공하는 방식입니다. 이는 프롬프트에 응답하기 위해 어떤 추가 정보가 필요한지를 결정하고, 해당 정보를 웹에서 검색한 후 웹 검색 정보를 프롬프트 맥락에 포함시킵니다.

  • OpenAI와 Google의 많은 비공개 모델에는 웹 기반 기능이 내장되어 있지만, 다른 모델의 경우 사용자가 일부 파이썬 로직과 웹 검색 API 접근을 통해 자체 웹 기반 워크플로를 구현해야 합니다.

1단계 — API 키 받기

웹을 검색하고 응답을 반환할 수 있는 도구가 필요합니다. Google과 Bing을 포함한 많은 검색 서비스는 최근 검색 API를 중단하고, 사용자가 자사 모델을 사용하거나 에이전트 생태계를 사용하는 자체 웹 기반 서비스로 전환했습니다. 이 튜토리얼에서는 Serp API를 사용하여 제한된 무료 요금제로 Duck Duck Go 검색을 사용할 것입니다. 모든 검색 API는 웹 검색 결과의 제목, 요약, 날짜에 접근할 수 있다면 사용할 수 있습니다.

Serp Api에서 무료 계정을 만들고 API 키를 받으려면, 그들의 웹사이트로 가서 무료 플랜으로 계정을 생성하세요.

또한 LLM에 접근할 수 있어야 합니다. 이 튜토리얼에서는 Linux-Console.net Gradient AI 플랫폼의 Serverless Inference 도구를 통해 Llama 3.3 70B를 사용합니다. Linux-Console.net Gradient AI 플랫폼에서 Serverless Inference를 사용하려면 모델 접근 키가 필요합니다.

2단계 — 환경 설정

이미 Python이 설치되어 있지 않다면, Python 웹사이트에서 다운로드하여 설치해야 합니다.

web_grounding_tutorial.py라는 이름의 파이썬 스크립트를 만드세요. 스크립트에서 requests 라이브러리를 임포트합니다. Serp API 키를 추가하세요. 강조 표시된 your_api_key 자리 표시자 값을 무료 Serp API 계정에서 발급받은 API 키로 교체하세요. Serp API에서 API 키를 찾으려면 웹사이트에 로그인하고, 대시보드의 API 키 섹션으로 이동하세요.

그런 다음 추론 요청을 위한 헤더와 함께 Linux-Console.net 추론 URL을 추가합니다. 강조 표시된 your_model_access_key 자리 표시자 값을 Linux-Console.net Gradient AI Platform 콘솔에서 받은 액세스 키로 바꾸세요.

import requests

SERP_API_KEY = "your_api_key"

llm_url = "https://inference.do-ai.run/v1/chat/completions"
headers = {
    "Content-Type": "application/json",
    "Authorization": "Bearer your_model_access_key"
}

LLM URL에 대한 요청 헤더는 JSON 콘텐츠 유형과 인증을 위한 모델 액세스 키를 정의하도록 지정되었습니다. 이제 LLM을 호출하는 함수를 정의해야 합니다. 이 함수는 Linux-Console.net Serverless Inference 엔드포인트에 HTTP 요청을 보냅니다.

def call_llm(prompt):
    data = {
        "model": "llama3.3-70b-instruct",
        "messages": [
            {
                "role": "user",
                "content": prompt
            }
        ],
        "max_tokens": 500
    }

    response = requests.post(llm_url, headers=headers, json=data)
    message = response.json()['choices'][0]['message']['content']
    return message

call_llm 함수는 프롬프트를 받아 HTTP 요청을 통해 모델을 호출하고, 응답 텍스트를 반환합니다.

3단계 — 프롬프트가 근거가 필요한지 확인하기

모든 요청이 웹 검색에 기반할 필요는 없습니다. 예를 들어, 수프 레시피를 요청하는 경우 웹 검색 없이 LLM이 제공하도록 하고 싶을 수 있습니다. 웹 검색 API에는 사용 제한이나 비용이 있을 수 있으며, 이를 줄이고 싶을 수 있습니다. 또한 웹 검색은 추가적인 지연 시간을 발생시킬 수 있으므로, 필요한 경우에만 LLM이 쿼리를 수행하는 것이 가장 좋습니다.

웹 검색이 필요한지 확인하려면, 사용자의 입력을 웹 검색 결과로 보강해야 하는지 LLM에 묻는 함수를 만들어야 합니다.

def needs_web_grounding(query):

    prompt = f"Does the following prompt need information more recent than December 2023 to answer correctly?. Answer only YES or NO.\nQuestion: {query}\nAnswer:"

    judgement = call_llm(prompt)

    return judgement.strip().upper().startswith("YES")

needs_web_grounding 함수는 사용자의 프롬프트가 우리가 사용하고 있는 Llama 3.3 70B 모델의 2023년 12월 지식 컷오프 날짜 이후의 최신 정보를 필요로 하는지 모델에 묻습니다. LLM의 응답이 ‘YES’이면, 이 함수는 True 불리언 객체를 반환합니다. 그렇지 않으면 False 객체를 반환합니다. 우리는 이 함수의 응답을 사용하여 웹 검색을 수행할지 여부를 결정할 것입니다.

만약 쿼리 “현재 대통령은 누구인가?”에 대해 needs_web_grounding 함수를 호출하면, 그것은 “YES”(참)를 반환하고, “왜 하늘은 파란가?”에 대해 호출하면 “NO”(거짓)를 반환합니다.

needs_web_grounding 함수의 프롬프트는 프롬프트가 매번 웹 검색이 필요한지 여부를 정확하게 식별할 수 없습니다. 예를 들어, 어떤 나라가 수도를 변경하면 모델은 나라들이 수도를 자주 변경하지 않는다고 생각하여 ‘NO’를 반환할 수 있습니다. 웹 검색의 필요성을 정확히 식별하는 데에는 많은 유사한 사례들이 있어, 이 문제는 여러 층의 복잡성을 가진 문제로 만듭니다. 위에서 사용한 간단한 프롬프트는 이 튜토리얼의 목적에는 충분히 잘 작동할 것입니다. 실제 운영 환경에서는 애플리케이션에서 허용 가능한 오류 수준에 맞게 이 프롬프트를 조정해야 합니다.

4단계 — 웹 검색 구현

이제 검색어를 입력하면 해당 검색어에 대한 상위 세 개 결과를 반환하는 웹 검색 기능을 만들어야 합니다.

def search_web(search_query): 
    params = {
        'engine': 'duckduckgo_light',
        'q': search_query,
        'api_key': SERP_API_KEY,
    }

    response = requests.get('https://serpapi.com/search', params=params)
    data = response.json()

    return data["organic_results"][:3]

search_web 함수는 사용자의 입력을 받아 2단계에서 정의된 SERP_API_KEY를 사용하여 Serp API를 통해 사용자의 입력으로 Duck Duck Go 웹 검색을 수행합니다. 그런 다음 상위 세 검색 결과의 세부 정보를 반환합니다. 반환된 결과를 받아 사용자의 프롬프트에 추가한 후 LLM이 응답을 처리하도록 합니다.

단계 5 — 워크플로 논리 작성

이제, 당신이 작성한 다른 모든 함수를 호출하는 함수를 작성해야 합니다. 이 함수는 사용자의 입력을 받아야 하고, 웹 검색이 필요한지 판단하기 위해 needs_web_grounding 함수를 호출한 후, 필요하면 웹을 검색하여 컨텍스트를 프롬프트에 추가하거나 추가 컨텍스트 없이 LLM을 호출해야 합니다.

def answer_with_web_grounding(user_input):

    if needs_web_grounding(user_input):
        print("Web search needed; fetching info...")
        search_results = search_web(user_input)
        prompt = f"Please respond to the user's query: {user_input}\n\nYou may use the following web search results as context in your answer: {search_results}"
    
    else:
        print("Web search NOT needed; answering from LLM knowledge.")
        prompt = user_input
      
    answer = call_llm(prompt)
    return answer

만약 needs_web_grounding 함수가 True 불리언 값을 반환하면, 이는 LLM이 답변할 때 웹 검색 결과를 맥락으로 사용하도록 요청하는 프롬프트를 작성합니다. 그렇지 않으면 프롬프트로 사용자 입력만 사용합니다.

이제 다음 코드를 실행하면:

answer_with_web_grounding("Who is the current president?")

아래와 유사한 응답을 받게 될 것입니다.

The current president of the United States is Donald J. Trump. He was sworn in as the 47th president on January 20, 2025, and his current term is set to end on January 20, 2029.

이 결과들은 최신일 뿐만 아니라, 선서일과 임기 종료일은 모델만 사용했을 경우 정확하게 반환되지 않았을 수도 있는 세부 사항을 보여줍니다.

웹 기반 작업 흐름 향상

이 LLM 워크플로를 귀하의 애플리케이션에 맞게 개선하려면, 다음 단계로 애플리케이션의 필요에 따라 프롬프트가 웹 검색이 필요한지 여부를 결정하는 방법을 업데이트하는 것입니다. 만약 애플리케이션이 다가오는 콘서트 정보를 찾는 용도라면, 매번 웹 검색이 필요할 수 있습니다. 그렇지 않고 단지 일부 오래된 응답을 잡아내는 용도라면, 이 튜토리얼에서 작성한 흐름을 그대로 사용하면 됩니다. needs_web_grounding 함수의 프롬프트는 이 목적에 맞게 조정할 수 있습니다.

다음으로, 검색 결과의 출력물을 정리하면 사용되는 토큰 수를 줄이고 응답의 정확성을 높일 수 있습니다. 현재 search_web 함수는 다음과 같은 JSON 객체를 반환합니다:

[
  {
    "position": 1,
    "title": "President Donald J. Trump - The White House",
    "link": "https://www.whitehouse.gov/administration/donald-j-trump/",
    "displayed_link": "www.whitehouse.gov/administration/donald-j-trump/",
    "snippet": "President Trump built on his success in private life when he entered into politics and public service. He remarkably won the Presidency in his first ever run for any political office.",
    "favicon": "https://external-content.duckduckgo.com/ip3/www.whitehouse.gov.ico"
  },
  {
    "position": 2,
    "title": "Donald Trump Sworn In as 47th President in Capitol Ceremony ... - Yahoo",
    ...
  }
]

이러한 필드 중 많은 것들(예: 'position', 'link', 'displayed_link' 등)은 필요하지 않을 수 있습니다. 검색 API가 반환하는 결과의 형식을 확인하고, 컨텍스트에 포함되는 정보의 양을 줄이기 위해 불필요한 필드를 제거하는 함수를 작성하세요. 또한 사용되는 토큰 수를 줄이기 위해 JSON을 TOON 형식으로 인코딩하는 것이 유리할 수도 있습니다.

검색 결과에서 제공되는 스니펫에는 많은 질문에 대한 답이 포함되지 않을 수 있습니다. 예를 들어, 다음 시호크스 경기 시간을 물어보면, 날짜는 알 수 있지만 링크의 실제 페이지 내용에 접근하지 않으면 정확한 시간은 알 수 없을 수 있습니다.

answer_with_web_grounding("What time is the next Seahawks game?")
The next Seahawks game is today, Sunday, December 28, 2025, against the Panthers. However, the exact time of the game is not specified in the provided search results. I recommend checking the official Seattle Seahawks website...

이를 해결하는 한 가지 방법은 워크플로우에 추가 단계를 추가하여 Beautiful Soup과 같은 Python 라이브러리를 사용하여 웹 페이지의 콘텐츠를 가져오고 쿼리에 그 내용을 포함시키는 것입니다. 해당 기능을 추가한 후에는 공식 시호크스 경기 일정 페이지의 내용이 포함되며, 모델은 오늘 경기가 이미 끝났고 다음 경기의 정확한 날짜와 시간은 아직 예정되지 않았음을 알 수 있습니다.

The next Seahawks game is scheduled for Week 18, but the date, time, and location are still "TBD" (to be determined) as it is against the 49ers at Levi's Stadium.

보안 모범 사례를 위해 코드에서 하드코딩된 API 키와 URL을 제거하세요. 한 가지 방법은 이를 환경 변수로 저장하고 스크립트로 불러오는 것입니다.

마지막으로, 모든 LLM 워크플로우는 AI 애플리케이션에 내재된 비결정적이고 예측 불가능한 특성을 감안하여 코드에 충분한 오류 처리 기능이 포함되어 있어야 합니다.

자주 묻는 질문

웹 기반 정보 연결을 단일 프롬프트가 아니라 대화 중에 활성화할 수 있나요?

네, 사용자가 후속 질문을 할 수 있는 대화 형식을 원한다면 대화 자체에 컨텍스트를 포함할 수 있습니다. 만약 이것이 대화 컨텍스트에 너무 많은 토큰을 추가한다면, 애플리케이션이 허용한다면 과거 대화 항목을 제거하여 토큰 수를 줄이는 것을 고려해 보세요.

웹 근거를 추가하면 웹 검색에 시간이 걸리기 때문에 지연이 증가합니다. 이를 빠르게 할 수 있는 방법이 있을까요?

네, 사용자의 프롬프트를 컨텍스트에서 검색 결과 없이 LLM에 보내는 동시에, 사용자의 프롬프트가 웹 검색에 근거해야 하는지 LLM에 묻는 호출을 보내서 LLM 호출을 비동기적으로 실행할 수 있습니다. 결과가 반환된 후, 웹 검색이 필요하다면 웹 검색을 수행하고, 웹 검색에서 얻은 컨텍스트와 함께 다시 LLM을 호출할 수 있습니다. 이를 통해 웹 검색이 필요하지 않은 LLM 호출의 지연 시간을 줄일 수 있습니다. 다만, 웹 검색이 필요한 경우 첫 번째 LLM 응답을 버리게 되므로 추가 토큰 비용이 발생합니다.

웹 검색 때문에 너무 많은 토큰이 소모되고 있어요. 이러한 모든 호출에 사용되는 토큰 수를 줄일 수 있는 방법이 있을까요?

네, 한 가지 방법은 needs_web_grounding 함수에서 더 작은 모델을 사용하는 것입니다. 답변이 웹 검색이 필요한지 여부를 판단하는 데 70B 파라미터 모델이 꼭 필요하지 않을 수도 있습니다. 더 작은 모델을 사용하면 비용이 저렴하고 속도가 빠를 수 있습니다.

결론

웹 그라운딩은 AI 애플리케이션의 정확성을 향상시킬 수 있는 강력한 도구이며, 설정해 놓은 다른 검색 보강 생성(RAG) 워크플로우에 직접 통합할 수 있습니다. 이를 통해 최신 정보를 덜 학습했거나 오래된 모델, 또는 작은 모델에서도 정확하고 최신의 결과를 얻을 수 있습니다.

이 튜토리얼에서는 Serp API 엔드포인트를 사용하여 Duck Duck Go를 통해 웹 검색을 수행하고 검색 결과의 맥락으로 프롬프트를 보강했습니다. 다음 단계는 애플리케이션에 맞게 구현을 맞춤화하는 것입니다.

Linux-Console.net 커뮤니티와 함께 학습해 주셔서 감사합니다.