找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

2670

积分

0

好友

360

主题
发表于 1 小时前 | 查看: 3| 回复: 0

卡通仓鼠云朵君打招呼

大模型再强,也是个“纸上谈兵”的理论家。本文将带你用代码实现一个能自主上网搜索的智能体,让它从“只会说”变成“会动手”,彻底告别幻觉。

写在前面

相信你肯定遇到过这种场景:满心欢喜地打开一个号称“无所不知”的AI助手,问它“今天北京天气怎么样?”,它却自信满满地给你编了个风和日丽的晴天,而你此刻正站在倾盆大雨里。或者问它“我公司上周的销售数据是多少?”,它只能抱歉地说“我的知识截止到xx年”。

这种无力感,就像你雇了一个自称读过万卷书的“理论派”顾问,却让他去执行一个简单的数据查询任务——他道理讲得头头是道,但一伸手就露怯。

问题出在哪?因为这些AI助手本质上只是一个封闭的推理引擎。它的知识库是静态的,只存在于训练时的那一刹那。它无法感知“现在”,也无法连接你的“私有数据”。

那怎么破局?答案就是给它一双手,让它学会使用工具

今天这篇文章,我们就来手把手拆解一个极具变革性的智能体架构:工具使用。我会带你用代码实现一个能自己上网搜最新信息的智能体,让它从一个“纯书生”进化成能调用API、获取实时数据的“实战派”。读完这篇文章,你不仅能理解其核心原理,更能直接拥有一个可以扩展的、真实可用的智能助手原型。

一、什么是“工具使用”智能体?

简单说,就是让大语言模型(LLM)具备了调用外部函数或API的能力,我们把这些外部能力称为“工具”。智能体就像一个聪明的项目经理,它会自主判断:当用户的问题自己搞不定时,就立刻去调用最合适的“工具人”来获取信息。

工作流程:

  1. 接收任务:用户说:“查一下最近一届WWDC发布了啥?”
  2. 自主决策:智能体一琢磨:“我训练数据里最新的WWDC还是去年的,得出去查查。”于是它决定调用“网页搜索”这个工具。
  3. 执行动作:它把“苹果 WWDC 最新发布”作为参数,精准地调用了一个叫 web_search 的函数。
  4. 获取反馈:系统执行搜索,把网页结果扔回给智能体。
  5. 合成答案:智能体把搜索结果和自己的语言组织能力结合,给出一个有理有据、最新最热的答案。

它能用在哪儿?

  • 研究助手:写报告、查新闻,需要最新信息时,它能自己上网搜。
  • 企业智能助理:“查一下上周三在‘技术群’里谁最活跃?”——它能直接调取公司内部数据库。
  • 数学与科学计算:算个复杂的微积分,直接调计算器工具,比LLM自己硬算靠谱得多。

优点和缺点

  • 优点
    • 告别幻觉:获取的是真实、实时的数据,回答有根有据,可信度飙升。
    • 能力无限:你想让它会什么,就给它配什么工具,加个工具就像给手机装个App一样,能力边界可以无限扩展。
  • 缺点
    • 接入成本:你得先把这些“管道”搭好,定义好每个工具是干啥的,还要处理API密钥、错误重试这些烦心事。
    • 工具依赖:工具是“猪队友”还是“神助手”,直接决定了智能体的上限。如果搜索工具返回的是垃圾信息,那它的答案也不会好到哪去。

二、动手实现一个会自己上网的智能体

理论说完了,咱们直接上代码。别怕,我带你一步步走,每一步都会解释清楚为什么这么干。

0. 准备工作

首先,我们需要安装一些“神器”。这就像是组装机器人前,得先把螺丝刀、电焊准备好。

# 一键安装所有需要的库:用于编排的langchain、构建工作流的langgraph、管理环境变量的dotenv,以及我们今天的主角——网页搜索工具tavily
!pip install -q -U langchain-nebius langchain langgraph rich python-dotenv tavily-python

接着,我们设置好环境变量。你需要去注册获取几个API密钥:Nebius(用来调用大模型)、LangSmith(用来追踪智能体的思考过程,方便调试)、Tavily(我们的网页搜索工具)。

创建一个 .env 文件,写入你的密钥:

NEBIUS_API_KEY="你的Nebius_API密钥"
LANGCHAIN_API_KEY="你的LangSmith_API密钥"
TAVILY_API_KEY="你的Tavily_API密钥"

然后用代码加载这些密钥并导入所需库。

import os
import json
from typing import List, Annotated, TypedDict, Optional
from dotenv import load_dotenv

from langchain_nebius import ChatNebius
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_core.messages import BaseMessage, ToolMessage
from pydantic import BaseModel, Field

from langgraph.graph import StateGraph, END
from langgraph.graph.message import AnyMessage, add_messages
from langgraph.prebuilt import ToolNode

from rich.console import Console
from rich.markdown import Markdown

# 加载环境变量
load_dotenv()
# 开启LangSmith追踪,让你能看到智能体每一步的内心戏
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_PROJECT"] = "Agentic Architecture - Tool Use"

# 检查一下密钥有没有成功加载
for key in ["NEBIUS_API_KEY", "LANGCHAIN_API_KEY", "TAVILY_API_KEY"]:
    if not os.environ.get(key):
        print(f"⚠️ {key} 未找到,请检查 .env 文件")
print("✅ 环境变量加载完毕")

1. 定义工具箱:给智能体配一把“搜索引擎”

智能体强不强,就看它的工具箱里有什么。这里我们给它配一把最实用的武器——实时网页搜索

TavilySearchResults 这个工具很强大,但更重要的是,我们要给它一个清晰、精确的描述。LLM就是靠这个描述来理解“什么时候该用这个工具”。描述写得越清楚,它决策就越准确。

# 初始化工具,最多返回2条结果,避免上下文过长
search_tool = TavilySearchResults(max_results=2)
# 给它起个响亮的名字和描述
search_tool.name = "web_search"
search_tool.description = "用这个工具去互联网上搜任何最新的信息,比如新闻、事件、最新比分。它能拿到实时内容。"
tools = [search_tool]

# 先测试一下,看看它返回的原始数据长啥样
console = Console()
test_query = "最近一届超级碗的比分是多少?"
test_result = search_tool.invoke({"query": test_query})
console.print(f"[bold green]🔍 测试查询:[/bold green] {test_query}")
console.print("\n[bold green]📦 返回的原始数据:[/bold green]")
console.print(test_result)

输出示例(实际内容会实时变化):

[
    {
        'title': '超级碗LIX: 费城老鹰40-22击败堪萨斯城酋长',
        'url': 'https://...',
        'content': '2025年2月9日,新奥尔良,费城老鹰队以40-22的比分战胜堪萨斯城酋长队...',
        'score': 0.95
    },
    {
        'title': 'NFL官网:第59届超级碗战报',
        'url': 'https://...',
        'content': '老鹰队四分卫赫茨表现神勇,带领球队夺冠...',
        'score': 0.92
    }
]

可以看到,工具返回的是一个结构化的数据列表,里面包含了标题、链接、内容片段和相关性评分。这,就是智能体将要处理的“原材料”。

2. 搭建大脑与双手:用LangGraph构建智能体

现在,我们要把“大脑”(LLM)和“双手”(工具)通过一个工作流连接起来。这里我们用LangGraph,它就像一个精密的流程图,能完美实现“思考-行动-观察”的循环。

2.1 定义状态:给智能体一个“记忆本”

智能体在工作过程中需要记住“我们聊到哪了”。这个“记忆本”就是一个消息列表,里面存着用户说的话、智能体的思考、以及工具返回的结果。

class AgentState(TypedDict):
    # messages是一个列表,每次有新的消息就追加进去
    messages: Annotated[list[AnyMessage], add_messages]

2.2 绑定工具:让大模型“开眼”

这是最神奇的一步。我们用 .bind_tools() 方法,把工具的名字描述告诉LLM。这样,LLM在内部推理时,就知道“哦,原来我还有这个能力可以用”。

llm = ChatNebius(model="meta-llama/Meta-Llama-3.1-8B-Instruct", temperature=0)
# 将工具绑定到LLM,让它变得有“感知力”
llm_with_tools = llm.bind_tools(tools)

2.3 定义节点:大脑(Agent)和双手(Tool)

工作流由节点构成。我们有两个核心节点:

  • agent_node:智能体的“大脑”。它负责思考,调用绑定了工具的LLM,并决定下一步是“给出答案”还是“调用工具”。
  • tool_node:智能体的“双手”。它接收来自大脑的“工具调用指令”,真的去执行那些工具,并把结果带回来。
def agent_node(state: AgentState):
    """这是智能体的大脑:决定下一步怎么做"""
    console.print("🤔 --- 智能体:思考中... ---")
    # 把当前所有的对话记忆都喂给LLM
    response = llm_with_tools.invoke(state["messages"])
    # 将LLM的思考结果(可能是答案,也可能是工具调用请求)存入状态
    return {"messages": [response]}

# LangGraph自带的ToolNode,就像一个专业的“工具执行器”
tool_node = ToolNode(tools)

2.4 定义路由:大脑的决策开关

大脑思考完了,我们怎么知道它下一步要干嘛?这就需要路由器。它就像一个裁判,看一眼大脑最后输出的消息:如果里面包含 tool_calls(要调用工具的请求),就跳转到 tool_node;如果没有,就代表工作流结束,可以输出最终答案了。

def router_function(state: AgentState) -> str:
    """裁判函数:根据智能体的最后一条消息,决定下一步去哪儿"""
    last_message = state["messages"][-1]
    if last_message.tool_calls:
        console.print("🔧 --- 路由器:决策是调用工具 ---")
        return "call_tool"  # 去执行工具
    else:
        console.print("🎉 --- 路由器:决策是结束,输出答案 ---")
        return "__end__"  # 流程结束

3. 组装运行:启动你的第一个“动手派”智能体

所有零件都准备好了,现在我们把它们拼装成一个完整的机器人。

# 创建一个状态图
graph_builder = StateGraph(AgentState)

# 添加两个核心节点
graph_builder.add_node("agent", agent_node)
graph_builder.add_node("call_tool", tool_node)

# 设定入口点:从大脑开始
graph_builder.set_entry_point("agent")

# 添加条件边:大脑之后,由裁判决定去工具还是结束
graph_builder.add_conditional_edges(
    "agent",
    router_function,
)

# 添加普通边:工具执行完后,必须回到大脑(让它整理结果,并决定是否继续)
graph_builder.add_edge("call_tool", "agent")

# 编译成最终可运行的应用
tool_agent_app = graph_builder.compile()

让我们来问它一个新鲜热乎的问题,看看它能不能自己找到答案。

user_query = "苹果最近一届 WWDC 大会有哪些主要发布内容?"
initial_input = {"messages": [("user", user_query)]}

console.print(f"🚀 启动智能体,问题:'{user_query}'\n")

# 以流式方式执行,可以看到每一步的细节
for chunk in tool_agent_app.stream(initial_input, stream_mode="values"):
    # 打印最后一条消息,格式友好
    chunk["messages"][-1].pretty_print()
    console.print("\n---\n")

console.print("\n✅ 工作流执行完成!")

执行过程解读:

你会看到类似下面的输出,清晰地展示了智能体的思考轨迹:

  1. 用户提问苹果最近一届 WWDC 大会有哪些主要发布内容?
  2. 智能体思考🤔 --- 智能体:思考中... ---,然后它输出了一条消息,里面包含了 Tool Calls 请求,参数是 query: 苹果 WWDC 最新发布
  3. 路由器决策🔧 --- 路由器:决策是调用工具 ---
  4. 工具执行:系统调用 web_search,返回一堆搜索结果(Tool Message)。
  5. 智能体再次思考:拿到搜索结果后,它再次进入 agent_node,这次它会基于工具返回的原始数据进行整理。
  6. 路由器再次决策🎉 --- 路由器:决策是结束,输出答案 ---
  7. 最终输出:智能体给出了一个条理清晰的、基于最新信息的回答,比如:“苹果最近一届WWDC大会推出了全新的Liquid Glass设计语言,并为iOS、iPadOS和macOS带来了大量新功能,包括…”。

4. 效果评估:如何判断它做得好不好?

一个智能体好不好,不能只看感觉。我们可以用“LLM作为评委”的方式,给它打个分。

我们定义一个评分标准:工具选择是否正确(选对了工具吗?)、工具输入是否精准(搜索关键词对吗?)、信息整合质量如何(把搜索结果组织得好吗?)。

from pydantic import BaseModel, Field

class ToolUseEvaluation(BaseModel):
    tool_selection_score: int = Field(description="1-5分:工具选对了吗?")
    tool_input_score: int = Field(description="1-5分:工具的参数(如搜索词)写得好吗?")
    synthesis_quality_score: int = Field(description="1-5分:最终答案整合得如何?")
    justification: str = Field(description="打分理由")

# 用另一个LLM来当评委
judge_llm = llm.with_structured_output(ToolUseEvaluation)

# 调用我们的智能体,拿到完整对话记录
final_answer = tool_agent_app.invoke(initial_input)
conversation_trace = "\n".join([f"{m.type}: {m.content or ''}{getattr(m, 'tool_calls', '')}" for m in final_answer['messages']])

def evaluate_tool_use(trace: str):
    prompt = f"""你是AI智能体评审专家,请根据对话追踪打分:
{trace}
    """
    return judge_llm.invoke(prompt)

evaluation = evaluate_tool_use(conversation_trace)
console.print(evaluation.model_dump_json(indent=2))

评分结果示例

{
  "tool_selection_score": 5,
  "tool_input_score": 5,
  "synthesis_quality_score": 4,
  "justification": "智能体准确判断需要最新信息,选择了web_search工具,查询词精准。最终答案很好地整合了搜索结果,但部分细节可以更突出。总体表现优秀。"
}

通过这个自动化的评估,我们能对智能体的性能有一个量化的、客观的认识,这对于持续优化非常关键。

写在最后

恭喜你!你已经亲手构建了一个能够“动手”的智能体。这不仅仅是写了几行代码,而是掌握了一种让LLM突破自身局限的核心范式。

回顾一下今天的三个核心记忆点:

  1. 原理:工具使用架构让LLM从静态的推理引擎,变成了能连接外部世界的动态智能体。
  2. 实践:我们用LangGraph搭建了“思考-行动-观察”的循环,并配了一个强大的网页搜索工具。
  3. 避坑:工具的描述至关重要,这是LLM决策的关键;同时,执行追踪和LLM作为评委是保证智能体可靠性的两大法宝。

在技术探索的路上,我们经常会惊叹于大模型的“智力”,但真正让它在现实世界中发光发热的,往往是它“调用工具”的能力。这就像是一个刚毕业的博士生,空有一肚子理论,当你能教会他使用实验仪器、查阅最新文献时,他才真正成为了一个有生产力的科研人员。

未来,你可以为这个智能体添加更多工具:让它查内部数据库、发邮件、操作Excel……当工具集越来越丰富,一个真正属于你自己的、无所不能的数字助手就诞生了。

智能体从工具箱中探出头来的幽默梗图

最后,留一个思考题:在你的工作中,如果让你为智能体设计一个最实用的工具,它会是什么?是帮你写周报?自动生成代码注释?还是监控服务器状态?欢迎在 云栈社区 分享你的想法,我们一起探讨。




上一篇:LlamaParse 实测:如何用 AI 原生解析器高效提取 PDF 表格数据?
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-3-25 16:19 , Processed in 0.687805 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表