使用 Rust 开发 AI Agent:工具调用之网络搜索工具
核心结论:大语言模型有时间局限,不知道现在或近期发生了什么;给它装一个网络搜索工具,并按“选对接口、最小可用、补容错”的步骤实现,是用真实场景串起工具调用原理的典型做法。
一、背景与目标
本节继续讲使用 Rust 开发 AI Agent 中的工具调用。前面已经讲了很多原理,这一节用一个真实场景把这些原理串起来。
大语言模型的时间有局限:它并不知道现在或者近期发生了什么。为了弥补这个局限,就给模型装一个网络搜索工具。因此,本小节的目标是做一个网络搜索工具,并把它接入 AI Agent 的工具调用链路。
原文中该搜索服务名称出现 TAII、TAVII、tavii 等写法,以下按原文保留,未做纠正;具体以官方文档为准。原文还提到国内可选“博茶”等服务。
二、核心方法论:制作网络搜索工具的三步
| 步骤 | 做法 | 为什么重要 | 注意事项与局限 |
|---|---|---|---|
| 1. 选对接口 | 优选专门为大模型场景优化过的搜索服务,例如原文使用的 TAII/TAVII,国内如博茶等 | 这类服务返回的结果本身就是干净的,适合喂给大模型,能省掉大量数据清洗功夫 | 不同服务的能力、价格、参数、结果质量不同,需要按场景选择 |
| 2. 从最小可用开始 | 最开始只暴露一个查询关键词参数,先把整条链路跑通,再逐步加时间范围、主题过滤等可选参数 | 避免一上来把接口支持的所有参数,可能十几个或几十个,全部暴露给大语言模型 | 否则 schema 会变得又臃肿又难以应对 |
| 3. 补上容错处理 | 处理接口超时、鉴权失败、被限流等情况 | 往往生产级的错误处理代码量,比核心搜索逻辑本身还要长 | 视频教程中简化参数和容错;后续会在 Rust 项目代码里做优化 |
这里有一个关键取舍:教学视频先追求链路跑通和原理展示,因此参数与容错都做了非常简化的处理;真正生产级实现还需要补齐超时、鉴权、限流、重试、错误分类等逻辑。
三、准备与接入搜索服务
1. 修改模型与创建项目文件
打开 VS Code 后,先修改模型常量,改成 GPT4O 迷你,同时把前面的名称也改一下。
然后进入 tools 文件夹:
- 创建
web_search.rs,再创建一个文件夹。 - 来到
tools.rs,声明这个模块。 - 在 web 搜索文件夹中创建两个文件:
definition和execute。 - 别忘了声明这两个模块。
原文口播中还提到“web 测试文件夹”,从上下文看应指 web 搜索相关文件夹。
2. 注册搜索服务并配置 API key
查看要使用的搜索服务 TAII/TAVII 网址,然后登录进去。登录后可以看到它每个月有一定的免费服务:1000 credits。
接下来要创建一个 API key。创建完之后,把 API key 复制下来,回到 .env 文件,创建这样一个 key:
- 名称:
TAVII_API_KEY,大写 - 值:刚才创建的 API key
也就是把 API key 贴进去即可。
3. 阅读 API 文档
再回到搜索服务网站,查看文档。原文操作是点击 API SDK,然后看 search 这个节点:
- 它是一个
POST search。 - 文档中有 Python SDK 的代码。
- 没有 Rust SDK。
- 原文说“看一下 KERR”,从上下文看应指 cURL 示例。
- 可以看到它的 URL、
authorization、Bearer token、content type,以及参数。
参数情况如下:
authorizationheader 是必填的。- body 里边
query参数是 string 类型,必填。 - 其余参数要么有默认值,要么是可选的。
- 本节主要用到
query,也就是查询的关键字。 - 还会用
max results限制搜索结果数量。 topic也是 string,只不过是枚举的形式。time range是时间范围。- 具体参数怎么用,需要看官方文档;视频里参数解释不是重点。
四、实现工具定义 definition
在 definition 中创建 web search 网络搜索工具的定义。这个函数与之前计算器的定义基本一样,因此原文直接贴入代码。
本版网络搜索工具定义了这些参数:
| 参数 | 是否必填 | 说明 |
|---|---|---|
query | 必填 | 查询关键字 |
max_results | 否 | 默认 5,最小 0,最大 20 |
topic | 否 | 主题,实际上是枚举形式 |
time_range | 否 | 时间范围 |
其中必填的就是 query。后续如果想自己扩展,可以根据官方文档自行扩展。
五、实现执行层 execute
1. 定义相关 struct
首先把相关的 struct 写上,然后一个一个看。
web search AR
原文称 web search AR,它就是工具的参数结构。因为在工具定义里定义了四个参数,所以它里边对应这四个参数。
注意:max_results 和 topic 都使用了注解,结合函数给它们提供默认值。
tavii request
tavii request 是真正发送给 TAVII API 的请求结构,需要序列化成 JSON。
除了前面工具里定义的四个参数以外,这里还需要使用 API key。此外还额外包括:
search_stepsinclude_answer
注意这个 struct 定义使用到了生命周期。这里没有采用 String,也就是能拥有的字符串,而是借用了外边的字符串切片,目的是避免复制。
原文还提到一个 third 注解,意思是如果这个 Option 是 None,那么在序列化 JSON 的时候就忽略这个字段。
search result
search result 是 TAVII 返回的一条搜索结果结构,包括:
- 标题
- 网址
- 内容
实际上它还有一个字段也比较重要,但这里没有使用,就是搜索的分数。原文先把它注释掉。
taily response
taily response 是 TAVII API 返回的响应数据结构:
results结果里包含多个search result。answer是TAILY AI自动总结出来的答案。- 它有可能没有,所以是
Option。
web search output
最后又建立了一个 web search output,它是网络搜索工具返回给大语言模型的最终结果数据结构,需要进行序列化转化成 JSON。它的字段跟前面的 taily response/heavily response 相对应。
原文作者感觉 web search output 和 terry response 好像有点重复,是不是给它加一个序列化就可以了。但当时先不改,也忘了当初为什么又多写一个 web search output,留给同学回去研究。
2. 执行函数流程
真正的执行函数逻辑如下:
- 从环境变量取得 API key。
- 组成
request数据结构。 - 发送请求,访问网址,使用
POST JSON。 - 如果请求失败,就加上错误说明。
- 判断请求是不是成功了:
200或201等。 - 如果没有成功,再返回一个错误。
- 如果成功,就把响应转成
taily response/TaviiResponse数据结构。 - 最后函数返回
web search output,即用来给大模型用的结果。
原文还提到“返回这个 web service output”这一口播,从上下文看应指 web search output。
六、接入 LLM 工具调用分支
来到 LLM complete。在调用工具之前,原本有一个判断:调用计算器。现在用同样的逻辑,判断是不是调用了 web search。
这里加了一个分支,判断是否调用了 web search 工具。里边的逻辑跟计算器的调用基本一样,因此原文不细讲。
运行过程中曾出现错误:好像调用了计算器,有问题。检查后发现,tools 函数目前只返回了一个计算器工具定义,没有返回 web search。把 web search 设置加进去后再跑一遍。
这说明一个问题:工具定义没有正确暴露给模型时,模型可能不会按预期调用新工具,甚至仍然走旧工具分支。
七、运行案例与结果
回到 example,尝试调用 web search 工具。
系统提示稍微改一下,因为这个提示有时候不会去调用 web search。原文改成类似这样:
- 也是一个全能助手。
- 今天是哪天。
- 你可以使用工具来搜索。
用户提示也改一下,改成:
2026世界杯决赛的比分式
原文提到变量名最好不用这个变量名,就叫 result,然后跑一下。
第一次跑的时候出现错误,好像是调用计算器。修复 tools 函数返回后,再跑一遍。
关于“世界杯”查询,原文一共跑了两次:
- 第一次:模型说结果还没出来,还没比。第一次响应说“这个比分尚未公布,因为比赛还没举行”。
- 第二次:才把正确结果“西班牙一比零战胜阿根廷”返回回来。
查看第一次结果时,作者认为:
- 它应该是搜索的有问题,可能是传的参数有点问题。
- 搜索出来的结果好像当时确实还没有比决赛。
- 所以要么是搜索服务本身的问题,要么就是传的参数有问题。
- 作者感觉更可能是传的搜索参数有问题。
- 但这块需要大家自己去微调。
这个案例说明:即使工具链路已经接通,搜索参数、搜索服务质量和查询时机都会影响最终结果。网络搜索工具不是“接上就一定准”,还需要调参和结果验证。
八、限制、风险与待确认问题
原文明确或隐含的限制包括:
- 视频教程在参数和容错上做了非常简化的处理。
- 生产级的错误处理,比如接口超时、鉴权失败、被限流,原文没有展开。
- 后续会在 Rust 项目代码里把这些优化做上,但视频教程里没有做。
web search output和terry response疑似重复,作者自己也不确定为什么多写一个,未修改。search result中搜索的分数字段比较重要,但当前没有使用,被注释掉。tavii request中额外包括search_steps和include_answer,但原文没有解释它们的具体用途。- 第一次搜索“2026 世界杯决赛比分”时结果不正确,作者归因可能是搜索服务问题或传参问题,更倾向传参问题,但未给出最终修正。
- 搜索服务名称在原文中拼写不一致:
TAII、TAVII、tavii等,具体以官方为准。 - 文档中具体枚举值、时间范围取值、参数细节没有完整展开,需要查官方文档。
九、行动清单
- 修改模型常量为
GPT4O 迷你,并同步修改相关名称。 - 在
tools下创建web_search.rs和相关文件夹。 - 在
tools.rs中声明模块。 - 在 web 搜索文件夹中创建
definition与execute两个文件,并声明模块。 - 注册
TAII/TAVII搜索服务,确认每月1000 credits免费额度。 - 创建 API key,并写入
.env:TAVII_API_KEY=...。 - 阅读官方
API SDK文档,确认POST search、authorization、Bearer token、content type、query必填等要求。 - 在
definition中定义工具参数:query必填,max_results默认5、最小0、最大20,topic枚举,time_range时间范围。 - 在
execute中定义请求与响应结构:web search AR、tavii request、search result、taily response、web search output。 - 实现执行函数:读取 API key、构造请求、发送
POST JSON、判断200/201、处理失败、解析响应、返回给大模型。 - 在
LLM complete中增加web search工具调用分支。 - 确保
tools函数同时返回计算器工具定义和web search工具定义。 - 在
example中调整系统提示和用户提示,测试“2026 世界杯决赛的比分式”。 - 根据结果微调搜索参数;必要时换搜索服务或增加更多参数来优化搜索结果。
十、总结
本节用“网络搜索工具”这个真实场景串起了工具调用原理:大语言模型不知道现在或近期发生了什么,所以需要外部搜索工具补充实时信息;实现时要选专门为大模型优化的搜索服务,从最小可用参数开始,再补容错;在 Rust 项目中分别实现工具定义 definition 和执行逻辑 execute,定义请求、响应和输出结构,最后接入 LLM complete 的工具调用分支。
案例中,第一次查询“2026 世界杯决赛的比分式”时返回“比分尚未公布”,第二次才返回“西班牙一比零战胜阿根廷”。作者判断这可能与搜索参数有关,也可能与搜索服务有关,需要继续微调。网络搜索工具本身还有很多优化空间,例如更换搜索服务、增加参数、改进结果质量,这些都留给后续研究。