Skip to content

🔎 Web Research Agent:让 Agent 帮你做深度调研,输出带引用的报告

📌 这篇适合谁

  • 每天花大量时间搜索、阅读、整理信息的知识工作者
  • 想做一个"真正有用"的 Agent 项目但不想做 ChatBot 的开发者
  • 学完 ReAct + Tool Use 想挑战"多工具协作"场景的 Lv2 毕业生
  • 对 Deep Research / Perplexity 类产品好奇、想自己实现一个的人

这是案例库「个人项目」系列的第四篇。

我是🍋AI小柠檬。这个 Agent 是我日常写文章时的"研究助手"——给它一个主题,它自动搜索、阅读、交叉验证、输出带引用来源的报告。每周帮我省 3-5 小时的调研时间。


写在前面:为什么"搜索+阅读+总结"是 Agent 的杀手级场景 🎯

⚠️ 我之前的调研流程

写一篇关于"LangGraph vs CrewAI"的对比文章,我之前的流程:

  1. Google 搜索 "LangGraph vs CrewAI 2026"(5 分钟)
  2. 打开前 10 个链接,逐个阅读(40 分钟)
  3. 发现有些文章过时了,再搜一轮(10 分钟)
  4. 把关键信息复制到笔记里(20 分钟)
  5. 整理成结构化对比(30 分钟)
  6. 回头找每个观点的出处链接(15 分钟)

总计 2 小时。 其中真正需要"我的判断力"的只有第 5 步。其余全是机械操作。

现在:给 Agent 一句话"对比 LangGraph 和 CrewAI 的优劣势,面向生产环境",10 分钟出报告,我花 15 分钟审核修改。从 2 小时压缩到 25 分钟。


1️⃣ 这个 Agent 的能力边界 📦

✅ 能做的

能力说明
多源搜索同时搜 Google、学术论文、GitHub
深度阅读读取网页全文,不是只看摘要
交叉验证多个来源说同一件事才采信
带引用输出每个观点标注来源 URL
追问深挖第一轮搜索不够,自动生成新查询继续搜

❌ 不能做的

  • 不能访问付费墙内容(WSJ、知网等)
  • 不能保证 100% 准确(仍需人工审核)
  • 不适合实时性极强的信息(股价、突发新闻)

2️⃣ 技术架构:ReAct + 四个工具 🏗

💡 核心设计

一个 ReAct Agent + 四个工具(搜索、阅读、笔记、输出)。Agent 自主决定搜几轮、读哪些页面、什么时候信息够了可以写报告。

架构图

用户输入: "对比 LangGraph 和 CrewAI"

┌─────────────────────────────────────┐
│           ReAct Agent               │
│   (System Prompt: 研究分析师)        │
│                                     │
│   Tool 1: web_search()              │ ← 搜索引擎查询
│   Tool 2: read_page()               │ ← 读取网页全文
│   Tool 3: take_note()               │ ← 记录关键信息+来源
│   Tool 4: write_report()            │ ← 生成最终报告
└─────────────────────────────────────┘

输出: Markdown 报告(带 [来源] 引用)

为什么需要 take_note 工具

这是很多人做 Research Agent 时忽略的关键设计。如果 Agent 搜索了 10 个网页,所有内容都堆在 Context 里,会:

  1. Token 爆炸:10 个网页 × 3000 字 = 30000 字,一轮对话就烧完预算
  2. 信息淹没:LLM 在超长 Context 里找关键信息的能力会下降(Lost in the Middle 问题)

take_note 的作用:Agent 每读完一个网页,提取关键信息存到"笔记本"里,然后原文可以丢掉。最终写报告时只看笔记本,不看原文。


3️⃣ 完整代码 💻

工具定义

python
# tools.py
import json
import httpx
from langchain_core.tools import tool

# === 搜索工具 ===
@tool
def web_search(query: str, num_results: int = 5) -> str:
    """搜索互联网,返回相关网页的标题、URL 和摘要。
    用于获取某个主题的初始信息来源。"""

    # 方案 A:Tavily(专为 Agent 设计的搜索 API,有免费额度)
    # 方案 B:SerpAPI(Google 搜索代理)
    # 方案 C:Bing Search API

    # 使用 Tavily 示例
    from tavily import TavilyClient
    client = TavilyClient(api_key="YOUR_TAVILY_KEY")

    results = client.search(
        query=query,
        max_results=num_results,
        include_raw_content=False,  # 只要摘要,不要全文
    )

    formatted = []
    for r in results["results"]:
        formatted.append({
            "title": r["title"],
            "url": r["url"],
            "snippet": r["content"][:200],
        })

    return json.dumps(formatted, ensure_ascii=False)


# === 网页阅读工具 ===
@tool
def read_page(url: str) -> str:
    """读取指定 URL 的网页全文内容。
    返回清洗后的纯文本(去除导航、广告等干扰内容)。
    内容会被截断到 4000 字以控制 Token 消耗。"""

    # 方案 A:Jina Reader API(免费,效果最好)
    # 方案 B:自己用 BeautifulSoup 解析
    # 方案 C:Firecrawl(付费,支持 JS 渲染)

    # 使用 Jina Reader(免费,无需 API Key)
    reader_url = f"https://r.jina.ai/{url}"
    headers = {"Accept": "text/plain"}

    try:
        resp = httpx.get(reader_url, headers=headers, timeout=15, follow_redirects=True)
        if resp.status_code != 200:
            return f"无法读取该页面(HTTP {resp.status_code})"

        content = resp.text
        # 截断到 4000 字(约 2000 Token)
        if len(content) > 4000:
            content = content[:4000] + "\n\n[... 内容已截断 ...]"

        return content
    except Exception as e:
        return f"读取失败:{str(e)}"


# === 笔记工具 ===
_notebook: list[dict] = []  # 全局笔记本

@tool
def take_note(content: str, source_url: str, relevance: str = "high") -> str:
    """将一条研究发现记录到笔记本。
    每条笔记包含:内容摘要、来源 URL、相关度。
    最终写报告时会基于笔记本内容生成。"""

    note = {
        "id": len(_notebook) + 1,
        "content": content,
        "source": source_url,
        "relevance": relevance,
    }
    _notebook.append(note)

    return f"✅ 已记录笔记 #{note['id']}(当前共 {len(_notebook)} 条笔记)"


@tool
def write_report(topic: str, style: str = "对比分析") -> str:
    """基于笔记本中的所有笔记,生成最终的研究报告。
    报告格式为 Markdown,每个观点带 [来源] 引用。
    在收集了足够信息后调用此工具。"""

    if not _notebook:
        return "❌ 笔记本为空,请先搜索和阅读网页,用 take_note 记录关键信息"

    # 整理笔记供 LLM 生成报告
    notes_text = "\n".join([
        f"[{n['id']}] {n['content']} (来源: {n['source']})"
        for n in _notebook
    ])

    return f"请基于以下 {len(_notebook)} 条笔记生成{style}报告:\n\n{notes_text}"

Agent 组装

python
# agent.py
from langchain_openai import ChatOpenAI
from langgraph.prebuilt import create_react_agent
from tools import web_search, read_page, take_note, write_report

SYSTEM_PROMPT = """你是一个专业的研究分析师。你的任务是针对用户给定的主题,进行深度网络调研,输出带引用的研究报告。

## 工作流程

1. **搜索阶段**:用 web_search 搜索 2-3 个不同角度的查询词
2. **阅读阶段**:从搜索结果中选择最相关的 3-5 个页面,用 read_page 阅读
3. **记录阶段**:每读完一个页面,用 take_note 记录关键发现和来源
4. **验证阶段**:如果某个观点只有一个来源,再搜一轮验证
5. **输出阶段**:笔记够了之后,用 write_report 生成最终报告

## 搜索策略

- 第一轮:搜索主题的核心关键词
- 第二轮:搜索主题的对比/评价/缺点
- 第三轮(可选):搜索最新动态/更新

## 质量标准

- 每个核心观点至少有 2 个独立来源支撑
- 不要只看一篇文章就下结论
- 区分"事实"和"观点"——事实需要引用,观点需要标注是谁的观点
- 如果信息有冲突,两边都记录,在报告里说明分歧

## 输出格式

最终报告用 Markdown 格式,结构:
- 一句话结论
- 详细分析(分点)
- 每个观点后标注 [来源N]
- 末尾附完整引用列表
"""

tools = [web_search, read_page, take_note, write_report]
llm = ChatOpenAI(model="gpt-4o", temperature=0.2)

agent = create_react_agent(llm, tools, prompt=SYSTEM_PROMPT)


def research(topic: str) -> str:
    """执行一次完整的研究任务"""
    global _notebook
    _notebook = []  # 清空笔记本

    result = agent.invoke({
        "messages": [("user", f"请深度调研以下主题并输出报告:{topic}")]
    })

    return result["messages"][-1].content

运行示例

python
report = research("LangGraph vs CrewAI:2026 年生产环境该选哪个")
print(report)

输出:

markdown
# LangGraph vs CrewAI:2026 年生产环境选型报告

## 一句话结论

LangGraph 适合需要精细控制流程的生产系统,CrewAI 适合快速搭建角色协作的原型。
生产环境首选 LangGraph。

## 详细对比

### 1. 架构理念

- **LangGraph**:声明式状态图,开发者定义节点和边,完全控制执行流程 [来源1][来源3]
- **CrewAI**:角色扮演范式,定义 Agent 角色和任务,框架自动编排 [来源2][来源4]

### 2. 生产就绪度

- LangGraph 内置 Checkpointer(持久化)、Interrupt(人工介入)、流式输出 [来源1]
- CrewAI 的生产特性较少,缺乏原生的状态持久化和断点恢复 [来源3][来源5]

### 3. 学习曲线

- LangGraph 概念较多(State/Node/Edge/Conditional),上手需要 2-3 天 [来源1]
- CrewAI 上手极快,定义角色和任务即可运行,30 分钟出 Demo [来源2]

...

## 引用

- [来源1] LangGraph 官方文档 - https://langchain-ai.github.io/langgraph/
- [来源2] CrewAI 官方文档 - https://docs.crewai.com/
- [来源3] "LangGraph in Production" - https://blog.langchain.dev/...
- [来源4] "CrewAI vs LangGraph" - https://medium.com/...
- [来源5] Reddit r/LangChain 讨论 - https://reddit.com/...

4️⃣ 搜索 API 选型 📡

📊 三大搜索 API 对比

API价格特点适合场景
Tavily免费 1000 次/月,$20/月起专为 AI Agent 设计,返回结构化结果⭐ Agent 首选
SerpAPI$50/月 5000 次Google 搜索代理,结果最全需要 Google 级搜索质量
Bing Search API免费 1000 次/月微软出品,Azure 生态已有 Azure 账号

我的选择:Tavily。原因:

  1. 免费额度够个人用(1000 次/月)
  2. 返回的结果已经做了相关性排序,比原始 Google 结果更适合 Agent
  3. include_raw_content 选项,搜索和阅读一步完成

5️⃣ 关键设计决策 🧠

决策 1:搜几轮?

python
# 不要写死"搜 3 轮"——让 Agent 自己判断
# 在 System Prompt 里写:
# "如果前两轮搜索已经覆盖了主题的主要方面,不需要第三轮"
# "如果发现信息有冲突或空白,追加搜索"

经验值:大多数主题 2 轮搜索就够。第一轮搜核心关键词,第二轮搜对立观点/最新动态。超过 3 轮通常是 Agent 在"打转"。

决策 2:读几个页面?

Token 成本计算:

每个页面 ≈ 4000 字 ≈ 2000 Token(输入)
GPT-4o 输入价格:$2.5 / 1M Token
读 5 个页面:5 × 2000 = 10000 Token = $0.025

加上 Agent 推理的输出 Token:约 $0.05/次调研

结论:读 3-5 个页面是性价比最高的。超过 5 个页面边际收益递减。

决策 3:怎么处理信息冲突?

python
# 在 take_note 里加 confidence 字段
@tool
def take_note(content: str, source_url: str, confidence: str = "high") -> str:
    """
    confidence 取值:
    - high: 多个来源一致,或来自权威来源(官方文档、论文)
    - medium: 单一来源,但来源可信
    - low: 单一来源,或来源可信度不明
    """
    ...

Agent 写报告时,low confidence 的信息会被标注"待验证"。


6️⃣ 生产化补丁 🔧

补丁问题解决方案
缓存同一个 URL 重复读取浪费钱用 URL 做 key 缓存 read_page 结果,TTL 24 小时
并发读取串行读 5 个页面太慢用 asyncio.gather 并发读取
Token 预算单次调研不能无限烧钱设 max_steps=15,超过就强制输出当前笔记
域名白名单搜索结果可能包含垃圾站优先读取 github.com、官方文档、arxiv.org
结果持久化报告生成后想回看自动保存到本地 Markdown 文件

缓存实现

python
import hashlib
import json
from pathlib import Path
from datetime import datetime, timedelta

CACHE_DIR = Path(".cache/pages")
CACHE_DIR.mkdir(parents=True, exist_ok=True)
CACHE_TTL = timedelta(hours=24)


def get_cached_page(url: str) -> str | None:
    """检查缓存"""
    key = hashlib.md5(url.encode()).hexdigest()
    cache_file = CACHE_DIR / f"{key}.json"

    if cache_file.exists():
        data = json.loads(cache_file.read_text())
        cached_time = datetime.fromisoformat(data["timestamp"])
        if datetime.now() - cached_time < CACHE_TTL:
            return data["content"]
    return None


def cache_page(url: str, content: str):
    """写入缓存"""
    key = hashlib.md5(url.encode()).hexdigest()
    cache_file = CACHE_DIR / f"{key}.json"
    cache_file.write_text(json.dumps({
        "url": url,
        "content": content,
        "timestamp": datetime.now().isoformat(),
    }, ensure_ascii=False))

并发读取

python
import asyncio
import httpx

async def read_pages_concurrent(urls: list[str]) -> list[str]:
    """并发读取多个网页"""
    async with httpx.AsyncClient(timeout=15) as client:
        tasks = [
            client.get(f"https://r.jina.ai/{url}", headers={"Accept": "text/plain"})
            for url in urls
        ]
        responses = await asyncio.gather(*tasks, return_exceptions=True)

    results = []
    for resp in responses:
        if isinstance(resp, Exception):
            results.append(f"读取失败:{str(resp)}")
        elif resp.status_code == 200:
            results.append(resp.text[:4000])
        else:
            results.append(f"HTTP {resp.status_code}")

    return results

7️⃣ 和 Perplexity / Deep Research 的区别 🤔

维度Perplexity你的 Research Agent
搜索深度1 轮搜索2-3 轮,可追问
阅读深度摘要级全文阅读
可定制性不可定制完全可控(Prompt/工具/策略)
成本$20/月~$0.05/次(按需付费)
输出格式固定自定义(对比表/报告/PPT 大纲)
数据隐私数据经过第三方本地运行,数据不出境

你的 Agent 的独特价值:可以针对你的领域定制搜索策略。比如我做 AI Agent 调研时,会在 Prompt 里写"优先搜索 GitHub、LangChain 博客、Anthropic 文档"——这是 Perplexity 做不到的。


8️⃣ 常见坑 🚨

症状修复
Agent 搜索打转反复搜同一个关键词Prompt 里加"不要重复搜索已搜过的关键词" + 传入搜索历史
网页内容太长Token 爆炸 / 超时read_page 截断到 4000 字
搜索结果全是广告报告质量差加域名白名单过滤
引用链接失效报告里的链接打不开缓存原文 + 在报告里标注"访问日期"
Agent 过早写报告只搜了一轮就输出Prompt 里加"至少搜索 2 轮、记录 5 条笔记后才能写报告"
信息过时引用了 2023 年的文章搜索时加年份限制"2025 OR 2026"

🎓 总结

🎯 一句话总结

Web Research Agent = ReAct + 搜索/阅读/笔记/报告四个工具。核心设计是"笔记本模式"——读完就记、记完就丢原文,避免 Token 爆炸。搜索用 Tavily(免费额度够用),阅读用 Jina Reader(免费无限制)。每次调研成本约 $0.05,比 Perplexity 便宜且可定制。


💡 自测题

  1. 为什么需要 take_note 工具?不用它直接让 Agent 记住所有网页内容会怎样?
  2. 搜索策略为什么要分多轮?每轮搜什么?
  3. 怎么处理多个来源信息冲突的情况?
  4. 和 Perplexity 相比,自建 Research Agent 的核心优势是什么?
  5. 单次调研的 Token 成本大约是多少?怎么控制?

答不上来的,回头再读一遍对应章节 ✍️


📌 转载声明

本文为🍋AI小柠檬原创。 转载请注明来源,付费产品包内禁止转载。

© 2026 🍋AI小柠檬 · 内容原创,转载请注明出处