六、工具调用

Tool calling

AI大模型借助工具来完成自己本身不能完成的一些事情。

AI 工具调用

1、web搜索,天气API

2、数据库数据获取

3、PDF生成、文件生成

4、API访问

大模型工具调用流程图:

注意:工具的调用一定是程序中自己去调用的,而非直接给大模型去调用,LLM本身具备不稳定性,在这种意料之外的情况发生时,可能造成你的数据被异常删改等情况。

这里很关键的一点是安全性,AI模型永远无法直接接触你的API或者系统资源,所有操作都必须通过你的程序来执行,这样你可以完全控制AI能做什么,不能做什么。

举例,你有一个爆破工具,用户给AI提需求拆房子,AI表示我要用爆破工具,但是需要经过你的同意才能执行爆破。

建议使用已有的成熟框架来进行工具的开发调用,因为LLM调用的成功率会高,效果会好一些。

工具调用成功率:本应去调用工具的一个问题,一些LLM判断问题不需要工具,则其回答很可能是有偏差或错误的。

推荐使用SpringAI langChain等框架,经过专业测试的,成功率会高,效果会好。

工具上下文:

作用:

1、安全性,一些用户登录信息保存在系统的上下文中,而不传给AI。

2、避免给AI传递一些多余的信息。

SpringAI提供了两种控制工具调用的方式:

  • SpringAI框架来调用
  • 自己手动来调用

ToolCoolingManager: 管理工具调用的整个生命周期。

和LLM沟通对话,并且将对话内容保存, 以此来提供给大模型和SpringAI框架交互的上下文信息。以便LLM语义理解,给出一个恰当合适的答案。

java 复制代码
public ToolExecutionResult executeToolCalls(Prompt prompt, ChatResponse chatResponse) {
        Assert.notNull(prompt, "prompt cannot be null");
        Assert.notNull(chatResponse, "chatResponse cannot be null");
        Optional<Generation> toolCallGeneration = chatResponse.getResults().stream().filter((g) -> !CollectionUtils.isEmpty(g.getOutput().getToolCalls())).findFirst();
        if (toolCallGeneration.isEmpty()) {
            throw new IllegalStateException("No tool call requested by the chat model");
        } else {
            AssistantMessage assistantMessage = ((Generation)toolCallGeneration.get()).getOutput();
            ToolContext toolContext = buildToolContext(prompt, assistantMessage);
            InternalToolExecutionResult internalToolExecutionResult = this.executeToolCall(prompt, assistantMessage, toolContext);
            List<Message> conversationHistory = this.buildConversationHistoryAfterToolExecution(prompt.getInstructions(), assistantMessage, internalToolExecutionResult.toolResponseMessage());
            return ToolExecutionResult.builder().conversationHistory(conversationHistory).returnDirect(internalToolExecutionResult.returnDirect()).build();
        }
    }

Spring AI和LLM的交互:

根据LLM的响应结果,来判断是否需要工具调用。需要调用时则LLM以固定格式来返回工具名,参数,SpringAI拿到工具名和参数之后去执行工具调用。工具执行结果再添加到对话上下文中,再继续请求AI,结合工具执行结果来回到用户问题。或者直接返回工具执行结果returnDirectly。

鱼皮提供的用户自定义的手动工具执行的示例:

java 复制代码
// 配置不自动执行工具
ChatOptions chatOptions = ToolCallingChatOptions.builder()
    .toolCallbacks(ToolCallbacks.from(new WeatherTools()))
    .internalToolExecutionEnabled(false)  // 禁用内部工具执行
    .build();

// 创建工具调用管理器
ToolCallingManager toolCallingManager = DefaultToolCallingManager.builder().build();

// 创建初始提示
Prompt prompt = new Prompt("获取编程导航的热门项目教程", chatOptions);
// 发送请求给模型
ChatResponse chatResponse = chatModel.call(prompt);
// 手动处理工具调用循环
while (chatResponse.hasToolCalls()) {
    // 执行工具调用
    ToolExecutionResult toolExecutionResult = toolCallingManager.executeToolCalls(prompt, chatResponse);
    // 创建包含工具结果的新提示
    prompt = new Prompt(toolExecutionResult.conversationHistory(), chatOptions);
    // 再次发送请求给模型
    chatResponse = chatModel.call(prompt);
}

// 获取最终回答
System.out.println(chatResponse.getResult().getOutput().getText());

异常处理

当工具调用出现异常,这是很常见的情况,例如,AI识别参数有误,构造的参数格式不正确,选择错了工具等等。在工具执行过程中都会出现异常。当工具调用时异常,默认是不中断不抛出,打日志,让LLM继续执行。也可以手动将alwaysThrow改为true,则出现异常时直接中断。

实现工具执行异常处理器的接口,直接返回默认异常处理器,设置alwaysThrow为true,则每次都抛出异常。

java 复制代码
@Bean
ToolExecutionExceptionProcessor toolExecutionExceptionProcessor() {
    // true 表示总是抛出异常,false 表示返回错误消息给模型
    return new DefaultToolExecutionExceptionProcessor(true);
}

自定义异常的处理

java 复制代码
@Bean
ToolExecutionExceptionProcessor customExceptionProcessor() {
    return exception -> {
        if (exception.getCause() instanceof IOException) {
            // 网络错误返回友好消息给模型
            return "Unable to access external resource. Please try a different approach.";
        } else if (exception.getCause() instanceof SecurityException) {
            // 安全异常直接抛出
            throw exception;
        }
        // 其他异常返回详细信息
        return "Error executing tool: " + exception.getMessage();
    };
}
相关推荐
JavaPub-rodert5 小时前
Apache Jena 详解:Java 开发者如何真正搭起一套知识图谱与语义服务
java·apache·知识图谱
lhldsg5 小时前
线上住宿预约系统开发实战:从需求分析到接口设计
java·大数据·小程序·需求分析
zzzll11115 小时前
Skill 不只是 Prompt:从概念到实践
prompt
剑指offer.5 小时前
Linux多任务-线程篇
java·jvm·算法
2601_962063975 小时前
Spring Boot拦截器(Interceptor)详解
java·spring boot·后端
这是程序猿5 小时前
深入剖析Java HashMap:底层原理、数据结构与核心机制全解析
java·开发语言·数据结构
不才不才不不才5 小时前
Spring 源码系列(25): @EnableTransactionManagement 与 TransactionInterceptor
java·数据库·spring
隔窗听雨眠6 小时前
CI/CD测试数据清理完全指南:从污染根因到三层清理覆盖的完整方案
java·数据库·ci/cd
MetaLite6 小时前
Spring-Scheduled多实例重复执行怎么办-Redis分布式锁如何只让一个节点运行
redis·分布式·spring
Query*6 小时前
Agent 开发之项目 AI 能力自我进化:通过浏览器自动化与数据采集实现持续学习
java·人工智能·ai·自动化