이 레포가 무엇을 하는 물건인가.
당신이 인턴 연구원에게 "엔비디아 주가가 왜 오르는지 조사해서 보고서로 정리해줘"라고 시킨다고 해보자. 똑똑한 인턴은 먼저 무엇을 찾아봐야 할지 질문 목록을 만들고, 검색하고, 좋은 자료를 골라 읽고, 요약하고, 출처를 붙여 보고서를 쓴다. gpt-researcher는 그 인턴의 일을 통째로 자동화한 프로그램이다.
gpt-researcher는 스스로를 "웹과 로컬 문서 양쪽을 조사하도록 설계된 최초의 오픈소스 딥 리서치 에이전트"라고 소개한다. 핵심은 단순하다 — LLM(거대 언어모델)에게 "답을 알려줘"라고 직접 묻는 대신, "답을 찾기 위해 무엇을 검색할지 계획을 세우게" 한 뒤, 실제 웹 검색 결과를 읽혀서 답하게 한다. 그래서 결과물이 최신이고, 출처가 달리고, 한쪽으로 치우치지 않는다.
이 프로젝트가 푸는 문제는 명확하다. ① 사람이 직접 하는 조사는 며칠~몇 주가 걸린다. ② LLM 혼자서는 옛날 데이터로 학습돼 환각(hallucination)을 일으키고 출처도 없다. ③ LLM은 한 번에 다룰 수 있는 글자 수(토큰)에 한계가 있어 긴 리포트를 못 쓴다. gpt-researcher는 여러 AI 에이전트를 분업·병렬로 굴려 이 세 문제를 한 번에 푼다.
트렌딩 이유 · 경쟁 제품 대비 장점.
gpt-researcher가 다시 TrendShift 라이브 멘션에 오른 직접적 계기는 "Claude 스킬(Claude Skill)로 설치 가능"해졌기 때문이다. npx skills add assafelovic/gpt-researcher 한 줄이면 Claude의 대화 안에서 이 딥 리서치 능력을 바로 부를 수 있게 됐고, 이게 X(트위터)·Reddit에서 회자되며 노출이 늘었다. 하지만 인기의 근본은 따로 있다 — "비공개 제품(ChatGPT·Perplexity의 Deep Research)에 맞먹는 기능을 통째로 오픈소스로 제공"한다는 점이다.
차별점은 네 가지다. ① 오픈소스 + 셀프호스팅(내 서버·내 API 키로 돌려 데이터가 밖으로 안 샌다), ② 20개 이상의 검색엔진을 섞어 한쪽 소스 편향을 줄인다, ③ 웹과 로컬 문서를 함께 조사한다(사내 PDF + 최신 웹), ④ 어떤 LLM이든 끼울 수 있다(OpenAI·로컬 모델·기타 호환 API). 비공개 서비스는 이 중 어느 것도 사용자가 마음대로 못 바꾼다.
| 항목 | LLM에 그냥 질문 | gpt-researcher |
|---|---|---|
| 정보 최신성 | 학습 시점에 멈춤(옛날 데이터) | 실시간 웹 검색으로 최신 반영 |
| 출처·근거 | 없음(환각 위험) | 20+ 소스 교차검증 + 인용 표기 |
| 리포트 길이 | 토큰 한계로 짧음 | 2,000단어 이상 장문 + 상세 모드 |
| 편향 | 소스 선택이 불투명 | 다중 소스 집계로 편향 완화 |
| 통제권 | 비공개 서비스에 종속 | 오픈소스·셀프호스팅·LLM 교체 자유 |
LLM에게 "이 주제 조사해줘"라고 바로 물으면, 모델은 학습 때 외운 옛 지식으로 그럴듯하게 답한다. 출처가 없으니 어디까지 사실인지 확인할 수 없고(환각), 최신 사건은 모르며, 한 번에 쓸 수 있는 분량도 토큰 한계에 막힌다. "그럴듯하지만 못 믿을 답"이 나오는 구조다.
gpt-researcher는 LLM을 '기획자'와 '실행자'로 나눠 굴린다. 기획자가 "이걸 알려면 이런 하위 질문들을 검색해야 한다"고 계획을 세우면, 실행자 여러 명이 동시에(병렬로) 검색·스크래핑·요약을 하고, 마지막에 모은 근거를 합쳐 출처 달린 리포트를 쓴다. 오픈북 시험을 여러 명이 분담하는 셈이라 빠르고, 최신이고, 검증 가능하다.
에이전트·검색·스크래핑·백엔드·프론트 한눈에.
백엔드는 Python, 프론트엔드는 Next.js로 나뉜 풀스택 구조다. 핵심 두뇌는 gpt_researcher 파이썬 패키지이고, 그 둘레를 검색 어댑터 20종·스크래퍼 7종·LLM 추상화·메모리·웹서버가 감싼다. 레이어별로 보면 이렇다.
| 레이어 | 기술 / 버전 | 역할 |
|---|---|---|
| 언어·런타임 | Python 3.11+ · asyncio 비동기 | 전체 에이전트 로직 + 병렬 검색/스크래핑. |
| 에이전트 프레임워크 | LangChain v1 + LangGraph 0.2 | LLM 호출 추상화 + 멀티에이전트 상태 그래프. |
| LLM(3단 분리) | FAST(gpt-4o-mini) · SMART(gpt-4.1) · STRATEGIC(o4-mini) | 작업 난이도별로 빠른/똑똑한/전략 모델을 갈아 쓴다. |
| 검색(retriever) | 20종 · 기본 Tavily · google·bing·brave·duckduckgo·exa·arxiv·pubmed·semantic_scholar·searx·serper… | 웹·학술 등 여러 엔진에서 검색 결과를 가져온다. |
| 스크래핑(scraper) | 7종 · BeautifulSoup·Selenium(브라우저)·Firecrawl·PyMuPDF·Tavily Extract·arxiv | 검색으로 찾은 페이지의 본문을 실제로 긁어온다. |
| 백엔드 서버 | FastAPI + WebSocket + uvicorn | 리서치 진행 상황을 실시간 스트리밍하는 API 서버. |
| 프론트엔드 | Next.js 14 + React 18 + Tailwind + framer-motion (+ 경량 HTML/CSS/JS 버전) | 리서치 요청·실시간 로그·리포트 열람 UI. |
| 메모리·임베딩 | vector store · text-embedding-3-small · 유사도 0.42 컷 | 모은 자료를 벡터로 저장해 관련 내용만 추려 쓴다. |
| 문서 출력 | python-docx · md2pdf · htmldocx · PyMuPDF | 리포트를 Markdown·PDF·Word로 내보낸다. |
| MCP 연동 | langchain-mcp-adapters · mcp | GitHub·DB 등 MCP 데이터소스를 웹검색과 함께 hybrid로. |
| 인프라 | Docker 멀티스테이지(Chromium+Firefox 내장) · docker-compose · Terraform | JS 렌더링 스크래핑까지 되는 컨테이너 + 배포. |
질문 하나가 들어오면 어디서 어디로 흐르는가.
먼저 독수리 시점으로 전체 그림을 보자. 중심에는 GPTResearcher라는 지휘자 객체가 있고, 그가 여러 스킬(skill) 부하들에게 일을 시킨다 — 검색 계획을 짜는 ResearchConductor, 모은 자료를 추리는 ContextManager·SourceCurator, 리포트를 쓰는 ReportGenerator. 그 바깥에 실제 일손인 retriever(검색)·scraper(스크랩)·LLM·memory(벡터 저장)가 붙는다.
이제 대표 흐름 한 줄기를 입구부터 출구까지 따라가 보자 — 코드의 5%가 일의 95%를 한다. 가장 흔한 사건은 "리서치 한 건을 요청받았다"이다. 핵심은 gpt_researcher/skills/researcher.py의 ResearchConductor가 쥐고 있다.
핵심은 ②와 ⑤다. ②에서 "무엇을 검색할지 스스로 계획"(원 질문을 여러 하위 질문으로 쪼갬)하기 때문에 한 번의 검색으로 끝나지 않고, ⑤에서 "모은 자료 중 질문과 진짜 관련된 부분만 골라" LLM에게 넘기기 때문에 토큰 한계 안에서도 깊이 있는 리포트가 나온다. 이 두 단계가 "딥 리서치"라는 이름의 알맹이다.
이 프로젝트 특유의 관례 하나: GPTResearcher는 모든 기능을 자기가 직접 들고 있지 않고, 스킬 객체들을 조립(composition)해 갖는다. 생성자에서 부하들을 한 줄씩 임명하는 모양이 그대로 보인다.
# gpt_researcher/agent.py — 지휘자가 스킬(부하)들을 '조립'해 갖는다
self.research_conductor = ResearchConductor(self) # 계획·검색·수집
self.context_manager = ContextManager(self) # 벡터로 관련 맥락만 추림
self.source_curator = SourceCurator(self) # 출처 정리·인용
self.report_generator = ReportGenerator(self) # 리포트 작성
self.deep_researcher = DeepResearchSkill(self) # (옵션) 재귀 심층 탐색
덕분에 한 부분만 바꿔 끼우기 쉽다("작성기만 교체", "검색 수집만 커스텀"). 상속 대신 조립으로 짠 이 구조가 코드베이스를 읽는 첫 열쇠다.
한편 gpt-researcher에는 더 무거운 멀티에이전트 모드(multi_agents/)가 따로 있다. 여기선 LangGraph 상태 그래프로 여러 전문 에이전트가 신문사 편집국처럼 협업한다 — ChiefEditorAgent(편집장)가 흐름을 지휘하고, 노드(node) 하나하나가 한 직무를 맡는다.
# multi_agents/agents/orchestrator.py — 편집국 워크플로(LangGraph StateGraph)
browser ─▶ planner ─▶ human ─▶ researcher ─▶ writer ─▶ fact_checker
(초기조사) (개요) (검토) (병렬 연구) (집필) (사실확인)
│ │
└── 사람이 개요를 검토·수정(최대 5회) ◀──────┘ ─▶ publisher (발행)
여기엔 사람이 끼어드는 단계(human-in-the-loop)가 있어서, AI가 짠 리서치 개요를 사람이 승인·수정한 뒤에야 본 조사가 시작된다(최대 5회 수정). "AI에게 다 맡기되 중요한 갈림길에선 사람이 확인"하는 실전 패턴의 좋은 예다.
skills/deep_research.py의 심층 모드는 자료를 나무가 가지를 뻗듯 판다. breadth(너비)=한 단계에서 만들 검색어 수, depth(깊이)=몇 단계까지 더 파고들지. 한 단계에서 검색해 얻은 '배운 점(learnings)'과 '후속 질문'으로 다음 단계의 새 검색어를 만들어 더 깊이 재귀한다. 내려갈수록 너비를 절반으로 줄여(breadth // 2) 폭발을 막고, asyncio.Semaphore로 동시 실행 수를 제한한다.215개 파일, 어디부터 봐야 하나.
파일이 많지만 최상위 폴더 6덩어리로 묶으면 길이 보인다. ★ 표시한 gpt_researcher/가 두뇌(라이브러리)이고, backend/는 웹서버, multi_agents/는 LangGraph 협업 모드, frontend/는 화면이다.
| 위치 | 역할 |
|---|---|
gpt_researcher/agent.py | 모든 것의 진입점. GPTResearcher 클래스가 스킬들을 조립하고 conduct_research()·write_report()를 노출. |
skills/ | 지휘자가 부리는 능력 단위. 계획·수집(researcher), 맥락 압축(context_manager), 출처 정리(curator), 작성(writer), 심층 재귀(deep_research). |
retrievers/ | 검색엔진마다 폴더 하나. 모두 같은 인터페이스라 RETRIEVER 환경변수로 갈아 끼운다 — 어댑터 패턴의 교과서. |
scraper/ | 본문 추출기. 정적 페이지는 BeautifulSoup, JS 페이지는 브라우저(Selenium), PDF는 PyMuPDF로 분기. |
backend/server/ | FastAPI + WebSocket. 리서치 진행 로그를 토큰 단위로 프론트에 실시간 전송. |
multi_agents/agents/ | LangGraph 협업 모드의 전문 에이전트들. 각 파일이 한 직무(editor·writer·fact_checker…). |
config/default.py | 모델 3종·검색기·딥리서치 너비/깊이·온도 등 모든 기본값. "무엇을 바꿀 수 있나"의 목록. |
이 레포에서 배울 만한 것 + 어느 파일을 펴면 되는지.
하위 질문 10개를 하나씩 검색하면 10배 느리다. 이 레포는 asyncio.gather()로 여러 검색·스크랩을 동시에 띄우고, asyncio.Semaphore로 "한꺼번에 몇 개까지"를 제한해 서버·API가 터지지 않게 한다. skills/researcher.py·deep_research.py에서 비동기 병렬 처리의 실전 예제를 통째로 볼 수 있다.
GPTResearcher는 거대한 한 클래스가 아니라, 작은 스킬 객체들을 합성(composition)해 만든다. 새 기능은 스킬을 하나 더 끼우면 된다. "기능이 늘어도 한 파일이 비대해지지 않는" 구조 — 객체지향 설계에서 자주 권하는 "상속보다 조립" 원칙의 살아있는 사례다(agent.py).
multi_agents/agents/orchestrator.py는 StateGraph에 노드(직무)와 간선(순서)을 추가해 편집국 워크플로를 만든다. 조건부 분기(add_conditional_edges)로 사람 검토 → 통과/재작성을 라우팅하는 부분이 백미 — "LLM 호출을 어떻게 안정적인 워크플로로 묶나"를 배운다.
retrievers/의 엔진들은 전부 동일한 인터페이스를 따른다. 그래서 코드를 안 고치고 RETRIEVER=duckduckgo처럼 환경변수만 바꿔도 검색 백엔드가 바뀐다. "구현은 다른데 사용법은 똑같게" 만드는 어댑터(adapter) 패턴을 20번 반복해 둔 표본이라 패턴 학습에 최적이다.
웹에서 긁어온 글을 통째로 LLM에 넣으면 토큰이 폭발한다. context/·memory/는 모은 글을 임베딩 벡터로 바꿔, 질문과 유사도가 높은(0.42 이상) 조각만 골라 넘긴다. "검색해 온 산더미에서 알맹이만 뽑는" RAG의 핵심 기교를 코드로 익힐 수 있다.
리서치는 몇 분 걸린다. 사용자를 빈 화면에서 기다리게 하지 않으려고, backend/server/websocket_manager.py는 각 단계 로그("계획 중…", "검색 중…")를 WebSocket으로 토큰 단위 스트리밍한다. LLM 앱에서 "느린 작업을 어떻게 체감 빠르게 보여주나"의 모범 답안이다.
예컨대 검색 백엔드를 무료인 DuckDuckGo로 바꾸고, 웹+MCP를 동시에 쓰는 hybrid 모드를 켜는 건 환경변수 한 줄씩이다:
# 유료 Tavily 대신 무료 DuckDuckGo로 검색 / 웹+MCP 하이브리드
export RETRIEVER=duckduckgo # 검색기 교체 (코드 수정 0줄)
export RETRIEVER=tavily,mcp # 웹 검색 + MCP 데이터소스 동시
돌리려면 무엇이 필요한가.
무겁진 않다 — 일반 노트북에서 충분히 돈다. 진짜 관문은 컴퓨팅 파워가 아니라 API 키다. 기본 설정은 LLM에 OpenAI, 검색에 Tavily를 쓰므로 두 키가 필요하다(둘 다 무료 대안으로 교체 가능).
| 항목 | 요구사항 |
|---|---|
| Python | 3.11+ (LangChain v1 요구). pip install -r requirements.txt로 설치. |
| API 키 | OPENAI_API_KEY(LLM) + TAVILY_API_KEY(검색)가 기본. OPENAI_BASE_URL로 로컬·호환 모델 사용 가능, 검색도 무료 DuckDuckGo로 교체 가능. |
| Docker (선택) | 이미지에 Chromium·Firefox가 내장돼 JS로 그려지는 페이지까지 스크랩. 멀티스테이지 빌드라 무겁지만 한 방에 풀스택 기동. |
| Node.js (선택) | v18+ — Next.js 프로덕션 프론트엔드를 띄울 때만. 경량 HTML 프론트는 Node 없이도 동작. |
| 메모리/디스크 | 특별한 요구 없음. 임베딩·캐시가 약간 쌓이는 정도. GPU 불필요(추론은 외부 LLM API가 담당). |
| OS | Linux · macOS · Windows 모두. 단 mcp 패키지는 비(非)Windows에서만 설치(가이드에 명시). |
gpt-researcher는 '두뇌를 빌려 쓰는 비서'다. 무거운 생각(LLM 추론)은 OpenAI 같은 외부 두뇌에 외주를 주고, 자기 컴퓨터에선 심부름의 조율(검색 보내기·페이지 긁기·결과 정리)만 한다. 그래서 내 PC 사양은 별로 안 타지만, 외부 두뇌 사용료(API 비용)는 든다.
읽기만 하지 말고 손에 익히는 단계.
가장 빠른 시작. 키 두 개만 넣고 5줄짜리 파이썬으로 첫 리포트를 받아본다. 콘솔에 단계별 로그가 흐르는 걸 관찰하는 게 목표.
pip install gpt-researcher
export OPENAI_API_KEY=... && export TAVILY_API_KEY=...
python -c "import asyncio; from gpt_researcher import GPTResearcher; \
r=GPTResearcher(query='왜 전기차 배터리에 LFP가 늘어나나?'); \
asyncio.run(r.conduct_research()); print(asyncio.run(r.write_report()))"
백엔드(FastAPI)+프론트(Next.js)를 한 번에 올려, 브라우저에서 질문을 넣고 실시간 진행 로그가 흐르는 걸 본다. http://localhost:8000 접속이 성공 신호.
git clone https://github.com/assafelovic/gpt-researcher && cd gpt-researcher
# .env에 OPENAI_API_KEY / TAVILY_API_KEY 채운 뒤
docker-compose up --build
Tavily 키 없이도 돌려본다. RETRIEVER=duckduckgo로 바꾸면 코드 수정 없이 검색 백엔드가 교체된다. 결과 품질·속도가 어떻게 달라지는지 비교하며 어댑터 패턴의 효용을 체감하자.
export RETRIEVER=duckduckgo # tavily → 무료 DuckDuckGo
python -m uvicorn main:app --reload
웹 대신 내 문서를 근거로 답하게 한다. ./my-docs에 PDF를 넣고 report_source="local"로 주면, 사내 자료·논문 더미에 대해 출처 달린 요약을 만들어 준다. RAG가 "내 데이터"에 붙는 순간을 경험하는 단계.
config/default.py의 DEEP_RESEARCH_BREADTH·DEEP_RESEARCH_DEPTH·CONCURRENCY를 바꿔가며, 재귀 트리가 넓어/깊어질 때 리포트 품질 vs 소요 시간·API 비용이 어떻게 변하는지 측정해 본다. s4에서 본 breadth // 2 축소 규칙이 왜 필요한지 몸으로 이해하는 게 목표.
retrievers/custom/을 본떠 사내 검색 API를 새 retriever로 만들거나, RETRIEVER=tavily,mcp로 GitHub·DB 같은 MCP 소스를 웹검색과 함께 쓰게 연결한다. "에이전트에게 새로운 눈을 달아주는" 확장 실습.
한 주씩 따라가는 6주 계획.
| 주차 | 주제 | 학습 자료 |
|---|---|---|
| 1주차 | RAG·딥리서치 개념 잡고 첫 리포트 받아보기 | README + docs.gptr.dev 시작하기 + 실습 1·2 |
| 2주차 | pip 패키지 API와 설정값 이해 (모델 3종·검색·딥리서치 파라미터) | gpt_researcher/config/default.py · pip 문서 + 실습 3 |
| 3주차 | retriever·scraper 구조 — 어댑터 패턴으로 검색·수집 분리 | retrievers/ · scraper/ · actions/ |
| 4주차 | 비동기 병렬 처리 + 맥락 압축(임베딩 유사도) | skills/researcher.py · context/ · memory/ |
| 5주차 | LangGraph 멀티에이전트 + human-in-the-loop 워크플로 | multi_agents/agents/orchestrator.py · LangGraph 문서 |
| 6주차 | 딥 리서치 재귀 + 확장(커스텀 retriever·MCP·프론트엔드) | skills/deep_research.py · mcp-server/ · frontend/ + 실습 5·6 |
본문에 나온 용어 빠른 참조.
| 용어 | 의미 |
|---|---|
| 딥 리서치 에이전트 | 주제를 여러 하위 질문으로 쪼개 반복 조사하며 출처 달린 장문 리포트를 만드는 AI. |
| RAG | 검색 증강 생성. LLM이 외부 자료를 먼저 검색해 그걸 근거로 답하는 방식(오픈북 시험). |
| planner–executor | 기획자 LLM이 할 일 목록을 만들고, 실행자들이 나눠 처리하는 분업 구조. Plan-and-Solve 유래. |
| retriever | 검색기. 질의에 맞는 페이지(URL) 목록을 가져오는 쪽. 20종(tavily·google·arxiv…). |
| scraper | 스크래퍼. URL에 들어가 본문 텍스트를 긁어오는 쪽. 7종(BeautifulSoup·브라우저·PyMuPDF…). |
| Tavily | AI 에이전트용 검색 API. gpt-researcher의 기본 검색기(무료 DuckDuckGo 등으로 교체 가능). |
| LangChain | LLM·도구·프롬프트를 레고처럼 조립하는 파이썬 프레임워크. 이 레포는 v1 사용. |
| LangGraph | LangChain 위에서 멀티에이전트 흐름을 그래프(노드+간선)로 그리는 라이브러리. |
| StateGraph | LangGraph의 핵심 — 상태를 들고 노드 사이를 오가며 작업을 진행하는 그래프 객체. |
| human-in-the-loop | AI 흐름 중간에 사람이 검토·승인·수정하는 단계. 멀티에이전트 모드의 개요 검토(최대 5회). |
| embedding | 임베딩. 글을 의미가 담긴 숫자 벡터로 바꾼 것. 질문과 가까운 자료만 골라낼 때 쓴다. |
| vector store | 임베딩 벡터를 저장·검색하는 저장소. "비슷한 의미 찾기"를 빠르게 해준다. |
| FAST/SMART/STRATEGIC | 작업 난이도별 3단 LLM. 빠른 요약은 FAST, 본문 작성은 SMART, 전략 판단은 STRATEGIC. |
| breadth / depth | 딥 리서치 재귀의 너비(단계별 검색어 수)와 깊이(파고드는 단계 수). 내려갈수록 너비 절반. |
| asyncio / Semaphore | 파이썬 비동기 병렬 도구. gather=동시 실행, Semaphore=동시 개수 제한(과부하 방지). |
| MCP | Model Context Protocol. AI가 GitHub·DB 등 외부 데이터소스에 표준 방식으로 접속하는 규약. |
| WebSocket 스트리밍 | 서버가 진행 로그를 토큰 단위로 실시간 전송하는 통신. "생각 중…"을 흐르게 보여준다. |