Spring AI + VLLM + 本地模型 开发 一个自己的 agent

我是基于本地大模型,你也可以调用openAI或者deepseek等模型。

1、使用 vLLm 启动模型

python -m vllm.entrypoints.openai.api_server \
  --model /code/models/Qwen/Qwen3-8B \
  --served-model-name qwen3-8b \
  --max-model-len 8k \
  --host 0.0.0.0 \
  --port 6006 \
  --dtype bfloat16 \
  --gpu-memory-utilization 0.8 \
  --enable-auto-tool-choice \
  --tool-call-parser hermes

我的显卡5090 32G显存

2、确认启动

访问地址:IP + 6006/docs 我的地址为:192.168.10.102:6006


启动成功页面

3、编写基于spring AI 代码

所需依赖: springboot 3.5.9 jdk17+ SpringAI 1.1.2

3.1 pom.xml如下

<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.5.9</version>
        <relativePath/>
        <!--  lookup parent from repository  -->
    </parent>
    <groupId>com.shujunjun.spring.ai</groupId>
    <artifactId>sjjai</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>sjjai</name>
    <description>shujunjun ai</description>
    <url/>
    <licenses>
        <license/>
    </licenses>
    <developers>
        <developer/>
    </developers>
    <scm>
        <connection/>
        <developerConnection/>
        <tag/>
        <url/>
    </scm>
    <properties>
        <java.version>21</java.version>
        <spring-ai.version>1.1.2</spring-ai.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- vLLM部署的模型是按照 OpenAI 接口来输出的,因此可以使用该依赖 -->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-starter-model-openai</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.33</version>
        </dependency>

        <!-- 增加历史对话记忆功能 -->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-starter-model-chat-memory-repository-jdbc</artifactId>
        </dependency>
       
       <!-- http 请求依赖 -->
        <dependency>
            <groupId>io.projectreactor.netty</groupId>
            <artifactId>reactor-netty-http</artifactId>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.ai</groupId>
                <artifactId>spring-ai-bom</artifactId>
                <version>${spring-ai.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

3.2 配置代码如下

application.yml

spring:
  application:
    name: sjjai
  ai:
    chat:
      memory:
        repository:
          jdbc:
            # 自动初始化数据库
            initialize-schema: always
            # mysql mariadb
            plateform: mysql
    openai:
      # https://api.deepseek.com
      base-url: http://192.168.10.102:6006/
      api-key: sk-xxxxxxx
      chat:
        options:
          model: qwen3-8b

  datasource:
    url: jdbc:mysql://192.168.47.130:3306/sjj-ai?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&allowMultiQueries=true&useSSL=false&allowPublicKeyRetrieval=true
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
    hikari:
      connection-init-sql: SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci;
server:
  port: 8082

config配置

import com.shujunjun.spring.ai.sjjai.tool.ToolService;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.ai.chat.memory.repository.jdbc.JdbcChatMemoryRepository;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ChatConfig {

    @Bean
    public ChatClient chatClient(ChatModel chatModel, ChatMemory chatMemory, ToolService toolService) {
        return ChatClient.builder(chatModel)
                .defaultSystem("你作为一名专业的AI助手,请根据用户提示信息回答问题。再根据用户信息,自定判断是否需要调用工具")
                .defaultAdvisors(MessageChatMemoryAdvisor
                        .builder(chatMemory).build())
                // 自定义工具类型
                .defaultTools(toolService)
                .build();
    }

    /**
     * 聊天配置基于jdbc 存储于 MySQL 数据库中
     */
    @Bean
    public ChatMemory chatMemory(JdbcChatMemoryRepository repository) {
        return MessageWindowChatMemory.builder()
                .chatMemoryRepository(repository)
                .maxMessages(200)
                .build();
    }
}

ToolService 工具实现类。该实现类有两个能力,一个是获取天气信息,一个是根据省份获取油价信息

import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

@Slf4j
@Component
public class ToolService {

    RestTemplate restTemplate = new RestTemplate();


    @Tool(name = "getWeather", description = "获取天气信息")
    public String getWeather(@ToolParam(description = "要查询的天气的城市名称") String city) {
        log.info("调用获取天气工具,城市:{}", city);
        return city + "的天气晴朗。气温23摄氏度";
    }

    @Tool(name = "getLocalOilPrice", description = "根据省份获取当地油价信息")
    public String getLocalOilPrice(@ToolParam(description = "要查询的油价的省份名称") String province) {
        log.info("调用获取当地油价工具,省份:{}", province);
        String format = String.format("https://shanhe.kim/api/youjia/youjia.php?province=%s", province);
        return restTemplate.getForObject(format, String.class);
    }
}

测试类如下

import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

@RestController
@RequestMapping
@RequiredArgsConstructor
public class AiController {

    final ChatClient chatClient;

    @GetMapping("chat")
    public Flux<String> chatAi(String question, String userId, HttpServletResponse response) {
        response.setCharacterEncoding("utf-8");
        return chatClient.prompt()
                .user(question)
                .advisors(adv -> adv.param(ChatMemory.CONVERSATION_ID, userId))
                .stream()
                .content();
    }
}

调用接口效果


调用接口展示

参考代码地址: https://gitee.com/JustNickNameWSH/sjjai-server

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容