六、工具调用

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();
    };
}
相关推荐
鱼宵6 分钟前
取集合第一个元素,Java 为什么要写三种写法?
java·windows·python·java21
学长毕设19 分钟前
基于SpringBoot的餐厅管理系统-附源码
java·spring boot·后端
刚入门的大一新生41 分钟前
Linux-线程互斥
java·开发语言
赵小哥要冲啦1 小时前
社区邻里互助小程序从 0 到 1 复盘:需求拆解与信任机制设计
java·小程序·需求分析
xiaogai_gai1 小时前
采购业务退款单同步实战:从钉钉审批流到金蝶云星空付款单
java·数据库·钉钉
二哈赛车手2 小时前
新人笔记---hermes定时功能设计,具体讲解以及可优化点
java·人工智能·笔记·spring·ai·typescript
ZWZhangYu2 小时前
从 Dify 实践理解大模型上下文缓存
java·后端·spring
小羊没烦恼!2 小时前
关于分布式锁原理的一些学习与思考-redis分布式锁,zookeeper分布式锁
java·服务器·网络·学习·负载均衡
Fuyo_11193 小时前
C++ AVL树保姆级详解:从平衡因子到四大旋转机制
java·c++·算法
Sarvartha4 小时前
Date、LocalDate、LocalTime、LocalDateTime、DateTimeFormatter类
java