LangGraph+BrightData+PaperSearch的研究助理

LangGraph+BrightData+PaperSearch的研究助理

摘要:使用LangGraph的ReAct-Agent范式集成了BrightData和PaperSearch的MCP工具,通过搜索和爬取领英和学术网站,实现论文搜索和读取,学者信息提取,邮箱查找等功能。

链接:github.com/16Miku/Agen...

🚀 前言:我的 AI 助理"精神分裂"了

大家好,我是你们的技术博主。最近在开发 AI Agent 的时候,我遇到了一个很头疼的问题:如何让一个 Agent 既能做"正经事",又能"好好聊天"?

  • 当我让它分析论文时,我希望它给我一个结构清晰、字段分明的 JSON 报告。
  • 当我让它抓取个人主页时,我也希望得到一份规整的结构化数据。
  • 但当我只是想跟它说句"干得不错"时,我希望它能像个正常"人"一样回复我,而不是冷冰冰地甩给我一个空的 JSON 对象。

传统的 Agent 开发模式,比如用 response_format 强行规定输出格式,虽然能解决前两个问题,但却让 Agent 丧失了灵活性,变成了一个只会"填表"的机器人。这显然不是我们想要的"智能助理"。

经过一番探索,我找到了一种极其优雅的解决方案:把结构化输出本身,也变成一种"工具"! 让 Agent 自己来决定什么时候该"填表",什么时候该"聊天"。

今天,我就将手把手带大家,使用 LangGraph 和 Google 最新的 gemini-2.5-flash 模型,构建一个能够无缝切换学术研究信息抓取日常对话三种模式的"全能AI研究助理"。


🔥 核心思路:让"格式"成为一种可选工具

我们这次优化的核心思想,简单来说就是:不强迫,只引导

我们不再粗暴地告诉 LLM:"你必须用这个格式回复我!" 而是换一种更聪明的方式:

  1. 我们定义好想要的报告格式,比如 PaperAnalysis(论文分析报告)和 LinkedinProfile(领英主页报告)。
  2. 但我们不把它们作为强制的 response_format,而是把它们伪装成两个特殊的"工具"交给 Agent。
  3. 我们在 System Prompt 中明确告诉 Agent:"你的工具箱里有搜索、抓取等普通工具,还有两个特殊的'报告生成'工具。当你需要产出正式报告时,请在收集完所有信息后,调用这两个工具来格式化你的最终答案。"

这样做的好处是显而易见的:

  • 高度灵活:Agent 掌握了主动权,可以根据对话上下文自主判断是否需要结构化输出。
  • 任务解耦:将"信息收集"和"格式化输出"两个步骤分开,Agent 的思考过程(Chain of Thought)更加清晰,有助于完成复杂任务。
  • 自然交互:对于普通聊天,Agent 可以直接回复,交互体验大大提升。

理论说完了,让我们 show the code!


🛠️ 实战演练:三步构建全能助理

步骤 1: 环境准备与依赖安装

首先,我们需要在 Colab 环境中安装所有必需的库。这里我们用到了 langgraph 核心框架,langchain-google-genai 用于驱动 Gemini 模型,以及 beautifulsoup4 等辅助库。

⚠️ 注意: 每次安装或升级库之后,为了让新版本生效,请务必在 Colab 菜单栏点击 "代码执行程序" -> "重启会话"。

python 复制代码
# --- 步骤 1: 安装与重启 ---
!pip install --upgrade --quiet langchain langchain-core langchain-mcp-adapters langchain-google-genai langgraph beautifulsoup4
print("✅ 库已升级。请务必从菜单栏点击 '代码执行程序' -> '重启会话',然后再继续运行下面的代码!")

步骤 2: 编写"灵魂"代码

这是我们整个项目的核心代码。我会逐一拆解,让你看懂每一部分的功能。

2.1 导入与密钥配置

常规操作,导入所有需要的模块,并从 Colab 的 userdata 中加载我们的 API 密钥。这种方式比直接把密钥写在代码里更安全。

python 复制代码
# 2.1: 导入
import asyncio
import os
from typing import List, Union
from dataclasses import dataclass
import nest_asyncio
nest_asyncio.apply() # 允许在Jupyter/Colab环境中嵌套运行asyncio事件循环

from langchain_google_genai import ChatGoogleGenerativeAI
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain.agents import create_agent
from langgraph.checkpoint.memory import InMemorySaver
from pydantic import BaseModel, Field # 导入 Pydantic 用于定义我们的"伪工具"

# 2.2: 加载密钥
from google.colab import userdata
os.environ["GEMINI_API_KEY"] = userdata.get('GEMINI_API_KEY')
BrightData_API_KEY = userdata.get('BrightData_API_KEY')
Paper_Search_API_KEY = userdata.get('Paper_Search_API_KEY')
2.2 🔥 核心优化点:创建"伪工具"🔥

这里就是我们整个方案的"魔法"所在!我们使用 Pydantic 的 BaseModel 来定义两个类:PaperAnalysisLinkedinProfile

  • BaseModel: Pydantic 的基础类,能让我们像定义一个普通的 Python 类一样来定义数据结构。
  • Field : 用于为类的属性添加额外的描述信息。这一点至关重要 ,因为 LLM 正是通过读取这些 description 来理解每个字段的含义以及整个"工具"的作用。
  • 类的文档字符串 (docstring) : 比如 """当用户要求...调用此工具来格式化最终报告。""",这是对整个工具最直接的描述,Agent 会优先读取它来判断工具的用途。
python 复制代码
# ================================================================= #
#  🔥🔥🔥 核心优化点 1: 创建专门用于结构化输出的"伪工具" 🔥🔥🔥
# ================================================================= #
# 我们不再将 dataclass 作为 response_format,而是将它们包装成 Pydantic 模型,
# 并作为"工具"提供给 Agent。这让 Agent 可以自己决定何时调用它们。

class PaperAnalysis(BaseModel):
    """当用户要求对一篇学术论文进行详细分析时,调用此工具来格式化最终报告。"""
    title: str = Field(description="论文的完整标题")
    authors: List[str] = Field(description="论文的核心作者列表")
    research_field: str = Field(description="根据内容总结出的研究方向")
    summary: str = Field(description="对论文核心贡献的详细总结")
    author_contact: str = Field(description="从抓取内容中找到的作者邮箱或个人主页,如果找不到则为 '联系方式未找到'")

class LinkedinProfile(BaseModel):
    """当用户要求提取领英个人主页信息时,调用此工具来格式化最终报告。"""
    full_name: str = Field(description="用户的全名")
    headline: str = Field(description="用户的头衔或当前职位")
    location: str = Field(description="用户所在的地理位置")
    summary: str = Field(description="个人简介部分的总结")
    experience: List[str] = Field(description="一个包含所有工作经历的列表")
    contact: str = Field(description="从抓取内容中找到的邮箱或个人主页,如果找不到则为 '联系方式未找到'")
2.3 设计"行动指南":System Prompt

一个好的 System Prompt 是 Agent 的"灵魂"。在这里,我们明确地为 Agent 设定了角色、能力,以及最重要的------行动指南(ReAct 思考模式)。

请注意,我们特地强调了 PaperAnalysisLinkedinProfile特殊的"报告生成"工具 ,并指导 Agent 在收集完信息后必须调用它们来生成报告。这种明确的指令对于引导 Agent 行为至关重要。

python 复制代码
# --- 步骤 2.3: 设计一个更通用的 System Prompt ---
SYSTEM_PROMPT = """
你是一个全能的AI研究助理。你可以处理多种任务,包括分析学术论文和查询个人资料。

**你的能力 (工具箱):**
*   你拥有学术搜索、通用网页搜索和网页抓取等一系列工具。
*   **特别注意:** 你还拥有两个特殊的"报告生成"工具:`PaperAnalysis` 和 `LinkedinProfile`。

**你的行动指南 (ReAct 思考模式):**
1.  **分析与规划:** 理解用户的请求。如果用户的最终目的是生成一份结构化的报告(比如论文分析或个人资料总结),你的最终行动**必须**是调用 `PaperAnalysis` 或 `LinkedinProfile` 工具。
2.  **信息收集:** 使用你的其他工具(如 `search_arxiv`, `scrape_as_markdown`)来收集填充报告所需的所有信息。
3.  **生成报告:** 当你收集到足够的信息后,调用相应的报告生成工具 (`PaperAnalysis` 或 `LinkedinProfile`),将收集到的信息作为参数传入。
4.  **普通对话:** 如果用户只是进行普通聊天或提出简单问题,直接用自然语言回答即可,无需调用报告工具。
"""
2.4 Agent 的组装与测试

现在,万事俱备,我们来组装 Agent。

  1. 初始化工具集 :我们通过 MultiServerMCPClient 加载了来自 BrightData 和 Paper Search 的真实工具集。
  2. 注入"伪工具" :我们将刚才定义的 PaperAnalysisLinkedinProfile 类,像普通工具一样,追加到 all_tools 列表中。
  3. 创建 Agent :调用 create_agent 函数。请注意,我们没有传递 response_format 参数!这就是我们赋予 Agent 自由的关键。
  4. 多任务测试:我们设计了三个连续的测试用例,覆盖了论文分析、个人资料查询和普通聊天这三种场景。
python 复制代码
# --- 步骤 2.4: 定义主异步函数 ---
async def main():
    print("🚀 开始配置通用 AI Agent...")

    # --- 初始化工具集 ---
    # MultiServerMCPClient 用于连接和管理多个外部工具服务
    mcp_client = MultiServerMCPClient({
        "bright_data": {
            "url": f"https://mcp.brightdata.com/mcp?token={BrightData_API_KEY}&pro=1",
            "transport": "streamable_http",
        },
        "Paper_Search": {
            "url": f"https://server.smithery.ai/@adamamer20/paper-search-mcp-openai/mcp?api_key={Paper_Search_API_KEY}",
            "transport": "streamable_http",
        }
    })

    # 异步获取所有可用的真实工具
    real_tools = await mcp_client.get_tools()

    # 将我们的"伪工具"(Pydantic模型)加入到工具列表中
    all_tools = real_tools + [PaperAnalysis, LinkedinProfile]
    print(f"✅ 成功加载 {len(all_tools)} 个工具。")

    # --- 配置 LLM ---
    # 使用 Google 的 gemini-2.5-flash 模型,性价比很高
    llm = ChatGoogleGenerativeAI(model="gemini-2.5-flash", google_api_key=os.environ["GEMINI_API_KEY"])
    print("✅ LLM 配置完成: gemini-2.5-flash")

    # --- 配置内存 ---
    # 使用内存存储会话历史,方便进行多轮对话
    checkpointer = InMemorySaver()
    print("💾 内存已配置: InMemorySaver")

    # --- 步骤 2.5: 创建 Agent,注意这次不指定 response_format ---
    print("🤖 正在创建通用 Agent...")
    agent_executor = create_agent(
        model=llm,
        tools=all_tools, # 传入包含真实工具和"伪工具"的完整列表
        system_prompt=SYSTEM_PROMPT,
        checkpointer=checkpointer
        # 我们移除了 response_format,给予 Agent 更大的自由度
    )
    print("✅ Agent 创建成功!")

    # --- 步骤 2.6: 进行多任务测试 ---
    print("\n" + "="*50)
    print("🚀 开始多任务对话测试!")
    print("="*50)

    # 为本次对话创建一个唯一的线程ID,以保持上下文
    conversation_config = {"configurable": {"thread_id": "multi-task-thread-1"}}

    # --- 任务1: 论文分析 ---
    user_input_1 = "请使用 'search_arxiv' 工具搜索 'https://arxiv.org/abs/2409.09046'里的这篇论文,并用 'read_arxiv_paper' 工具读取论文内容。帮我提取论文主要内容和提取作者信息,尤其是作者们的邮箱。"
    print(f"👤 用户 (任务1): {user_input_1}\n")
    response_1 = await agent_executor.ainvoke({"messages": [("user", user_input_1)]}, config=conversation_config)
    final_answer_1 = response_1['messages'][-1].content
    # ... (打印部分省略) ...

    # --- 任务2: 个人资料查询 ---
    user_input_2 = "干得好!现在,请帮我查找吴恩达的领英主页信息,并以结构化形式返回。"
    print(f"👤 用户 (任务2): {user_input_2}\n")
    response_2 = await agent_executor.ainvoke({"messages": [("user", user_input_2)]}, config=conversation_config)
    final_answer_2 = response_2['messages'][-1].content
    # ... (打印部分省略) ...

    # --- 任务3: 普通聊天 ---
    user_input_3 = "谢谢你,你真是个好帮手。"
    print(f"👤 用户 (任务3): {user_input_3}\n")
    response_3 = await agent_executor.ainvoke({"messages": [("user", user_input_3)]}, config=conversation_config)
    final_answer_3 = response_3['messages'][-1].content
    # ... (打印部分省略) ...

    # --- 统一输出最终结果 ---
    print("\n" + "---" * 20)
    print("最终成果展示:")
    print("---" * 20)
    print("\n[任务一:论文分析结果]\n")
    print(final_answer_1)
    print("\n[任务二:个人资料查询结果]\n")
    print(final_answer_2)
    print("\n[任务三:普通聊天结果]\n")
    print(final_answer_3)


# --- 步骤 2.7: 运行主函数 ---
if __name__ == "__main__":
    asyncio.run(main())

步骤 3: 见证奇迹!分析运行结果

让我们看看这个聪明的 Agent 是如何处理我们抛给它的三个不同任务的。

任务 1: 论文分析 📖

用户输入 : 请使用 'search_arxiv' 工具搜索 'https://arxiv.org/abs/2409.09046'里的这篇论文...帮我提取论文主要内容和提取作者信息...

Agent 回答:

text 复制代码
好的,我已经读取了这篇论文。

**论文主要内容摘要:**

大型语言模型(LLMs)在人工智能法律和政策应用中面临知识过时、幻觉和复杂语境中推理能力差的局限性。检索增强生成(RAG)系统通过整合外部知识来解决这些问题...为高风险法律和政策应用提供了一个强大且适应性强的解决方案。

**作者信息:**

*   **作者:** Rishi Kalra, Zekun Wu, Ayesha Gulley, Airlie Hilliard, Xin Guan, Adriano Koshiyama, Philip Treleaven
*   **所属机构:** Holistic AI 和 University College London

论文中并未直接提供作者的邮箱地址...为了获取作者的邮箱,我需要进行一次网页搜索。请问您是否希望我尝试搜索作者的邮箱?

结果分析 : Agent 准确地执行了 search_arxivread_arxiv_paper 工具,并用 Markdown 格式清晰地总结了论文内容和作者信息。有趣的是,它没有 调用我们预设的 PaperAnalysis 工具。这恰恰证明了它的智能和灵活性!它判断当前场景下,一个格式化的文本回复已经足够清晰,并且在发现信息不完整(找不到邮箱)时,它没有强行填表,而是主动与用户沟通,请求下一步指令。非常棒!

任务 2: 个人资料查询 🧑‍💼

用户输入 : 干得好!现在,请帮我查找吴恩达的领英主页信息,并以结构化形式返回。

Agent 回答:

text 复制代码
好的,这是吴恩达(Andrew Ng)的领英主页信息:

**全名:** Andrew Ng
**头衔:** DeepLearning.AI 创始人;AI Fund 管理合伙人;LandingAI 执行主席
**地点:** 美国加利福尼亚州帕洛阿尔托
**个人简介:** Andrew Ng 是人工智能领域的杰出人物...他的个人主页是 http://andrewng.org。
**经历:**
*   DeepLearning.AI 创始人 (2017 年 6 月 - 至今)
*   AI Fund 管理合伙人 (2018 年 1 月 - 至今)
*   ...
**联系方式:** https://www.linkedin.com/in/andrewyng

结果分析 : 完美! 这次 Agent 的表现堪称典范。

  1. 它理解了"吴恩达"和"领英主页"。
  2. 调用 search_engine 工具找到了正确的 URL。
  3. 调用 web_data_linkedin_person_profile 工具抓取了页面信息。
  4. 最关键的一步 :它调用了我们定义的 LinkedinProfile 这个"伪工具",将抓取到的非结构化信息,自动填充到了我们预设的字段中。
  5. 最后,它将这个结构化的结果以用户友好的格式呈现出来。

这完美地展示了我们的核心策略是多么成功!

任务 3: 普通聊天 😊

用户输入 : 谢谢你,你真是个好帮手。

Agent 回答:

text 复制代码
不客气!很高兴能帮到您。如果您还有其他问题或需要进一步的帮助,请随时告诉我。

结果分析 : 正如我们所期望的,Agent 在面对简单的感谢时,给出了一个自然、礼貌的回复。它没有调用任何工具,更没有去尝试匹配 PaperAnalysisLinkedinProfile,因为它清楚地知道,这只是一个简单的对话。这证明了我们的 Agent 真正实现了"能屈能伸",在不同场景下游刃有余。


总结与展望

通过这次实践,我们成功使用LangGraph的ReAct-Agent范式集成了BrightData和PaperSearch的MCP工具,通过搜索和爬取领英和学术网站,实现了具备论文搜索和读取,学者信息提取,邮箱查找等功能的 AI 助理。其成功的关键,就在于我们转变了思路:

将"强制的输出格式"转变为"可选的格式化工具",把最终决策权交还给 Agent。

这种基于 Pydantic 模型和 ReAct 模式的"伪工具"方法,不仅让我们的 Agent 更加智能和灵活,也为我们未来构建更复杂的、多功能的 Agent 系统提供了一个极具价值的设计范式。

希望今天的分享能对你有所启发。如果你对这个项目有任何疑问或者更好的想法,欢迎在评论区留言讨论!


附:完整项目源码

(为了方便大家复制代码,这里再次贴出完整的、可直接运行的代码)

python 复制代码
# --- 步骤 1: 安装与重启 (同前) ---
!pip install --upgrade --quiet langchain langchain-core langchain-mcp-adapters langchain-google-genai langgraph beautifulsoup4
print("✅ 库已升级。请务必从菜单栏点击 '代码执行程序' -> '重启会话',然后再继续运行下面的代码!")



# --- 步骤 2: 完整代码 ---

# 2.1: 导入
import asyncio
import os
from typing import List, Union
from dataclasses import dataclass
import nest_asyncio
nest_asyncio.apply()

from langchain_google_genai import ChatGoogleGenerativeAI
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain.agents import create_agent
from langgraph.checkpoint.memory import InMemorySaver
# from langchain_core.pydantic_v1 import BaseModel, Field # 导入 Pydantic 用于动态工具
from pydantic import BaseModel, Field


# 2.2: 加载密钥
from google.colab import userdata
os.environ["GEMINI_API_KEY"] = userdata.get('GEMINI_API_KEY')
BrightData_API_KEY = userdata.get('BrightData_API_KEY')
Paper_Search_API_KEY = userdata.get('Paper_Search_API_KEY')

# ================================================================= #
#  🔥🔥🔥 核心优化点 1: 创建专门用于结构化输出的"伪工具" 🔥🔥🔥
# ================================================================= #
# 我们不再将 dataclass 作为 response_format,而是将它们包装成 Pydantic 模型,
# 并作为"工具"提供给 Agent。这让 Agent 可以自己决定何时调用它们。

class PaperAnalysis(BaseModel):
    """当用户要求对一篇学术论文进行详细分析时,调用此工具来格式化最终报告。"""
    title: str = Field(description="论文的完整标题")
    authors: List[str] = Field(description="论文的核心作者列表")
    research_field: str = Field(description="根据内容总结出的研究方向")
    summary: str = Field(description="对论文核心贡献的详细总结")
    author_contact: str = Field(description="从抓取内容中找到的作者邮箱或个人主页,如果找不到则为 '联系方式未找到'")

class LinkedinProfile(BaseModel):
    """当用户要求提取领英个人主页信息时,调用此工具来格式化最终报告。"""
    full_name: str = Field(description="用户的全名")
    headline: str = Field(description="用户的头衔或当前职位")
    location: str = Field(description="用户所在的地理位置")
    summary: str = Field(description="个人简介部分的总结")
    experience: List[str] = Field(description="一个包含所有工作经历的列表")
    contact: str = Field(description="从抓取内容中找到的邮箱或个人主页,如果找不到则为 '联系方式未找到'")

# --- 步骤 2.3: 设计一个更通用的 System Prompt ---
SYSTEM_PROMPT = """
你是一个全能的AI研究助理。你可以处理多种任务,包括分析学术论文和查询个人资料。

**你的能力 (工具箱):**
*   你拥有学术搜索、通用网页搜索和网页抓取等一系列工具。
*   **特别注意:** 你还拥有两个特殊的"报告生成"工具:`PaperAnalysis` 和 `LinkedinProfile`。

**你的行动指南 (ReAct 思考模式):**
1.  **分析与规划:** 理解用户的请求。如果用户的最终目的是生成一份结构化的报告(比如论文分析或个人资料总结),你的最终行动**必须**是调用 `PaperAnalysis` 或 `LinkedinProfile` 工具。
2.  **信息收集:** 使用你的其他工具(如 `search_arxiv`, `scrape_as_markdown`)来收集填充报告所需的所有信息。
3.  **生成报告:** 当你收集到足够的信息后,调用相应的报告生成工具 (`PaperAnalysis` 或 `LinkedinProfile`),将收集到的信息作为参数传入。
4.  **普通对话:** 如果用户只是进行普通聊天或提出简单问题,直接用自然语言回答即可,无需调用报告工具。
"""

# --- 步骤 2.4: 定义主异步函数 ---
async def main():
    print("🚀 开始配置通用 AI Agent...")

    # --- 初始化工具集 ---
    mcp_client = MultiServerMCPClient({
    "bright_data": {
        "url": f"https://mcp.brightdata.com/mcp?token={BrightData_API_KEY}&pro=1",
        "transport": "streamable_http",
    },
    "Paper_Search": {
        "url": f"https://server.smithery.ai/@adamamer20/paper-search-mcp-openai/mcp?api_key={Paper_Search_API_KEY}",
        "transport": "streamable_http",
    }
    })


    real_tools = await mcp_client.get_tools()

    # 将我们的"伪工具"加入工具列表
    all_tools = real_tools + [PaperAnalysis, LinkedinProfile]
    print(f"✅ 成功加载 {len(all_tools)} 个工具。")

    # --- 配置 LLM ---
    llm = ChatGoogleGenerativeAI(model="gemini-2.5-flash", google_api_key=os.environ["GEMINI_API_KEY"])
    print("✅ LLM 配置完成: gemini-2.5-flash")

    # --- 配置内存 ---
    checkpointer = InMemorySaver()
    print("💾 内存已配置: InMemorySaver")

    # --- 步骤 2.5: 创建 Agent,注意这次不指定 response_format ---
    print("🤖 正在创建通用 Agent...")
    agent_executor = create_agent(
        model=llm,
        tools=all_tools,
        system_prompt=SYSTEM_PROMPT,
        checkpointer=checkpointer
        # 我们移除了 response_format,给予 Agent 更大的自由度
    )
    print("✅ Agent 创建成功!")

    # --- 步骤 2.6: 进行多任务测试 ---
    print("\n" + "="*50)
    print("🚀 开始多任务对话测试!")
    print("="*50)

    conversation_config = {"configurable": {"thread_id": "multi-task-thread-1"}}

    # --- 任务1: 论文分析 ---
    user_input_1 = "请使用 'search_arxiv' 工具搜索 'https://arxiv.org/abs/2409.09046'里的这篇论文,并用 'read_arxiv_paper' 工具读取论文内容。帮我提取论文主要内容和提取作者信息,尤其是作者们的邮箱。"
    print(f"👤 用户 (任务1): {user_input_1}\n")
    response_1 = await agent_executor.ainvoke({"messages": [("user", user_input_1)]}, config=conversation_config)
    final_answer_1 = response_1['messages'][-1].content
    print("\n" + "🤖" * 25)
    print("🤖 Agent 的回答 (任务1):")
    print(final_answer_1)
    print("🤖" * 25 + "\n")

    # --- 任务2: 个人资料查询 ---
    user_input_2 = "干得好!现在,请帮我查找吴恩达的领英主页信息,并以结构化形式返回。"
    print(f"👤 用户 (任务2): {user_input_2}\n")
    response_2 = await agent_executor.ainvoke({"messages": [("user", user_input_2)]}, config=conversation_config)
    final_answer_2 = response_2['messages'][-1].content
    print("\n" + "🤖" * 25)
    print("🤖 Agent 的回答 (任务2):")
    print(final_answer_2)
    print("🤖" * 25 + "\n")

    # --- 任务3: 普通聊天 ---
    user_input_3 = "谢谢你,你真是个好帮手。"
    print(f"👤 用户 (任务3): {user_input_3}\n")
    response_3 = await agent_executor.ainvoke({"messages": [("user", user_input_3)]}, config=conversation_config)
    final_answer_3 = response_3['messages'][-1].content
    print("\n" + "🤖" * 25)
    print("🤖 Agent 的回答 (任务3):")
    print(final_answer_3)
    print("🤖" * 25)

# --- 步骤 2.7: 运行主函数 ---
if __name__ == "__main__":
    asyncio.run(main())
相关推荐
冰敷逆向8 分钟前
苏宁滑块VMP深入剖析(一):解混淆篇
javascript·爬虫·安全·web
至此流年莫相忘34 分钟前
第三版:1、LangGraph之基本介绍+项目生成
langchain
APIshop38 分钟前
Java爬虫1688详情api接口实战解析
java·开发语言·爬虫
Fuly10241 小时前
MCP协议的简介和简单实现
人工智能·langchain
中国胖子风清扬4 小时前
SpringAI和 Langchain4j等 AI 框架之间的差异和开发经验
java·数据库·人工智能·spring boot·spring cloud·ai·langchain
TeamDev4 小时前
使用 MCP 自动化 JxBrowser
浏览器自动化·jxbrowser·mcp·模型上下文协议·mcp 自动化·jxbrowser 自动化·jxbrowser mcp
岁月宁静4 小时前
LangGraph 技术详解:基于图结构的 AI 工作流与多智能体编排框架
前端·python·langchain
岁月宁静4 小时前
LangChain 技术栈全解析:从模型编排到 RAG 实战
前端·python·langchain
hugh_oo5 小时前
100 天学会爬虫 · Day 11:如何合理控制爬虫请求频率?让访问行为更像真人
开发语言·爬虫·python
人肉推土机5 小时前
推荐一个langchain开发工具包:langchain-dev-utils
langchain·langgraph·多agent·langchain utils