一文看懂Gemini DeepResearch

547 阅读10分钟

引言

Gemini DeepResearch项目是一个融合了最新AI技术的研究型对话应用,它展示了如何将Google Gemini大语言模型与LangGraph代理框架结合,打造一个能够进行深度网络研究的智能助手。本文将从架构设计、技术实现、工作流程等多个维度深入解析这个项目。

项目核心价值

这个项目的核心价值在于展示了一种全新的AI应用范式:

  • 自主研究能力:AI不再仅仅回答已知问题,而是能够主动进行网络研究
  • 反思与迭代:通过反思分析机制,识别知识缺口并优化搜索策略
  • 透明决策过程:前端实时展示AI的思考和研究过程,增强可解释性
  • 全栈技术融合:将前沿的AI技术与现代全栈开发实践无缝结合

image.png

系统架构深度解析

前后端分离架构

项目采用了完全分离的前后端架构,这种设计带来了几个关键优势:

  1. 技术栈独立优化:前端使用React 19、TypeScript和Vite,后端使用Python、FastAPI和LangGraph
  2. 并行开发效率:前后端团队可以并行工作,提高开发效率
  3. 扩展性与可维护性:模块化设计使系统易于扩展和维护

LangGraph代理架构

LangGraph代理是系统的核心,它采用了基于有向图的工作流设计:

  1. 节点化设计:每个功能点被设计为独立节点,职责单一

    • generate_query:生成搜索查询
    • web_research:执行网络搜索
    • reflection:反思分析结果
    • finalize_answer:生成最终答案
  2. 状态驱动流程:使用多层状态管理系统

    • OverallState:全局状态,包含消息、查询、结果等
    • ReflectionState:反思状态,包含充足性判断和后续查询
    • QueryGenerationState:查询生成状态
    • WebSearchState:网络搜索状态
  3. 智能路由机制:基于反思结果动态决定下一步行动

技术栈深度剖析

前端技术栈

image.png

前端采用了现代化的React技术栈,特别值得关注的是:

  1. React 19:利用最新的并发特性和改进的服务器组件支持
  2. Shadcn/UI:基于Radix UI构建的高度可定制组件库,提供优秀的可访问性
  3. LangGraph SDK:前端与LangGraph后端的桥梁,提供实时通信能力
// 前端与LangGraph的集成示例
const thread = useStream<{
  messages: Message[];
  initial_search_query_count: number;
  max_research_loops: number;
  reasoning_model: string;
}>({
  apiUrl: import.meta.env.DEV 
    ? "http://localhost:2024" 
    : "http://localhost:8123",
  assistantId: "agent",
  messagesKey: "messages",
});

后端技术栈

image.png

后端技术栈的核心是LangGraph和Google Gemini模型:

  1. LangGraph:构建AI代理的图形框架,支持复杂工作流

  2. FastAPI:高性能的异步Web框架,提供API接口

  3. Google Gemini:不同节点使用不同规格的模型

    • 查询生成:Gemini 2.0 Flash(快速响应)
    • 反思分析:Gemini 2.5 Flash(平衡速度与质量)
    • 最终答案:Gemini 2.5 Pro(高质量输出)

LangGraph工作流程详解

LangGraph研究代理的工作流程可以分为以下几个关键阶段:

用户输入与状态初始化

工作流程始于用户通过前端界面输入研究问题,系统随即初始化OverallState全局状态,包含:

  • 对话消息列表
  • 搜索查询列表
  • 研究结果集合
  • 收集的来源信息
  • 研究循环计数器

查询生成阶段

查询生成节点(generate_query)是整个研究流程的起点:

def generate_query(state: OverallState, config: RunnableConfig) -> QueryGenerationState:
    """LangGraph节点:基于用户问题生成搜索查询"""
    configurable = Configuration.from_runnable_config(config)
    
    # 检查自定义的初始搜索查询数量
    if state.get("initial_search_query_count") is None:
        state["initial_search_query_count"] = configurable.number_of_initial_queries
        
    # 初始化Gemini 2.0 Flash模型
    llm = ChatGoogleGenerativeAI(
        model=configurable.query_generator_model,
        temperature=1.0,  # 温度设为1.0增加创造性
        max_retries=2,
        api_key=os.getenv("GEMINI_API_KEY"),
    )
    structured_llm = llm.with_structured_output(SearchQueryList)  # 结构化输出

    # 格式化提示词
    current_date = get_current_date()
    formatted_prompt = query_writer_instructions.format(
        current_date=current_date,
        research_topic=get_research_topic(state["messages"]),
        number_queries=state["initial_search_query_count"],
    )
    # 生成搜索查询
    result = structured_llm.invoke(formatted_prompt)
    return {"query_list": result.query}  # 返回查询列表

该节点使用Gemini 2.0 Flash模型,通过精心设计的提示词生成多个搜索查询,并确保输出符合预定义的结构。

查询生成提示词模板

query_writer_instructions = """你的目标是生成复杂且多样化的网络搜索查询。这些查询用于高级自动化网络研究工具,该工具能够分析复杂结果、跟踪链接并综合信息。

指令:
- 总是优先使用单个搜索查询,只有当原始问题涉及多个方面或元素且一个查询不够时才添加其他查询。
- 每个查询应专注于原始问题的一个特定方面。
- 不要生成超过 {number_queries} 个查询。
- 查询应该多样化,如果主题广泛,生成超过1个查询。
- 不要生成多个相似的查询,1个就足够了。
- 查询应确保收集最新信息。当前日期是 {current_date}。

格式:
- 将你的回复格式化为包含以下三个确切键的JSON对象:
   - "rationale": 简要解释为什么这些查询相关
   - "query": 搜索查询列表

上下文:{research_topic}"""

并行网络搜索

查询生成后,系统使用LangGraph的Send机制并行分发多个搜索任务:

def continue_to_web_research(state: QueryGenerationState):
    """LangGraph节点:将搜索查询发送到网络研究节点

    用于为每个搜索查询生成n个网络研究节点。
    """
    return [
        Send("web_research", {"search_query": search_query, "id": int(idx)})  # 发送到web_research节点
        for idx, search_query in enumerate(state["query_list"])  # 遍历查询列表
    ]

每个web_research节点独立执行一个搜索查询,使用Google Search API获取结果,并生成带引用的摘要:

def web_research(state: WebSearchState, config: RunnableConfig) -> OverallState:
    """LangGraph节点:使用原生Google搜索API工具执行网络研究"""
    # 配置
    configurable = Configuration.from_runnable_config(config)
    formatted_prompt = web_searcher_instructions.format(
        current_date=get_current_date(),
        research_topic=state["search_query"],
    )

    # 使用google genai客户端,因为langchain客户端不返回grounding metadata
    response = genai_client.models.generate_content(
        model=configurable.query_generator_model,
        contents=formatted_prompt,
        config={
            "tools": [{"google_search": {}}],  # 使用Google搜索工具
            "temperature": 0,  # 温度为0确保准确性
        },
    )
    # 将URL解析为短URL以节省token和时间
    resolved_urls = resolve_urls(
        response.candidates[0].grounding_metadata.grounding_chunks, state["id"]
    )

网络搜索提示词

web_searcher_instructions = """进行有针对性的Google搜索,收集关于"{research_topic}"的最新、可信信息,并将其综合成可验证的文本。

指令:
- 查询应确保收集最新信息。当前日期是 {current_date}。
- 进行多样化搜索以收集全面信息。
- 整合关键发现,同时仔细跟踪每个具体信息的来源。
- 输出应该是基于搜索发现的精心撰写的摘要或报告。
- 只包含在搜索结果中找到的信息,不要编造任何信息。

研究主题:
{research_topic}
"""

反思分析阶段

所有搜索结果合并后,系统进入反思分析阶段,由reflection节点执行:

def reflection(state: OverallState, config: RunnableConfig) -> ReflectionState:
    """LangGraph节点:反思当前研究结果,识别知识缺口,并决定是否需要更多研究"""
    configurable = Configuration.from_runnable_config(config)
    
    # 增加研究循环计数
    research_loop_count = state.get("research_loop_count", 0) + 1
    
    # 初始化Gemini 2.5 Flash模型进行反思分析
    llm = ChatGoogleGenerativeAI(
        model=configurable.reflection_model,
        temperature=0,  # 温度设为0确保一致性
        max_retries=2,
        api_key=os.getenv("GEMINI_API_KEY"),
    )
    structured_llm = llm.with_structured_output(Reflection)
    
    # 格式化提示词
    formatted_prompt = reflection_instructions.format(
        research_topic=get_research_topic(state["messages"]),
        summaries="\n\n".join(state["web_research_result"]),
    )
    
    # 生成反思结果
    result = structured_llm.invoke(formatted_prompt)
    
    return {
        "is_sufficient": result.is_sufficient,
        "knowledge_gap": result.knowledge_gap,
        "follow_up_queries": result.follow_up_queries,
        "research_loop_count": research_loop_count,
        "number_of_ran_queries": len(state["web_research_result"]),
    }

反思节点使用Gemini 2.5 Flash模型分析当前研究结果,识别知识缺口,并决定是否需要继续研究。

反思分析提示词

reflection_instructions = """你是一个专业的研究助手,正在分析关于"{research_topic}"的摘要。

指令:
- 识别知识缺口或需要深入探索的领域,并生成后续查询(1个或多个)。
- 如果提供的摘要足以回答用户问题,则不生成后续查询。
- 如果存在知识缺口,生成有助于扩展理解的后续查询。
- 专注于技术细节、实施细节或未充分涵盖的新兴趋势。

要求:
- 确保后续查询是自包含的,并包含网络搜索所需的必要上下文。

输出格式:
- 将你的回复格式化为包含以下确切键的JSON对象:
   - "is_sufficient": true 或 false
   - "knowledge_gap": 描述缺失或需要澄清的信息
   - "follow_up_queries": 写出解决此缺口的具体问题

摘要:
{summaries}
"""

决策评估与路由

基于反思结果,系统通过evaluate_research函数决定下一步行动:

def evaluate_research(state: ReflectionState):
    """LangGraph节点:评估研究结果,决定是继续研究还是生成最终答案"""
    # 如果信息充足或已达到最大循环次数,则生成最终答案
    if state["is_sufficient"] or state["research_loop_count"] >= state.get("max_research_loops", 2):
        return "finalize_answer"
    # 否则继续研究
    else:
        # 为每个后续查询创建一个web_research节点
        return [
            Send("web_research", {"search_query": query, "id": state["number_of_ran_queries"] + idx})
            for idx, query in enumerate(state["follow_up_queries"])
        ]

系统会在以下情况下结束研究循环:

  1. 反思结果表明信息已经充足
  2. 已达到最大研究循环次数(默认为2)

否则,系统会生成新的搜索查询并继续研究。

最终答案生成

当决策评估确定研究已完成时,系统进入最终答案生成阶段:

def finalize_answer(state: OverallState, config: RunnableConfig) -> OverallState:
    """LangGraph节点:基于收集的研究结果生成最终答案"""
    configurable = Configuration.from_runnable_config(config)
    
    # 初始化Gemini 2.5 Pro模型生成高质量答案
    llm = ChatGoogleGenerativeAI(
        model=configurable.answer_model,
        temperature=0,  # 温度设为0确保一致性
        max_retries=2,
        api_key=os.getenv("GEMINI_API_KEY"),
    )
    
    # 格式化提示词
    formatted_prompt = answer_writer_instructions.format(
        research_topic=get_research_topic(state["messages"]),
        summaries="\n\n".join(state["web_research_result"]),
    )
    
    # 生成最终答案
    result = llm.invoke(formatted_prompt)
    
    # 处理引用链接
    answer_text = result.content
    for idx, source in enumerate(state["sources_gathered"]):
        answer_text = answer_text.replace(f"[{idx+1}]", f"[{source['title']}]({source['url']})")
    
    # 创建AIMessage并返回
    return {"messages": [AIMessage(content=answer_text)]}

最终答案生成使用Gemini 2.5 Pro模型,确保输出高质量的综合性答案,并自动处理引用链接。

LangGraph图构建

整个工作流程通过LangGraph的图结构定义和编译:

# 创建我们的代理图
builder = StateGraph(OverallState, config_schema=Configuration)

# 定义我们将循环使用的节点
builder.add_node("generate_query", generate_query)  # 查询生成节点
builder.add_node("web_research", web_research)  # 网络研究节点
builder.add_node("reflection", reflection)  # 反思分析节点
builder.add_node("finalize_answer", finalize_answer)  # 最终答案节点

# 设置入口点为 `generate_query`
# 这意味着这个节点是第一个被调用的
builder.add_edge(START, "generate_query")
# 添加条件边以在并行分支中继续搜索查询
builder.add_conditional_edges(
    "generate_query", continue_to_web_research, ["web_research"]
)
# 对网络研究进行反思
builder.add_edge("web_research", "reflection")
# 评估研究结果
builder.add_conditional_edges(
    "reflection", evaluate_research, ["web_research", "finalize_answer"]
)
# 完成答案
builder.add_edge("finalize_answer", END)

graph = builder.compile(name="pro-search-agent")  # 编译图,名称为"专业搜索代理"

图结构包含两种类型的边:

  1. 固定边:如START → generate_queryweb_research → reflection
  2. 条件边:如generate_query → web_research(并行分发)和reflection → [web_research, finalize_answer](智能决策)

结论

Gemini DeepResearch 项目不仅是一个技术演示,更是一个展示AI代理未来发展方向的窗口。通过结合最新的LLM技术、图形化代理框架和现代全栈开发实践,它展示了如何构建真正智能、自主的AI系统。

这种系统不再局限于简单的问答,而是能够主动研究、反思和优化自己的策略,为用户提供深度、全面且有据可查的信息。随着AI技术的不断发展,这种智能代理将在知识工作、研究辅助和决策支持等领域发挥越来越重要的作用。

关注chaincat,每早8:00一篇深度技术早餐

公众号二维码.jpg