Spring AI的动态工具发现
Spring AI 的动态工具发现
1. 概述
在构建 AI 集成系统时,我们通常会为 AI 客户端提供大量工具。每次请求时,我们都会将所有可用工具的定义发送给 LLM,以便其决定使用哪些工具。结果,在模型处理用户查询之前,我们就浪费了大量 token。本文探讨如何使用工具搜索工具解决这个问题。
2. 工具搜索工具的工作原理
使用工具搜索工具,我们不再将所有工具定义随上下文一起发送。只有当模型实际需要工具时,我们才会暴露它们。首先,我们会在启动时索引所有已注册的工具。我们将它们存储在 ToolSearcher 中,但不会发送给 LLM。接着,我们在初始请求中仅发送工具搜索工具本身。这保持了提示的简洁和专注。当模型需要某个能力时,它会用自然语言查询调用工具搜索工具。
我们将此视为发现信号,并使用配置的策略触发对索引工具的搜索。然后,我们只从 ToolSearcher 返回最相关的匹配结果,并将其定义注入下一个 LLM 请求中,从而使模型看到一组聚焦的工具,而不是完整的注册表。
一旦相关工具可用,模型就会选择并调用实际的工具。我们执行该工具,并将结果返回给 LLM,然后 LLM 利用它生成最终答案。
3. 构建旅行助手示例
让我们构建一个帮助用户规划旅行的旅行助手。我们连接了多个工具,例如航班、酒店、天气和景点。我们采用工具搜索工具的方法,避免一开始就将所有工具发送给 LLM,而是在运行时动态发现工具。
3.1. 依赖关系
首先,我们添加工具搜索依赖支持:
同时,添加正则搜索器依赖:
使用它,我们将获得一个 regex 工具搜索策略。其他可用策略可以在项目仓库中找到。
3.2. 航班工具
创建一个简单的 FlightTools。我们将用它来获取可用的航班选项。此外,我们创建一些人工工具来模拟上下文过载:
这里我们返回一个航班选项。
3.3. TokenCounterAdvisor
现在创建一个简单的 TokenCounterAdvisor,用于统计生成最终结果所使用的 token 数量。我们将用它来比较不同配置(开启/关闭工具搜索)下的 token 使用情况:
这里我们将 token 数量存储在 AtomicInteger 字段中,并在执行期间记录这个信息。我们将此 advisor 附加到最大顺序,使其在处理管道的末尾运行。因此,它在所有其他 advisor 完成后捕获 token 总使用量。
3.4. 配置
接下来,添加 TravelAssistantConfig 实现:
我们配置了一个使用动态工具发现的旅行助手,而不是将所有工具加载到 LLM 中。接着,我们设置了一个 ToolSearcher,使用 RegexToolSearcher 实现。这允许我们基于命名模式和快速的关键词类查询来匹配工具。然后,创建 ToolSearchToolCallAdvisor 并将其连接到搜索器。之后,构建 ChatClient 并注册航班工具。
按照设计,我们添加了包含许多无关工具定义的 RandomTools。但是,我们不会一开始就将这些工具定义发送给 LLM,而只是在系统中索引它们。最后,我们在开始时只向模型暴露工具搜索工具。模型随后使用它来发现对于给定请求实际需要哪些工具。 此外,我们配置了另一个不使用的 ToolSearchToolCallAdvisor 的 ChatClient bean。
3.5. 调用旅行助手
最后,创建一个 ToolsSearchToolLiveTest,为两个客户端提供类似的测试用例:
我们用相同的提示调用了两个旅行顾问客户端,并获得了相同的结果。现在,比较两者使用的 token 数量:
可见,token 使用量的差异非常关键。我们的系统中工具越多,工具搜索工具带来的 token 节省就越大。
4. 结论
在本文中,我们回顾了工具搜索工具,并演示了它如何帮助减少实际场景中的 token 使用。使用它,我们可以构建拥有数百个附属工具的大型 AI 集成系统,并高效地使用它们,而不会浪费 token。此外,我们可以探索其他工具搜索策略,例如向量搜索,甚至构建自定义策略,使工具发现更加高效。
与往常一样,代码可在 GitHub 上获取。