Focal Harvest 구축 한 달: 무엇이 변했고 어디로 향하는가
요약
웹 검색, 스크래핑, 요약을 자동화하여 구조화된 보고서를 생성하는 오픈소스 도구 Focal Harvest의 개발 한 달 경과와 업데이트를 소개합니다. 플러그인 아키텍처를 통한 사이트별 맞춤 파싱과 Telegram/Discord 봇 리스너 기능을 통해 사용성을 크게 개선했습니다.
핵심 포인트
- 웹 검색부터 Markdown 보고서 합성까지의 루프 자동화
- 외부 LLM 없이도 작동하는 내장 추출적 요약 기능
- GitHub, arXiv 등 특정 도메인 대응을 위한 플러그인 시스템
- Telegram 및 Discord를 통한 모바일 명령 제어 지원
Focal Harvest를 GitHub에 처음 공개한 지 거의 한 달이 지났으며, 프로젝트는 원래의 모습과는 거의 다르게 변했습니다. 새로운 기여자들, 새로운 아이디어들, 그리고 수많은 밤샘 작업이 이 프로젝트를 초기의 조잡한 개인용 도구에서 제가 원래 의도했던 기능을 실제로 수행하는 무언가로 탈바꿈시켰습니다.
이전에 보지 못하셨다면, 짧은 요약 버전입니다. 이미 보셨다면 업데이트 부분으로 건너뛰세요.
Focal Harvest란 무엇인가
이 도구가 해결하는 문제는 특정한 종류의 지루함입니다. 무언가를 조사해야 할 때, 검색 엔진을 열고, 15개의 탭을 띄우고, SEO(검색 엔진 최적화)용 채우기 글들을 훑어 넘긴 뒤, 중요한 부분만 복사하여 문서에 넣고, 그 모든 것을 LLM(대규모 언어 모델)에 붙여넣고, 다음 주에 무언가 바뀌었을 때 이 과정을 다시 반복해야 하는 상황 말입니다.
Focal Harvest는 그 루프(loop)를 자동화합니다. 주제와 집중 분야를 입력하면, 웹을 검색하고, 페이지를 동시에 스크래핑(scraping) 및 정제하며, 구조화된 Markdown 보고서를 합성하여 디스크에 작성합니다. 반복적인 모니터링을 위한 루프 모드도 있습니다. 정해진 일정에 따라 재실행되며, MD5 핑거프린팅(fingerprinting)을 사용하여 새로운 내용이 없을 때는 알림을 억제하고 실제 콘텐츠가 변경되었을 때만 알림을 보냅니다.
이 도구는 GPU도, Docker도, 데이터베이스도 없는 노트북에서 실행됩니다. 보고서는 더블 클릭만으로 열 수 있는 일반적인 Markdown 및 JSON 파일입니다. 또한 API 키 없이도 완전히 오프라인으로 작동합니다. 내장된 추출적 요약기(extractive summarizer)가 키워드 밀도와 위치에 따라 문장의 점수를 매기고 순위를 정하여, 외부 모델을 전혀 사용하지 않고도 실제적인 보고서를 생성합니다.
이것이 기본 기능입니다. 지난 한 달 동안 무엇이 변했는지 소개합니다.
새로운 기능
사이트별 파싱을 위한 플러그인 시스템
일반적인 가독성(readability) + BeautifulSoup 폴백(fallback) 방식은 표준적인 기사에는 잘 작동합니다. 하지만 GitHub 저장소 페이지, arXiv 초록, Reddit 스레드, Amazon 제품 목록, 또는 Stack Overflow 질문과 같이 가독성 라이브러리가 구조를 이해하지 못하는 페이지에는 잘 작동하지 않습니다.
해결책은 플러그인 아키텍처 (plugin architecture)였습니다. 각 플러그인은 SUPPORTED_DOMAINS 리스트와 parse(html, url) 함수를 포함하는 단일 Python 파일입니다. 스크래퍼 (scraper)가 URL을 가져올 때, 일반 파서 (generic parser)로 넘어가기 전에 먼저 로드된 플러그인들을 확인합니다. 현재 GitHub, arXiv, Reddit, Hacker News, Stack Overflow 등을 지원하는 17개의 내장 플러그인이 작동하고 있습니다.
새로운 플러그인을 추가하는 데는 단 한 번의 오후 시간만 있으면 충분합니다. 인터페이스는 설계 단계부터 단순하게 만들어졌습니다.
모바일 봇 리스너 (mobile bot listener)
이 기능은 제가 개인적으로 이 도구를 사용하는 방식을 바꾸어 놓았습니다. Focal Harvest는 이제 Telegram과 Discord에서 슬래시 명령 (slash commands)을 폴링 (polling)하는 봇 리스너를 실행합니다. 휴대폰에서 다음과 같이 사용할 수 있습니다:
/research 'topic'— 표준 스윕 (sweep)을 실행하고 요약 보고서 (Executive Summary)와 함께 PDF 및 Markdown 첨부 파일을 반환합니다./deep 'topic'— 주제를 5개의 하위 쿼리 (sub-queries)로 분해하여 각각 독립적으로 검색한 뒤, 이를 모두 종합합니다./url 'link'— 특정 URL 또는 URL 세트를 심층 분석합니다./status— 서버 상태, 활성 프로바이더 (active provider), 보고서 개수를 반환합니다./help— 명령 목록을 보여줍니다.
인증은 설정 파일의 사용자 ID 화이트리스트 (whitelist)를 통해 처리되므로, 본인이 요청하지 않은 것은 아무것도 실행되지 않습니다. 전체 시스템은 단순한 requests 폴링 방식으로 작동하며, 웹훅 (webhook) 서버나 프레임워크, 개방된 포트가 필요하지 않습니다.
PDF 및 Word 내보내기
이제 보고서는 ReportLab을 통해 .pdf로, python-docx를 통해 .docx로 내보낼 수 있으며, 적절한 제목 스타일, 표 렌더링, 글머리 기호 형식이 적용됩니다. 두 기능 모두 선택 사항입니다. 해당 패키지가 설치되어 있지 않으면 도구가 경고를 표시하고 유연하게 해당 단계를 건너뜁니다. 봇 리스너는 보고서가 완료되면 PDF와 Markdown 파일을 Telegram 또는 Discord 메시지에 직접 첨부합니다.
5개 엔진 검색 보충
기존 도구는 DuckDuckGo나 Tavily를 검색하고 결과가 나오는 대로 가져왔습니다. 만약 소스 (sources)가 부족하거나 차단되어 있다면 보고서의 품질이 떨어졌습니다.
이제 적응형 보충 파이프라인 (adaptive replenishment pipeline)이 있습니다: Tavily → DuckDuckGo → Google Mobile → Bing → Brave 순으로, 목표 소스 개수가 충족될 때까지 순차적으로 시도합니다. 만약 5개의 좋은 소스를 요청했는데 3개가 차단된다면, 대체 소스를 찾을 때까지 검색을 계속합니다. scrape_urls_adaptive가 품질 게이트 (quality gate)를 처리하며, 글자 수 임계값 미만의 결과는 모두 제외되고 파이프라인이 자동으로 백필 (backfill)을 수행합니다.
3단계 아카이브 폴백 (Three-tier archive fallback)
봇 차단(bot-blocked)되거나 JS 렌더링 (JS-rendered)이 필요한 페이지의 경우, 이제 스크래퍼가 세 가지 아카이브 서비스를 순차적으로 시도합니다: Wayback Machine (속도 제한이 있는 가용성 API를 피하기 위해 직접 리다이렉트 단축키 사용), Memento, 그리고 Archive.ph입니다. 캐시된 항목은 제공되기 전에 Wayback의 기부 배너 보일러플레이트 (boilerplate)가 있는지 확인합니다. 오염된 캐시 항목이 발견되면 새로운 라이브 크롤링 (live crawl)을 강제하고 디스크의 잘못된 항목을 덮어씁니다.
쿼리 분해 (Query decomposition)
/deep 명령은 decompose_query_locally를 사용하여 LLM 계획 호출 (planning call) 없이 주제를 하위 질문들로 분해합니다. 각 하위 질문은 개별적인 검색 단계를 거치며, 결과는 URL 기준으로 중복 제거(deduplicated)된 후 모든 데이터가 단일 합성 (synthesis) 단계로 전달됩니다. 이를 통해 보고서는 단일 쿼리 검색이 다룰 수 있는 것보다 더 많은 측면의 주제를 다룹니다.
소스 관련성 필터링 (Source relevance filtering)
합성(synthesis) 전, 스크래핑된 각 소스는 이제 원래의 쿼리 및 초점 영역에 대한 관련성 점수가 매겨집니다. 임계값 미만의 소스는 제외됩니다. 이는 합성기 (synthesiser)가 사용자가 질문한 내용과 명백히 관련된 콘텐츠만 보게 된다는 것을 의미하며, 노이즈는 줄어들고 보고서의 품질은 향상됩니다.
프록시 지원 (Proxy support)
config.json은 이제 무작위 로테이션을 위한 proxy_enabled, proxy_url, proxy_list를 지원합니다. 검색, 스크래핑, 알림, 아카이브 가져오기를 포함한 모든 외부 요청은 설정된 프록시를 통해 라우팅됩니다. 자격 증명 (credentials)은 로그에서 마스킹 처리됩니다.
아직 누락된 것들
아직 구현하지 못한 몇 가지 사항들입니다:
- 고급 쿼리 계획 (Advanced query planning) (현재의 분해 방식은 로컬 및 규칙 기반이며, 의미론적 인지 (semantically aware) 기능은 없습니다)
- 로컬 문서 수집 (Local document ingestion) — 웹 소스와 함께 PDF, Word 파일 지원
- 터미널을 원하지 않는 사용자를 위한 웹 UI (Web UI)
이 중 관심 있는 사항이 있다면, 저장소 (repo)는 공개되어 있으며 아키텍처는 확장 가능하도록 설계되었습니다. 특히 플러그인 시스템 (plugin system)이 좋은 진입점이 될 것입니다. 정기적으로 스크래핑하는 사이트를 선택하여 파서 (parser)를 작성해 보세요.
만약 이 분야에서 무언가를 구축했거나, 동일한 탭 과다 축적 (tab-hoarding) 문제를 겪고 다른 방식으로 해결했다면, 댓글을 통해 진심으로 이야기를 듣고 싶습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기