摸索AI(五)Spring AI定制JSON结果

Spring Ai
placeholder image
admin 发布于:2026-03-12 13:53:21
阅读:loading

之前也是花了巨多的时间从Github上下载了一些AI摸索的实践项目,涉及到的有文生图、文生视频、图生图、语音克隆、数字人等,它们的实践对电脑配置的依赖有一定的要求,而且实践的过程复杂程度较高,属于摸索着玩玩而已。本次摸索AI的范围则是面向对擅长领域的代码接入实践。如果你对Java代码交互的AI大模型有一些兴趣,又或者是跟我一样不知从哪里入手,或许看看我这里分享的前后实践过程与实践的案例范围,也是不错的选择。

1.基本介绍

所以,本系列教程相关的实践是在本地部署大模型,并且使用Java代码与本地的大模型进行交互。除了本地部署的大模型以为,也是可以付费接入网络上的一些付费大模型,比如Deepseek、千问等等,但是对我个人来讲,私有化的本地大模型更加有意义,毕竟可以免费的集成到企业级应用实践当中。

本次实践围绕使用Spring AI在企业级应用中的专业层面进行的实践,主要考虑在使用Spring AI调用大模型处理接口响应时,根据要求来定制结果。在接口调用处理过程中,AI返回的结果更多的是Markdown结构的,有文本和HTML标签或代码块等形式,在实际应用中,如果用于数据分析处理,则更多的是期望响应与程序解析处理契合度更高的JSON格式

本篇文章的示例实践则是从本站拷贝了一些个人信息的文本,使用Spring AI来调用本地的一个大模型,让其从这些文本数据中提取我所指定的三个参数,并以JSON格式的结果返回,相关信息参考如下:

String content = """
        站点域名:https://www.chendd.cn
        博主名称:陈冬冬(本站域名不出手)
        故乡:湖北省老河口市(曾经的小汉口,一个被历史遗忘的城市)
        博主是一名酷(ku)比(bi)的80后老爷们,现役搬土质砖人士,也会持续的在行业内摸爬滚打。别轻易加我,一对一答疑解惑(教学)将占用我的时间,知识付费。
          QQ:88911006
          微信:java-dong
          邮箱:88911006@qq.com
          抖音:haiyangyiba
        """;

(内容信息)

请帮我提取出来数据,按照兼容同时存在多条数据信息的方式,将关键要素提取为json数组的格式。请严格按照这个结构的JSON对象输出:[{"name":"姓名","website":"域名","weiChat":"微信"}]。注意:输出的信息不要包含思考过程、提取说明、解答思路、数据分析、内容总结、以及Markdown代码块,等等,仅需要返回符合条件的JSON格式参数。信息如下:${content}

(内容提取要求)

{
  "id": "chatcmpl-176",
  "object": "chat.completion",
  "created": 1773285781,
  "model": "qwen3-vl:2b",
  "system_fingerprint": "fp_ollama",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "[\n  {\n    \"name\": \"陈冬冬\",\n    \"website\": \"https://www.chendd.cn\",\n    \"weiChat\": \"java-dong\"\n  }\n]",
        "reasoning": "好的,我现在需要帮用户提取数据,按照指定的JSON数组格式输出。首先,用户给了一个信息,里面包含站点域名、博主名称、故乡、一些联系方式等。我需要仔细看看每个字段对应的关键要素。\n\n首先,用户提到的站点域名是https://www.chendd.cn,这个应该对应到website字段。然后博主名称是陈冬冬,但要注意用户说本站域名不出手,可能意味着这个域名是网站的,但博主名称可能需要确认。不过根据用户提供的信息,博主名称是陈冬冬,所以name应该是这个。\n\n接下来,故乡是湖北省老河口市,但用户可能希望这个作为某个字段的值,不过根据要求,关键要素包括namewebsiteweiChat。这里可能需要确认,但用户给的信息里有故乡这个字段,但可能不是关键要素,所以可能不需要包含。不过用户说可能包含如下关键要素,所以需要看哪些是必须的。\n\n用户给出的结构是namewebsiteweiChat。所以需要从提供的信息中提取这三个。现在看用户提供的信息:\n\n站点域名:https://www.chendd.cn → website\n\n博主名称:陈冬冬(本站域名不出手)→ name\n\nQQ88911006 → 但用户说“QQ88911006”,可能需要看是否对应到其他字段。但用户要求的三个字段中,weiChat是微信,而用户提供的信息里有微信:java-dong,所以weiChat应该是java-dong\n\n另外,用户还提到邮箱和抖音,但这些可能不是关键要素,所以可能不需要包含。但用户说可能包含如下关键要素,所以需要看哪些是必须的。根据用户给的示例,关键要素是namewebsiteweiChat,所以其他信息如QQ、微信、邮箱、抖音等可能不需要,但用户可能希望将微信作为weiChat,而QQ可能属于其他,但用户要求的三个字段中,weiChat是微信,所以可能需要将微信的信息提取出来。\n\n现在,用户提供的信息中:\n\n- 站点域名:https://www.chendd.cn → website\n- 博主名称:陈冬冬 → name\n- 微信:java-dong → weiChat\n- QQ88911006 → 但用户可能没有要求,所以可能不需要\n- 邮箱:88911006@qq.com → 但用户可能没有要求\n- 抖音:haiyangyiba → 但用户可能没有要求\n\n所以,根据用户的要求,需要提取namewebsiteweiChat。因此,name是陈冬冬,websitehttps://www.chendd.cnweiChatjava-dong\n\n但用户提到博主名称:陈冬冬(本站域名不出手),这里可能需要注意,是否博主名称和站点域名有关联,但用户说本站域名不出手,可能意味着这个域名是网站的,但博主名称是陈冬冬,所以name应该是陈冬冬。\n\n另外,用户还提到故乡:湖北省老河口市,但这个可能不是关键要素,所以不需要包含在JSON中。\n\n现在,检查是否有其他信息需要提取。用户说按照兼容同时存在多条数据信息的方式,但这里可能只有一条数据,所以输出一个JSON数组,包含一个对象。\n\n现在,确认每个字段:\n\nname: 陈冬冬\n\nwebsite: https://www.chendd.cn\n\nweiChat: java-dong\n\n其他字段如QQ、邮箱、抖音等可能不需要,所以只保留这三个。\n\n但用户提到可能包含如下关键要素,所以需要确认是否所有提到的信息都属于这三个字段。例如,用户说博主是一名酷(ku)比(bi)的80后老爷们,现役搬土质砖人士,也会持续的在行业内摸爬滚打。别轻易加我,一对一答疑解惑(教学)将占用我的时间,知识付费。这里可能有微信:java-dong”,所以weiChatjava-dong\n\n所以,最终的JSON数组应该包含这三个字段,每个字段的值对应正确。\n\n现在,检查是否有错误。例如,用户说本站域名不出手,可能意味着这个域名是网站的,但博主名称是陈冬冬,所以name是陈冬冬,websitehttps://www.chendd.cnweiChatjava-dong\n\n所以,最终的JSON应该是:\n\n[{\"name\": \"陈冬冬\", \"website\": \"https://www.chendd.cn\", \"weiChat\": \"java-dong\"}]\n\n但用户提到站点域名:https://www.chendd.cn”,所以website是这个。而博主名称是陈冬冬,所以name是陈冬冬。\n\n另外,用户提到微信:java-dong”,所以weiChatjava-dong\n\n确认无误后,输出这个JSON数组。"
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 354,
    "completion_tokens": 1084,
    "total_tokens": 1438
  }
}

 

(全量数据结果)

2.示例代码

@RunWith(JUnit4.class)
public class AiHelloJsonTest {

    @Test
    public void helloJsonTest() {
        String content = """
                站点域名:https://www.chendd.cn
                博主名称:陈冬冬(本站域名不出手)
                故乡:湖北省老河口市(曾经的小汉口,一个被历史遗忘的城市)
                博主是一名酷(ku)比(bi)的80后老爷们,现役搬土质砖人士,也会持续的在行业内摸爬滚打。别轻易加我,一对一答疑解惑(教学)将占用我的时间,知识付费。
                  QQ:88911006
                  微信:java-dong
                  邮箱:88911006@qq.com
                  抖音:haiyangyiba
                """;
        String prompt = this.reload(content);
        final ResponseEntity<OpenAiApi.ChatCompletion> responseEntity = RestClient.builder(new RestTemplateConfig().restTemplate())
                .build().post().uri("/v1/chat/completions")
                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .body(prompt).retrieve().toEntity(OpenAiApi.ChatCompletion.class);
        final OpenAiApi.ChatCompletion body = responseEntity.getBody();
        System.out.println(body);
        if (body != null) {
            final String result = body.choices().get(0).message().content();
            System.out.println(result);
        }

    }

    private String reload(String content) {
        String prompt;
        try(InputStream resourceAsStream = getClass().getResourceAsStream("/prompt.template.json")) {
            prompt = StreamUtils.copyToString(resourceAsStream , StandardCharsets.UTF_8);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        String jsonString = JSON.toJSONString(content);
        String temp = StringUtils.substring(jsonString,1,jsonString.length() - 1);
        return StringUtils.replace(prompt, "[{content}]", temp);
    }

}

3.参考截图

1)调试参数-含水印.jpg

(1.调试参数)

2)全量数据对象-含水印.jpg

(2.全量参数输出)

3)最终结果-含水印.jpg

(3.最终数据)

4.其它说明

(1)本次实践的代码是使用Spring AI调用本地大模型并返回定制格式JSON的实现;

(2)示例在执行过程中有一定的等待,受电脑性能影响实际的执行耗费了5分钟;

(3)示例执行的结果包含了$root.choices[0].message.content为最终数据的JSON Array字符串;$root.choices[0].message.reasoning为最终数据的思考过程;

(4)给出两个案例实际执行的全过程预览效果图,上述的图片省流只摘选了一部分,完整的预览参考《示例下载.zip》,包含完整的示例请求和响应结果;

(5)许多案例给出的是结合Spring Boot后的配置注入方式,这种把大模型的服务URL、模型名称等等参数细节配置在application配置文件中的形式,我感觉不够灵活,后续相关的示例均使用硬编码的形式给出,面向底层代码更加便于新手水平理解掌握;


 点赞


 发表评论

当前回复:作者

 评论列表


留言区