유료 검색 없이 LLM에 실시간 웹 그라운딩 붙이기
DuckDuckGo HTML 파싱으로 API 키 없이 LLM에 실시간 웹 검색 결과를 RAG 컨텍스트로 주입하는 방법을 정리했다
유료 검색 없이 LLM에 실시간 웹 그라운딩 붙이기
유료 검색 없이 LLM에 실시간 웹 그라운딩 붙이기
LLM 응답에 최신 정보가 없었다. 유료 검색 API 없이 웹 그라운딩을 붙이고 싶었다.
1. 서론 (Introduction)
- 문제/상황 (Problem): LLM의 학습 데이터는 일정 시점이 컷오프다. 최신 정보가 필요한 질문에 틀린 답이나 오래된 정보를 줬다. Tavily 같은 유료 검색 API를 쓰지 않고 해결하고 싶었다.
- 목적 (Purpose): DuckDuckGo HTML 파싱으로 무료로 실시간 웹 검색 결과를 LLM 컨텍스트에 주입한다.
- 대상 (Target Audience): 유료 검색 API 없이 LLM 그라운딩을 구현하고 싶은 개발자.
2. 방법 및 과정 (Methods & Process)
- 배경 조사 및 데이터 (Data Collection): DuckDuckGo는
https://html.duckduckgo.com/html/?q=query형태로 JavaScript 없이 결과를 반환하는 라이트 엔드포인트가 있다. Google과 달리 이 비 JavaScript 결과 페이지가 파싱 가능한 상태로 유지되고 있고, API 키가 필요 없다. - 접근 방법 (Approach Methods):
- [방법 1]:
requests(또는httpx)로 DuckDuckGo HTML 엔드포인트를 직접 호출해 결과 제목·URL을 파싱한다(라이트 페이지엔 발췌가 없어 비워둔다). 가장 가볍고 빠르다. - [방법 2]: 1차 요청이 실패(네트워크 오류·차단·파싱 실패)하면 목(mock) fetch로 폴백해 검색 없이도 동일한 인터페이스로 결과를 반환한다. 실제 서비스에서는 이 자리에 Playwright headless 렌더링을 넣어 JS 필요 페이지의 본문을 가져온다.
- [방법 1]:
- 분석 및 해결 프로세스 (Analysis Flow):
- 도구/기술: requests, DuckDuckGo HTML 엔드포인트, Playwright(JS 렌더링 필요 시), 표준 라이브러리 HTMLParser.
- 주요 단계: 질의 입력 $\rightarrow$ DuckDuckGo HTML 요청 $\rightarrow$ (성공) 결과 파싱 $\rightarrow$ (실패) mock/Playwright 폴백 $\rightarrow$ 스니펫을 시스템 프롬프트 앞단에 RAG 컨텍스트로 주입.
- 결과 도출 및 검증: 검색 결과 발췌를 “다음 검색 결과를 참고해 답하라”는 지시와 함께 주입하면 LLM이 제공된 문서를 우선 참고했다. 그라운딩 실패 시에도 검색 없이 LLM에 직접 질문하는 경로로 넘어가 전체 요청 실패로 이어지지 않았다.
3. 결과 (Results)
- 분석 결과 요약: 최신 뉴스, 가격 정보, 새 기술 동향 질문에서 정확도가 크게 올라갔다. API 키 비용이 없었고 DuckDuckGo 파싱이 막히는 경우 Bing HTML 파싱으로 교체도 가능했다. 검색 결과 품질이 LLM 응답의 병목이 되면서 검색 쿼리 최적화의 중요성이 커졌다.
4. 인사이트 및 액션 (Insights & Action)
- 인사이트 (Insight): 웹 그라운딩의 핵심은 검색 API가 아니라 HTML 파싱 가능 여부다. 공개 검색 엔진은 파싱 가능한 라이트 엔드포인트를 가진 경우가 많다.
- 실행 방안 (Action Plan): 질문을 그대로 검색 쿼리로 쓰는 것보다 LLM이 검색 쿼리를 먼저 최적화하게 하면 관련성 높은 결과를 얻는다. “질문 → 검색 쿼리 생성 → 검색 → LLM 답변” 2단계 구조로 간다.
- 한 줄 결론 (Key Takeaway): DuckDuckGo HTML 파싱으로 유료 API 없이도 LLM 웹 그라운딩이 된다. 샘플 코드
- 다음 스텝 (Next Step): DuckDuckGo HTML 구조가 바뀌면 파서가 조용히 깨질 수 있어, 파싱 결과 0건을 감지해 즉시 폴백하는 것 외에 구조 변경 자체를 감지하는 회귀 테스트가 필요하다.
This post is licensed under CC BY 4.0 by the author.