1.引入SpringAI相关的依赖

plugins {
    kotlin("jvm") version "1.9.25"
    kotlin("plugin.spring") version "1.9.25"
    id("org.springframework.boot") version "3.4.4"
    id("io.spring.dependency-management") version "1.1.7"
}

group = "com.wanna.project.ai"
version = "0.0.1-SNAPSHOT"

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

configurations {
    compileOnly {
        extendsFrom(configurations.annotationProcessor.get())
    }
}

repositories {
    mavenCentral()
}

extra["springAiVersion"] = "1.0.0-M7"

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
    implementation("org.jetbrains.kotlin:kotlin-reflect")
    implementation("org.springframework.ai:spring-ai-starter-model-openai")
    implementation("org.springframework.ai:spring-ai-starter-mcp-server-webmvc")
    compileOnly("org.projectlombok:lombok")
    annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
    annotationProcessor("org.projectlombok:lombok")
    testImplementation("org.springframework.boot:spring-boot-starter-test")
    testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}

dependencyManagement {
    imports {
        mavenBom("org.springframework.ai:spring-ai-bom:${property("springAiVersion")}")
    }
}

kotlin {
    compilerOptions {
        freeCompilerArgs.addAll("-Xjsr305=strict")
    }
}

tasks.withType<Test> {
    useJUnitPlatform()
}

2.在SpringBoot中配置访问大模型的配置信息

我们这里使用的是OpenAI的GPT4大模型(注意:GPT低版本的大模型,对于FunctionCall支持不太好,GPT-3.5对FunctionCall的都支持不太好):

# openai大模型
spring.ai.openai.chat.options.model=gpt-4
spring.ai.openai.chat.options.temperature=0
# openai大模型地址, 这里使用的是第三方的中转地址
spring.ai.openai.base-url=https://hk.xty.app
# 访问大模型的apiKey
spring.ai.openai.api-key=sk-xxxxxx

3. 配置ChatClient并绑定大模型

新增如下的配置类:

@Configuration(proxyBeanMethods = false)
class ChatClientConfiguration {

    @Bean
    fun chatMemory(): ChatMemory {
        return InMemoryChatMemory()
    }

    @Bean
    fun chatClient(model: OpenAiChatModel, chatMemory: ChatMemory): ChatClient {
        return ChatClient.builder(model)
            .defaultAdvisors(
                MessageChatMemoryAdvisor(chatMemory),
                SimpleLoggerAdvisor()
            )
            .build()
    }
}

4.创建给AI使用的Tool工具

新增如下的AIToolService:

@Service
class AIToolService {

    @Tool(name = "getWeather", description = "根据城市名称查询天气信息")
    fun getWeather(@ToolParam(description = "城市") city: String): String {
        return "城市 $city 的天气是晴朗的,25℃"
    }

    @Tool(name = "getFlightNum", description = "根据航线查询合适的航班")
    fun getFlightNum(
        @ToolParam(description = "出发城市三字码(例如PEK)") depCity: String,
        @ToolParam(description = "到达城市三字码(例如HKG)") arrCity: String
    ): String {
        return "CX345,CX393"
    }
}

5.封装Service和Controller接口

封装如下的ChatService供聊天用:

@Component
class ChatService {

    @Autowired
    private lateinit var chatClient: ChatClient

    @Autowired
    private lateinit var aiToolService: AIToolService

    fun chat(prompt: String): String? {
        return chatClient
            .prompt(prompt)
            .system("你可以且应该使用工具来帮助用户解决问题")
            .tools(aiToolService)
            .call()
            .content()
    }
}

再封装一个Controller测试使用

@RestController
class ChatController {

    @Autowired
    private lateinit var chatService: ChatService

    @RequestMapping("/chat")
    fun chat(prompt: String): String? {
        return chatService.chat(prompt)
    }

}

6. 测试FunctionCall接口

我们使用如下的接口进行测试,并输入提示词:

http://127.0.0.1:8080/chat?prompt=请帮我查询一下北京的天气

返回结果,发现FunctionCall成功,成功调用我们本地的查询天气的函数。

北京的天气是晴朗的,气温为25℃。