ai(5) spring ai 单agent

 

  1. 上一篇
    1.   ai(4) spring ai  rag检索
  2. ai agent
    1.   概念
      1.   ai chat:用户输入,请求LLM(大模型), LLM返回答案。
      2. RAG(检索增强):知识分块 -》存入向量库。用户输入-》检索向量库-》请求LLM-》LLM返回数据
      3. Agent:用户输入-》LLM规划-》调用工具-》LLM规划-》调用工具。。。-》完成(闭环循环
      4. 大模型告诉你答案。rag引用资料说话。agent会说会做。
    2. 流程  
      1.   开发工具:工具是什么,其实就是封装好的方法。比如封装一个天气的工具就是查询天气返回天气结果,封装一个查询订单的工具就是查询数据库返回数据。另外需要在方法上特别标注是做什么用,方便LLM快速找到想要的工具  
        1. api调用:天气、股价、汇率等等
        2. 数据库查询:用户信息、订单、学生信息...
        3. 本地文件:读取配置、读日志、
        4. 知识库检索:RAG向量搜索
        5. 搜索引擎:es、日志、google等
        6. 数学计算:复杂公式、统计计算等
        7. 单位转换:货币、长度、重量等
        8. 时间处理:时区转换、日期计算等
        9. 数据统计:平均值、趋势分析等
        10. 文件操作:创建、移动、删除等
        11. 发送通知:邮件、短信、钉钉、企业微信等
        12. 执行命令:shell脚本、python脚本等
      2. 记忆实现  
        1.   短期记忆:对话上下文
        2. 配置ChatMemory,主要包括储存和记录条数,短期记忆存储可以选择服务器内存、redis、数据库等
        3. 将ChatMemory注入到ChatClient中,ChatClient是ai sdk的一个接口或者方法。
        4. 会话id:conversationId,每次请求后端接口都需要一个会话id,来区分不同的用户和会话。通过会话id获取之前的记忆
        5. 长期记忆:Rag向量库    
      3. 工具加载和调用
        1.   java springboot框架中有专门的方法,将工具注册到对话模型上(别的语言应该一样)
        2. 调用的时候,LLM会根据工具的描述,遍历所有工具,找到匹配度最高的工具,然后决定调用这个工具,这个一般内置好的不用自己开发
        3. springboot会循环调用需要的工具处理数据
      4. 大模型处理  
        1.   等大模型自动调用合适的工具处理好数据,然后组装数据返回给用户。
  3.   spring ai对接
    1.   首先开发对应的工具,例如开发一个查询天气的工具


      import com.zwj.ai.core.ai.dto.GeoResponse;
      import com.zwj.ai.core.ai.dto.WeatherResponse;
      import com.zwj.ai.core.utils.HttpUtil;
      import lombok.extern.slf4j.Slf4j;
      import org.springframework.ai.tool.annotation.Tool;
      import org.springframework.ai.tool.annotation.ToolParam;
      import org.springframework.stereotype.Service;
      import org.springframework.web.client.RestClient;
      import tools.jackson.databind.ObjectMapper;

      import java.util.ArrayList;
      import java.util.List;
      import java.util.Map;
      import java.util.Random;
      import java.util.concurrent.ThreadLocalRandom;

      /**
      * 天气查询工具类
      * <p>提供天气相关的 AI 工具方法,包括温度查询、湿度查询、天气状况和天气预报</p>
      *
      * @author ext.zhangwujie5
      * @since 2026/6/6
      */
      @Slf4j
      @Service
      public class WeatherTools {

      private final RestClient restClient = RestClient.create("https://devapi.qweather.com");

      private final ObjectMapper objectMapper = new ObjectMapper();

      @Tool(description = "获取指定坐标的当前天气信息,包括温度等")
      public double getCurrentTemperature(
      @ToolParam(description = "纬度") double lat,
      @ToolParam(description = "经度") double lon
      ) {
      log.info("WeatherTools getCurrentTemperature start lat={} lon={}", lat, lon);
      String forecastUrl = "https://api.open-meteo.com/v1/forecast?latitude=" + lat + "&longitude=" + lon + "&current_weather=true&timezone=auto";
      String s = HttpUtil.get(forecastUrl);
      WeatherResponse response = objectMapper.readValue(s, WeatherResponse.class);
      log.info("WeatherTools getCurrentTemperature start response={}", objectMapper.writeValueAsString(response));

      if (response == null || response.getCurrentWeather() == null) {
      throw new RuntimeException("无法获取温度数据");
      }

      return response.getCurrentWeather().getTemperature();
      }

      @Tool(description = "根据城市名称查询当前天气信息")
      public String getWeatherByCity(@ToolParam(description = "城市名称,如:北京、上海") String cityName) {
      try {
      log.info("WeatherTools getWeatherByCity start cityName={}", cityName);

      // 第一步:通过城市名获取坐标(Open-Meteo 内置地理编码)
      String searchUrl = "https://geocoding-api.open-meteo.com/v1/search?name=" + cityName + "&count=1&language=zh";
      GeoResponse geo = HttpUtil.get(searchUrl, GeoResponse.class);

      if (geo == null || geo.getResults().isEmpty()) {
      return "未找到城市:" + cityName;
      }

      double lat = geo.getResults().getFirst().getLatitude();
      double lon = geo.getResults().getFirst().getLongitude();
      String displayName = geo.getResults().getFirst().getName();

      double currentTemperature = getCurrentTemperature(lat, lon);
      return String.format("%s 当前温度: %.1f°C", displayName, currentTemperature);
      } catch (Exception ex) {
      log.error("WeatherTools getWeatherByCity catch error msg={}", ex.getMessage(), ex);
      return "获取天气失败: " + ex.getMessage();
      }
      }

      @Tool(description = "获取指城市的当温度,返回摄氏度")
      public String getTemperature(@ToolParam(description = "城市名称") String city) {
      log.info("WeatherTools getTemperature start city={}", city);
      Map<String, Integer> temps = Map.of(
      "北京", 10, "上海", 20, "深圳", 30, "广州", 40,
      "成都", 50, "杭州", 60, "武汉", 70, "西安", 80
      );
      int temp = temps.getOrDefault(city, 21);
      log.info("WeatherTools getTemperature temp={}", temp);
      return String.format("%s当前温度 %d°C", city, temp);
      }

      @Tool(description = "获取指定城市的湿度")
      public String getHumidity(@ToolParam(description = "城市名称") String city) {
      log.info("WeatherTools getHumidity start city={}", city);
      Map<String, Integer> humidity = Map.of(
      "北京", 10, "上海", 20, "深圳", 30, "广州", 40,
      "成都", 50, "杭州", 60, "武汉", 70, "西安", 80
      );
      int h = humidity.getOrDefault(city, 65);
      log.info("WeatherTools getHumidity h={}", h);
      return String.format("%s当前湿度 %d%%", city, h);
      }

      @Tool(description = "获取指定城市的天气状况(晴/雨/多云等)")
      public String getWeatherCondition(@ToolParam(description = "城市名称") String city) {
      log.info("WeatherTools getWeatherCondition start city={}", city);
      List<String> conditions = List.of("晴朗☀️", "多云⛅", "小雨🌧️", "大雨🌧️", "雷阵雨⚡", "雾霾😷");
      Random rand = new Random(city.hashCode());
      String condition = conditions.get(rand.nextInt(conditions.size()));
      return String.format("%s天气: %s", city, condition);
      }

      @Tool(description = "获取指定城市未来N天的天气预报")
      public String getForecast(@ToolParam(description = "城市名称") String city,
      @ToolParam(description = "天数(1-7)") int days) {
      log.info("WeatherTools getForecast start city={}", city);
      days = Math.min(days, 7);
      List<String> forecasts = new ArrayList<>();
      for (int i = 1; i <= days; i++) {
      int temp = 20 + ThreadLocalRandom.current().nextInt(15);
      forecasts.add(String.format("第%d天: %d°C", i, temp));
      }
      return String.format("%s未来%d天预报:\n%s", city, days, String.join("\n", forecasts));
      }

      }

       

    2. 将写好的工具注册到对应的agent上
      import com.zwj.ai.core.ai.tools.WeatherTools;
      import lombok.extern.slf4j.Slf4j;
      import org.springframework.ai.chat.client.ChatClient;
      import org.springframework.ai.chat.client.advisor.ToolCallAdvisor;
      import org.springframework.ai.ollama.OllamaChatModel;
      import org.springframework.ai.support.ToolCallbacks;
      import org.springframework.stereotype.Service;
      
      /**
       * 天气执行 Agent
       * <p>专门处理天气相关任务,集成温度查询、湿度查询、天气状况和天气预报等能力</p>
       */
      @Slf4j
      @Service
      public class WeatherExecutorAgent {
      
          /** 天气查询 ChatClient,配置了天气工具 */
          private final ChatClient weatherClient;
      
          public WeatherExecutorAgent(
              OllamaChatModel chatModel,
              WeatherTools weatherTools
          ) {
              this.weatherClient = ChatClient.builder(chatModel)
                  .defaultSystem("""
                      你是天气查询专家,专门处理所有与天气相关的问题。
      
                      你可以使用以下工具:
                      1. getTemperature - 获取指定城市的温度
                      2. getHumidity - 获取指定城市的湿度
                      3. getWeatherCondition - 获取指定城市的天气状况(晴/雨/多云等)
                      4. getForecast - 获取指定城市未来N天的天气预报
      
                      工作原则:
                      - 仔细理解用户需要什么天气信息
                      - 如果用户问多个城市,分别查询每个城市
                      - 如果用户问未来天气,使用 getForecast 并指定天数
                      - 用友好、清晰的语言组织回答
                      """)
                  .defaultToolCallbacks(
                      ToolCallbacks.from(
                          weatherTools
                      )
                  )
                  .defaultAdvisors(ToolCallAdvisor.builder().build())
                  .build();
          }
      
          /**
           * 执行天气查询任务
           *
           * @param question 任务描述
           * @return 执行结果
           */
          public String execute(String question) {
              log.info("WeatherExecutorAgent execute start question={}", question );
              return weatherClient.prompt()
                  .user(question)
                  .call()
                  .content();
          }
      }

 

posted @ 2026-08-13 11:01  zwjvzwj  阅读(1)  评论(0)    收藏  举报