跳出框架:一步步实现简易Deep Search Agent

1. 引言

2025年初,AI Agent已经无处不在------从智能聊天助手到增强型搜索工具,它们正在改变我们的生活方式。市面上有不少框架,代码框架有LangChain、AutoGen等,无代码框架也有Coze、Dify等,它们都能帮你迅速搭建一个Agent,但这些工具往往隐藏了底层的细节。如果你是个初学者,想真正搞懂AI Agent是怎么工作的,那从零开始手工打造一个是最好的办法。

这篇文章的目标很简单:不依赖任何框架,带你一步步做一个简易的Deep Search Agent。你输入一个问题,比如"2025年AI趋势是什么?",它会通过大语言模型(LLM)和外部工具(像Web搜索)给你一个深度答案。通过这个过程,你将学会调用LLM、集成工具、状态状态等这些核心技能。更重要的是,你会明白这些"魔法"背后到底发生了什么,为以后用框架打下扎实基础。准备好了吗?让我们直接开始!

2. 预期效果

我们要打造一个简易的Deep Search Agent。它的任务是接收你的查询,比如"2025年AI趋势是什么?",先通过大语言模型(LLM)生成一个研究框架(research outline),然后针对每个段落用外部工具(比如Web搜索)挖细节,再反思优化,最终输出高质量的答案。整个过程不靠框架,手工实现,让你掌握每个环节。

2.1 预览

来看预览效果!输入:"2025年AI趋势是什么?" (为了防止内容过长,这里限制它只输出两段内容)

3.1 你需要准备什么

  • 环境:Python 3.9+,虚拟环境推荐使用 uv。

  • LLM API :用智谱AI(免费好用),申请API Key: 智谱AI开放平台。当然你也可以使用任何你觉得更强大的 LLM API,比如 Gemini(这个也免费,但需要一点魔法)。

  • 搜索 :Tavily AI(用于搜索,每月免费用1000次),申请API Key: Tavily AI

  • 代码 :想直接看成品?我的仓库在 vanilla-research-agent 。准备好就开干吧!

3.2 LLM调用模块

这个模块的目标是给Agent装上"大脑"------通过调用大语言模型(LLM)生成研究框架或回答问题。实现上其实不复杂,就是通过API接入LLM(或者调用你本地部署的模型)。为了让代码更规范,我们先定义一个抽象类作为标准:

python 复制代码
from abc import ABC, abstractmethod

class BaseLLM(ABC):
    @abstractmethod
    def invoke(self, system_prompt: str, user_prompt: str) -> str:
        pass

这个BaseLLM要求每个LLM类都有一个invoke方法,输入system_prompt(系统指令)和user_prompt(用户问题),输出LLM的回答。这是LLM最核心的功能。比如:

python 复制代码
system_prompt = "你是一个有帮助的AI助手。"
user_prompt = "请简要介绍一下Python。"

接下来,我们以智谱AI为例,实现一个具体的LLM类:

python 复制代码
from zhipuai import ZhipuAI

class ZhipuAILLM(BaseLLM):
    def __init__(self, api_key: str):
        self.client = ZhipuAI(api_key=api_key) 
        self.default_model_type = 'charglm-4'
        
    def invoke(self, system_prompt: str, user_prompt: str) -> str:
        messages = [
            {'role': 'system', 'content': system_prompt},
            {'role': 'user', 'content': user_prompt}
        ]

        response = self.client.chat.completions.create(
            model=self.default_model_type,
            messages=messages,
        )
        
        return response.choices[0].message.content if response.choices[0].message.content is not None else ""

我们来简单的测试一下:

python 复制代码
zhipuai_api_key = "这里替换成你申请的 api key"
zhipuai_llm = ZhipuAILLM(zhipuai_api_key)

system_prompt = "你是一个有帮助的AI助手。"
user_prompt = "请简要介绍一下Python。"

try:
    print("===== Test Zhipu =====\n")
    zhipuai_response = zhipuai_llm.invoke(system_prompt, user_prompt)
    print("智谱AI 响应:", zhipuai_response)
except Exception as e:
    print(f"发生错误: {str(e)}")

运行后,你可能会看到类似这样的结果:

python 复制代码
===== Test Zhipu =====

智谱AI响应: Python是一种广泛使用的高级编程语言,由Guido van Rossum于1989年底创建,1991年发布第一个公开版。它强调代码可读性和简洁语法(用空格缩进区分代码块,而不是大括号)。Python是解释型语言,支持面向对象、函数式等多种编程范式。

Python应用广泛,比如网站开发、数据分析、人工智能等。它有丰富的标准库和第三方生态,让复杂任务变简单。语法友好,很适合初学者。

主要特点:
- 动态类型
- 自动内存管理
- 强大库支持
- 跨平台
- 易扩展(可嵌入C/C++)

这个模块就搞定了!你可以基于BaseLLM写自己的实现,比如换其他厂商的API(像OpenAI)、用HTTP客户端直接调接口(省下SDK依赖),甚至本地部署LLM。灵活性全在你手里!

3.3 Prompt 模块

LLM模块实现了"大脑",但要让它按我们的想法工作,Prompt模块是关键。为什么需要这个模块?因为LLM的表现很大程度上取决于输入的指令(Prompt)。我们需要精心设计不同的System Prompt,指导LLM完成生成报告结构、搜索准备、内容总结、反思改进等步骤,确保每一步输出都符合预期格式。这样,Agent才能有条理地完成Deep Search任务。

下面,我们为每个环节定义了专属的System Prompt,并用JSON Schema规范输入输出。代码直接可用,你可以根据需要调整: (你可能会好奇每个环节都是干嘛的,不用着急,继续往下看就知道了)

python 复制代码
import json

## 生成报告结构段落的 SYSTEM PROMPT 

output_schema_report_structure = {
        "type": "array",
        "items": {
            "type": "object",
            "properties": {
                "title": {"type": "string"},
                "content": {"type": "string"}
            }
        }
    }

SYSTEM_PROMPT_REPORT_STRUCTURE = f"""
你是一位深度研究助手。给定一个查询,你需要规划一个报告的结构和其中包含的段落。最多五个段落。
确保段落的排序合理有序。
一旦大纲创建完成,你将获得工具来分别为每个部分搜索网络并进行反思。
请按照以下JSON模式定义格式化输出:

<OUTPUT JSON SCHEMA>
{json.dumps(output_schema_report_structure, indent=2)}
</OUTPUT JSON SCHEMA>

标题和内容属性将用于更深入的研究。
确保输出是一个符合上述输出JSON模式定义的JSON对象。
只返回JSON对象,不要有解释或额外文本。
"""

## 每个段落第一次搜索的 SYSTEM PROMPT 

input_schema_first_search = {
            "type": "object",
            "properties": {
                "title": {"type": "string"},
                "content": {"type": "string"}
            }
        }

output_schema_first_search = {
            "type": "object",
            "properties": {
                "search_query": {"type": "string"},
                "reasoning": {"type": "string"}
            }
        }

SYSTEM_PROMPT_FIRST_SEARCH = f"""
你是一位深度研究助手。你将获得报告中的一个段落,其标题和预期内容将按照以下JSON模式定义提供:

<INPUT JSON SCHEMA>
{json.dumps(input_schema_first_search, indent=2)}
</INPUT JSON SCHEMA>

你可以使用一个网络搜索工具,该工具接受'search_query'作为参数。
你的任务是思考这个主题,并提供最佳的网络搜索查询来丰富你当前的知识。
请按照以下JSON模式定义格式化输出(文字请使用中文):

<OUTPUT JSON SCHEMA>
{json.dumps(output_schema_first_search, indent=2)}
</OUTPUT JSON SCHEMA>

确保输出是一个符合上述输出JSON模式定义的JSON对象。
只返回JSON对象,不要有解释或额外文本。
"""

## 每个段落第一次总结的 SYSTEM PROMPT 

input_schema_first_summary = {
            "type": "object",
            "properties": {
                "title": {"type": "string"},
                "content": {"type": "string"},
                "search_query": {"type": "string"},
                "search_results": {
                    "type": "array",
                    "items": {"type": "string"}
                }
            }
        }

output_schema_first_summary = {
            "type": "object",
            "properties": {
                "paragraph_latest_state": {"type": "string"}
            }
        }

SYSTEM_PROMPT_FIRST_SUMMARY = f"""
你是一位深度研究助手。你将获得搜索查询、搜索结果以及你正在研究的报告段落,数据将按照以下JSON模式定义提供:

<INPUT JSON SCHEMA>
{json.dumps(input_schema_first_summary, indent=2)}
</INPUT JSON SCHEMA>

你的任务是作为研究者,使用搜索结果撰写与段落主题一致的内容,并适当地组织结构以便纳入报告中。
请按照以下JSON模式定义格式化输出:

<OUTPUT JSON SCHEMA>
{json.dumps(output_schema_first_summary, indent=2)}
</OUTPUT JSON SCHEMA>

确保输出是一个符合上述输出JSON模式定义的JSON对象。
只返回JSON对象,不要有解释或额外文本。
"""

## 反思(Reflect)的 SYSTEM PROMPT 

input_schema_reflection = {
            "type": "object",
            "properties": {
                "title": {"type": "string"},
                "content": {"type": "string"},
                "paragraph_latest_state": {"type": "string"}
            }
        }

output_schema_reflection = {
            "type": "object",
            "properties": {
                "search_query": {"type": "string"},
                "reasoning": {"type": "string"}
            }
        }

SYSTEM_PROMPT_REFLECTION = f"""
你是一位深度研究助手。你负责为研究报告构建全面的段落。你将获得段落标题、计划内容摘要,以及你已经创建的段落最新状态,所有这些都将按照以下JSON模式定义提供:

<INPUT JSON SCHEMA>
{json.dumps(input_schema_reflection, indent=2)}
</INPUT JSON SCHEMA>

你可以使用一个网络搜索工具,该工具接受'search_query'作为参数。
你的任务是反思段落文本的当前状态,思考是否遗漏了主题的某些关键方面,并提供最佳的网络搜索查询来丰富最新状态。
请按照以下JSON模式定义格式化输出:

<OUTPUT JSON SCHEMA>
{json.dumps(output_schema_reflection, indent=2)}
</OUTPUT JSON SCHEMA>

确保输出是一个符合上述输出JSON模式定义的JSON对象。
只返回JSON对象,不要有解释或额外文本。
"""


## 总结反思的 SYSTEM PROMPT 

input_schema_reflection_summary = {
            "type": "object",
            "properties": {
                "title": {"type": "string"},
                "content": {"type": "string"},
                "search_query": {"type": "string"},
                "search_results": {
                    "type": "array",
                    "items": {"type": "string"}
                },
                "paragraph_latest_state": {"type": "string"}
            }
        }

output_schema_reflection_summary = {
            "type": "object",
            "properties": {
                "updated_paragraph_latest_state": {"type": "string"}
            }
        }

SYSTEM_PROMPT_REFLECTION_SUMMARY = f"""
你是一位深度研究助手。
你将获得搜索查询、搜索结果、段落标题以及你正在研究的报告段落的预期内容。
你正在迭代完善这个段落,并且段落的最新状态也会提供给你。
数据将按照以下JSON模式定义提供:

<INPUT JSON SCHEMA>
{json.dumps(input_schema_reflection_summary, indent=2)}
</INPUT JSON SCHEMA>

你的任务是根据搜索结果和预期内容丰富段落的当前最新状态。
不要删除最新状态中的关键信息,尽量丰富它,只添加缺失的信息。
适当地组织段落结构以便纳入报告中。
请按照以下JSON模式定义格式化输出:

<OUTPUT JSON SCHEMA>
{json.dumps(output_schema_reflection_summary, indent=2)}
</OUTPUT JSON SCHEMA>

确保输出是一个符合上述输出JSON模式定义的JSON对象。
只返回JSON对象,不要有解释或额外文本。
"""

## 最终研究报告格式化的 SYSTEM PROMPT 

input_schema_report_formatting = {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "title": {"type": "string"},
                    "paragraph_latest_state": {"type": "string"}
            }
        }
    }

SYSTEM_PROMPT_REPORT_FORMATTING = f"""
你是一位深度研究助手。你已经完成了研究并构建了报告中所有段落的最终版本。
你将获得以下JSON格式的数据:

<INPUT JSON SCHEMA>
{json.dumps(input_schema_report_formatting, indent=2)}
</INPUT JSON SCHEMA>

你的任务是将报告格式化为美观的形式,并以Markdown格式返回。
如果没有结论段落,请根据其他段落的最新状态在报告末尾添加一个结论。
使用段落标题来创建报告的标题。
"""

3.4 工具调用模块

工具调用是Agent的"外挂",让LLM不再只靠语言"空想",而能真正触达外部世界。这是打造Deep Search Agent的关键一步。因为是入门文章,我先聚焦于搜索工具的实现,帮你把Agent的搜索能力搞定。市面上可选工具有不少,比如DuckDuckGo、Tavily,或者自己写爬虫。这里我选Tavily,它简单好用,适合初学者。

实现代码如下:

python 复制代码
import os
from tavily import TavilyClient

def tavily_search(query, include_raw_content=True, max_results=5):

    # 记得在命令行 export TAVILY_API_KEY
    tavily_client = TavilyClient(api_key=os.getenv("TAVILY_API_KEY"))

    return tavily_client.search(query,
                                include_raw_content=include_raw_content,
                                max_results=max_results,
                                timeout=240)['results']

我们来测一下效果:

python 复制代码
query = "2025 AI Agent 发展趋势"

    # 测试 Tavily 搜索
    print("\n=== Tavily 搜索 ===")
    tavily_results = tavily_search(query)
    print(f"Tavily 搜索结果 for '{query}':\n")
    print(tavily_results)
    for i, result in enumerate(tavily_results, 1):
        print(f"{i}. {result['title']}")
        print(f"   链接: {result['url']}")
        print(f"   内容: {result['content'][:100]}...\n")

运行后,你可能会看到类似这样的结果:

ruby 复制代码
1. 2025年AI Agent发展趋势与应用分析:7大领域完整解析 - 知乎
   链接: https://zhuanlan.zhihu.com/p/17658886144
   内容: 2025年AI Agent发展趋势与应用分析:7大领域完整解析 - 知乎 2025年AI Agent发展趋势与应用分析
:7大领域完整解析 2025年标志着人工智能(AI)发展的重要转折点,人工智能代理...

2. 微软亚洲研究院2025六大预测:AI Agents 将颠覆传统工作模式
   链接: https://cloud.tencent.com/developer/article/2485822
   内容: 微软亚洲研究院2025六大预测:AI Agents 将颠覆传统工作模式-腾讯云开发者社区-腾讯云 AIGC新知
 微软亚洲研究院2025六大预测:AI Agents 将颠覆传统工作模式 腾讯云 登录/注...

这个模块就完成了!Tavily帮我们把搜索结果抓回来,接下来可以用这些内容喂给LLM,生成更丰富的回答。想换工具?DuckDuckGo免费,爬虫更灵活,随你发挥!

3.5 状态定义模块

状态管理是Agent的"记事本",用来追踪研究进度和保存关键信息。没有它,LLM和工具调用就没法协作,报告也无法逐步完善。在Deep Search Agent中,我们需要记录报告标题、每个段落的结构、搜索历史、最新总结和反思次数。所以,这里用几个数据类(dataclass)来定义状态,简单又直观。

实现代码如下:

python 复制代码
from dataclasses import dataclass, field
from typing import List

@dataclass
class Search:
    """单个搜索结果的状态"""
    url: str = ""          # 搜索结果的链接
    content: str = ""      # 搜索返回的内容

@dataclass
class Research:
    """段落研究过程的状态"""
    search_history: List[Search] = field(default_factory=list)  # 搜索记录列表
    latest_summary: str = ""                                    # 当前段落的最新总结
    reflection_iteration: int = 0                               # 反思迭代次数

@dataclass
class Paragraph:
    """报告中单个段落的状态"""
    title: str = ""                # 段落标题
    content: str = ""              # 段落的预期内容(初始规划)
    research: Research = field(default_factory=Research)  # 研究进度

@dataclass
class State:
    """整个报告的状态"""
    report_title: str = ""                    # 报告标题
    paragraphs: List[Paragraph] = field(default_factory=list)  # 段落列表

这些类怎么用呢?想象一下:

  • State是整个Agent的"大脑内存",保存报告的整体结构。
  • 每个Paragraph代表报告的一段,包含标题、初始内容和研究进度。
  • Research追踪某段的研究细节,比如搜索了什么、总结到哪步。
  • Search记录单次搜索的URL和内容。

举个例子,初始化一个状态可能是这样:

python 复制代码
# 示例:初始化状态
state = State(report_title="2025 AI Agent 发展趋势")
state.paragraphs.append(Paragraph(title="多模态AI", content="介绍多模态AI的发展"))
state.paragraphs[0].research.search_history.append(Search(url="example.com", content="教育领域增长30%"))
state.paragraphs[0].research.latest_summary = "多模态AI在教育领域有显著应用。"
state.paragraphs[0].research.reflection_iteration = 1

print(state)

这个模块就搞定了!它为后续节点实现提供了基础,Agent可以随时读写状态,逐步完善报告。

3.6 节点实现模块

基础模块都准备好了,现在我们来实现上层的"节点"。这些节点就像Agent的"工作单元",负责把LLM调用、工具搜索和状态更新串起来,逐步完成Deep Search任务。每个节点对应一个具体步骤,比如生成报告结构、搜索、总结、反思等。我们会定义一系列类,每个类都有run方法执行任务,有些还有mutate_state方法更新状态。细节如下:

python 复制代码
import json
from json.decoder import JSONDecodeError
from prompts import (SYSTEM_PROMPT_REPORT_STRUCTURE, SYSTEM_PROMPT_FIRST_SEARCH, 
                    SYSTEM_PROMPT_FIRST_SUMMARY, SYSTEM_PROMPT_REFLECTION, 
                    SYSTEM_PROMPT_REFLECTION_SUMMARY, SYSTEM_PROMPT_REPORT_FORMATTING)
from state import State, Paragraph, Research, Search
from utils import clean_json_tags, remove_reasoning_from_output, clean_markdown_tags, extract_clean_response
from llms import BaseLLM

class ReportStructureNode:
    """生成报告结构的节点"""
    def __init__(self, llm_client: BaseLLM, query: str):
        self.llm_client = llm_client
        self.query = query

    def run(self) -> str:
        """调用LLM生成报告结构"""
        response = self.llm_client.invoke(SYSTEM_PROMPT_REPORT_STRUCTURE, self.query)
        return response

    def mutate_state(self, state: State) -> State:
        """将报告结构写入状态"""
        report_structure = self.run()
        report_structure = remove_reasoning_from_output(report_structure)
        report_structure = clean_json_tags(report_structure)

        report_structure = json.loads(report_structure)
        for paragraph in report_structure:
            state.paragraphs.append(Paragraph(title=paragraph["title"], content=paragraph["content"]))
        return state    

class FirstSearchNode:
    """为段落生成首次搜索查询的节点"""
    def __init__(self, llm_client: BaseLLM):
        self.llm_client = llm_client

    def run(self, message: str) -> dict:
        """调用LLM生成搜索查询和理由"""
        response = self.llm_client.invoke(SYSTEM_PROMPT_FIRST_SEARCH, message)
        response = remove_reasoning_from_output(response)
        response = clean_json_tags(response)
        response_dict = extract_clean_response(response)  # 提取干净的JSON字典
        return response_dict

class FirstSummaryNode:
    """根据搜索结果生成段落首次总结的节点"""
    def __init__(self, llm_client: BaseLLM):
        self.llm_client = llm_client

    def run(self, message: str) -> str:
        """调用LLM生成段落总结"""
        response = self.llm_client.invoke(SYSTEM_PROMPT_FIRST_SUMMARY, message)
        return response

    def mutate_state(self, message: str, idx_paragraph: int, state: State) -> State:
        """更新段落的最新总结到状态"""
        summary = self.run(message)
        summary = remove_reasoning_from_output(summary)
        summary = clean_json_tags(summary)
        
        try:
            summary = json.loads(summary)
        except JSONDecodeError:
            summary = {"paragraph_latest_state": summary}  # 容错处理非JSON输出

        state.paragraphs[idx_paragraph].research.latest_summary = summary["paragraph_latest_state"]
        return state

class ReflectionNode:
    """反思段落并生成新搜索查询的节点"""
    def __init__(self, llm_client: BaseLLM):
        self.llm_client = llm_client

    def run(self, message: str) -> dict:
        """调用LLM反思并生成搜索查询"""
        response = self.llm_client.invoke(SYSTEM_PROMPT_REFLECTION, message)
        response = remove_reasoning_from_output(response)
        response = clean_json_tags(response)
        response_dict = extract_clean_response(response)
        return response_dict

class ReflectionSummaryNode:
    """根据反思搜索结果更新段落总结的节点"""
    def __init__(self, llm_client: BaseLLM):
        self.llm_client = llm_client

    def run(self, message: str) -> str:
        """调用LLM更新段落内容"""
        response = self.llm_client.invoke(SYSTEM_PROMPT_REFLECTION_SUMMARY, message)
        return response

    def mutate_state(self, message: str, idx_paragraph: int, state: State) -> State:
        """将更新后的总结写入状态"""
        summary = self.run(message)
        summary = remove_reasoning_from_output(summary)
        summary = clean_json_tags(summary)

        try:
            summary = json.loads(summary)
        except JSONDecodeError:
            summary = {"updated_paragraph_latest_state": summary}  # 容错处理

        state.paragraphs[idx_paragraph].research.latest_summary = summary["updated_paragraph_latest_state"]
        return state

class ReportFormattingNode:
    """格式化最终报告的节点"""
    def __init__(self, llm_client: BaseLLM):
        self.llm_client = llm_client

    def run(self, message: str) -> str:
        """调用LLM生成Markdown格式报告"""
        response = self.llm_client.invoke(SYSTEM_PROMPT_REPORT_FORMATTING, message)
        response = remove_reasoning_from_output(response)
        response = clean_markdown_tags(response)
        return response

这些节点就像流水线上的工人:

  • ReportStructureNode:根据查询生成报告大纲,填入状态。
  • FirstSearchNode:为每个段落设计搜索查询。
  • FirstSummaryNode:用搜索结果生成段落初稿,更新状态。
  • ReflectionNode:反思段落,提出新查询。
  • ReflectionSummaryNode:根据反思结果完善段落,更新状态。
  • ReportFormattingNode:把所有段落格式化为最终报告。

接下来,我们会把这些节点串起来,跑通整个Agent!

3.7 整合所有模块实现Agent

所有基础模块都就位了,现在我们要把它们拼成一个完整的Deep Search Agent!这个Agent会从查询开始,生成报告结构,遍历每个段落进行搜索、总结、反思,最后输出格式化的报告。为了理解整体流程,可以参考下面的Mermaid图:

graph LR Start([开始]) --> RSN[ReportStructureNode
生成报告结构] RSN --> Loop{遍历每个段落} Loop --> FSN[FirstSearchNode
初始搜索] FSN --> SR1[搜索工具调用
tavily_search] %% SR1 --> US1[更新状态
update_state_with_search_results] %% US1 --> FSumN[FirstSummaryNode
生成初始搜索总结] SR1 --> FSumN[FirstSummaryNode
生成初始搜索总结] FSumN --> RLoop{循环反思
NUM_REFLECTIONS次} RLoop --> RN[ReflectionNode
生成反思后的搜索] RN --> SR2[搜索工具调用
tavily_search] SR2 --> RSumN[ReflectionSummaryNode
生成反思后的搜索总结] RSumN --> RLoop RLoop --> |完成反思| Loop Loop --> |所有段落处理完成| RFN[ReportFormattingNode
格式化最终报告] RFN --> End([结束])

具体实现如下:

python 复制代码
import json
import argparse
import os
from datetime import datetime

from nodes import (ReportStructureNode, FirstSearchNode, FirstSummaryNode, 
                  ReflectionNode, ReflectionSummaryNode, ReportFormattingNode)
from state import State
from tools import tavily_search
from utils import update_state_with_search_results
from llms import ZhipuAILLM

# 全局配置
STATE = State()
QUERY = "2025年AI趋势是什么?"
NUM_REFLECTIONS = 2  # 反思次数
NUM_RESULTS_PER_SEARCH = 3  # 每次搜索结果数
CAP_SEARCH_LENGTH = 20000  # 搜索内容截断长度

def main(topic: str = QUERY):
    # 初始化LLM客户端
    api_key = "你申请的API Key"  # 替换为你的智谱AI Key
    llm_client = ZhipuAILLM(api_key)

    # 创建所有节点实例
    report_structure_node = ReportStructureNode(llm_client, topic)
    first_search_node = FirstSearchNode(llm_client)
    first_summary_node = FirstSummaryNode(llm_client)
    reflection_node = ReflectionNode(llm_client)
    reflection_summary_node = ReflectionSummaryNode(llm_client)
    report_formatting_node = ReportFormattingNode(llm_client)

    # Step 1: 生成报告结构并更新状态
    _ = report_structure_node.mutate_state(STATE)
    print(f"总段落数: {len(STATE.paragraphs)}")
    for idx, paragraph in enumerate(STATE.paragraphs, 1):
        print(f"\n段落 {idx}: {paragraph.title}")

    # Step 2: 遍历每个段落,执行搜索和反思
    for j in range(len(STATE.paragraphs)):
        print(f"\n\n============== 段落 {j+1} ==============\n")
        print(f"============== {STATE.paragraphs[j].title} ==============\n")

        # 初始搜索
        message = json.dumps({"title": STATE.paragraphs[j].title, "content": STATE.paragraphs[j].content}, ensure_ascii=False)
        output = first_search_node.run(message)
        print("\n[初始搜索] 查询:", output.get("search_query"))
        print("[初始搜索] 推理:", output.get("reasoning"))
        
        search_results = tavily_search(output.get("search_query"), max_results=NUM_RESULTS_PER_SEARCH)
        print("\n[搜索结果]:")
        for idx, result in enumerate(search_results, 1):
            print(f"\n结果 {idx}:")
            print(f"标题: {result['title']}")
            print(f"链接: {result['url']}")
            print(f"摘要: {result['content'][:200]}...")
        
        _ = update_state_with_search_results(search_results, j, STATE)
        
        # 初始总结
        message = {
            "title": STATE.paragraphs[j].title,
            "content": STATE.paragraphs[j].content,
            "search_query": output.get("search_query"),
            "search_results": [result["content"][:CAP_SEARCH_LENGTH] for result in search_results if result["content"]]
        }
        _ = first_summary_node.mutate_state(json.dumps(message, ensure_ascii=False), j, STATE)
        print("\n[初始总结]:")
        print(STATE.paragraphs[j].research.latest_summary)
        
        # 反思循环
        for i in range(NUM_REFLECTIONS):
            print(f"\n[反思 {i+1}] 开始...")
            message = {
                "paragraph_latest_state": STATE.paragraphs[j].research.latest_summary,
                "title": STATE.paragraphs[j].title,
                "content": STATE.paragraphs[j].content
            }
            output = reflection_node.run(json.dumps(message, ensure_ascii=False))
            print(f"\n[反思 {i+1}] 查询:", output.get("search_query"))
            print(f"[反思 {i+1}] 推理:", output.get("reasoning"))
            
            search_results = tavily_search(output.get("search_query"), max_results=NUM_RESULTS_PER_SEARCH)
            print(f"\n[反思 {i+1}] 搜索结果:")
            for idx, result in enumerate(search_results, 1):
                print(f"\n结果 {idx}:")
                print(f"标题: {result['title']}")
                print(f"链接: {result['url']}")
                print(f"摘要: {result['content'][:200]}...")
            
            _ = update_state_with_search_results(search_results, j, STATE)
            
            message = {
                "title": STATE.paragraphs[j].title,
                "content": STATE.paragraphs[j].content,
                "search_query": output.get("search_query"),
                "search_results": [result["content"][:CAP_SEARCH_LENGTH] for result in search_results if result["content"]],
                "paragraph_latest_state": STATE.paragraphs[j].research.latest_summary
            }
            _ = reflection_summary_node.mutate_state(json.dumps(message, ensure_ascii=False), j, STATE)
            print(f"\n[反思 {i+1}] 更新总结:")
            print(STATE.paragraphs[j].research.latest_summary)

    # Step 3: 生成最终报告
    print("\n=============== 最终报告生成 ===============\n")
    report_data = [{"title": p.title, "paragraph_latest_state": p.research.latest_summary} for p in STATE.paragraphs]
    final_report = report_formatting_node.run(json.dumps(report_data, ensure_ascii=False))
    
    print("\n=============== 最终报告 ===============\n")
    print(final_report)

    # 保存到文件
    os.makedirs("reports", exist_ok=True)
    filename = f"reports/report_{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.md"
    with open(filename, "w", encoding="utf-8") as f:
        f.write(final_report)
    print(f"\n报告已保存至: {filename}")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Deep Search Agent")
    parser.add_argument("--topic", type=str, default=QUERY, help="研究主题")
    args = parser.parse_args()
    main(args.topic)

4. 成果展示及改进建议

4.1 完整代码运行

我们已经从零打造了一个Deep Search Agent!为了让成果更直观,我用Streamlit搭了个简单界面,输入查询就能看到报告生成过程和最终结果。完整代码跑起来是这样的:输入"2025年AI趋势是什么?",Agent会生成报告结构、搜索资料、多次反思,最后输出格式化的Markdown报告。Streamlit界面的代码可以参考我的仓库:vanilla-research-agent

下面是界面截图和生成的报告示例:

(为了防止内容过长,这里限制它只输出两段内容)

  • 截图展示
  • 视频展示

vanilla-research-agent/demo/demo.webm at main · Congregalis/vanilla-research-agent

  • 生成的报告示例
text 复制代码
好的,这是根据您提供的数据生成的Markdown格式报告:

# 人工智能在2025年的总体发展趋势

对2025年人工智能发展的大方向进行总体概述,包括技术进步速度、应用普及程度、以及对社会经济的影响等方面的预测。涵盖AI算力、模型复杂度、数据可用性等关键因素。联合国的一份报告预测,人工智能市场预计将发展至4.8万亿美元,类似于德国的经济规模。此外,AI发展中新出现的战争突显了2025年建立健全AI治理的紧迫性。全球合作对于避免军备升级、同时确保跨境安全和伦理的AI使用至关重要。在没有健全的伦理框架下,AI可能且已经被用于有害的目的,从大规模监视到虚假信息的传播。随着我们迈入2025年,先进机器学习、多模态AI系统和复杂生成技术的融合有望实现创新,这将从根本上改变企业和专业人士与智能系统交互的方式。

# 2025年AI关键技术突破预测

深入探讨2025年可能出现关键技术突破的领域,例如:生成式AI(Generative AI)的进一步发展与应用,Transformer架构的演进,多模态学习的突破,以及联邦学习、边缘计算等技术的成熟与应用,以及新兴的AI芯片技术。预计2025年,AI技术将不仅在技术层面取得显著进步,更将在行业应用中展现强大的影响力,从内容创作、智能硬件到工业制造、医疗健康等领域,AI的应用正在逐渐深化和普及。业内分析认为,2025年将是AI产业发展进程中的关键节点,未来几年AI技术将迎来爆发式增长,并在多个领域实现突破性应用。尤其在协作式AI系统方面,多个专业AI代理将在人类的高层指导下协同工作,从而推动形成人与AI代理协同工作的新模式。值得注意的是,AIGC技术将在内容创作领域发挥重要作用,为文本、图像、音频和视频的高效生成开辟新途径,从而推动产业多样化。有趣的是,面对日益增长的计算需求,基于太空能源和空间的AI数据中心可能会出现,展示出AI基础设施构造的进一步可能性。另外,人们对AI伦理的重视程度也将在2025年变得至关重要。建立健全的AI伦理框架和监管规则对于驾驭AI复杂的情势至关重要,以确保这些技术服务于人类的最佳利益。欧洲联盟在AI监管方面居于领先地位,其具有里程碑意义的《AI法案》是世界上首个全面的AI专属法律框架,通过关注技术的使用而非技术本身,从最初就根据AI系统对个人和基本权利的潜在风险对AI系统进行分类。

# AI在各行业中的应用趋势(2025)

分析人工智能(AI)在不同行业(医疗、金融、交通、教育、零售等)的应用趋势。重点关注AI如何改变这些行业的运作模式,以及可能出现的新型商业模式。例如:AI 驱动的个性化医疗、智能金融风控系统、自动驾驶技术的商业化进展等。根据前瞻产业研究院的预测,2025年人工智能将在多个领域迎来新的发展趋势,尤其是在中国市场。另外,TE Connectivity 2025 Industrial Technology Index 显示,各行业正在利用 AI 来进行先进的创新和可持续发展工作,表明了利用 AI 来提高效率和解决关键挑战的兴趣。但是,随着时间推移,AI 的应用面临的问题已经从普遍采用转变为执行障碍、安全问题和人才短缺。IBM Institute of Business Value 的一份报告显示,组织在采用生成式 AI 时,主要面临高质量数据不足的问题,可以通过数据增强、合成数据生成和战略数据合作来克服这一挑战。另外,联邦学习可以在不移动数据的情况下在多个分布式数据集上训练 AI 模型,保护隐私。因此,在 AI 应用过程中仍然存在一些挑战,主要包括数据质量不足、执行难度、安全隐患和人才短缺等问题。此外,AI 的应用还受到能源需求增长和可持续发展限制的影响。为了推动 AI 技术的发展,需要采取有效的战略,以最大限度地将 AI 集成到企业运营中。关键的发展趋势包括:

1.  **推理计算提升大模型准确率,并强化学习激发模型推理能力**:OpenAI 的新型 AI 推理模型和强化学习正在提升复杂问题的解决能力,使大模型能够在科学、代码和数学领域表现更优异。
2.  **高质量数据更为稀缺,合成数据价值显现**:高质量数据的不足使合成数据变得至关重要。英伟达发布的 Nemotron-4 340B 模型允许开发者生成合成数据,这对训练规模较小的大语言模型至关重要。
3.  **缩放法则依然有效,O3 与 GPT5 循环驱动有望开启**:尽管随着模型参数增加的边际收益逐步递减,但扩大训练语料库仍然是提升大语言模型性能的重要手段。O3 模型和 GPT-5 之间的循环优化有望启动。
4.  **应用的最佳形态一一超级智能体(AI Agent)**:AI Agent 将进入能力快速跃升的阶段,全球巨头争相布局端侧 AIAgent。
5.  **具身智能不断突破,人形机器人进入量产元年**:Nvidia 的世界模型正在加速机器人训练,人形机器人或将进入量产元年,在工厂实训并在家庭场景中发挥作用。
6.  **AI4Science: 黄金时代已经到来**:AI 正在加速科学研究进展,尤其是在蛋白质结构预测、小分子药物研发和数学问题证明方面。
7.  **端侧创新将不断涌现,AI 塑造端侧新分工新格局**:随着 AI 大模型的成熟,可以预见"AI+硬件"模式在多个场景下的应用,并可能催生新的产业链分工。
8.  **自动驾驶迈向端到端,Robotaxi 进入商业化落地阶段**:端到端的驾驶算法和多模态大模型的融合正在提升自动驾驶的环境理解能力,Robotaxi 逐步迈入商业化落地阶段。
9.  **"人工智能+"全面铺开,企业数字化率先落地**:企业通过自动化和智能预测提升效率,并通过数据驱动的决策支持系统提高竞争力。
10. **AI 对能源需求大幅增长,可持续发展日益紧迫**:AI 对能源的需求正在大幅增长,这使得可持续发展变得更加紧迫。

# AI伦理与监管挑战(2025)

讨论与人工智能发展相关的伦理和社会问题,例如:算法偏见、数据隐私、AI安全等。分析2025年可能面临的监管挑战,以及各国政府和国际组织可能采取的政策措施。包括对AI透明度、可解释性、以及问责制的要求。正如科学杂志"Information and Software Technology"上发表的一篇文章所强调的,AI系统的透明度和可解释性正变得越来越重要,许多组织的AI伦理指导方针都强调要以可解释性为透明度的核心。截至2024年,全球已有69个国家和地区制定了人工智能相关政策和立法,这些政策涵盖了从AI治理、隐私保护、数据监管到伦理规范等广泛的主题,其中,欧盟《人工智能法案》是全球首部全面且具有影响力的人工智能法规,采取基于风险的方法,将AI系统分为不同风险等级进行管理。人工智能的管理需要通过政策制定、法律监管、伦理指导等手段,对人工智能的研发、应用等行为进行全面管理和调控。因此,开发者、政策制定者和整个社会需要对AI的伦理问题进行持续的讨论。而对于2025年,全球人工智能治理的发展趋势如下:

*   欧盟:继续实施《人工智能法案》,包括分阶段落地、逐步完善相关细节行为准则,以及建立国内的人工智能监管机构体系。欧盟数据保护监管机构可能会继续严格限制互联网企业使用个人数据开展算法训练,尤其是在个人数据处理和透明度方面。对于超大型在线平台及超大型在线搜索引擎,欧盟将加强人工智能执法,关注非法内容传播、虚假信息、深度伪造、选举操纵等风险。
*   美国:可能会继续采用敏捷、渐进式的治理方式,采取部门化、有针对性、可实现的方式回应需规制的问题,形成灵活的监管框架。美国在数据隐私方面仍然可能保持相对宽松的监管态度,但如果特朗普上台,则有可能出台更多有助于保持美国在全球人工智能领域竞争优势的政策。另外,美国在国家安全、标准、指南与最佳实践、知识产权、内容真实性等方面会做出更多努力,推动负责任的人工智能的发展。
*   中国:预计大模型备案工作将进一步深化,包括大模型备案数量显著增加。多项标准、指南将进一步细化生成式人工智能合规义务,尤其是在内容标识、数据标注、训练数据、安全应急响应等各方面。人工智能知识产权、人格权侵权判例预计仍会大量涌现,模型安全和内容安全依旧是重点执法领域。

影响公众的AI发展、运营将受到不同监管机构的关注。对于用户量较大的AI的发展、运营均会受到不同监管机构的关注。这些监管机构的关注重点、发展趋势、可接受的解决方案,在一定程度上又影响着未来AI产品的发展。目前可见的是,在AI的监管方面,哪种监管方式更为有效、哪些监管的底线能够保住未来AI发展的安全,似乎在全球范围内还没有一个检验出的"黄金"做法。2025年是AI发展的重要一年,这一年在欧洲、美国监管的路径是否会发生重要的变化也非常值得关注。

# 2025年AI人才需求与技能发展

预测2025年对人工智能领域人才的需求,包括数据科学家、机器学习工程师、AI伦理专家等。分析未来所需的核心技能,以及教育培训机构如何适应这些需求。讨论人工智能对传统工作岗位的冲击,以及如何进行技能转型以适应AI时代。全球AI市场增长迅猛,预计2025-2030年市场规模将从244亿美元增至827亿美元,2020-2030年复合年增长率达24%,超越物联网和公共云等技术领域。预计到2025年,全球AI投资可能达到2000亿美元,推动劳动生产率每年提高1个百分点以上。根据相关报告,中国的人工智能人才数量迅速增长,总数已经突破940万。预计到2030年,中国对熟练AI人才的需求将达到2022年的6倍,可见中国在高技能AI人才培养方面还有很大的发展空间。其他数据显示,猎聘平台上的AI技术整体人才紧缺指数已经达到了一个较高的水平,尤其是在搜索和推荐算法的相关职位上,人才紧缺的情况最为严重。
为落实《新一代人工智能发展规划》,国家也相继出台了一系列政策、法规与文件,旨在构建智能、公平、高效的教育生态。例如,科技部等六部门印发了《关于加快场景创新以人工智能高水平应用促进经济高质量发展的指导意见》(国科发规〔2022〕199号),提出要"加快人工智能技术攻关、产品开发和产业培育,探索人工智能发展新模式新路径,以人工智能高水平应用促进经济高质量发展"。教育部高等教育司于2024年4月公布首批"人工智能+高等教育"应用场景典型案例,旨在利用智能技术支撑人才培养模式的创新、教学方法的改革、教育治理能力的提升,推进人工智能在高等教育中的广泛应用。

**结论:**

综上所述,2025年人工智能将在技术突破、行业应用、伦理监管和人才需求等方面呈现出显著的发展趋势。全球合作对于确保AI的安全和伦理使用至关重要。AI技术将在各行业得到更广泛的应用,并推动经济高质量发展。然而,也面临着数据质量、执行难度、安全隐患和人才短缺等挑战。各国政府和国际组织需要加强对AI的伦理监管,并采取措施促进AI人才的培养和技能转型,以适应AI时代的需求。

希望这份报告对您有所帮助!

运行一下试试,效果是不是很酷?从查询到报告,Agent一步步"思考"出结果,全程透明可见。

4.2 后续改进建议

总体来说,Agent已经能用,但还有很大的提升空间,比如:

  1. 丰富工具:增加图片、视频生成或搜索的工具,让你生成的报告更丰富。

  2. 优化反思:让LLM多对比搜索结果,减少遗漏,比如增加反思次数或调整Prompt。

  3. 优化框架:目前我展示的只是比较基础的 deep search 框架,你可以发挥你的想象力,搭建更完备的框架。

    试试这些改进,你的Agent会更强大!

5. 总结与展望

5.1 学到了什么

通过这篇文章,我们从零开始,手工打造了一个Deep Search Agent。从LLM调用到工具集成,再到状态管理和节点设计,每一步都亲手实现。你应该已经掌握了Agent的核心部件:如何让LLM生成结构化的研究框架,如何用工具搜索外部信息,如何通过状态追踪进度,最终整合成一份完整的报告。不用框架的意义在于,你真正理解了这些"魔法"背后的原理,而不是只依赖现成的黑盒工具。这是个宝贵的起点!

5.2 2025年的Agent框架与展望

到了2025年,Agent开发框架百花齐放,帮我们更快、更简单地搭建智能助手。LangChain首当其冲,凭借强大的链式调用和工具集成,成为热门选择。还有Pydantic AI用类型安全的模型管理,Agno和Eino提供灵活的Agent协作能力,甚至低代码平台如Coze和Dify,让不懂代码的人也能拖拖拽拽做出Agent。这些工具大大降低了开发门槛,但掌握了底层原理的你,能更得心应手地驾驭它们。遇到Bug时,你也能一眼看出问题所在,快速修复,而不是束手无策。

未来,你可以试试更复杂的挑战:加个语音输入,让Agent听你说话;或者搞个多Agent系统,模拟团队协作。可能性无穷多,就看你敢不敢玩!

5.3 结语

从零到一,你已经做出了一个实用的AI Agent,这证明初学者也能搞定看似高深的技术。只要肯动手、多试错,AI开发其实没那么神秘。希望你能把这个Agent当作起点,继续探索2025年的AI世界。去 我的仓库 看看完整代码,或者在评论区分享你的改进版本,一起交流进步吧!

相关推荐
耘瞳科技3 小时前
喜讯 | 耘瞳科技视觉检测与测量装备荣膺“2024机器视觉创新产品TOP10”
人工智能·科技·视觉检测
__Benco5 小时前
OpenHarmony子系统开发 - DFX(一)
人工智能·harmonyos
小西几哦5 小时前
3D点云配准RPM-Net模型解读(附论文+源码)
人工智能·pytorch·3d
CareyWYR5 小时前
每周AI论文速递(250331-250404)
人工智能
码视野5 小时前
基于快速开发平台与智能手表的区域心电监测与AI预警系统(源码+论文+部署讲解等)
人工智能·智能手表·毕业论文·计算机论文·物联网论文
skywalk81636 小时前
OpenRouter开源的AI大模型路由工具,统一API调用
服务器·前端·人工智能·openrouter
ejinxian6 小时前
大模型应用初学指南
人工智能·大模型·向量数据库
秋96 小时前
使用人工智能大模型kimi,如何免费高效制作PPT?
人工智能·kimi·制作ppt
IT古董7 小时前
【漫话机器学习系列】181.没有免费的午餐定理(NFL)
人工智能·机器学习