在人工智能的浪潮下,各家 AI 公司纷纷推出了能够深入研究特定主题并生成全面报告的智能系统,例如 OpenAI 的 Deep Research。这些 Agentic AI 系统提供了自动化的流程,能够处理多步骤搜索和信息整合等复杂任务。然而,大多数现有解决方案都是闭源的,难以定制,并且限制了你对模型行为和数据处理的控制。

想象一下,你希望使用不同的 LLM(大型语言模型),集成自定义的搜索引擎,或者调整 Agent 的规划和行为方式,以生成特定格式的输出。这引出了一个有趣的问题:你是否可以构建自己的深度搜索 Agent,拥有完全的灵活性,可以使用你喜欢的 AI 模型,集成你首选的搜索引擎,并自定义 Agent 的行为方式?答案是肯定的。本文将引导你利用 Novita AI、LangChain 和 Tavily 构建自己的深度研究 Agent

什么是 Deep Search Agentic Workflow?

传统的搜索过程通常耗时且效率低下,需要筛选大量的在线信息,最终可能无法得出有价值的见解。Deep Search Agentic Workflow 通过 AI Agent 解决这些问题,这些 Agent 可以自主推理、做出决策并协调外部工具,形成一个完整的工作流程。

这个工作流程通常包括以下几个关键步骤:

  • 任务结构化 (Task Structuring): AI Agent 将主题分解为更小的、更易于管理的任务。例如,如果主题是“气候变化的影响”,Agent 可能会将其分解为“对农业的影响”、“对海平面上升的影响”和“对极端天气事件的影响”等子任务。
  • 规划 (Planning): Agent 为每个任务制定策略,这有助于工作流程确定任务执行的顺序。例如,Agent 可能会决定首先研究气候变化对农业的影响,然后再研究对海平面上升的影响,因为它认为农业是更紧迫的问题。
  • 工具集成 (Tool Integration): 为了收集信息,Agent 使用不同的工具,例如数据库和网络搜索引擎。没有这些工具,Agent 无法执行工作流程中的关键操作。例如,Agent 可能会使用 Tavily API 来执行网络搜索,或者使用学术数据库来查找相关的研究论文。
  • 信息整合 (Synthesis): Agent 分析收集到的信息,并将其整合为连贯的数据。例如,Agent 可能会将来自不同来源的数据进行比较和对比,以得出关于气候变化影响的结论。
  • 报告生成 (Report Generation): 在整合数据后,Agent 会创建一个结构化的报告,其中包含摘要和引文。例如,Agent 可能会生成一份关于气候变化影响的报告,其中包含对每个子任务的摘要、相关的统计数据和对来源的引文。

打造深度搜索 Agent 所需的工具

在深入了解构建过程之前,我们需要准备好必要的工具。

Novita AI

构建 Agentic Workflow 需要一个 LLM,而 Novita AI 提供了一个经济实惠、高性能的 API,让你可以访问最新的 LLM、图像生成模型等。Novita AI 兼容 OpenAI,可以直接使用 Langchain 的 ChatOpenAI 模块进行集成,使用方式与 OpenAI 模型无异。关键在于将 base_url 设置为 Novita 的 API 端点:https://api.novita.ai/v3/openai。这使得开发者能够利用 Novita AI 强大的计算能力和丰富的大模型资源,构建出色的 AI 应用。例如,我们就可以使用 llama-4-maverick-17b-128e-instruct-fp8 模型,这是一个强大的 LLaMA 4 变体,经过微调,专门用于指令遵循任务。

Tavily

Tavily 是一个针对 AI 应用优化的高级网络搜索 API。通过 Tavily 的搜索引擎工具,我们可以让我们的 Agent 访问互联网,以帮助收集准确和公正的信息。Tavily 专门为 AI 应用设计,可以更有效地提供相关和可靠的搜索结果,减少噪音和冗余信息,从而提高 Agent 的效率和准确性。

LangChain

LangChain 是一个开源框架,专为使用 LLM 构建应用程序而设计。通过 LangChain,你可以创建一个 Agentic Workflow,它可以逐步推理、使用工具并与 API 交互。对于我们的深度研究 Agent,我们将使用 LangChain 来构建研究过程,使用网络搜索等工具,并将所有内容整合为结构化的报告。LangChain 提供了构建复杂 AI 应用所需的模块化组件和抽象,例如提示模板、内存管理和工具集成,使得开发过程更加高效和便捷。

Google Colab

在本教程中,我们将使用 Google Colab 构建和测试深度搜索 Agent。Colab 允许你在浏览器中编写和运行 Python 代码,无需进行任何设置;这使得教程简单易懂。Google Colab 提供了免费的 GPU 资源,方便开发者进行大模型的实验和开发,特别适合于构建和测试计算密集型的 AI 应用。

工作流程概述

深度 Agentic Search 是不同工作流程阶段的组合,从规划搜索任务到交付最终搜索报告。让我们回顾一下工作流程的每个步骤,以了解应用程序的工作原理。

研究规划

这是工作流程的第一步。在这里,Agent 分析主题,并根据主题的上下文创建一个结构化的研究计划。它将广泛的主题分解为有针对性的子主题,例如历史、技术细节、优缺点、用例等。你可以将这视为工作流程中最重要的部分,因为如果没有它,Agent 可能会执行通用搜索并返回不相关的结果。

例如,如果你的研究主题是 “自动驾驶汽车”,研究计划可能包括以下几个方面:

  • 自动驾驶汽车的历史和发展: 探索自动驾驶技术的起源和演进,以及关键的里程碑事件。
  • 自动驾驶汽车的技术原理: 深入研究自动驾驶汽车所使用的传感器、算法和软件系统。
  • 自动驾驶汽车的优缺点: 分析自动驾驶汽车的潜在优势和劣势,例如提高安全性、降低交通拥堵和失业风险。
  • 自动驾驶汽车的应用案例: 考察自动驾驶汽车在不同领域的应用,例如出租车服务、物流运输和公共交通。
  • 自动驾驶汽车的伦理和社会影响: 探讨自动驾驶汽车所引发的伦理和社会问题,例如责任归属、隐私保护和公平性。

通过这种结构化的研究计划,Agent 可以更有针对性地进行搜索,并收集到更全面和深入的信息。

深度搜索

LLM 受其训练数据的限制,无法访问当前趋势。例如,如果你在没有搜索功能的情况下提示 Agent 列出足球队的当前球员,它将返回基于其训练数据的结果。但是,由于这些结果不包含最新的更新,因此它们可能不再是事实正确的。

使用 Tavily 执行高级网络搜索使 Agent 能够访问最新和最相关的网络资源。例如,如果我们需要研究 “最新的大模型技术”,LLM 本身可能只知道截止到其训练数据的模型信息。但通过 Tavily 搜索,Agent 可以找到最新的论文、博客文章和新闻报道,从而了解最新的模型架构、训练方法和应用案例。这大大提高了 Agent 输出结果的准确性和时效性。

来源管理

为了确保 Agent 生成的每一条信息都可以追溯到可靠的来源,跟踪所有来源非常重要。工作流程的这部分建立了与想要验证最终报告中的声明或数据的用户的信任。例如,如果 Agent 在报告中提到 “2023 年全球电动汽车销量增长了 35%”,它应该能够提供可靠的来源,例如国际能源署 (IEA) 或其他权威机构的报告。这使得用户可以验证数据的准确性,并评估报告的可信度。

信息整合

为了连接所有收集到的信息之间的点,Agent 将来自所有研究方面的见解整合到一份连贯的报告中。如果没有工作流程的这个阶段,搜索输出可能只是一份脱节的报告。Agent 需要对来自不同来源的信息进行分析、比较和对比,从而找出它们之间的联系和差异,并得出有意义的结论。例如,在研究 “人工智能在医疗保健领域的应用” 时,Agent 可能会整合来自学术论文、行业报告和新闻报道的信息,从而全面了解人工智能在疾病诊断、药物研发和个性化治疗等方面的应用,以及所面临的挑战和机遇。

最终格式化

即使报告在事实上是准确的并且经过充分研究,糟糕的格式也会使其难以阅读。需要一个额外的步骤来确保清晰的结构、一致的标题、项目符号、间距等。最终报告的格式应该清晰、简洁和易于理解,以便读者能够快速找到所需的信息。例如,可以使用标题和副标题来组织内容,使用项目符号列表来突出关键点,并使用图表和图像来可视化数据。此外,还需要确保报告的语言流畅、语法正确,避免出现拼写错误和排版错误。

实现 Deep Search Agent

到目前为止,你已经了解了深度 Agentic Search Workflow 的全部内容。现在是时候在 Google Colab 中实现它了。

步骤 1:安装依赖项

!pip install -q langchain langchain-openai tavily-python

步骤 2:导入必要的库

# 导入标准 Python 库
import os   # 用于与环境变量交互
import re   # 用于正则表达式
import ast  # 用于安全地从字符串解析 Python 表达式

# LangChain 模型和工具
from langchain_openai import ChatOpenAI # 用于与 OpenAI 的聊天模型交互的接口
from langchain_community.utilities.tavily_search import TavilySearchAPIWrapper  # 通过 Tavily API 启用搜索

# LangChain 消息模式
from langchain.schema import SystemMessage, HumanMessage  # 定义对话中使用的消息类型

# LangChain Agent 组件
from langchain.agents import Tool, AgentExecutor, create_react_agent  # 用于构建和运行带有工具的 Agentic Workflow

# LangChain 提示模板
from langchain.prompts import PromptTemplate  # 允许创建可重用的提示模板

# LangChain 内存
from langchain.memory import ConversationBufferMemory  # 帮助维护跨回合的对话状态

步骤 3:设置环境变量

# 设置和检索环境变量
os.environ["NOVITA_API_KEY"] = ""      # 替换为你的实际 Novita API 密钥
os.environ["TAVILY_API_KEY"] = ""      # 替换为你的实际 Tavily API 密钥

# 将 API 密钥加载到变量中
novita_api_key = os.getenv("NOVITA_API_KEY")
tavily_api_key = os.getenv("TAVILY_API_KEY")

步骤 4:定义 LLM 辅助函数

# LLM 辅助函数
def llm(api_key,
        model="meta-llama/llama-4-maverick-17b-128e-instruct-fp8",
        temperature=0.7):
    """创建具有指定参数的 ChatOpenAI LLM 实例。"""
    return ChatOpenAI(
        model=model,
        openai_api_key=api_key,
        base_url="https://api.novita.ai/v3/openai",
        temperature=temperature,
    )

构建 Agentic 研究员

整个深度研究工作流程都封装在一个名为 AgenticResearcher 的模块化类中。此类将管理生成研究计划、执行网络搜索、整合结果、格式化最终报告以及运行完整研究循环的过程。

我们将初始化研究 Agent,使其可以通过 Novita API 访问 LLM,并通过 Tavily API 访问网络搜索工具。然后,我们定义一个方法,该方法使用 Tavily 对给定的查询执行高级网络搜索。结果经过格式化以提高可读性,并且原始数据存储以供以后使用。

class AgenticResearcher:
    def __init__(self, novita_api_key, tavily_api_key, num_researchers=3, temperature=0.3):
        """使用必要的 API 密钥和参数初始化研究 Agent。"""
        self.llm = llm(api_key=novita_api_key, temperature=temperature)
        self.tavily_search = TavilySearchAPIWrapper(tavily_api_key=tavily_api_key)
        self.num_researchers = num_researchers
        self.raw_search_results = []

    def search(self, query):
        """在网上搜索有关给定查询的信息。"""
        try:
            results = self.tavily_search.results(
                query, max_results=7,
                search_depth="advanced"
            )
            # 存储原始结果以进行引用处理
            self.raw_search_results.extend(results)
            formatted = []
            for r in results:
                formatted += [
                    f"TITLE: {r.get('title','')}",
                    f"URL: {r.get('url','')}",
                    f"CONTENT:\n{r.get('content','')}",
                    "---"
                ]
            return "\n".join(formatted) if formatted else "未找到结果"
        except Exception as e:
            return f"搜索错误:{str(e)}"

创建多方面研究计划

仍然是 AgenticResearcher 类的一部分,让我们创建一个方法,该方法通过使用 LLM 将主题分解为几个不同的方面来生成结构化的研究计划。每个方面都包括一个标题和一个特定的搜索查询。目标是从多个角度探索主题,以确保深度和广度。

    def create_research_plan(self, topic):
        """创建具有多个研究方面的结构化研究计划以进行调查。"""
        sys = "你是一个规划师,输出 Python 字典列表。"
        hum = f"""为以下内容创建一个全面的研究计划:{topic}
确定 {self.num_researchers} 个要彻底调查的不同方面。
分析主题并确定要研究的最相关的方面。
准确输出:
ASPECT {{'title': '...', 'search_query': '...'}}"""
        try:
            resp = self.llm.invoke([SystemMessage(content=sys), HumanMessage(content=hum)])
            aspects = []
            for line in resp.content.splitlines():
                if line.strip().startswith("ASPECT"):
                    m = re.search(r"\{.*\}", line)
                    if m:
                        try:
                            aspects.append(ast.literal_eval(m.group(0)))
                        except Exception as e:
                            print(f"解析方面时出错:{e}")
            if not aspects:
                # 适用于不同领域的一般用途默认方面
                default_aspects = [{"title": "概述和关键信息", "search_query": f"{topic} 概述 关键事实"},
                                   {"title": "背景和上下文", "search_query": f"{topic} 背景 历史 上下文"},
                                   {"title": "详细信息和规范", "search_query": f"{topic} 详细信息 规范 数据"},
                                   {"title": "分析和意义", "search_query": f"{topic} 分析 重要性 意义"}]
                aspects = default_aspects[:self.num_researchers]
            return aspects
        except Exception as e:
            print(f"创建研究计划时出错:{e}")
            return [{"title": f"{topic} 研究", "search_query": topic}]

来源管理和引用

接下来,我们需要处理工作流程如何生成可信的报告。我们将通过创建一个方法来提取原始搜索结果中的唯一来源来实现这一点,确保每个来源都被引用。

    def extract_sources(self):
        """从原始搜索结果中提取唯一来源以进行引用。"""
        unique_sources = {}
        for result in self.raw_search_results:
            url = result.get('url', '')
            if url and 'http' in url:
                domain = url.split('/')[2] if len(url.split('/')) > 2 else url
                if domain not in unique_sources:
                    unique_sources[domain] = url
        return unique_sources

报告整合

现在我们已经从多个来源收集了原始信息,下一步是将混乱的输入转换为带有标题、副标题、关键发现和来源引用的详细研究报告。我们将编写一些方法来整合原始文本,生成可读、有组织的报告。

    def synthesize(self, text):
        """将原始文本合成为带有引文的详细、结构化的发现。"""
        sources = self.extract_sources()
        sources_list = list(sources.keys())
        sys = """你是一个专家研究合成器,可以创建详细、结构化的报告。
对于你包含的每个声明或事实,请添加对源域的引用。
使用提供的确切域名,不要修改。
调整你的报告结构以适应主题的领域和性质。"""
        hum = f"""要合成的信息:{text}
可用来源域(使用这些确切的名称进行引用):{sources_list}
创建一个全面的、详细的研究报告,其中包含:
1. 主要发现,包括具体的事实和数据
2. 结构化的信息,带有清晰的标题和副标题,适合主题
3. 使用方括号中的确切来源域名进行引用 [像这样]
4. 清晰、专业地呈现相关数据
5. 格式适合主题的特定性质(技术、历史、新闻事件等)
专业、彻底地格式化报告,不要假设任何特定的领域。"""
        try:
            return self.llm.invoke([SystemMessage(content=sys), HumanMessage(content=hum)]).content
        except Exception as e:
            return f"合成错误:{str(e)}"

为 Agent 创建工具

为了使我们的深度 Agentic Search Workflow 能够正常运行,我们需要定义工具。工具使 Agent 能够执行某些操作。

此工作流程所需的核心工具是“计划”,以帮助 Agent 分解主题并创建结构化的研究计划,“搜索”,让 Agent 可以执行深度网络搜索,以及“合成”,让 Agent 可以将所有原始发现转换为有组织的报告。

    def get_tools(self):
        """创建并返回 Agent 可用的工具列表。"""
        return [Tool(name="plan",
                     func=lambda t: str(self.create_research_plan(t)),
                     description="创建一个详细的研究计划,其中包含多个要调查的方面。"),
                Tool(name="search",
                     func=self.search,
                     description="彻底搜索查询的网络并返回全面的结果。"),
                Tool(name="synthesize",
                     func=self.synthesize,
                     description="将原始文本转换为带有引文的详细、结构化的发现。"),
                ]

构建 Agent 执行器

现在我们的工具已准备就绪,我们需要使用 ReAct 样式提示将所有内容绑定到 AI Agent 中。这有助于 Agent 计划、使用定义的工具执行操作、观察结果并循环,直到它有足够的数据来生成最终的研究报告。

    def get_agent_executor(self):
        """创建并返回带有工具和内存的 Agent 执行器。"""
        tools = self.get_tools()
        template = """你是一个自主研究 Agent,专门用于生成关于任何主题的全面、详细的报告。
{tool_names}
工具:{tools}
以前的对话:{chat_history}
在给出研究主题(如 {input})时,请遵循以下过程:
1. 分析主题的类型并创建针对其领域量身定制的研究计划
2. 搜索有关该主题每个方面的全面信息
3. 收集详细的、相关的信息,包括来源
4. 将所有内容合成为专业、详细的报告,格式适合主题

准确遵循以下格式:
问题:{input}
思考:(你在这里的推理)
行动:要调用的工具的名称,必须是 [{tool_names}] 之一
行动输入:工具的输入
观察:(工具的输出)
...(根据需要重复“思考/行动/行动输入/观察”)
当你收集到全面的信息时,输出:
最终答案:(你的最终、详细的研究报告,其中包含结构化的部分、适当的格式、具体的细节和引文)

从一个彻底的计划开始!
{agent_scratchpad}"""
        prompt = PromptTemplate(template=template,
                                input_variables=["input", "agent_scratchpad", "tools", "tool_names",
                                                 "chat_history"])

        # 创建低级 Agent
        agent = create_react_agent(llm=self.llm, tools=tools, prompt=prompt)

        # 将其包装在带有内存的执行器中
        memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
        agent_executor = AgentExecutor.from_agent_and_tools(agent=agent,
                                                           tools=tools,
                                                           verbose=True,
                                                           memory=memory,
                                                           max_iterations=18,
                                                           early_stopping_method="generate",
                                                           handle_parsing_errors=True)
        return agent_executor

最终报告格式化

在研究 Agent 完成其深度搜索后,输出可能信息丰富但不具可读性。这个额外的步骤有助于使最终报告更精简。为了实现这一点,我们将提示 LLM 作为专家编辑来改进最终报告。

    def format_final_report(self, raw_report):
        """将最终格式添加到报告中,使其更专业。"""
        sys = """你是一位改进研究报告的专家编辑。
保留所有事实内容、引文和结构不变。
只需改进格式、组织和可读性。"""
        hum = f"""这是一份需要最终格式改进的研究报告:{raw_report}
请在保留以下所有内容的同时改进格式和演示文稿:
1. 事实和数据
2. 引文
3. 内容组织
4. 统计数据
使其看起来专业,具有适当的标题、间距和布局。"""
        try:
            improved = self.llm.invoke([SystemMessage(content=sys), HumanMessage(content=hum)]).content
            return improved
        except Exception as e:
            # 如果格式化失败,则返回原始报告
            return raw_report

将所有内容整合在一起

让我们将整个工作流程绑定到一个管道中,该管道协调整个研究流程。这将用作入口点,允许 Agent 执行完整的研究过程,然后格式化最终报告。

    def run(self, topic):
        """对给定的主题运行研究过程。"""
        try:
            # 重置存储的搜索结果
            self.raw_search_results = []
            # 运行 Agent
            executor = self.get_agent_executor()
            raw_report = executor.invoke({"input": topic})["output"]
            # 格式化最终报告
            final_report = self.format_final_report(raw_report)
            return final_report
        except Exception as e:
            return f"研究失败:{str(e)}"

运行研究 Agent

现在我们已经构建了深度 Agentic Search Workflow,让我们对其进行测试,看看它在真实世界的研究任务中的表现如何。我们将创建研究 Agent 的实例,并要求它生成一份关于当前主题的详细报告。

# 创建研究员对象
researcher = AgenticResearcher(novita_api_key, tavily_api_key, num_researchers=3)

# 运行测试研究
topic = "2025 年最有前途的气候技术创业公司有哪些?"
report = researcher.run(topic)

# 显示研究报告
print("\n" + "="*60)
print(f"📝 详细研究报告:{topic}")
print("="*60)
print(report)
print("="*60 + "\n")

结果

深度 Agentic Workflow 分解了主题并创建了研究计划,执行了互联网搜索以检索相关信息,整合了收集到的数据,最后返回了基于搜索查询的结构化报告。

结论

祝贺你构建了自己的深度 Agentic Search Workflow!你现在可以研究任何主题,并获得关于你的查询的详细报告。

让我们快速回顾一下你所完成的工作:

在本文中,你学习了如何构建一个深度搜索 Agent,它可以分解复杂的主题、生成研究计划、执行互联网搜索并将所有发现整合到结构化的报告中。

整个工作流程由以下机构提供支持:

  • Novita AI 作为 LLM 提供商
  • LangChain 用于 Agentic 框架
  • Tavily 作为实时搜索引擎

请随意在不同的主题上尝试 Agent,以测试其功能!

这只是当今强大的模型可以实现的众多令人兴奋的 AI 应用之一。访问 Novita LLM Playground 试用其他顶级、最新的 AI 模型;谁知道,你的下一个伟大的 AI 想法可能就是从那里开始的。

查看 Colab 中的代码

Novita AI 是一个 AI 云平台,它为开发人员提供了一种使用我们简单的 API 部署 AI 模型的简单方法,同时还提供经济实惠且可靠的 GPU 云,用于构建和扩展。

通过本文的指导,你可以构建自己的 Deep Search Agent,利用 LangChain、Novita AI 和 Tavily 的强大功能,探索 LLM 的无限可能。无论是进行学术研究、行业分析,还是仅仅为了满足你的好奇心,这个 Agent 都能成为你可靠的助手,助你快速获取信息、深入理解问题,并在大模型时代保持领先。