GPT Researcher
一句话定位
GPT Researcher 是一个检索增强的自主研究 / 报告生成流水线,而非通用工具-循环(ReAct)agent。它把一个研究问题拆成多个子查询,并发调用一批检索器(retriever)搜索 + 抓取网页,用 embedding 相似度压缩上下文、可选 LLM 打分筛选来源,最后写成带引用的结构化 markdown 报告。深度靠两条可选路径提供:deep research 递归树(每个子查询 fork 一个新的子研究器实例向下钻)和基于 LangGraph 的多 agent 详细报告流水线(editor 规划 → 并行 section 研究 → writer → fact-check → publish,带 human / reviewer 条件回环)。它不是 code agent(无沙箱、不执行 LLM 代码),无跨会话长期记忆,无自进化,轨迹不反哺训练。
核心架构总览(目录结构关键路径 + 引用的 commit)
分析基于 commit 18d405166948e11b4a0304c0c4ec440bead9e4a5(2026-06-28,Merge PR #1820 security/content-hardening)。Python + asyncio,LLM 走 LangChain provider 抽象,多 agent 模块基于 LangGraph StateGraph。License Apache-2.0。
三种交付形态对应三套代码路径:
- 单 researcher(核心库
gpt_researcher/):入口GPTResearcher类(agent.py),主研究循环ResearchConductor(skills/researcher.py)。 - deep research 递归树(
gpt_researcher/skills/deep_research.py的DeepResearchSkill):breadth/depth 树式深挖,每节点 fork 新GPTResearcher。 - LangGraph 多 agent 详细报告(
multi_agents/):ChiefEditorAgent(orchestrator.py)建主状态图,EditorAgent(editor.py)为每个 section 建并行子图。
关键内聚模块(agent.py:185-196 硬装配):ResearchConductor / ReportGenerator / ContextManager(skills/context_manager.py)/ BrowserManager / SourceCurator(skills/curator.py)/ DeepResearchSkill / ImageGenerator。提示集中在 prompts.py 的 PromptFamily(40KB,按模型族分派)。配置默认值在 config/variables/default.py。
Agent Loop(主循环 / 何时继续何时停)
要点:不是 ReAct/自由工具循环,而是有限步固定管线 + 可选递归树。 存在三条循环形态:
-
标准 researcher(
skills/researcher.py: conduct_researchL89-211)——步骤固定、无”是否继续”的自省判断:choose_agent(选 persona)→plan_research(L48-87:先跑一次检索拿 SERP 上下文,再让 LLM 产出固定条数子查询 outline)→ 按report_source分派到_get_context_by_web_search(L266)→ 每个子查询asyncio.gather并发_process_sub_query(L449)→ scrape → embedding 压缩 → 可选 curate(L195-197)→ 返回 context。- 停止条件 = “子查询列表跑完”,无循环反思。子查询条数由
cfg.max_iterations(默认 3,config/variables/default.py:22)+ 原始 query 决定(researcher.py:334-335,非 subtopic 时追加原 query)。
-
Deep research(
skills/deep_research.py: deep_researchL376-538)——递归树,是真正的迭代深挖:- 参数
breadth(默认 4)、depth(默认 2)、concurrency(默认 2),DeepResearchSkill.__init__L247-249。 - 每层:
generate_search_queries产 breadth 个{query, researchGoal}(L259)→ 每个 query 实例化一个新的GPTResearcher子研究器跑完整conduct_research(L422-438)→process_research_results抽取 learnings + followUpQuestions(L344)。 - 递归下钻条件:
if depth > 1(L494)——new_breadth = max(2, breadth//2)、new_depth = depth-1,用上一层 researchGoal + follow-up 拼next_query递归(L500-514)。停止条件 =depth递减到 1。 - 并发用
asyncio.Semaphore(concurrency_limit)(L413)。
- 参数
-
多 agent(LangGraph)——停止靠 conditional edges +
MAX_REVISIONS上限,见”Router / 编排”。
记忆与上下文管理(压缩、长期记忆、会话持久化)
- 无跨会话长期记忆。
self.context是本次运行内累积的 list(agent.py:162);visited_urlsset 去重(researcher.py:_get_new_urlsL728)。 - 上下文压缩 = embedding 相似度过滤,不是 LLM 摘要。
ContextManager.get_similar_content_by_query(context_manager.py:37)用ContextCompressor(context/compression.py:85),底层为 LangChainEmbeddingsFilter,similarity_threshold默认 0.35(compression.py:119),max_results默认 10;小文档量走 direct passthrough(compression.py:162)。 - Deep research 另有硬 word-budget 裁剪:
MAX_CONTEXT_WORDS = 25000,trim_context_to_word_limit保留最近项(deep_research.py:18, 213)。 - 会话持久化:核心库层没有 checkpoint;multi_agents 的 LangGraph 传了
thread_id/thread_tsconfig(orchestrator.py:138-143),但MemorySavercheckpointer 在源码里被注释掉(orchestrator.py:5),所以状态持久化实际未启用。后端另有 report JSON 持久化(backend/server/report_store.py,架构参考文档提及,未直接读源码核对,标注为架构文档口径)。 _research_id仅用于图片生成去重,取 md5(query+timestamp)(agent.py:202)。
工具体系(定义/调用协议/注册/权限)
两套”工具”概念,需分清:
- Retrievers(检索器)——主力”工具”。约 24 个(
retrievers/下含 tavily/google/bing/brave/exa/arxiv/pubmed/semantic_scholar/serper/searx/duckduckgo/mcp… 目录数量级,未逐一点数,标注为”约 24 个”)。注册靠配置字符串cfg.retrievers,actions/retriever.py: get_retrievers按名字加载类。统一 duck-typing 接口:retriever(query, query_domains=...)+.search(max_results=...)(researcher.py:_searchL836)。这不是 LLM 决定调用的工具,而是管线固定遍历所有配置的 retriever。 - MCP 工具——唯一真正”LLM 决定调用”的工具通道,两阶段(
retrievers/mcp/retriever.py;mcp/tool_selector.py+mcp/research.py):- Stage 1 选工具:
MCPToolSelector.select_relevant_tools(tool_selector.py:35)——列出所有 MCP server 暴露的 tool(name+description),用 strategic LLM(temperature 0)产 JSON 选最多 3 个(promptprompts.py: generate_mcp_tool_selection_prompt);失败回退到 pattern 匹配(_fallback_tool_selectionL163,关键词 search/get/read/fetch…)。 - Stage 2 执行:
MCPResearchSkill.conduct_research_with_tools(mcp/research.py:34)——llm_provider.llm.bind_tools(selected_tools)(LangChain 原生 tool-calling)→ainvoke→ 遍历response.tool_calls逐个tool.ainvoke(args)(L79-135)→_process_tool_result把 MCPstructured_content/content归一化成{title,href,body}(L158-270)。 - MCP 执行策略
mcp_strategy:fast(默认,只对原始 query 跑一次并缓存复用,researcher.py:296)/deep(每个子查询都跑)/disabled。解析逻辑agent.py:_resolve_mcp_strategyL216。
- Stage 1 选工具:
- 权限:无工具级权限门,见”安全与权限”。
Prompt 设计(系统提示结构、动态组装)
- 全部提示集中在
prompts.py的PromptFamily类,按模型族分派:PromptFamily/GranitePromptFamily/Granite3/Granite33(L752+),get_prompt_family(L885)按cfg.prompt_family选。 - 动态 persona/role,没有固定系统提示。
choose_agent(actions/agent_creator.py:16)用auto_agent_instructions()(prompts.py:486,few-shot 举例 Finance/Business/Travel Agent)让 LLM 为每个 query 生成{server: "💰 Finance Agent", agent_role_prompt: "You are a seasoned finance analyst…"}。该agent_role_prompt之后作为 system content 注入报告/curate 等调用(如curator.py:64system=role)。 - 报告 prompt
generate_report_prompt(prompts.py:258):把压缩后 context 直接内联进 user message,强约束 markdown + APA 引用 + hyperlink 引用 + 最少字数 + 语言 + 当前日期;含 “this is very important to my career” 之类激励语(约 L310)。 - 子查询 prompt
generate_search_queries_prompt(约 L213)动态注入 SERP context +datetime.now。 - Deep research 的 prompt 内联在
deep_research.py(L259-374)而非 PromptFamily,强制 “Return valid JSON only”。
Router / 编排(任务分解、多 agent、子 agent)
三层编排:
- 子查询分解(最轻):
plan_research把 query 拆成 N 个子查询并发跑。 - Deep research 递归树:每个子查询 fork 一个新
GPTResearcher实例当子 agent(deep_research.py:422),把 MCP 配置向下传播(L433-434)。 - LangGraph 多 agent(
multi_agents/,即”detailed report multi-agent”):ChiefEditorAgent建StateGraph(orchestrator.py:60-97)。主图节点:browser(初始研究)→planner(EditorAgent 规划 sections)→human(可选评审)→researcher(并行深研)→writer→fact_checker→visualizer→publisher→END。- 角色 agent:Editor / Researcher / Reviewer / Reviser / Writer / FactChecker / Publisher / Human / Visualizer(
multi_agents/agents/)。 - 并行子研究:
EditorAgent.run_parallel_research(editor.py:52)为每个 section 建子状态图researcher→reviewer→reviser(editor.py:126-144),全部 sectionasyncio.gather并行。 - 条件路由(真正的 router 逻辑):
- human 节点:
human_feedback is None → accept;revisions>=MAX_REVISIONS(5) → force_accept;否则revise回 planner(orchestrator.py:89-97)。 - fact_checker:
fact_check_notes is None → visualizer,否则回writer(L100-104)。 - section 子图 reviewer:
review is None → END,否则回reviser(editor.py:138-142)。
- human 节点:
- 角色 agent:Editor / Researcher / Reviewer / Reviser / Writer / FactChecker / Publisher / Human / Visualizer(
Skill / 插件体系
- 内部 “skills”(
gpt_researcher/skills/):ResearchConductor / ReportGenerator / ContextManager / BrowserManager / SourceCurator / DeepResearchSkill / ImageGenerator——是代码内聚模块(agent.py:185-196硬装配),不是可插拔运行时插件。 - 真正的插件面:(a) Retriever 插件——加新搜索引擎 = 在
retrievers/加目录并用配置字符串启用;(b) Scraper 插件(scraper/下 bs4/browser/firecrawl/pymupdf/tavily_extract…);(c) MCP 是官方的外部工具插件机制(第三方 MCP server 即插即用,mcp_configs传入);(d) LLM/embedding provider 插件(llm_provider/、memory/embeddings.py支持约 20 个 provider)。 - 仓库另有
.claude/SKILL.md、.codex-plugin/、顶层skills/、mcp-server/——是把 GPTR 作为工具暴露给别的 agent(Claude/Codex),不是 GPTR 自身的插件加载器。
自进化能力(自我改进 / 学习型记忆 / eval 驱动纠错)
- 未实现自进化 / 无学习型记忆。不存在跨运行的经验沉淀、权重更新或 prompt 自动优化。
- 运行内有准 eval 驱动的自纠:多 agent 的
fact_checker→writer回环、reviewer→reviser回环(见 Router 章),是”批评-修订”循环,但每次都从零、无长期学习。 evals/目录(hallucination_eval/、simple_evals/SimpleQA)是离线人工评测脚本,不反哺运行时。- Deep research 的 learnings 只在单次运行的树内累积传递(
deep_research.py:406-538),运行结束即丢弃。 - 结论:自进化维度 = 未实现。
可观测性(日志 / trace 格式)
- 双通道日志:(a) 标准
logging(logger 名research),文件 + console(utils/logging_config.py: setup_research_logging);(b) 结构化 JSON 事件流JSONResearchHandler(logging_config.py:8):写logs/research_<ts>.json,schema ={timestamp, events[], content{query,sources,context,report,costs}},每事件{timestamp,type,data},每次追加即落盘(约 L34)。 GPTResearcher._log_event(agent.py:310)统一分发 tool/action/research 三类事件到可插拔log_handler(on_tool_start/on_agent_action/on_research_step)。- 实时流:
stream_output(actions/utils.py)把带 emoji 的进度经 WebSocket 推前端(贯穿researcher.py)。 - 成本可观测:
add_costs(agent.py:717)按_current_step归集,step_costs分步成本 dict +research_costs总额。 - LangSmith trace:多 agent 若设
LANGCHAIN_API_KEY自动开LANGCHAIN_TRACING_V2(multi_agents/main.py:11-12),子图带 taggpt-researcher(editor.py:70)。
安全与权限(审批门、密钥管理)
- 设计上不是硬化的多租户服务——
SECURITY.md明确威胁模型:后端 FastAPI/WS 默认无鉴权、无网络隔离,by design;SSRF、用户传source_urls/document_urls、配置 MCPcommand/args导致的 RCE 都被列为 operator 配置责任、out of scope(SECURITY.md“Threat model” 段)。即 MCP 可执行任意本地命令,无审批门。 - in-scope 安全:untrusted 内容(抓取网页 / LLM 输出)的 XSS / 不安全渲染、解压炸弹 DoS、路径穿越、依赖漏洞。分析所用 HEAD commit 正是
security/content-hardening(sanitize untrusted content + pin brotli 防解压炸弹)。 - 密钥管理:走环境变量(
.env/os.environ),provider key(OPENAI_API_KEY 等)由 LangChain provider 读取;无内置 secret vault。 - 一个防污染修复:MCP 配置刻意不写
os.environ,只改self.cfg.retrievers,避免并发请求间 env 污染(agent.py:_process_mcp_configsL282-308,fix issue #1676)。 - 人审批门:仅多 agent 的
HumanAgent.review_plan(multi_agents/agents/human.py)——对研究计划(sections outline)做 human-in-the-loop 评审,靠task.include_human_feedback开关(默认 false)。这是内容评审门,不是工具/权限门。
沙箱与执行隔离
- 无沙箱。GPTR 不执行 LLM 生成的代码(不是 code agent)。
- 唯一”执行”面是网页抓取:
scraper/支持 bs4 / Selenium / nodriver / playwright-browser / firecrawl / pymupdf。浏览器抓取跑--no-sandbox(scraper/browser/browser.py:107,是关闭 Chrome 沙箱以便容器内运行,非隔离增强)。 scraper/scraper.py:96会subprocess pip install缺失的 scraper 依赖(tavily-python / firecrawl-py)——运行时装包,非隔离。- MCP 工具在主进程内通过 LangChain 调用(stdio/http/ws 连到外部 server),执行隔离取决于 MCP server 自身,GPTR 侧无额外隔离层。
- 并发隔离靠
WorkerPool(utils/workers.py)+MAX_SCRAPER_WORKERS=15(config/variables/default.py:25)。
与模型的协同设计
- 三档 LLM 角色分工(
config/variables/default.py:7-12):FAST_LLM= gpt-4o-mini(轻任务,token 上限 3000)SMART_LLM= gpt-4.1(长报告写作 / curate,上限 6000,选 persona 也用它)STRATEGIC_LLM= o4-mini(推理密集:MCP 选工具、deep research 规划、抽 learnings,上限 4000)EMBEDDING= text-embedding-3-small
- reasoning_effort 显式调节:deep research 的 plan / 抽 learnings 用
ReasoningEfforts.High(deep_research.py:338,370);MCP 选工具用 temperature 0(tool_selector.py:154)。 - Provider 无关:
GenericLLMProvider.from_provider(mcp/research.py:60)+ LangChain,支持 OpenAI/Anthropic/Google/Groq/Ollama…;tool-calling 依赖 provider 的bind_tools(mcp/research.py:66)。 - 模型族适配 prompt:
GranitePromptFamily等专门为 IBM Granite 改文档拼接格式(prompts.py:752-855)——对小模型/特定模型做了 prompt 层协同。 - 鲁棒性设计:多处
json_repair+ 正则兜底解析 LLM JSON 输出(deep_research.py:_load_repaired_jsonL62、agent_creator.py: handle_json_error、tool_selector.py:88),对齐”模型输出不稳定 JSON”的现实。
轨迹利用(session/trajectory 是否反哺训练/评测)
- 不反哺训练。轨迹(events JSON、context、sources、costs)落
logs/*.json与后端 report store,仅供回放 / 调试 / 成本核算 / 前端展示。 - 评测:
evals/(SimpleQA、hallucination_eval)是独立离线评测,读固定 inputs 跑 GPTR 出报告再打分,结果落evals/*/results/,不闭环回运行时或训练。 - 无 RLHF/DPO/SFT 数据导出管道,无 trajectory→dataset 转换。
- 结论:轨迹利用(训练侧)= 未实现;仅观测 / 评测侧使用。
与同类 harness 的关键差异(1-3 条)
- “研究报告流水线”而非”工具循环 agent”:主路径是固定步的 plan→search→scrape→compress→curate→write,停止靠子查询跑完,而非 LLM 自省”是否继续”。相比 ReAct 型 code agent(如 SWE-agent / OpenHands),它把”迭代”外移到 deep research 递归树和多 agent 条件回环,而不是单循环内的工具自由调用。
- 上下文压缩用 embedding 相似度过滤而非 LLM 摘要(
similarity_threshold=0.35的EmbeddingsFilter)——这是它省 token、抗长文档的核心手法,与多数”map-reduce LLM 摘要”型 research agent 不同。 - 工具体系双轨:约 24 个 retriever 是管线固定遍历(非 LLM 决定),MCP 才是唯一 LLM 自主工具通道且硬上限选 ≤3 个工具——把”检索广度”与”LLM 自主性”解耦,降低了自由 tool-calling 的成本与不确定性。
原始源码定位
- repo: https://github.com/assafelovic/gpt-researcher
- commit/version analyzed:
18d405166948e11b4a0304c0c4ec440bead9e4a5(2026-06-28 11:39:06 -0700,Merge PR #1820security/content-hardening) - 关键文件列表(相对 repo 根):
gpt_researcher/agent.py— 主 orchestratorGPTResearcher(入口、装配 skills、成本/日志、MCP 配置处理)gpt_researcher/skills/researcher.py—ResearchConductor主研究循环gpt_researcher/skills/deep_research.py—DeepResearchSkill递归 breadth/depth 深挖gpt_researcher/skills/context_manager.py—ContextManagerembedding 相似度压缩gpt_researcher/skills/curator.py—SourceCuratorLLM 打分筛选来源gpt_researcher/context/compression.py—ContextCompressor/VectorstoreCompressor/WrittenContentCompressorgpt_researcher/prompts.py—PromptFamily提示模板 + 模型族分派gpt_researcher/actions/agent_creator.py—choose_agent动态 persona/rolegpt_researcher/actions/query_processing.py— 子查询规划、检索结果获取gpt_researcher/mcp/tool_selector.py—MCPToolSelectorLLM 选工具gpt_researcher/mcp/research.py—MCPResearchSkillbind_tools + 执行工具调用gpt_researcher/retrievers/mcp/retriever.py— MCP retriever(两阶段)gpt_researcher/config/variables/default.py— 默认配置(模型、token 上限、并发)gpt_researcher/utils/logging_config.py— JSON 事件日志 handlergpt_researcher/memory/embeddings.py—Memory多 provider embeddinggpt_researcher/scraper/browser/browser.py— 浏览器抓取(--no-sandbox)multi_agents/agents/orchestrator.py—ChiefEditorAgentLangGraph 主状态图multi_agents/agents/editor.py—EditorAgent规划 sections + 并行子研究子图multi_agents/agents/human.py—HumanAgenthuman-in-the-loop 计划评审multi_agents/main.py,task.json— 多 agent 入口与任务配置SECURITY.md— 官方威胁模型声明(安全维度权威口径)
一手源存档(sources/)
保存在 /Users/zhao/projects/self-wiki/ai-research/sources/harness/gpt-researcher/:
NOTES.md— 第一阶段源码级调研笔记(元信息、读过的文件清单、12 维度带行号笔记)src/agent.py— 主 orchestrator 留档src/researcher.py— 主研究循环留档src/deep_research.py— 递归深挖引擎留档src/context_manager.py— 上下文压缩留档src/curator.py— 来源筛选留档src/prompts.py— 提示模板 + 模型族分派留档src/mcp_tool_selector.py— MCP 选工具留档src/mcp_research.py— MCP 执行工具留档src/config_default.py— 默认配置留档src/ma_orchestrator.py— 多 agent 主状态图留档(multi_agents/agents/orchestrator.py)src/ma_editor.py— 多 agent editor/并行子图留档(multi_agents/agents/editor.py)src/architecture_ref.md— 官方仓库内架构参考文档留档