当前位置: 首页 > news >正文

AI Agent 项目学习笔记(九):网页搜索、网页抓取与资源下载工具

1. 本期目标

上一篇文章分析了ai_agent项目的 Tool Calling 总体机制。我们已经知道,项目通过:

@Tool ↓ ToolRegistration ↓ ToolCallback[] ↓ LoveApp.doChatWithTools()

把 Java 方法注册为大模型可调用的工具,从而让智能体不只会回答问题,还可以调用外部能力完成任务。

这一期继续深入工具模块中的联网能力,重点分析三个工具:

WebSearchTool WebScrapingTool ResourceDownloadTool

这三个工具共同解决的是:

智能体如何获取外部网络信息?

其中,WebSearchTool用于搜索信息,WebScrapingTool用于抓取网页内容,ResourceDownloadTool用于下载网络资源。项目的ToolRegistration会创建这三个工具,并和文件操作、终端执行、PDF 生成、任务终止等工具一起转换为ToolCallback[]LoveApp使用。(GitHub)

本期主要解决几个问题:

1. 为什么 Agent 需要联网工具? 2. WebSearchTool 如何通过搜索引擎获取信息? 3. WebScrapingTool 如何抓取网页内容? 4. ResourceDownloadTool 如何下载网络资源? 5. 搜索、抓取、下载三类工具之间是什么关系? 6. 这三个工具如何组成一个完整的信息获取链路? 7. 当前实现有什么优点? 8. 当前实现有哪些安全风险和优化方向?

2. 为什么要把这三个工具放在一起讲?

这三个工具都属于“外部信息获取能力”,但作用不一样。

可以简单理解为:

WebSearchTool: 帮智能体找到信息在哪里。 WebScrapingTool: 帮智能体读取网页里有什么。 ResourceDownloadTool: 帮智能体把某个资源保存到本地。

如果只搜索,不抓取,智能体只能得到搜索结果摘要,无法深入分析网页正文。

如果只抓取,不搜索,智能体需要用户直接提供 URL,缺少主动发现信息的能力。

如果只下载,不搜索和抓取,智能体虽然能保存资源,但不知道应该下载什么。

所以这三个工具组合起来,才能形成一条较完整的联网信息处理链:

搜索关键词 ↓ 得到候选网页 ↓ 抓取网页内容 ↓ 分析网页信息 ↓ 必要时下载资源

对于智能体来说,这条链路非常重要。

因为它让模型从“只能根据已有知识回答”扩展为:

可以查找外部资料, 可以阅读网页内容, 可以保存网络资源。

3. ToolRegistration 中如何注册联网工具?

ToolRegistration中,项目创建了多个工具对象,其中就包括:

WebSearchTool webSearchTool = new WebSearchTool(searchApiKey); WebScrapingTool webScrapingTool = new WebScrapingTool(); ResourceDownloadTool resourceDownloadTool = new ResourceDownloadTool();

随后,项目通过:

ToolCallbacks.from(...)

把这些工具和其他工具一起转换为ToolCallback[]WebSearchTool的 API Key 来自配置项search-api.api-key,这说明搜索工具需要外部搜索服务支持。(GitHub)

可以理解为:

工具类本身: 定义工具能做什么。 ToolRegistration: 决定哪些工具会暴露给模型。 ToolCallback[]: 最终交给 ChatClient 的工具列表。

所以联网工具并不是自动可用的,它们必须经过注册,才能被模型在工具调用过程中选择。


4. LoveApp 如何使用这些工具?

LoveApp中,项目通过:

@Resource private ToolCallback[] allTools;

注入所有工具,然后在doChatWithTools()中调用:

.toolCallbacks(allTools)

这表示当前这次模型调用可以使用allTools中注册的工具。doChatWithTools()同时传入ChatMemory.CONVERSATION_ID,并额外加入MyLoggerAdvisor,因此工具调用链路也可以结合会话记忆和日志观察。(GitHub)

整体流程可以写成:

用户提出任务 ↓ LoveApp.doChatWithTools() ↓ 注入 conversationId ↓ 注入 MyLoggerAdvisor ↓ 注入 allTools ↓ 模型判断是否需要联网工具 ↓ 后端执行搜索 / 抓取 / 下载 ↓ 工具结果返回模型 ↓ 模型生成最终回答

这就是联网工具接入 Agent 主链路的方式。


5. WebSearchTool:网页搜索工具

WebSearchTool的作用是通过搜索 API 查询外部信息。

源码中,这个类定义了一个搜索接口地址:

private static final String SEARCH_API_URL = "https://www.searchapi.io/api/v1/search";

工具方法是:

@Tool(description = "Search for information from Baidu Search Engine") public String searchWeb( @ToolParam(description = "Search query keyword") String query)

方法内部会构造请求参数,包括用户查询qapi_keyengine = baidu,然后使用 Hutool 的HttpUtil.get()发送请求。返回结果被解析成 JSON 后,代码提取organic_results,取前 5 条结果,并把这些结果拼接成字符串返回。(GitHub)

可以把它的流程理解为:

输入 query ↓ 构造搜索请求参数 ↓ 调用 SearchAPI ↓ 指定搜索引擎为 Baidu ↓ 解析 JSON 响应 ↓ 提取 organic_results 前 5 条 ↓ 拼接成字符串返回

这个工具让智能体拥有了“主动搜索”的能力。


6. WebSearchTool 的输入和输出

6.1 输入

WebSearchTool的输入只有一个:

query:搜索关键词

例如:

杭州 情侣 约会 地点 推荐 异地恋 沟通 方法 七夕 约会 活动 北京

模型会根据用户问题生成合适的搜索关键词。

例如用户说:

“帮我找几个适合周末约会的地方。”

模型可能调用搜索工具,并生成 query:

周末 情侣 约会 地点 推荐

6.2 输出

当前工具返回的是搜索结果 JSON 对象的字符串拼接。

也就是说,它不是返回一个结构化 Java 对象,而是把前 5 条organic_results转成字符串后用逗号连接返回。(GitHub)

可以理解为:

搜索结果 1 的 JSON 字符串, 搜索结果 2 的 JSON 字符串, 搜索结果 3 的 JSON 字符串, 搜索结果 4 的 JSON 字符串, 搜索结果 5 的 JSON 字符串

这种实现比较简单,模型可以从返回字符串中读取标题、链接、摘要等信息。

不过从工程角度看,这里后续还可以继续优化。


7. WebSearchTool 的适用场景

WebSearchTool适合处理需要外部信息的问题。

例如:

用户:帮我搜索一些适合情侣周末约会的地点。 用户:找一些恋爱沟通技巧的参考资料。 用户:查一下最近有什么适合情侣参加的城市活动。 用户:帮我找一篇关于亲密关系沟通的文章。

在这些场景中,大模型自身知识可能不够新,也不一定知道具体网页内容。搜索工具可以先提供候选信息来源。

可以把它理解为:

搜索工具负责扩大信息来源, 模型负责理解和整理搜索结果。

8. WebSearchTool 当前实现的优点

当前实现最大的优点是简单直接。

它只做三件事:

接收关键词 调用搜索 API 返回前 5 条自然搜索结果

这对于学习 Tool Calling 很友好。

因为读者可以清楚看到:

模型生成 query ↓ 后端发起 HTTP 请求 ↓ 工具返回搜索结果 ↓ 模型继续组织答案

而且它没有把搜索逻辑写在LoveApp中,而是单独封装成WebSearchTool,再通过ToolRegistration注册。这种结构符合工具模块化设计。(GitHub)


9. WebSearchTool 可以改进的地方

9.1 搜索结果结构化

当前搜索结果是把 JSON 对象直接转成字符串拼接。(GitHub)

这种方式虽然能用,但可读性一般。

后续可以整理成更清晰的格式:

标题:xxx 摘要:xxx 链接:xxx 排名:1

这样模型更容易理解每条搜索结果,也方便日志观察。


9.2 处理 organic_results 不足 5 条的情况

当前代码中有:

List objects = organicResults.subList(0, 5);

如果搜索结果少于 5 条,可能会出现越界异常。虽然 catch 会返回错误信息,但更好的做法是:

int limit = Math.min(5, organicResults.size()); List objects = organicResults.subList(0, limit);

这样搜索结果不足 5 条时也能正常返回。


9.3 增加搜索失败兜底信息

当前工具失败时返回:

Error searching Baidu: ...

这对调试有用,但对用户不够友好。

可以改成:

搜索失败,原因是:xxx。请稍后重试,或换一个关键词。

这样模型后续组织回答时也更容易给出自然提示。


9.4 增加搜索结果来源字段

后续可以让工具返回:

source = baidu api = searchapi query = 用户查询词 time = 调用时间

这样更适合做工具调用审计。


10. WebScrapingTool:网页抓取工具

WebScrapingTool的作用是抓取指定网页内容。

源码中,它提供了一个工具方法:

@Tool(description = "Scrape the content of a web page") public String scrapeWebPage( @ToolParam(description = "URL of the web page to scrape") String url)

方法内部使用:

Document document = Jsoup.connect(url).get(); return document.html();

也就是说,它通过 Jsoup 请求网页,然后返回完整 HTML。如果发生异常,就返回错误信息。(GitHub)

可以把它的流程写成:

输入 URL ↓ Jsoup.connect(url) ↓ 请求网页 ↓ 获取 Document ↓ 返回 document.html()

这个工具让智能体具备了“读取网页内容”的能力。


11. WebScrapingTool 和 WebSearchTool 的区别

WebSearchToolWebScrapingTool都是联网工具,但作用不同。

WebSearchTool: 输入关键词,输出搜索结果列表。 WebScrapingTool: 输入具体 URL,输出网页 HTML 内容。

例如用户问:

“帮我找一篇关于异地恋沟通的文章,并总结要点。”

可能的工具链是:

第一步:调用 WebSearchTool 搜索“异地恋 沟通 方法” 第二步:从搜索结果中选择一个 URL 第三步:调用 WebScrapingTool 抓取网页内容 第四步:模型总结网页要点

所以,搜索是“找入口”,抓取是“读内容”。


12. WebScrapingTool 的适用场景

WebScrapingTool适合处理用户已经给出 URL,或者搜索工具已经找到候选 URL 的情况。

例如:

用户:帮我总结这个网页的内容:https://xxx 用户:打开刚才搜索结果里的第一篇文章,提炼约会建议。 用户:抓取这个活动页面,看一下适不适合情侣参加。

在这些场景中,模型需要的不只是搜索结果标题,而是网页正文内容。

抓取工具可以把网页内容拿回来,再由模型做总结、提炼和判断。


13. 当前 WebScrapingTool 的问题

13.1 返回完整 HTML,内容太杂

当前工具直接返回:

document.html()

这会包含:

HTML 标签 导航栏 脚本 样式 广告区域 页脚 无关链接

这些内容进入模型后,可能造成上下文污染。(GitHub)

更适合模型处理的是正文文本,而不是完整 HTML。

后续可以改成:

return document.text();

或者进一步提取:

title meta description 正文段落 主要链接

这样更有利于模型理解网页内容。


13.2 缺少超时设置

当前使用:

Jsoup.connect(url).get()

如果目标网站响应很慢,可能导致工具调用阻塞。(GitHub)

后续可以增加:

Jsoup.connect(url) .timeout(5000) .get();

这样可以避免长时间等待。


13.3 缺少 User-Agent

部分网站会拒绝默认爬虫请求。

后续可以增加:

.userAgent("Mozilla/5.0 ...")

这样抓取成功率会更高。


13.4 缺少最大内容长度限制

网页 HTML 可能非常长。

如果完整返回给模型,可能导致:

上下文过长 token 成本增加 模型关注不到重点 甚至超过模型上下文窗口

后续可以限制返回长度:

最多返回前 5000 个字符

或者先做正文抽取,再返回精简内容。


13.5 需要防止 SSRF 风险

网页抓取工具接收任意 URL。

正式系统中,如果不限制 URL,可能被用来访问内网地址或敏感服务。

例如:

http://localhost:8080 http://127.0.0.1 http://169.254.169.254 http://内网服务地址

所以后续应该增加 URL 安全校验:

只允许 http / https 禁止 localhost 和内网 IP 禁止 file:// 等本地协议 设置域名白名单或黑名单

这类联网工具在正式系统中必须慎重处理。


14. ResourceDownloadTool:资源下载工具

ResourceDownloadTool的作用是从指定 URL 下载资源到本地。

源码中,它提供了一个工具方法:

@Tool(description = "Download a resource from a given URL") public String downloadResource( @ToolParam(description = "URL of the resource to download") String url, @ToolParam(description = "Name of the file to save the downloaded resource") String fileName)

方法内部先构造下载目录:

String fileDir = FileConstant.FILE_SAVE_DIR + "/download"; String filePath = fileDir + "/" + fileName;

然后创建目录,并调用:

HttpUtil.downloadFile(url, new File(filePath));

下载成功后,返回文件保存路径。FileConstant.FILE_SAVE_DIR的值是项目根目录下的tmp,因此资源下载目录实际是项目根目录/tmp/download。(GitHub)

可以把流程写成:

输入 url + fileName ↓ 确定保存目录 tmp/download ↓ 创建下载目录 ↓ 调用 HttpUtil.downloadFile() ↓ 保存到本地文件 ↓ 返回保存路径

15. ResourceDownloadTool 和 WebScrapingTool 的区别

这两个工具都接收 URL,但目的不同。

WebScrapingTool: 读取网页内容,返回给模型理解。 ResourceDownloadTool: 下载资源文件,保存到本地路径。

例如:

网页文章: 适合用 WebScrapingTool 抓取并总结。 图片、PDF、压缩包: 适合用 ResourceDownloadTool 下载保存。

可以这样理解:

抓取是“读网页” 下载是“存文件”

两者经常配合使用,但不能混为一谈。


16. ResourceDownloadTool 的适用场景

资源下载工具适合处理:

下载图片 下载 PDF 下载文档 下载网页附件 保存外部资源到本地

例如:

用户:帮我下载这个约会攻略 PDF。 用户:把这个活动海报保存下来。 用户:下载网页里的这个资源文件。

下载后,后续工具还可以继续处理。

例如:

下载 PDF ↓ 保存到 tmp/download ↓ 后续读取或解析 ↓ 模型总结内容

不过当前项目里还没有看到专门的 PDF 解析工具,PDFGenerationTool主要用于生成 PDF,而不是解析下载的 PDF。


17. ResourceDownloadTool 当前实现的优点

当前实现非常直接,适合学习:

接收 URL 和文件名 创建 download 目录 下载文件 返回保存路径

它还复用了项目统一的临时目录配置。FileConstant.FILE_SAVE_DIR指向项目根目录下的tmpResourceDownloadTool在此基础上使用tmp/download保存下载资源。(GitHub)

这比直接把文件下载到项目根目录更合理。

因为临时文件统一放在:

tmp/

更方便后续清理和管理。


18. ResourceDownloadTool 的安全风险

资源下载工具的风险也比较明显。

18.1 文件名路径穿越

当前代码直接拼接:

String filePath = fileDir + "/" + fileName;

如果fileName包含:

../ ..\ / \

就可能写到预期目录之外。(GitHub)

后续应该限制文件名只能包含:

字母 数字 下划线 短横线 点号

并且使用规范化路径检查,确保最终路径仍然位于tmp/download目录下。


18.2 文件大小不受限制

当前工具直接下载文件,没有看到文件大小限制。(GitHub)

如果下载的是大文件,可能导致:

磁盘被占满 请求长时间阻塞 服务性能下降

后续应该限制最大下载大小,例如:

最大 10 MB 最大 50 MB

并在下载前检查响应头中的Content-Length


18.3 文件类型不受限制

当前工具不限制下载文件类型。(GitHub)

正式系统中可以限制:

只允许图片 只允许 PDF 只允许 txt / md / docx 禁止 exe / bat / sh / jar 等可执行文件

这样可以降低被下载恶意文件的风险。


18.4 URL 需要安全校验

和网页抓取一样,下载工具也需要防止访问内网资源。

应该限制:

只允许 http / https 禁止 localhost 禁止 127.0.0.1 禁止内网 IP 禁止云元数据地址

否则下载工具也可能变成 SSRF 攻击入口。


19. 三个工具如何组成完整任务链?

假设用户提出:

帮我找一份适合情侣周末出游的攻略,看看内容是否适合,然后把有用的资源下载下来。

智能体可能形成这样的工具调用链:

第一步:WebSearchTool 搜索“情侣 周末 出游 攻略” 第二步:WebScrapingTool 抓取搜索结果中的攻略网页 第三步:模型分析 判断网页内容是否适合用户需求 第四步:ResourceDownloadTool 下载网页中有用的 PDF、图片或附件 第五步:模型总结 告诉用户资源已保存到哪里,以及主要内容是什么

这说明三个工具之间是递进关系:

搜索:发现信息 抓取:理解信息 下载:保存资源

这正是 Agent 联网能力的基本闭环。


20. 和 RAG 的区别

联网工具和 RAG 都是为了增强模型,但它们的信息来源不同。

RAG: 从项目已有知识库中检索信息。 联网工具: 从外部互联网获取信息。

例如:

用户问:恋爱中如何减少争吵?

这种问题适合先用 RAG,因为本地知识库里可能已经有稳定答案。

用户问:这个周末北京有哪些情侣活动?

这种问题更适合联网搜索,因为它依赖实时信息。

所以可以这样分工:

稳定领域知识: 优先 RAG。 实时外部信息: 优先 WebSearchTool。 具体网页分析: 使用 WebScrapingTool。 资源保存: 使用 ResourceDownloadTool。

21. 当前实现可以如何优化成更完整的联网 Agent?

21.1 搜索结果标准化

可以定义一个搜索结果格式:

title snippet url source rank

然后让工具返回标准化文本或 JSON。

这样模型更容易判断哪个链接值得继续抓取。


21.2 网页正文抽取

可以把document.html()改成正文抽取。

例如返回:

标题 正文前 3000 字 关键链接 页面描述

这样比完整 HTML 更适合模型处理。


21.3 搜索 + 抓取联动工具

目前搜索和抓取是两个工具。

后续可以封装一个更高层工具:

searchAndReadTopPages(query, topN)

流程是:

搜索关键词 ↓ 取前 N 个 URL ↓ 抓取正文 ↓ 返回摘要化结果

这样模型不需要多次规划工具调用,任务链更稳定。


21.4 下载前确认机制

对于资源下载,建议增加确认逻辑。

例如模型先告诉用户:

我找到了 3 个可下载资源: 1. xxx.pdf 2. yyy.jpg 3. zzz.docx 是否下载?

用户确认后再调用下载工具。

这样可以避免模型自动下载大量无关文件。


21.5 工具调用审计日志

联网工具应该记录:

调用用户 conversationId 工具名称 URL 或 query 调用时间 返回状态 错误信息 下载文件路径 文件大小

这样后续出现问题时可以定位。


21.6 网络访问白名单

正式系统中可以配置白名单,例如只允许访问:

可信新闻网站 知识库域名 企业内部文档系统 指定资源服务器

不建议让模型任意访问所有 URL。


22. 当前项目的学习价值

这一期的三个工具虽然代码都不复杂,但学习价值很大。

因为它们展示了 Agent 联网能力的三个基本动作:

Search Read Download

这三个动作对应真实智能体任务中的三个阶段:

找资料 读资料 保存资料

把这三个能力接入ChatClient后,智能体就不再局限于模型自身知识和本地 RAG 知识库,而是可以根据任务主动获取外部信息。


23. 我的理解

我认为WebSearchToolWebScrapingToolResourceDownloadToolai_agent项目中从“知识型 Agent”走向“行动型 Agent”的关键工具。

前面的 RAG 让智能体能够利用本地知识库。

而这三个工具让智能体能够接触外部网络世界。

可以这样理解:

RAG: 让智能体会查自己的资料库。 WebSearchTool: 让智能体会找外部资料。 WebScrapingTool: 让智能体会读外部网页。 ResourceDownloadTool: 让智能体会保存外部资源。

所以,这三个工具构成了 Agent 外部信息获取的基础能力。


24. 本期重点理解

这一期最重要的是理解三个联网工具的分工。

可以总结为五点:

第一,WebSearchTool 接收 query,通过 SearchAPI 调用百度搜索,并返回 organic_results 前 5 条结果。 第二,WebScrapingTool 接收 URL,通过 Jsoup 抓取网页,并返回网页 HTML。 第三,ResourceDownloadTool 接收 URL 和 fileName,把资源下载到 tmp/download 目录。 第四,搜索、抓取、下载三者可以组成“发现信息—读取内容—保存资源”的联网任务链。 第五,联网工具必须重视 URL 校验、文件名安全、内容长度限制、下载大小限制和工具调用审计。

一句话概括:

ai_agent 的联网工具模块,让模型可以通过搜索发现外部信息,通过网页抓取读取具体内容,并通过资源下载把有用文件保存到本地。

25. 本期小结

本期主要分析了ai_agent项目中的三个联网工具:WebSearchToolWebScrapingToolResourceDownloadToolWebSearchTool通过 SearchAPI 调用百度搜索,接收搜索关键词并返回前 5 条自然搜索结果;WebScrapingTool使用 Jsoup 根据 URL 抓取网页,并返回网页 HTML;ResourceDownloadTool使用 Hutool 的HttpUtil.downloadFile()下载指定 URL 的资源,并保存到项目tmp/download目录下。三者配合后,可以形成“搜索候选网页—抓取网页内容—下载相关资源”的完整联网信息获取链路。

这一期可以用一句话总结:

WebSearchTool 负责找信息,WebScrapingTool 负责读网页,ResourceDownloadTool 负责存资源,它们共同构成了 ai_agent 的基础联网能力。

下一期可以继续分析:

AI Agent 项目学习笔记(十):文件操作、终端执行与 PDF 生成工具

下一期重点分析FileOperationToolTerminalOperationToolPDFGenerationTool,理解智能体如何从“获取信息”进一步走向“处理本地文件、执行系统命令和生成交付物”。

http://www.cnnetsun.cn/news/2495490.html

相关文章:

  • 华硕笔记本终极性能优化方案:G-Helper轻量级控制工具完全指南
  • CANN/asc-devkit类型转换检查
  • 告别毕业焦虑!paperxie 论文查重 + 降重双 buff 加持,重复率 AIGC 率一次通关
  • CANN/asc-devkit sqrt数学函数API
  • STL进阶:手写forEach与map操作技巧
  • ElevenLabs印地文语音API接入全攻略:从零配置到生产级SSML控制,3小时内上线高保真语音服务
  • element-plus主题换色
  • Shiro反序列化漏洞深度解析:从Padding Oracle到TemplatesImpl链
  • 3分钟搞定百度网盘提取码:新手也能快速上手的终极解决方案
  • 5步终极指南:如何让四足机器人像猎豹一样奔跑
  • 【C++ AI 大模型接入 SDK】 - DeepSeek 模型接入(下)
  • AI教材写作大揭秘!低查重工具,为教材编写保驾护航!
  • AI教材生成秘籍!揭秘低查重的AI教材编写工具,高效产出优质教材
  • Tidal-Media-Downloader:3分钟掌握终极Tidal音乐下载方案
  • 华为OD机试真题 新系统【小学英语老师批改作文】
  • 每天节省25分钟:淘宝淘金币自动化脚本全攻略
  • USBIPD-Win终极指南:3步实现Windows与WSL 2的USB设备无缝共享
  • 如何在IDEA中高效编辑JAR文件:JarEditor插件完整指南
  • AhabAssistantLimbusCompany:PC端《Limbus Company》自动化助手终极指南
  • 快速制作系统启动盘:Rufus实战指南与高级配置技巧
  • AI编程助手的现状与未来:Copilot、CodeLlama与GPT-4
  • Cursor Free VIP技术架构深度解析:设备标识重置与多平台兼容实现
  • 如何在GTA V中安全使用YimMenu:新手完全指南与防封技巧
  • ElevenLabs东北话语音API调用失败率高达41%?一线工程师紧急封存的6个底层HTTP头配置方案
  • Obsidian Full Calendar插件完整指南:在笔记中高效管理你的日程
  • 告别数据锁定:用youdaonote-pull实现有道云笔记的本地化自由
  • 5分钟搞定歌词管理:LDDC免费歌词下载工具完全指南
  • cann/asc-devkit:内置数据类型
  • 如何通过CDCS项目快速提升数据科学实战能力:中国数据竞赛优胜解集锦的终极指南 [特殊字符]
  • TextShot多语言OCR配置指南:如何轻松识别中文、英文、法文等100+语言