☰
基于 Bright Data MCP + LangChain 构建实时网页问答 AI Agent:完整实战教程(TaoToken 统一 Key 配置版)
2026/9/26 3:32:06 网站建设 项目流程

1. 为什么你的 Agent 需要“实时网页”这只手

大模型的知识有截止日期,这是常识。你问它“今天有什么科技新闻”,它要么编一个看起来很像真的答案,要么直接说不知道。问题不在于模型不够聪明,而在于它被关在训练数据的笼子里,看不到笼子外面的世界。

Bright Data MCP 就是给模型开的那扇窗。MCP 全称 Model Context Protocol,你可以把它理解成 AI 世界的 USB-C 接口:以前每接一个数据源都要写一套专用代码,现在只要服务端实现了 MCP 协议,客户端就能用统一方式调用工具。Bright Data 提供的这个 MCP Server 内置了两个非常实用的工具——search_engine负责抓取主流搜索引擎的结果,scrape_as_markdown负责把指定网页内容转成干净的 Markdown 文本。这两个工具组合起来,Agent 就有了“先搜再读”的能力。

LangChain 这边负责的是编排。ReAct Agent 的核心思路是让模型在“思考”和“行动”之间循环:先想清楚需要什么信息,再决定调用哪个工具,拿到结果后继续推理,直到能给出最终答案。DeepSeek 作为驱动模型,负责这个推理过程。整条链路跑通之后,你问“某公司最新发布了什么产品”,Agent 会自动搜索、抓取、总结,而不是靠记忆瞎猜。

这篇文章面向的是想动手跑通实时网页问答 Agent 的开发者。我会给出 TaoToken 统一 Key 的完整配置骨架,包括config.toml和settings.json两个文件,然后演示一次端到端的问答验证。你跟着做,应该能在半小时内看到 Agent 返回带来源的实时答案。

2. TaoToken 前置:统一 Key 与两个配置文件

TaoToken 在这里扮演的角色是模型调用的统一入口。你不需要分别去申请 DeepSeek 的 Key、再配一套 OpenAI 兼容层,TaoToken 提供了一个统一的 API 地址和 Key,LangChain 的ChatOpenAI类可以直接对接。官网地址是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 端点是 https://taotoken.net/api ,注意 API 地址不带 UTM 参数。

先解决 Key 的问题。登录之后进入控制台,在 API Keys 页面创建一个新 Key。这个 Key 同时用于模型对话和后续可能的 Coding Plan 场景。创建时建议给它起个能认出来的名字,比如web-qa-agent-dev,方便后面排查问题时区分。

拿到 Key 之后,我们需要两个配置文件。第一个是config.toml,放在项目根目录,用来管理模型端点和默认参数:

# config.toml [llm] base_url = "https://taotoken.net/api" api_key = "sk-your-taotoken-key-here" model = "deepseek-chat" temperature = 0.1 max_tokens = 2048 [mcp] brightdata_api_token = "your-brightdata-token-here" search_engine = "google" scrape_timeout = 60 [agent] max_iterations = 5 verbose = true

第二个是settings.json,这个文件主要给 MCP 客户端读取,同时也方便你在不同环境之间切换配置:

{ "mcpServers": { "brightdata": { "command": "npx", "args": ["@brightdata/mcp"], "env": { "API_TOKEN": "your-brightdata-token-here" } } }, "taotoken": { "base_url": "https://taotoken.net/api", "api_key": "sk-your-taotoken-key-here", "default_model": "deepseek-chat" } }

这里有个细节要注意:config.toml里的base_url填的是 TaoToken 的 API 地址,LangChain 的ChatOpenAI会把它当作 OpenAI 兼容端点来用。DeepSeek 模型通过这个端点调用时,请求格式和 OpenAI 一致,所以不需要额外适配层。Bright Data 的 token 单独放在[mcp]段里,和模型 Key 分开管理,避免混在一起。

如果你还没有 Bright Data 的 API Token,去官网注册后可以在用户设置页面找到。免费额度是每月 5000 次请求,前三个月免费,对于开发和测试来说完全够用。把两个 Token 都填好之后,配置文件这部分就完成了。

3. 可复制配置:MCP 客户端与 ReAct Agent 骨架

配置文件准备好之后,接下来写代码。我习惯把 MCP 客户端和 Agent 逻辑分开,这样调试的时候能清楚知道是哪一层出了问题。

先写mcp_client.py,它负责和 Bright Data MCP Server 通信。这里用subprocess调用npx @brightdata/mcp的方式,把搜索和抓取封装成两个方法:

# mcp_client.py import json import subprocess import os from typing import Dict, Any import tomllib class BrightDataMCPClient: """Bright Data MCP 客户端封装""" def __init__(self, config_path: str = "config.toml"): with open(config_path, "rb") as f: config = tomllib.load(f) self.api_token = config["mcp"]["brightdata_api_token"] self.search_engine = config["mcp"].get("search_engine", "google") self.timeout = config["mcp"].get("scrape_timeout", 60) def _build_env(self) -> dict: env = dict(os.environ) env["API_TOKEN"] = self.api_token return env def search_web(self, query: str) -> Dict[str, Any]: """调用 search_engine 工具搜索网页""" cmd = ( f'npx @brightdata/mcp search_engine ' f'--query "{query}" --engine {self.search_engine}' ) try: result = subprocess.run( cmd, shell=True, capture_output=True, text=True, env=self._build_env(), timeout=30 ) if result.returncode == 0: return {"success": True, "data": result.stdout, "query": query} return {"success": False, "error": result.stderr, "query": query} except subprocess.TimeoutExpired: return {"success": False, "error": "搜索超时", "query": query} def scrape_webpage(self, url: str) -> Dict[str, Any]: """调用 scrape_as_markdown 工具抓取网页""" cmd = f'npx @brightdata/mcp scrape_as_markdown --url "{url}"' try: result = subprocess.run( cmd, shell=True, capture_output=True, text=True, env=self._build_env(), timeout=self.timeout ) if result.returncode == 0: return {"success": True, "content": result.stdout, "url": url} return {"success": False, "error": result.stderr, "url": url} except subprocess.TimeoutExpired: return {"success": False, "error": "抓取超时", "url": url}

然后是web_qa_agent.py,这里用 LangChain 的create_react_agent来组装 Agent。关键点是把 MCP 的两个方法包装成 LangChain 的Tool,让模型能通过 ReAct 循环调用它们:

# web_qa_agent.py from langchain.agents import Tool, AgentExecutor, create_react_agent from langchain.prompts import PromptTemplate from langchain_openai import ChatOpenAI from mcp_client import BrightDataMCPClient import tomllib class WebQAAgent: """实时网页问答 Agent""" def __init__(self, config_path: str = "config.toml"): with open(config_path, "rb") as f: config = tomllib.load(f) self.llm = ChatOpenAI( api_key=config["llm"]["api_key"], base_url=config["llm"]["base_url"], model=config["llm"]["model"], temperature=config["llm"].get("temperature", 0.1), ) self.mcp = BrightDataMCPClient(config_path) self.tools = self._create_tools() self.agent = self._create_agent(config) def _create_tools(self): def search_tool(query: str) -> str: result = self.mcp.search_web(query) if result["success"]: return f"搜索结果:\n{result['data'][:3000]}" return f"搜索失败: {result['error']}" def scrape_tool(url: str) -> str: result = self.mcp.scrape_webpage(url) if result["success"]: return f"网页内容:\n{result['content'][:3000]}" return f"抓取失败: {result['error']}" return [ Tool( name="search_web", description="搜索网页信息。输入搜索关键词,返回搜索结果列表。", func=search_tool, ), Tool( name="scrape_webpage", description="抓取指定网页的详细内容。输入网页 URL,返回 Markdown 文本。", func=scrape_tool, ), ] def _create_agent(self, config): prompt = PromptTemplate( template="""你是一个实时网页问答助手,能搜索和抓取网页内容来回答问题。 可用工具: {tools} 使用格式: Question: 用户问题 Thought: 思考需要什么信息 Action: 工具名称,必须是 [{tool_names}] 之一 Action Input: 工具输入 Observation: 工具返回结果 ... (可重复多次) Thought: 我现在知道最终答案了 Final Answer: 最终回答 开始! Question: {input} Thought: {agent_scratchpad}""", input_variables=["input", "agent_scratchpad", "tools", "tool_names"], ) agent = create_react_agent(llm=self.llm, tools=self.tools, prompt=prompt) return AgentExecutor( agent=agent, tools=self.tools, verbose=config["agent"].get("verbose", True), max_iterations=config["agent"].get("max_iterations", 5), handle_parsing_errors=True, ) def ask(self, question: str) -> dict: try: result = self.agent.invoke({"input": question}) return {"success": True, "answer": result["output"]} except Exception as e: return {"success": False, "error": str(e)}

依赖方面,requirements.txt里需要这几个包:

langchain>=0.2.0 langchain-openai>=0.1.0 tomli>=2.0.0

Python 3.11 以上自带tomllib,如果是 3.10 及以下,把tomllib换成tomli并调整导入即可。装完依赖,代码骨架就齐了。

4. 验证请求:一次端到端问答的完整过程

代码写完了,现在跑一次真实请求来验证整条链路。我选一个需要实时信息的问题:“LangChain 最近发布了什么新版本,主要更新了什么?”

先写一个简单的测试脚本:

# test_agent.py from web_qa_agent import WebQAAgent agent = WebQAAgent() result = agent.ask("LangChain 最近发布了什么新版本,主要更新了什么?") if result["success"]: print("=== 最终回答 ===") print(result["answer"]) else: print("=== 出错 ===") print(result["error"])

运行python test_agent.py,你会看到 Agent 的思考过程。因为verbose=True,终端会打印出 ReAct 循环的每一步。典型的输出结构是这样的:

> Entering new AgentExecutor chain... Thought: 我需要搜索 LangChain 最新版本的信息 Action: search_web Action Input: LangChain latest release notes Observation: 搜索结果: 1. LangChain v0.3 发布说明 - ... 2. LangChain changelog - ... ... Thought: 搜索结果里有官方发布说明的链接,我需要抓取详细内容 Action: scrape_webpage Action Input: https://example.com/langchain-release Observation: 网页内容: # LangChain v0.3 Release ... Thought: 我现在知道最终答案了 Final Answer: LangChain 最近发布了 v0.3 版本,主要更新包括...

最终回答会包含具体的版本号和更新要点,而且这些信息是从实时网页抓取的,不是模型记忆里的旧数据。你可以对比一下:如果直接问模型“LangChain 最新版本是什么”,它很可能给出一个过时的答案,或者干脆说“我的知识截止到某时间”。而经过 MCP 抓取之后,Agent 拿到的是当前网页上的真实内容。

这里有个验证技巧:在scrape_webpage的返回里,我限制了[:3000]的截断长度。这是为了防止网页内容过长导致 token 消耗过大。如果你抓取的页面内容特别多,可以适当调大这个值,但要注意模型的上下文窗口限制。实测下来,3000 字符对于大多数新闻页和文档页已经够用了。

另外,max_iterations=5这个参数控制 ReAct 循环的最大次数。如果 Agent 在 5 步之内没有得出最终答案,它会停止并返回当前状态。对于“搜索→抓取→总结”这种典型流程,3 到 4 步就够了。如果你发现 Agent 经常用完迭代次数还没给出答案,可以检查一下工具描述是否清晰,或者把max_iterations调到 8。

5. 本篇常见错排查

跑通之后,你可能会遇到一些报错。我把几个高频问题和排查思路整理出来,方便你对照。

第一个坑:npx @brightdata/mcp找不到命令。这个报错通常是因为 Node.js 环境没装好,或者 npx 不在 PATH 里。先在终端单独执行npx @brightdata/mcp --help,确认能正常输出帮助信息。如果提示command not found,去 Node.js 官网装一个 LTS 版本。另外,Windows 环境下subprocess.run的shell=True是必须的,否则 npx 可能无法正确解析。

第二个坑:API_TOKEN 无效或权限不足。Bright Data MCP 返回的错误信息里如果出现401或unauthorized,检查config.toml里的brightdata_api_token是否填对。注意不要有多余的空格或换行。另外,免费账户的 5000 次请求额度是按月重置的,如果当月用完了,也会返回权限错误。去 Bright Data 控制台确认一下用量。

第三个坑:LangChain 的 ReAct 解析失败。报错信息类似Could not parse LLM output。这通常是因为模型没有严格按照Action: xxx的格式输出。DeepSeek 在中文场景下偶尔会加一些额外的解释文字。解决办法是在 prompt 里强调“只输出指定格式”,或者把handle_parsing_errors=True保持开启,让 Agent 自动重试。如果频繁出现,可以把temperature调到 0,减少模型的自由发挥。

第四个坑:TaoToken 的 base_url 配置错误。如果你看到Connection error或404,检查config.toml里的base_url是不是https://taotoken.net/api。注意末尾不要加/v1或其他路径,LangChain 的ChatOpenAI会自动拼接/chat/completions。另外,API Key 要以sk-开头,如果复制的时候漏了前缀,也会认证失败。

第五个坑:抓取网页超时。有些页面加载特别慢,或者有反爬机制,scrape_webpage可能会超时。config.toml里的scrape_timeout默认是 60 秒,如果目标站点响应慢,可以调到 120。但更根本的解决办法是换一个来源页面,或者先用search_engine找到更轻量的页面再抓取。

第六个坑:Agent 不调用工具直接回答。如果你发现 Agent 没有走搜索流程,而是直接用模型知识回答了,检查一下工具描述是否足够明确。search_web的描述里要强调“当需要实时信息时使用”,scrape_webpage要说明“当需要网页详细内容时使用”。另外,在 prompt 里加一句“对于时效性问题,必须先搜索再回答”,能有效引导模型的行为。

6. 把 Key 和工具链固定下来,后面的事就顺了

整条链路跑通之后,你会发现最花时间的不是写代码,而是配置和排错。TaoToken 的统一 Key 省去了多平台切换的麻烦,Bright Data MCP 把网页抓取的脏活封装成了两个工具调用,LangChain 的 ReAct 循环负责编排推理步骤。这三者组合起来,就是一个能实时看网页、能推理、能回答的 Agent。

如果你后续要做更复杂的编码任务或者长时间运行的 Agent,可以了解一下 Coding Plan 相关的配置方式,它和本篇的 Key 体系是打通的。模型对话的调试入口在模型对话页面,接入文档在接入文档,API Key 管理在API Keys。这几个地址建议存一下,后面调参和排错会经常用到。

最后说一个实用技巧:把config.toml里的verbose设为true只在开发阶段用,上线前记得关掉,否则日志会非常长。另外,search_engine默认用 google,如果你主要搜中文内容,可以试试换成 bing,返回结果的格式略有不同,但都能被scrape_as_markdown正常处理。跑通一次之后,把测试脚本里的问题换成你真正关心的领域,比如“某开源项目最新 issue 里讨论了什么”,看看 Agent 能不能给出带来源的答案。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询