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),主研究循环 ResearchConductorskills/researcher.py)。
  • deep research 递归树gpt_researcher/skills/deep_research.pyDeepResearchSkill):breadth/depth 树式深挖,每节点 fork 新 GPTResearcher
  • LangGraph 多 agent 详细报告multi_agents/):ChiefEditorAgentorchestrator.py)建主状态图,EditorAgenteditor.py)为每个 section 建并行子图。

关键内聚模块(agent.py:185-196 硬装配):ResearchConductor / ReportGenerator / ContextManager(skills/context_manager.py)/ BrowserManager / SourceCurator(skills/curator.py)/ DeepResearchSkill / ImageGenerator。提示集中在 prompts.pyPromptFamily(40KB,按模型族分派)。配置默认值在 config/variables/default.py

Agent Loop(主循环 / 何时继续何时停)

要点:不是 ReAct/自由工具循环,而是有限步固定管线 + 可选递归树。 存在三条循环形态:

  1. 标准 researcherskills/researcher.py: conduct_research L89-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)。
  2. Deep researchskills/deep_research.py: deep_research L376-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)。
  3. 多 agent(LangGraph)——停止靠 conditional edges + MAX_REVISIONS 上限,见”Router / 编排”。

记忆与上下文管理(压缩、长期记忆、会话持久化)

  • 无跨会话长期记忆self.context 是本次运行内累积的 list(agent.py:162);visited_urls set 去重(researcher.py:_get_new_urls L728)。
  • 上下文压缩 = embedding 相似度过滤,不是 LLM 摘要ContextManager.get_similar_content_by_querycontext_manager.py:37)用 ContextCompressorcontext/compression.py:85),底层为 LangChain EmbeddingsFiltersimilarity_threshold 默认 0.35(compression.py:119),max_results 默认 10;小文档量走 direct passthrough(compression.py:162)。
  • Deep research 另有硬 word-budget 裁剪MAX_CONTEXT_WORDS = 25000trim_context_to_word_limit 保留最近项(deep_research.py:18, 213)。
  • 会话持久化:核心库层没有 checkpoint;multi_agents 的 LangGraph 传了 thread_id/thread_ts config(orchestrator.py:138-143),但 MemorySaver checkpointer 在源码里被注释掉(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.retrieversactions/retriever.py: get_retrievers 按名字加载类。统一 duck-typing 接口:retriever(query, query_domains=...) + .search(max_results=...)researcher.py:_search L836)。这不是 LLM 决定调用的工具,而是管线固定遍历所有配置的 retriever。
  • MCP 工具——唯一真正”LLM 决定调用”的工具通道,两阶段(retrievers/mcp/retriever.pymcp/tool_selector.py + mcp/research.py):
    • Stage 1 选工具:MCPToolSelector.select_relevant_toolstool_selector.py:35)——列出所有 MCP server 暴露的 tool(name+description),用 strategic LLM(temperature 0)产 JSON 选最多 3 个(prompt prompts.py: generate_mcp_tool_selection_prompt);失败回退到 pattern 匹配(_fallback_tool_selection L163,关键词 search/get/read/fetch…)。
    • Stage 2 执行:MCPResearchSkill.conduct_research_with_toolsmcp/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 把 MCP structured_content/content 归一化成 {title,href,body}(L158-270)。
    • MCP 执行策略 mcp_strategyfast(默认,只对原始 query 跑一次并缓存复用,researcher.py:296)/ deep(每个子查询都跑)/ disabled。解析逻辑 agent.py:_resolve_mcp_strategy L216。
  • 权限:无工具级权限门,见”安全与权限”。

Prompt 设计(系统提示结构、动态组装)

  • 全部提示集中在 prompts.pyPromptFamily 类,按模型族分派PromptFamily / GranitePromptFamily / Granite3 / Granite33(L752+),get_prompt_family(L885)按 cfg.prompt_family 选。
  • 动态 persona/role,没有固定系统提示choose_agentactions/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:64 system=role)。
  • 报告 prompt generate_report_promptprompts.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 多 agentmulti_agents/,即”detailed report multi-agent”):ChiefEditorAgentStateGraphorchestrator.py:60-97)。主图节点:browser(初始研究)→planner(EditorAgent 规划 sections)→human(可选评审)→researcher(并行深研)→writerfact_checkervisualizerpublisher→END。
    • 角色 agent:Editor / Researcher / Reviewer / Reviser / Writer / FactChecker / Publisher / Human / Visualizer(multi_agents/agents/)。
    • 并行子研究EditorAgent.run_parallel_researcheditor.py:52)为每个 section 建子状态图 researcher→reviewer→revisereditor.py:126-144),全部 section asyncio.gather 并行。
    • 条件路由(真正的 router 逻辑)
      • human 节点:human_feedback is None → acceptrevisions>=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,否则回 revisereditor.py:138-142)。

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_checkerwriter 回环、reviewerreviser 回环(见 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 事件流 JSONResearchHandlerlogging_config.py:8):写 logs/research_<ts>.json,schema = {timestamp, events[], content{query,sources,context,report,costs}},每事件 {timestamp,type,data},每次追加即落盘(约 L34)。
  • GPTResearcher._log_eventagent.py:310)统一分发 tool/action/research 三类事件到可插拔 log_handleron_tool_start/on_agent_action/on_research_step)。
  • 实时流stream_outputactions/utils.py)把带 emoji 的进度经 WebSocket 推前端(贯穿 researcher.py)。
  • 成本可观测add_costsagent.py:717)按 _current_step 归集,step_costs 分步成本 dict + research_costs 总额。
  • LangSmith trace:多 agent 若设 LANGCHAIN_API_KEY 自动开 LANGCHAIN_TRACING_V2multi_agents/main.py:11-12),子图带 tag gpt-researchereditor.py:70)。

安全与权限(审批门、密钥管理)

  • 设计上不是硬化的多租户服务——SECURITY.md 明确威胁模型:后端 FastAPI/WS 默认无鉴权、无网络隔离,by design;SSRF、用户传 source_urls/document_urls、配置 MCP command/args 导致的 RCE 都被列为 operator 配置责任、out of scopeSECURITY.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_configs L282-308,fix issue #1676)。
  • 人审批门:仅多 agent 的 HumanAgent.review_planmulti_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-sandboxscraper/browser/browser.py:107,是关闭 Chrome 沙箱以便容器内运行,非隔离增强)。
  • scraper/scraper.py:96subprocess pip install 缺失的 scraper 依赖(tavily-python / firecrawl-py)——运行时装包,非隔离。
  • MCP 工具在主进程内通过 LangChain 调用(stdio/http/ws 连到外部 server),执行隔离取决于 MCP server 自身,GPTR 侧无额外隔离层。
  • 并发隔离靠 WorkerPoolutils/workers.py)+ MAX_SCRAPER_WORKERS=15config/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.Highdeep_research.py:338,370);MCP 选工具用 temperature 0(tool_selector.py:154)。
  • Provider 无关GenericLLMProvider.from_providermcp/research.py:60)+ LangChain,支持 OpenAI/Anthropic/Google/Groq/Ollama…;tool-calling 依赖 provider 的 bind_toolsmcp/research.py:66)。
  • 模型族适配 promptGranitePromptFamily 等专门为 IBM Granite 改文档拼接格式(prompts.py:752-855)——对小模型/特定模型做了 prompt 层协同。
  • 鲁棒性设计:多处 json_repair + 正则兜底解析 LLM JSON 输出(deep_research.py:_load_repaired_json L62、agent_creator.py: handle_json_errortool_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 条)

  1. “研究报告流水线”而非”工具循环 agent”:主路径是固定步的 plan→search→scrape→compress→curate→write,停止靠子查询跑完,而非 LLM 自省”是否继续”。相比 ReAct 型 code agent(如 SWE-agent / OpenHands),它把”迭代”外移到 deep research 递归树和多 agent 条件回环,而不是单循环内的工具自由调用。
  2. 上下文压缩用 embedding 相似度过滤而非 LLM 摘要similarity_threshold=0.35EmbeddingsFilter)——这是它省 token、抗长文档的核心手法,与多数”map-reduce LLM 摘要”型 research agent 不同。
  3. 工具体系双轨:约 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 #1820 security/content-hardening
  • 关键文件列表(相对 repo 根):
    • gpt_researcher/agent.py — 主 orchestrator GPTResearcher(入口、装配 skills、成本/日志、MCP 配置处理)
    • gpt_researcher/skills/researcher.pyResearchConductor 主研究循环
    • gpt_researcher/skills/deep_research.pyDeepResearchSkill 递归 breadth/depth 深挖
    • gpt_researcher/skills/context_manager.pyContextManager embedding 相似度压缩
    • gpt_researcher/skills/curator.pySourceCurator LLM 打分筛选来源
    • gpt_researcher/context/compression.pyContextCompressor / VectorstoreCompressor / WrittenContentCompressor
    • gpt_researcher/prompts.pyPromptFamily 提示模板 + 模型族分派
    • gpt_researcher/actions/agent_creator.pychoose_agent 动态 persona/role
    • gpt_researcher/actions/query_processing.py — 子查询规划、检索结果获取
    • gpt_researcher/mcp/tool_selector.pyMCPToolSelector LLM 选工具
    • gpt_researcher/mcp/research.pyMCPResearchSkill bind_tools + 执行工具调用
    • gpt_researcher/retrievers/mcp/retriever.py — MCP retriever(两阶段)
    • gpt_researcher/config/variables/default.py — 默认配置(模型、token 上限、并发)
    • gpt_researcher/utils/logging_config.py — JSON 事件日志 handler
    • gpt_researcher/memory/embeddings.pyMemory 多 provider embedding
    • gpt_researcher/scraper/browser/browser.py — 浏览器抓取(--no-sandbox
    • multi_agents/agents/orchestrator.pyChiefEditorAgent LangGraph 主状态图
    • multi_agents/agents/editor.pyEditorAgent 规划 sections + 并行子研究子图
    • multi_agents/agents/human.pyHumanAgent human-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 — 官方仓库内架构参考文档留档