Groovy + Spring AI:用动态语法解锁 AI 开发 “丝滑体验”

Spring AI 作为 Java 生态的 AI 开发利器,极大降低了 AI 模型集成门槛,但在实际落地中,静态类型约束、语法繁琐等问题仍困扰着开发者:JDK 21+ 才支持的文字模板、AI 返回不确定性导致的类型适配难题、重复模板构建冗余等痛点,让"高效集成 AI"大打折扣。而 Groovy 凭借动态类型、灵活语法、无缝兼容 Java 的特性,恰好能精准破解这些痛点,让 Spring AI 开发更简洁、更灵活、更适配 AI 场景的不确定性。

一、前置准备

基于 Spring AI 1.1.0(最新稳定版本),Groovy 4.x 可无缝兼容,环境搭建仅需 3 步,比 Java 开发少去类型配置冗余。

1. 依赖配置(Maven 示例,Gradle 同理)

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <!-- Spring Boot 父依赖,统一管理 Spring Boot 版本 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.5.8</version>
        <relativePath/>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>groovy-spring-ai-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <!-- 统一管理 Spring AI 版本(等效 Gradle 的 dependencyManagement) -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.ai</groupId>
                <artifactId>spring-ai-bom</artifactId>
                <version>1.1.0</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <!-- Groovy 核心依赖(等效 Gradle 的 implementation) -->
        <dependency>
            <groupId>org.codehaus.groovy</groupId>
            <artifactId>groovy-all</artifactId>
            <version>4.0.29</version>
            <type>pom</type>
        </dependency>
        <!-- Spring Boot 基础依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <!-- Spring AI OpenAI 组件 -->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-openai</artifactId>
        </dependency>
        <!-- 测试依赖(等效 Gradle 的 testImplementation) -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- Groovy 编译插件(Gradle 中 groovy 插件的等效实现) -->
            <plugin>
                <groupId>org.codehaus.gmavenplus</groupId>
                <artifactId>gmavenplus-plugin</artifactId>
                <version>1.13.1</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal> <!-- 编译 Groovy 源码 -->
                            <goal>testCompile</goal> <!-- 编译 Groovy 测试源码 -->
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <!-- Spring Boot 打包插件(生成可执行 JAR) -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <mainClass>com.example.GroovySpringAiApplication</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <!-- 配置 Java 版本(等效 Gradle 的 java 配置块) -->
    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
</project>

2. API Key 配置(application.groovy)

Groovy 配置文件支持动态逻辑,无需硬编码,比 YAML 更灵活:

groovy 复制代码
// 读取环境变量,避免密钥泄露
spring.ai.openai.api-key = System.getenv("SPRING_AI_OPENAI_API_KEY")
spring.ai.openai.chat {
    model = "gpt-3.5-turbo"
    temperature = 0.6
    max-tokens = 2048
}

3. 启动类(极简写法)

groovy 复制代码
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication

@SpringBootApplication
class GroovySpringAiApplication {
    static void main(String[] args) {
        SpringApplication.run(GroovySpringAiApplication, args)
    }
}

二、核心痛点破解

痛点 1:Prompt 模板适配受限------Java 需 JDK 21+ 支持,Groovy 天生兼容

Java 中构建动态 Prompt 模板,需依赖 JDK 21 的文本块插值(STR."..."),低版本 JDK 只能用 String.format() 或字符串拼接,代码冗余且易出错;而 Groovy 原生支持字符串模板,无需依赖高版本 JDK,且语法更简洁。

Groovy 解决方案:动态字符串模板 + 闭包构建 Prompt

groovy 复制代码
import org.springframework.ai.chat.ChatClient
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service

@Service
class PromptService {

    @Autowired
    ChatClient chatClient

    // 场景:构建带动态参数的 RAG 检索 Prompt
    def ragPrompt(String userQuery, List<String> contextDocs) {
        // 1. Groovy 字符串模板:直接嵌入变量和表达式,无需拼接
        def systemPrompt = """
            你是知识库助手,基于以下上下文回答用户问题:
            ${contextDocs.join("\n\n")}
            规则:仅用上下文信息回答,不编造内容;回答简洁明了。
        """

        // 2. 闭包快速构建结构化 Prompt(比 Java 链式调用更直观)
        def response = chatClient.prompt {
            system(systemPrompt)
            user(userQuery)
        }.call()

        return response.content
    }
}

优势对比

特性 Java(JDK < 21) Groovy
模板语法 需用 String.format(),占位符易混乱 直接 $变量${表达式},直观简洁
多行 Prompt 需手动拼接 + 或文本块,格式易乱 三引号 """ 原生支持,保留换行和格式
低版本 JDK 兼容性 不支持文本插值,模板构建繁琐 全版本兼容,无需升级 JDK

痛点 2:AI 返回不确定性------静态类型适配难,判定逻辑冗余

AI 模型返回结果存在两大不确定性:一是格式不确定(如有时返回 JSON、有时返回纯文本),二是内容不确定(如可选字段可能缺失)。Java 静态类型需提前定义实体类,一旦返回格式不符就会抛出解析异常;且判定返回类型需写大量 instanceof 或字符串匹配逻辑,代码臃肿。

Groovy 解决方案:动态类型 + 简洁判定逻辑

groovy 复制代码
import org.springframework.ai.chat.ChatResponse
import groovy.json.JsonSlurper

@Service
class AiResponseService {

    private final JsonSlurper jsonSlurper = new JsonSlurper()

    // 处理 AI 动态返回(支持 JSON/纯文本两种格式)
    def processDynamicResponse(ChatResponse response) {
        def content = response.content.trim()

        // 1. 动态判定返回格式(Groovy 简洁语法替代 Java 冗长逻辑)
        if (content.startsWith("{") && content.endsWith("}")) {
            // 2. 动态解析 JSON,无需实体类(适配字段缺失场景)
            def json = jsonSlurper.parseText(content)
            return [
                type: "json",
                data: json,
                message: json.message ?: "默认提示" // 空值默认符,避免 NPE
            ]
        } else {
            // 3. 纯文本处理,动态提取关键信息
            return [
                type: "text",
                data: content,
                keywords: content.findAll(~/(Groovy|Spring AI|向量存储)/) // 正则快速提取
            ]
        }
    }
}

核心优势

  • 动态类型适配:无需定义 AiResponse 实体类,用 Map 直接接收动态结果,字段缺失不会报错;
  • 空值安全处理:json.message ?: "默认提示" 替代 Java if (json.getMessage() != null) 判定;
  • 判定逻辑简化:正则匹配、字符串判断用一行代码完成,比 Java 少 50% 冗余。

痛点 3:重复模板与参数配置------Java 需多类封装,Groovy DSL 一键解决

Spring AI 开发中,不同场景(如 RAG、翻译、摘要)需重复构建相似 Prompt 模板,且模型参数(如 temperaturemaxTokens)需灵活调整。Java 需创建多个模板类或 Builder 类,而 Groovy 可通过 DSL (领域特定语言)封装通用逻辑,实现"一键复用+动态配置"。

Groovy 解决方案:DSL 封装通用 Prompt 模板

groovy 复制代码
@Service
class AiTemplateService {

    @Autowired
    ChatClient chatClient

    // 定义 DSL:封装通用 Prompt 模板,支持动态参数配置
    def aiCall(String templateType, Map params = [:]) {
        // 通用参数默认值(Groovy 可选参数,无需重载)
        def temp = params.temperature ?: 0.6
        def maxTokens = params.maxTokens ?: 1024

        // 模板路由(Groovy switch 支持区间、字符串匹配,比 Java 强大)
        def (systemPrompt, userPrompt) = switch (templateType) {
            case "summary":
                def text = params.text
                ["你是文本摘要助手,总结需控制在 30 字内", "总结:$text"]
                break
            case "translate":
                def text = params.text
                def targetLang = params.targetLang ?: "中文"
                ["你是翻译助手,精准翻译为 $targetLang,无额外解释", "翻译:$text"]
                break
            case "rag":
                def context = params.context
                def query = params.query
                ["基于上下文回答:$context", query]
                break
        }

        // 动态调整模型参数
        def response = chatClient.prompt {
            system(systemPrompt)
            user(userPrompt)
            options {
                temperature(temp)
                maxTokens(maxTokens)
            }
        }.call()

        return response.content
    }
}

使用示例

less 复制代码
// 1. 调用摘要模板
def summary = aiTemplateService.aiCall("summary", [text: "Groovy 是 Java 动态增强版,支持简洁语法和函数式编程"])
// 2. 调用翻译模板(指定目标语言和温度)
def translate = aiTemplateService.aiCall("translate", [text: "Spring AI is powerful", targetLang: "英文", temperature: 0.3])

优势 :用 DSL 封装重复逻辑,新增模板只需添加 case 分支,无需创建新类;参数支持默认值,调用更灵活,比 Java 减少 60% 模板代码。

痛点 4:向量存储文档处理繁琐------Java 循环冗余,Groovy 集合一键搞定

Spring AI 向量存储(如 Pinecone、Chroma)需将原始文本转为 Document 对象,Java 需通过 for 循环遍历、手动设置属性,代码冗余;且相似性查询结果处理需多步 Stream 操作,可读性差。

Groovy 解决方案:集合闭包 + 动态属性赋值

groovy 复制代码
import org.springframework.ai.document.Document
import org.springframework.ai.pinecone.PineconeVectorStore
import org.springframework.beans.factory.annotation.Autowired

@Service
class VectorStoreService {

    @Autowired
    PineconeVectorStore vectorStore

    // 批量添加文档到向量库(一行完成转换)
    def batchAddDocs(List<String> texts, Map<String, String> commonMeta = [:]) {
        // Groovy 集合 collect 闭包:替代 Java for 循环,快速转为 Document
        def docs = texts.collect { text ->
            new Document(
                id: UUID.randomUUID().toString(),
                content: text,
                metadata: commonMeta + [createTime: new Date().format("yyyy-MM-dd HH:mm:ss")] // 元数据合并
            )
        }
        vectorStore.add(docs)
        return docs.size()
    }

    // 相似性查询 + 结果过滤
    def searchSimilar(String query, int topK = 3) {
        // 查询结果处理:过滤相似度低于 0.7 的文档
        def results = vectorStore.similaritySearch(query, topK)
            .findAll { it.metadata.similarityScore >= 0.7 } // 闭包过滤,比 Java Stream 简洁
            .collect { doc ->
                [
                    id: doc.id,
                    content: doc.content.take(100), // 截取前 100 字
                    score: doc.metadata.similarityScore
                ]
            }
        return results
    }
}

三、实战落地:RAG 检索增强流程示例

结合以上痛点解决方案,实现一个"文档入库→相似检索→AI 回答"的完整 RAG 流程,体现 Groovy + Spring AI 的丝滑开发体验:

groovy 复制代码
import org.springframework.boot.CommandLineRunner
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication

@SpringBootApplication
class GroovySpringAiRagApplication implements CommandLineRunner {

    @Autowired
    VectorStoreService vectorStoreService

    @Autowired
    AiTemplateService aiTemplateService

    static void main(String[] args) {
        SpringApplication.run(GroovySpringAiRagApplication, args)
    }

    @Override
    void run(String... args) throws Exception {
        // 1. 准备知识库文档
        def knowledgeDocs = [
            "Groovy 支持动态类型,用 def 关键字声明变量,无需指定具体类型",
            "Spring AI 1.0.0-M7 支持 OpenAI、Pinecone 等组件,需引入 spring-ai-bom 统一版本",
            "Groovy 字符串模板支持 $变量 和 ${表达式},无需拼接字符串"
        ]

        // 2. 批量添加文档到向量库
        def docCount = vectorStoreService.batchAddDocs(knowledgeDocs, [source: "groovy-spring-ai-docs"])
        println("成功入库 $docCount 条知识库文档")

        // 3. 用户查询
        def userQuery = "Groovy 如何简化 Spring AI 的 Prompt 构建?"

        // 4. 相似检索(获取相关上下文)
        def similarDocs = vectorStoreService.searchSimilar(userQuery)
        def context = similarDocs.collect { it.content }.join("\n\n")
        println("检索到相关文档:\n$context")

        // 5. 调用 AI 生成回答(使用 RAG 模板)
        def aiAnswer = aiTemplateService.aiCall("rag", [context: context, query: userQuery])
        println("\nAI 回答:\n$aiAnswer")
    }
}

运行结果

ruby 复制代码
成功入库 3 条知识库文档
检索到相关文档:
Groovy 字符串模板支持 $变量 和 ${表达式},无需拼接字符串

AI 回答:
Groovy 可通过字符串模板直接嵌入 $变量 或 ${表达式} 构建 Prompt,无需拼接;还能借助闭包快速构建结构化 Prompt(含 system 和 user 指令),比 Java 更简洁,且无需依赖高版本 JDK。

四、小结

对于 Java 开发者而言,Groovy 学习成本极低,且完全兼容 Spring AI 生态,无需重构现有代码即可无缝集成。无论是快速验证 AI 模型能力、搭建 RAG 检索系统,还是开发复杂多模型应用,Groovy + Spring AI 都能让 AI 开发从"繁琐适配"变为"丝滑落地",是 Java 生态下 AI 工程化的高效组合。

相关推荐
星火10241 天前
Groovy 区间:简洁高效的范围操作
groovy
星火10242 天前
Groovy 实战:从微服务到 GUI 应用的快速集成
groovy
雨中飘荡的记忆5 天前
Java + Groovy计费引擎详解
java·groovy
星火10246 天前
Groovy:告别 Java 痛点,纵享丝滑编程
groovy
小小测试开发7 天前
JMeter JSR223预处理程序高级用法:解锁自动化测试的灵活性上限
开发语言·jmeter·groovy
星火10249 天前
【Groovy翻译系列三】Groovy应用集成
groovy
勤劳打代码22 天前
isar_flutter_libs 引发 Namespace not specified
android·flutter·groovy
安冬的码畜日常1 个月前
【JUnit实战3_20】第十一章:用 Gradle 运行 JUnit 测试实战
测试工具·junit·单元测试·gradle·软件构建·groovy·junit5
ClassOps1 个月前
Gradle Groovy 和 Kotlin kts 语法对比
android·kotlin·gradle·groovy