spring ai function call 的使用尝试

一.背景

        在生成式 AI 技术快速普及的当下,Spring AI 作为 Spring 生态专为 AI 应用开发打造的框架,凭借其与 Spring Boot、Spring Cloud 等核心组件的无缝集成能力,大幅降低了开发者构建 AI 应用的门槛,成为 Java 生态中落地生成式 AI 场景的优选方案。Function Call(函数调用)作为大语言模型(LLM)突破 “仅生成文本” 局限的核心能力,允许模型根据用户需求自主识别并调用外部工具(如数据库查询、API 调用、文件处理等),将 AI 的推理能力与现实世界的业务系统深度联动,广泛应用于智能助手、自动化流程、数据查询分析等场景。

        在传统 AI 应用开发中,若需实现 LLM 与外部工具的协同,开发者需手动处理复杂的交互逻辑:包括设计提示词引导模型输出结构化指令、解析模型返回的非标准格式数据、处理工具调用的参数校验与异常捕获、管理多轮调用的上下文状态等。这不仅导致代码冗余、耦合度高,还面临跨工具适配、格式兼容性、上下文丢失等问题,尤其在 Java 生态中,缺乏统一的函数调用规范和集成框架,进一步提升了开发成本与维护难度。

        Spring AI 针对这一痛点,提供了标准化的 Function Call 封装能力:通过注解驱动的函数注册、统一的参数解析机制、内置的上下文管理与多轮调用支持,将 LLM 的函数调用能力与 Spring 生态的开发范式深度融合。开发者无需关注底层的格式解析、工具适配等细节,即可快速实现 LLM 与业务系统的联动,让 AI 模型成为业务流程的 “智能调度者”。

        然而,在实际落地过程中,开发者仍面临诸多实践挑战:如何合理设计函数签名以适配 LLM 的理解能力、如何处理复杂参数的校验与类型转换、如何实现多轮函数调用中的上下文衔接、如何兼容不同 LLM 厂商(如 OpenAI、Azure OpenAI、本地化模型)的函数调用协议差异等。因此,开展 Spring AI Function Call 的使用尝试,不仅是探索其在 Java 生态中落地可行性的关键步骤,更是为了解决上述实践痛点,沉淀标准化的开发流程与最佳实践,为后续构建复杂 AI 业务系统(如智能办公助手、实时数据查询机器人、自动化运维工具等)奠定基础,具有重要的技术探索价值与工程实践意义。

二.具体实现

1.定义工具函数

    /**
     * 提取实体函数tool定义
     */
    public static final String MEMORA_EXTRACT_ENTITIES_TOOL = "{\n" +
            "\t\"type\": \"object\",\n" +
            "\t\"properties\": {\n" +
            "\t\t\"entities\": {\n" +
            "\t\t\t\"type\": \"array\",\n" +
            "\t\t\t\"items\": {\n" +
            "\t\t\t\t\"type\": \"object\",\n" +
            "\t\t\t\t\"properties\": {\n" +
            "\t\t\t\t\t\"entity\": {\n" +
            "\t\t\t\t\t\t\"type\": \"string\",\n" +
            "\t\t\t\t\t\t\"description\": \"The name or identifier of the entity.\"\n" +
            "\t\t\t\t\t},\n" +
            "\t\t\t\t\t\"entity_type\": {\n" +
            "\t\t\t\t\t\t\"type\": \"string\",\n" +
            "\t\t\t\t\t\t\"description\": \"The type or category of the entity.\"\n" +
            "\t\t\t\t\t}\n" +
            "\t\t\t\t},\n" +
            "\t\t\t\t\"required\": [\"entity\", \"entity_type\"],\n" +
            "\t\t\t\t\"additionalProperties\": false\n" +
            "\t\t\t},\n" +
            "\t\t\t\"description\": \"An array of entities with their types.\"\n" +
            "\t\t}\n" +
            "\t},\n" +
            "\t\"required\": [\"entities\"],\n" +
            "\t\"additionalProperties\": false\n" +
            "}";

2.定义工具列表

List<OpenAiApi.FunctionTool> tools = List.of(
                new OpenAiApi.FunctionTool(new OpenAiApi.FunctionTool.Function(
                        "Extract entities and their types from the text.",
                        "extract_entities",
                        MEMORA_EXTRACT_ENTITIES_TOOL
                ))
        );

3.构建大模型调用

// 创建模型选项,指定模型名称和其他参数
        OpenAiChatOptions options = OpenAiChatOptions.builder()
                .model(model)
                .temperature(0.8)
                .maxTokens(2048)
                .tools(tools)
                .toolChoice("auto")
                .build();

        List<Message> content = new ArrayList<>();

        content.add(new UserMessage(userContent));
        content.add(new SystemMessage(systemContent));

        // 创建包含选项的Prompt
        Prompt prompt = new Prompt(content, options);

        // 调用模型获取响应
        ChatResponse response = openAiChatModel.call(prompt);

4.大模型返回结果如下:

请求参数:

{
    "userContent":"我爱吃饭",
    "systemContent":"你是一款能够理解文本中实体及其类型的智能助手。若用户消息包含 “我”“我自己”“我的” 等自指表述,需将 123 作为源实体。请从文本中提取所有实体。如果给定文本是一个问题,切勿回答问题本身。"
}

返回结果:

{
    "name": "functions.extract_entities",
    "arguments": {
        "entities": [
            {
                "entity": "123",
                "entity_type": "person"
            }
        ]
    }
}

5.到此调用function call成功

### 如何在Spring框架中调用AI函数 为了实现这一目标,配置文件中的`application.properties`或`application.yml`应当包含特定于OpenAI的属性设置。这些属性定义了连接至API的基础URL以及必要的认证密钥。 对于基于Spring的应用程序来说,在`application.properties`内指定如下内容: ```properties spring.ai.openai.base-url=https://api.openai.com/v1 spring.ai.openai.api-key=your_api_key_here ``` 上述配置使得应用程序能够通过设定好的基础地址访问OpenAI的服务,并利用提供的API键完成身份验证过程[^1]。 创建用于管理与外部AI服务交互的服务类时,推荐采用依赖注入的方式引入所需的HTTP客户端工具(如RestTemplate或是WebClient),以便更好地遵循面向对象编程原则并简化单元测试流程。下面是一个简单的例子展示如何构建这样的服务层组件来发起对AI端点的请求: ```java @Service public class AiService { private final RestTemplate restTemplate; private final String baseUrl; @Autowired public AiService(@Value("${spring.ai.openai.base-url}") String baseUrl, RestTemplateBuilder restTemplateBuilder) { this.restTemplate = restTemplateBuilder.build(); this.baseUrl = baseUrl; } /** * 发送消息给AI聊天接口. */ public ChatResponse sendMessage(String message, Long chatId){ HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); Map<String, Object> body = new HashMap<>(); body.put("message", message); body.put("chatId", chatId); HttpEntity<Map<String, Object>> request = new HttpEntity<>(body, headers); ResponseEntity<ChatResponse> response = restTemplate.postForEntity(baseUrl + "/chat/{id}", request, ChatResponse.class, chatId); return response.getBody(); } } ``` 此代码片段展示了怎样封装对外部AI服务的具体调用逻辑,同时保持良好的可读性和维护性。值得注意的是,这里假设存在一个名为`ChatResponse`的数据传输对象(DTO),它用来映射来自服务器响应的消息结构[^3]。 当涉及到个人数据处理时,务必考虑到技术中立性的保护措施,即无论自动化手段还是手动操作都应确保个人信息的安全和隐私得到妥善保障[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

路边草随风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值