🎯 一款可定制、具备反检测功能的云浏览器,由自主研发的 Chromium驱动,专为网页爬虫AI 代理设计。👉立即试用
返回博客

GPT 研究员 + 无废料 MCP:为您的研究代理提供一个真实的提取层

Alex Johnson
Alex Johnson

Senior Web Scraping Engineer

07-Aug-2026

TL;DR:

  • GPT Researcher 通过检索器读取网页,mcp 检索器将任何 MCP 服务器变成研究来源——因此,Scrapeless MCP 服务器成为实际获取页面的层。
  • 设置 RETRIEVER=mcp 是强制性的。如果在没有它的情况下传递 mcp_configs,MCP 检索器将保持关闭状态,运行将回退到配置的其他内容。
  • stdio 传输是今天可用的路径:npx -y scrapeless-mcp-server@0.4.9SCRAPELESS_KEYenv 中加载所有 21 个工具。
  • 远程 connection_url 路径在发布的客户端中没有身份验证。connection_token 作为不受支持的 token 参数到达传输,connection_headers 完全没有到达它。
  • 固定你的版本。gpt-researcher==0.16.0 在导入时引发 NameError,从 1.28 开始发行的 mcplangchain-mcp-adapters 中禁用 MCP 支持而没有错误消息。
  • scrape_markdownhttps://quotes.toscrape.com/ 上通过检索器返回 4308 个字符的页面内容,你可以在任何模型介入之前调用它。
  • 只有研究运行本身需要一个模型提供者密钥;加载和调用工具只需要你的 Scrapeless 密钥。
  • Scrapeless 免费计划 开始,并为你的研究代理提供一个真正的获取层。

GPT Researcher 计划一个查询,搜索,读取找到的内容,并撰写引用报告。搜索部分得到了很好的服务——它提供了 Google、Bing、Brave、arXiv、PubMed 等的检索器。阅读部分是自主研究悄然退化之处:检索器返回的 URL 列表,仍然需要某些东西将这些 URL 转换为文本。当该获取返回一个挑战页面或一个空壳时,报告照样会被撰写,来自于返回的任何稀薄内容。

mcp 检索器更改了在该插槽中生成的内容。将 GPT Researcher 指向 MCP 服务器,该服务器的工具变成研究表面,因此页面获取通过为此构建的基础设施运行,而不是普通的 HTTP 获取。此指南将 GPT Researcher 连接到 Scrapeless MCP 服务器,列出它暴露的工具,实际调用一个,并准确标记出第一个需要模型提供者密钥的步骤。

Scrapeless MCP 服务器为研究代理提供的内容

Scrapeless MCP 服务器通过模型上下文协议公开抓取和浏览器工具,因此获取层是你的代理调用的内容,而不是你构建的内容。一个连接服务于 21 个工具:scrape_markdownscrape_html 用于页面内容,google_searchgoogle_trends 用于搜索数据,scrape_screenshot 用于捕获,还有一套 16 个工具的 browser_*,通过导航、点击、输入、滚动和等待驱动云浏览器。

对研究代理而言,重要的是 scrape_markdown。GPT Researcher 的报告质量依赖于其收集的文本,而 markdown 已经是其上下文所需的形状。browser_* 工具在源仅在交互后渲染时变得重要——它们在 Scrapeless 云浏览器 上运行,因此代理可以在研究机器上无需浏览器即可访问渲染页面。

协议层遵循 模型上下文协议规范,其信息通过 JSON-RPC 2.0 规范 发送。如果你想先在其自身的条款上解释协议,什么是 MCP 涉及此内容,而 LangChain + Scrapeless MCP 显示了将相同服务器接入不同栈的情况。

先决条件

  • Python 3.10 或更高版本。
  • 在运行研究的机器上安装 Node.js,因为 stdio 传输通过 npx 启动服务器。
  • 从仪表板获得的 Scrapeless API 密钥,导出为 SCRAPELESS_KEY
  • 一种模型提供者密钥,例如 OPENAI_API_KEY。GPT Researcher 在构建研究对象时构建嵌入客户端,因此该变量必须在该步骤之前设置——在调用 MCP 工具时,所有内容都可以在没有它的情况下工作。

安装

版本固定在这里不是可选的,并且有两个特定的固定版本正在实际工作。

bash Copy
pip install "gpt-researcher==0.15.1" "langchain-mcp-adapters==0.3.1" "mcp==1.27.2"

gpt-researcher==0.16.0 不能被导入。它的 actions/query_processing.py 定义了一个帮助程序,其签名注释了 AnyListfrom typing import Any, List, Dict 之上几行的内容,并且因为普通 def 上的注释在函数对象构建时被评估 — 行为 延期注释提案 旨在改变 — 所以导入在其他任何代码运行之前引发 NameError: name 'Any' is not defined。版本 0.15.1 没有这个顺序问题。

mcp 引脚更微妙。langchain-mcp-adapters 将其需求声明为 mcp>=1.9.2,这是在 Python 依赖项标识符规范 所描述的无界底部,因此新安装会拉取最新的 mcp。自 1.28 版起,发布不再从 mcp.shared.context 导出 RequestContext,而适配器在模块加载时进行导入。GPT Researcher 捕获到该导入错误,并将内部可用性标志设置为 false,因此 MCP 不会大声失败 — 它只是不再存在,而您的研究会在不触及服务器的情况下运行。

在 shell 中而不是在源代码中设置您的密钥。

bash Copy
export SCRAPELESS_KEY="your_api_key_here"

通过标准输入连接并列出工具

GPT Researcher 的 MCP 层接受服务器配置字典的列表。对于一个标准输入服务器,您需要提供一个名称、命令、参数,以及服务器所需的任何环境。 MCPClientManager 将其转换为传输配置并运行握手。

python Copy
import asyncio
import os

from gpt_researcher.mcp.client import MCPClientManager

SCRAPELESS = {
    "name": "scrapeless",
    "command": "npx",
    "args": ["-y", "scrapeless-mcp-server@0.4.9"],
    "env": {"SCRAPELESS_KEY": os.environ["SCRAPELESS_KEY"], "PATH": os.environ["PATH"]},
}


async def main() -> None:
    manager = MCPClientManager([SCRAPELESS])
    tools = await manager.get_all_tools()
    print("tool count:", len(tools))
    print("tools:", ", ".join(sorted(tool.name for tool in tools)))


asyncio.run(main())

PATH 包含在 env 中。服务器进程是使用您提供的确切环境生成的,因此漏掉 PATH 意味着 npx 将无法被找到。

实时服务器仅返回设置了 Scrapeless 密钥的 21 个工具。

text Copy
tool count: 21
tools: browser_click, browser_close, browser_create, browser_get_html, browser_get_text, browser_go_back, browser_go_forward, browser_goto, browser_press_key, browser_screenshot, browser_scroll, browser_scroll_to, browser_snapshot, browser_type, browser_wait, browser_wait_for, google_search, google_trends, scrape_html, scrape_markdown, scrape_screenshot

为什么远程 URL 路径仍然不工作

GPT Researcher 文档中的配置表列出了 connection_urlconnection_token 用于远程服务器,这似乎是托管端点的自然选择。在发布的客户端中,没有任何密钥能让您获得经过身份验证的连接,而在您花费一个下午之前,值得了解一下原因。

这两种失败在没有网络调用的情况下都是可见的,因为 convert_configs_to_langchain_format 是决定传输接收到什么的函数。

python Copy
from gpt_researcher.mcp.client import MCPClientManager

URL = "https://api.scrapeless.com/mcp"

with_token = MCPClientManager([
    {"name": "s", "connection_url": URL, "connection_token": "PLACEHOLDER"},
]).convert_configs_to_langchain_format()["s"]

with_headers = MCPClientManager([
    {"name": "s", "connection_url": URL, "connection_headers": {"x-api-token": "PLACEHOLDER"}},
]).convert_configs_to_langchain_format()["s"]

print("connection_token   ->", sorted(with_token))
print("connection_headers ->", sorted(with_headers))
text Copy
connection_token   -> ['token', 'transport', 'url']
connection_headers -> ['transport', 'url']

connection_token 变成了 token 密钥,而流可传输的 HTTP 会话工厂不接受它 — 连接尝试以 _create_streamable_http_session() got an unexpected keyword argument 'token' 结束,而工具列表则返回为空。

connection_headers 完全未能在转换中生存。将其复制的分支测试 server_config.get("connection_type"),但转换仅写入 transport 密钥,因此测试永远不匹配,标头被丢弃。由于 Scrapeless 端点通过 x-api-token 标头进行身份验证,因此请求未经过身份验证前抵达。因此工具列表也是空的,这就是为什么这两个症状从外部看起来相同。

在发布能够将标头复制到传输的代码之前,使用标准输入。它会到达相同的服务器和相同的 21 个工具。

在代理存在之前调用工具

通过 MCP 客户端加载的工具是普通的可调用对象,因此您可以单独使用提取层。这是确认您的密钥和传输是否正确的最便宜方法,并且不需要模型提供者的密钥。

python Copy
import asyncio
import os

from gpt_researcher.mcp.client import MCPClientManager

SCRAPELESS = {
    "name": "scrapeless",
    "command": "npx",
    "args": ["-y", "scrapeless-mcp-server@0.4.9"],
    "env": {"SCRAPELESS_KEY": os.environ["SCRAPELESS_KEY"], "PATH": os.environ["PATH"]},
}


def as_text(result) -> str:
    if isinstance(result, str):
        return result
    if isinstance(result, (list, tuple)):
        parts = [b["text"] for b in result if isinstance(b, dict) and "text" in b]
        if parts:
            return "\n".join(parts)
    return str(result)


async def main() -> None:
    manager = MCPClientManager([SCRAPELESS])
    tools = await manager.get_all_tools()
    scrape = next(tool for tool in tools if tool.name == "scrape_markdown")
    text = as_text(await scrape.ainvoke({"url": "https://quotes.toscrape.com/"}))
    print("characters:", len(text))
    print("first line:", text.split("\n")[0])


asyncio.run(main())

该调用以 Markdown 形式返回页面,包装在协议定义的内容包裹中。 as_text 扁平化该包裹,这一点很重要,因为原始返回结果是一个块的列表,而不是字符串。

text Copy
characters: 4308
first line: Response:

将配置交给研究者

在已经证明传输有效的情况下,使用相同的字典输入 GPTResearcher。必须对齐两件事:RETRIEVER 必须命名 mcpmcp_configs 必须携带服务器。错过环境变量,MCP 提取器将永远无法构造,这是这种集成看似什么也不做的最常见原因。

注意:这个块是一个先决条件缺口。 GPTResearcher__init__ 期间构建了一个嵌入客户端,因此在对象存在之前需要 OPENAI_API_KEY,而 conduct_research 消耗真实的模型积分。本文的验证环境没有模型提供者密钥,因此下面的接线被确认到包括提取器解析,研究调用本身未被执行。

python Copy
import asyncio
import os

os.environ["RETRIEVER"] = "mcp"

from gpt_researcher import GPTResearcher

SCRAPELESS = {
    "name": "scrapeless",
    "command": "npx",
    "args": ["-y", "scrapeless-mcp-server@0.4.9"],
    "env": {"SCRAPELESS_KEY": os.environ["SCRAPELESS_KEY"], "PATH": os.environ["PATH"]},
}


async def main() -> None:
    researcher = GPTResearcher(
        query="Which quotes and authors appear on quotes.toscrape.com?",
        mcp_configs=[SCRAPELESS],
    )
    await researcher.conduct_research()
    report = await researcher.write_report()
    print(report)


asyncio.run(main())

该赋值必须在 GPTResearcher 被构造之前完成,因为研究者在构建其配置对象时读取环境。将其放在导入之前,在这里是最不容易出错的排序。
RETRIEVER=mcp 使 Scrapeless 成为唯一的研究来源,适合有关特定页面的问题。 RETRIEVER=tavily,mcp 和类似的组合在其旁边保留一个搜索引擎,这样代理可以以一种方式找到候选来源,并以另一种方式读取它们。还有 MCP_STRATEGY,它默认为 fast 并针对主查询运行一次 MCP 步骤;deep 对每个生成的子查询运行,并相应地增加成本。

准备好为你的研究代理提供一个在真实来源上可靠的提取层吗? 创建一个免费的 Scrapeless 账户,并在几行代码中将其连接。

结论

一旦版本固定和传输选择确定,接线就很简单:在 gpt-researcher==0.15.1 上安装 mcp==1.27.2,设置 RETRIEVER=mcp,并传递一个 stdio mcp_configs 条目指向 scrapeless-mcp-server。这会产生 21 个工具,而 scrape_markdown 在模型参与之前就返回真实页面内容——这使得提取层可以独立测试,而不必通过完成的报告来调试。

这两个陷阱值得记住,因为它们都不会自我提醒。未固定的 mcp 默默关闭 MCP 支持,而远程 connection_url 路径会将你的凭据掉在地板上。两者都会产生同样的症状,即代理进行研究而从未调用你的服务器。首先检查工具数量;如果不是 21,后续的所有操作都不会正常。

Scrapeless 定价页面 比较计划,完整的工具参考在 Scrapeless 文档 中。

常见问题

问:我需要模型提供者密钥仅仅为了测试 MCP 连接吗?

不需要。加载工具并调用它们完全通过 MCP 客户端进行,因此 Scrapeless 密钥足以确认传输正常,以及在真实 URL 上调用 scrape_markdown。一旦你构造 GPTResearcher,模型密钥就变得必要,因为在初始化期间构建了一个嵌入客户端。

问:为什么我的运行忽略了 MCP 服务器,即使我传递了 mcp_configs?

RETRIEVER 环境变量几乎总是原因。单独的 mcp_configs 不会启用 MCP 检索器;RETRIEVER 必须指定 mcp,无论是单独还是在列表中,如 tavily,mcp。在构造 GPTResearcher 之前设置它,因为在研究人员构建其配置时会读取该值。

问:我可以连接到托管的 Scrapeless 端点,而不是在本地运行服务器吗?

在发布的客户端中,不可以通过 mcp_configsconnection_token 作为一个参数传递给流式 HTTP 会话,但该参数不被接受,而 connection_headers 在到达传输之前便在配置转换中被丢弃。stdio 传输连接到同一服务器,并暴露相同的 21 个工具,因此它是目前的工作路径。

问:快速和深入的 MCP 策略有什么区别?

fast,默认情况下,使用主查询运行 MCP 步骤一次。deep 为代理生成的每个子查询运行它,这扩大了覆盖范围并乘以工具调用和模型花费。首先着眼于 fast,仅在特定报告返回较薄时才能转向 deep

问:我应该单独使用 RETRIEVER=mcp 还是将其与搜索检索器结合使用?

当你已经知道哪些页面重要时,单独使用 mcp,因为代理会跳过子查询生成,并处理你指向的来源。当发现是工作的一个部分时,结合使用,如 tavily,mcp,搜索检索器找到候选项,而 MCP 工具读取它们。

问:为什么要固定 mcp 而不是采用最新版本?

langchain-mcp-adapters 需要 mcp>=1.9.2 并且没有上限,因此新环境安装最新版本。从 1.28 开始,RequestContext 不再从 mcp.shared.context 导出,适配器的导入失败,GPT Researcher 记录 MCP 为不可用而不是抛出。固定 mcp==1.27.2 可以保持适配器可导入。

问:工具数量是我应该在设置中检查的东西吗?

是的,这是最快的诊断方式。21 的计数意味着传输、密钥和适配器都在正常工作。零意味着连接从未通过身份验证,而在 get_all_tools 期间的任何异常都会被记录而不是抛出,因此空列表就是你代码中失败连接的表现。

问:scrape_markdown 实际上返回什么?
一个协议内容块的列表,而不是一个普通字符串,文本块包含页面的Markdown。在测量或存储之前将其扁平化——将返回值视为字符串会导致列表的Python表示,而不是页面。

在Scrapeless,我们仅访问公开可用的数据,并严格遵循适用的法律、法规和网站隐私政策。本博客中的内容仅供演示之用,不涉及任何非法或侵权活动。我们对使用本博客或第三方链接中的信息不做任何保证,并免除所有责任。在进行任何抓取活动之前,请咨询您的法律顾问,并审查目标网站的服务条款或获取必要的许可。

最受欢迎的文章

目录