Spring AI 实战教程(七):Agent 智能体 —— 用电商购物助手学透自主规划与工具执行

本篇目标 :抛开 "时间/天气" 这种简单 Demo,用一个电商购物助手 Agent 真正学习 Agent 核心能力:需求理解、主动澄清、任务规划、工具执行、推荐总结。所有代码可直接落地到本项目。


一、为什么用电商购物助手学 Agent?

普通的 Function Calling 只是 "模型挑一个函数调用",根本算不上 Agent。一个真正的 Agent 至少要能:

  1. 理解模糊需求:把 "帮我买台电脑" 拆成结构化的品类/预算/用途/偏好;
  2. 判断信息是否充足 :信息不够时主动问用户,而不是瞎推荐;
  3. 规划多步骤任务:先搜索、再对比、再查选购指南、最后总结;
  4. 调用受控工具:每一步只能用白名单中的工具;
  5. 追踪执行过程:每一步用了什么工具、输入是什么、得到了什么结果,全部可观测;
  6. 基于事实总结:最终回复必须基于工具结果,不能编造商品。

电商购物天然具备多步决策、约束条件、候选比较、最终推荐这四个 Agent 必备特征,是最好的学习载体。


二、本篇关键概念

2.1 Agent(智能体)

含义:一个能 "感知---思考---行动" 循环的 AI 系统。不只是回答问题,而是为了完成某个目标,主动选择并调用工具,直到任务完成或达到边界条件。

作用:把 LLM 从 "聊天机器人" 升级为 "能做事的助手"。本项目中,购物 Agent 的目标是 "给用户合适的购买建议",过程中可以自主搜索商品、对比、查指南。

2.2 需求理解(Requirement Extraction)

含义 :从自然语言中提取结构化字段。例如把 "3000 以内主打拍照的手机" 解析为 {category=手机, budgetMax=3000, mustHave=[拍照好]}

作用:结构化的需求才能驱动后续的工具调用和过滤。第一阶段用规则提取,第二阶段可以让模型直接输出 JSON。

2.3 主动澄清(Clarification)

含义:当关键字段(品类、预算、用途)缺失时,Agent 不应直接推荐,而是先反问用户。

作用 :避免基于错误假设给出无用建议。这是 Agent 区别于普通对话的关键能力------"知道自己不知道什么"

2.4 规划(Planning)

含义:把目标拆解成有序的步骤序列。本项目购物 Agent 的固定计划是:搜索候选 → 对比候选 → 检索选购指南。

作用:把模糊目标变成可执行的步骤。第一阶段用固定模板,第二阶段可以让模型动态生成 Plan(输出 JSON 数组)。

2.5 工具(Tool)

含义 :Agent 可调用的外部能力的统一抽象。本项目定义了 ShoppingTool 接口,所有工具实现 name() / description() / supports() / execute() 四个方法。

作用:让 Agent 的行动空间标准化、可枚举、可白名单控制。

2.6 执行器(Executor)

含义 :循环遍历计划中的每一步,根据 toolName 找到对应工具,执行并记录 observationstatus

作用 :把 Plan 落到实际调用上,并提供 maxSteps 上限 防止 Agent 失控。

2.7 受控 Agent(Controlled Agent)

含义 :明确限制 Agent 能做什么、不能做什么。本项目第一阶段不允许下单/支付/改地址,且工具白名单只有四个。

作用:生产环境下,Agent 越自由越危险。学习阶段就要养成 "边界优先" 的习惯。

2.8 执行追踪(Trace)

含义:每一步的输入、输出、状态、耗时都记录下来,连同最终回复一起返回给前端。

作用:可观测性是 Agent 调试的命脉。前端可以把执行步骤渲染成卡片,让用户看到 Agent 的 "思考过程"。


三、整体架构

复制代码
前端请求 POST /api/agent/shopping/run
   │
   ▼
ShoppingAgentController.run()
   │
   ▼
ShoppingAgentService.run()  ← 总控
   │
   ├── ShoppingRequirementService.extract()    需求提取
   │      │
   │      ├── needClarification == true → 直接返回澄清问题
   │      └── needClarification == false → 进入规划
   │
   ├── ShoppingPlannerService.createPlan()     生成计划(3 步)
   │
   ├── ShoppingExecutorService.execute()       循环执行步骤
   │      │
   │      └── ShoppingToolRegistry.getTool()   工具白名单筛选
   │             │
   │             ├── ProductSearchTool   搜索 Mock 商品
   │             ├── ProductCompareTool  生成对比维度
   │             └── ShoppingGuideTool   走 RAG 知识库
   │
   └── ChatService.chat()                      让 LLM 基于工具结果总结
   │
   ▼
返回 ShoppingAgentResponse(含 needClarification / answer / steps)

四、包结构

为了避免和现有聊天/知识库/记忆代码混在一起,新增独立模块 agent.shopping

复制代码
src/main/java/com/wen/testai/agent/shopping/
├── controller/
│   └── ShoppingAgentController.java
├── dto/
│   ├── ShoppingAgentRequest.java
│   ├── ShoppingAgentResponse.java
│   ├── ShoppingAgentStepDTO.java
│   └── ProductDTO.java
├── model/
│   ├── ShoppingRequirement.java
│   ├── ShoppingPlan.java
│   ├── ShoppingStep.java
│   └── ShoppingStepStatus.java
├── service/
│   ├── ShoppingAgentService.java
│   ├── ShoppingRequirementService.java
│   ├── ShoppingPlannerService.java
│   ├── ShoppingExecutorService.java
│   └── ShoppingToolRegistry.java
└── tool/
    ├── ShoppingTool.java
    ├── MockProduct.java
    ├── MockProductRepository.java
    ├── ProductSearchTool.java
    ├── ProductCompareTool.java
    ├── ShoppingGuideTool.java
    └── ShoppingListTool.java

五、模型层:把需求和计划结构化

5.1 ShoppingStepStatus(步骤状态枚举)

复制代码
package com.wen.testai.agent.shopping.model;

public enum ShoppingStepStatus {
    PENDING,   // 待执行
    RUNNING,   // 正在执行
    SUCCESS,   // 成功
    FAILED,    // 失败
    SKIPPED    // 跳过(超过 maxSteps)
}

5.2 ShoppingRequirement(结构化需求)

复制代码
@Data
@Builder
public class ShoppingRequirement {
    private String category;          // 品类:手机/电脑/厨房用品
    private BigDecimal budgetMin;     // 最低预算
    private BigDecimal budgetMax;     // 最高预算
    private String usage;             // 用途:办公、拍照、续航
    private String brandPreference;   // 品牌偏好
    private List<String> mustHave;    // 必须满足的卖点
    private List<String> avoid;       // 避免的特性
}

关键:所有字段都是 "可空",因为初次提取时几乎不可能字段全填齐------这正是 "主动澄清" 的依据。

5.3 ShoppingStep / ShoppingPlan

复制代码
@Data
@Builder
public class ShoppingStep {
    private int index;
    private String description;        // 人类可读描述
    private String toolName;           // 调用的工具名
    private String input;              // 工具输入
    private ShoppingStepStatus status; // 状态
    private String observation;        // 工具返回结果
}

@Data
@Builder
public class ShoppingPlan {
    private ShoppingRequirement requirement;
    private List<ShoppingStep> steps;
}

六、工具层:统一抽象 + Mock 数据

6.1 ShoppingTool 接口

复制代码
public interface ShoppingTool {
    String name();                          // 工具唯一名(product_search 等)
    String description();                   // 给 LLM/前端看的描述
    boolean supports(String toolName);      // 是否处理某个 toolName
    String execute(String input);           // 执行:第一阶段 String→String
}

第一阶段输入输出都用 String便于学习和调试。后期链路稳定后再升级为结构化对象。

6.2 Mock 商品库

不接真实电商平台,避免被网络/反爬/价格实时性等问题干扰:

复制代码
public record MockProduct(
        String id,
        String name,
        String category,
        BigDecimal price,
        Double score,
        List<String> highlights
) {}

@Repository
public class MockProductRepository {

    private final List<MockProduct> products = List.of(
            new MockProduct("p1001", "星河 X 手机", "手机", new BigDecimal("2799"), 4.7,
                    List.of("拍照清晰", "续航强", "屏幕素质好")),
            new MockProduct("p1002", "青云 Pro 手机", "手机", new BigDecimal("2499"), 4.5,
                    List.of("轻薄", "充电快", "性价比高")),
            new MockProduct("p2001", "远航 14 笔记本", "电脑", new BigDecimal("4999"), 4.6,
                    List.of("适合办公", "重量轻", "续航稳定"))
    );

    public List<MockProduct> search(String keyword, BigDecimal minPrice, BigDecimal maxPrice) {
        return products.stream()
                .filter(p -> keyword.contains(p.category()) || keyword.contains(p.name()))
                .filter(p -> p.price().compareTo(minPrice) >= 0)
                .filter(p -> p.price().compareTo(maxPrice) <= 0)
                .toList();
    }
}

6.3 ProductSearchTool(商品搜索)

复制代码
@Component
public class ProductSearchTool implements ShoppingTool {

    private final MockProductRepository productRepository;

    public ProductSearchTool(MockProductRepository productRepository) {
        this.productRepository = productRepository;
    }

    @Override public String name() { return "product_search"; }
    @Override public String description() { return "根据品类、预算、用途和偏好搜索候选商品"; }
    @Override public boolean supports(String toolName) { return name().equalsIgnoreCase(toolName); }

    @Override
    public String execute(String input) {
        List<MockProduct> products = productRepository.search(input, BigDecimal.ZERO, new BigDecimal("999999"));
        if (products.isEmpty()) {
            return "没有找到匹配商品";
        }
        StringBuilder builder = new StringBuilder("候选商品:\n");
        for (MockProduct p : products) {
            builder.append("- ")
                    .append(p.id()).append(" | ")
                    .append(p.name()).append(" | ")
                    .append(p.price()).append("元 | ")
                    .append(p.score()).append("分 | ")
                    .append(String.join("、", p.highlights()))
                    .append("\n");
        }
        return builder.toString();
    }
}

6.4 ProductCompareTool(商品对比)

第一阶段不做真实排序,先用文本模板告诉 LLM "按什么维度对比",让 LLM 在最后总结时自己组织对比内容:

复制代码
@Component
public class ProductCompareTool implements ShoppingTool {

    @Override public String name() { return "product_compare"; }
    @Override public String description() { return "对候选商品进行价格、评分、卖点和适用场景对比"; }
    @Override public boolean supports(String toolName) { return name().equalsIgnoreCase(toolName); }

    @Override
    public String execute(String input) {
        return """
                对比维度:
                1. 价格:优先保留预算内商品
                2. 评分:优先推荐评分较高商品
                3. 卖点:匹配用户 mustHave 条件
                4. 取舍:说明每个商品适合什么用户
                待对比商品:%s
                """.formatted(input);
    }
}

6.5 ShoppingGuideTool(选购指南,对接 RAG)

复用教程四的 RagService,让 Agent 能从知识库中读 "怎么选":

复制代码
@Component
public class ShoppingGuideTool implements ShoppingTool {

    private final RagService ragService;

    public ShoppingGuideTool(@Autowired(required = false) RagService ragService) {
        this.ragService = ragService;
    }

    @Override public String name() { return "shopping_guide"; }
    @Override public String description() { return "检索品类选购指南、参数解释和避坑建议"; }
    @Override public boolean supports(String toolName) { return name().equalsIgnoreCase(toolName); }

    @Override
    public String execute(String input) {
        if (ragService == null) {
            return "知识库未启用,使用通用选购规则:看预算、用途、核心参数、售后和用户评价。";
        }
        String context = ragService.searchContext(input);
        return context == null ? "知识库暂无相关选购指南" : context;
    }
}

@Autowired(required = false) 是关键:即使将来拆掉 RAG 模块,Agent 也能降级为 "通用规则",不会崩。

6.6 ShoppingListTool(购物清单)

为 "搬新家配厨房" 这类多商品场景预留:

复制代码
@Component
public class ShoppingListTool implements ShoppingTool {
    @Override public String name() { return "shopping_list"; }
    @Override public String description() { return "根据预算和场景生成多商品购物清单"; }
    @Override public boolean supports(String toolName) { return name().equalsIgnoreCase(toolName); }
    @Override public String execute(String input) {
        return "根据场景生成购物清单,控制总价不超过预算。用户需求:" + input;
    }
}

七、服务层:需求提取 → 规划 → 执行 → 总控

7.1 ShoppingToolRegistry(工具注册中心 + 白名单)

Spring 自动把所有 ShoppingTool 注入成一个 List,再按 enabledTools 白名单过滤:

复制代码
@Component
public class ShoppingToolRegistry {

    private final List<ShoppingTool> tools;

    public ShoppingToolRegistry(List<ShoppingTool> tools) {
        this.tools = tools;
    }

    public ShoppingTool getTool(String toolName, List<String> enabledTools) {
        return tools.stream()
                .filter(t -> enabledTools == null || enabledTools.isEmpty() || enabledTools.contains(t.name()))
                .filter(t -> t.supports(toolName))
                .findFirst()
                .orElseThrow(() -> new IllegalArgumentException("Unsupported shopping tool: " + toolName));
    }
}

白名单为空 ⇒ 默认全开放;非空 ⇒ 只允许在列表里的工具。这层是 Agent 安全边界的第一道闸。

7.2 ShoppingRequirementService(规则式需求提取 + 澄清判断)

复制代码
@Service
public class ShoppingRequirementService {

    public ShoppingRequirement extract(String message) {
        return ShoppingRequirement.builder()
                .category(resolveCategory(message))
                .budgetMax(resolveBudgetMax(message))
                .usage(resolveUsage(message))
                .mustHave(resolveMustHave(message))
                .avoid(new ArrayList<>())
                .build();
    }

    public boolean needClarification(ShoppingRequirement r) {
        return r.getCategory() == null || r.getBudgetMax() == null || r.getUsage() == null;
    }

    public String buildClarificationQuestion(ShoppingRequirement r) {
        List<String> qs = new ArrayList<>();
        if (r.getCategory() == null) qs.add("你想买哪一类商品");
        if (r.getBudgetMax() == null) qs.add("预算大概是多少");
        if (r.getUsage() == null) qs.add("主要使用场景是什么");
        return String.join("?", qs) + "?";
    }

    // 规则式提取(关键词命中)
    private String resolveCategory(String msg) {
        if (msg == null) return null;
        if (msg.contains("手机")) return "手机";
        if (msg.contains("电脑") || msg.contains("笔记本")) return "电脑";
        if (msg.contains("厨房")) return "厨房用品";
        return null;
    }
    // resolveBudgetMax / resolveUsage / resolveMustHave 见仓库源码
}

学习提示:第一阶段用规则提取最容易调试;等链路跑通,再把这部分换成 "让 LLM 输出 JSON",能立刻支持任意品类。

7.3 ShoppingPlannerService(生成 3 步固定计划)

复制代码
@Service
public class ShoppingPlannerService {

    public ShoppingPlan createPlan(ShoppingRequirement r) {
        List<ShoppingStep> steps = new ArrayList<>();
        steps.add(ShoppingStep.builder()
                .index(1).description("搜索候选商品")
                .toolName("product_search").input(buildSearchInput(r))
                .status(ShoppingStepStatus.PENDING).build());
        steps.add(ShoppingStep.builder()
                .index(2).description("对比候选商品")
                .toolName("product_compare").input(buildSearchInput(r))
                .status(ShoppingStepStatus.PENDING).build());
        steps.add(ShoppingStep.builder()
                .index(3).description("检索选购指南")
                .toolName("shopping_guide").input(r.getCategory() + " 选购指南")
                .status(ShoppingStepStatus.PENDING).build());
        return ShoppingPlan.builder().requirement(r).steps(steps).build();
    }

    private String buildSearchInput(ShoppingRequirement r) {
        return r.getCategory() + " 预算" + r.getBudgetMax()
                + " 用途" + r.getUsage() + " 偏好" + r.getMustHave();
    }
}

7.4 ShoppingExecutorService(带 maxSteps 限制的执行循环)

复制代码
@Service
public class ShoppingExecutorService {

    private final ShoppingToolRegistry toolRegistry;

    public ShoppingExecutorService(ShoppingToolRegistry toolRegistry) {
        this.toolRegistry = toolRegistry;
    }

    public ShoppingPlan execute(ShoppingPlan plan, List<String> enabledTools, int maxSteps) {
        int executed = 0;
        for (ShoppingStep step : plan.getSteps()) {
            if (executed >= maxSteps) {
                step.setStatus(ShoppingStepStatus.SKIPPED);
                step.setObservation("超过最大执行步数,已跳过");
                continue;
            }
            try {
                step.setStatus(ShoppingStepStatus.RUNNING);
                ShoppingTool tool = toolRegistry.getTool(step.getToolName(), enabledTools);
                step.setObservation(tool.execute(step.getInput()));
                step.setStatus(ShoppingStepStatus.SUCCESS);
            } catch (Exception e) {
                step.setObservation(e.getMessage());
                step.setStatus(ShoppingStepStatus.FAILED);
            }
            executed++;
        }
        return plan;
    }
}

maxSteps 是 Agent 的保险丝:哪怕 LLM 出现循环规划,最多跑 N 步就停。

7.5 ShoppingAgentService(总控)

整个 Agent 流水线的串联在这里:

复制代码
@Service
public class ShoppingAgentService {

    private final ShoppingRequirementService requirementService;
    private final ShoppingPlannerService plannerService;
    private final ShoppingExecutorService executorService;
    private final ChatService chatService;

    public ShoppingAgentService(ShoppingRequirementService requirementService,
                                ShoppingPlannerService plannerService,
                                ShoppingExecutorService executorService,
                                ChatService chatService) {
        this.requirementService = requirementService;
        this.plannerService = plannerService;
        this.executorService = executorService;
        this.chatService = chatService;
    }

    public ShoppingAgentResponse run(ShoppingAgentRequest request) {
        // 1. 提取结构化需求
        ShoppingRequirement requirement = requirementService.extract(request.getMessage());

        // 2. 信息不足直接返回澄清
        if (requirementService.needClarification(requirement)) {
            return ShoppingAgentResponse.builder()
                    .needClarification(true)
                    .clarificationQuestion(requirementService.buildClarificationQuestion(requirement))
                    .requirements(requirement)
                    .products(List.of()).steps(List.of())
                    .build();
        }

        // 3. 规划 + 执行(带 maxSteps 上限)
        int maxSteps = request.getMaxSteps() == null ? 5 : Math.min(request.getMaxSteps(), 10);
        ShoppingPlan plan = plannerService.createPlan(requirement);
        ShoppingPlan executed = executorService.execute(plan, request.getEnabledTools(), maxSteps);

        // 4. 让 LLM 基于工具结果生成最终建议
        String answer = chatService.chat(
                buildRecommendationPrompt(request.getMessage(), executed),
                request.getModel());

        return ShoppingAgentResponse.builder()
                .needClarification(false).answer(answer)
                .requirements(requirement).products(List.of())
                .steps(toStepDtos(executed.getSteps()))
                .build();
    }

    private String buildRecommendationPrompt(String userMessage, ShoppingPlan plan) {
        StringBuilder b = new StringBuilder();
        b.append("你是一个电商购物助手 Agent。\n");
        b.append("请根据用户需求和工具执行结果,给出具体、克制、可比较的购买建议。\n");
        b.append("不要编造工具结果中不存在的商品价格、评分和参数。\n\n");
        b.append("用户原始需求:").append(userMessage).append("\n\n");
        b.append("结构化需求:").append(plan.getRequirement()).append("\n\n");
        b.append("工具执行结果:\n");
        for (ShoppingStep s : plan.getSteps()) {
            b.append(s.getIndex()).append(". ").append(s.getDescription())
                    .append("\n工具:").append(s.getToolName())
                    .append("\n状态:").append(s.getStatus())
                    .append("\n结果:").append(s.getObservation()).append("\n\n");
        }
        b.append("请输出:推荐商品、推荐理由、适合人群、注意事项。");
        return b.toString();
    }
    // toStepDtos 略
}

Prompt 工程要点:明确告诉模型 "只能基于工具结果回答,不准编造"。这是 Agent 防幻觉的最后一道闸门。


八、Controller 与 API

复制代码
@RestController
@RequestMapping("/api/agent/shopping")
public class ShoppingAgentController {

    private final ShoppingAgentService shoppingAgentService;

    public ShoppingAgentController(ShoppingAgentService shoppingAgentService) {
        this.shoppingAgentService = shoppingAgentService;
    }

    @PostMapping("/run")
    public ResponseEntity<ShoppingAgentResponse> run(@RequestBody ShoppingAgentRequest request) {
        return ResponseEntity.ok(shoppingAgentService.run(request));
    }
}

8.1 调用示例 1:信息齐全 → 返回推荐

复制代码
POST /api/agent/shopping/run
{
  "message": "我想买一款 3000 元以内的手机,主要拍照和续航好",
  "model": "qwen-plus",
  "maxSteps": 5,
  "enabledTools": ["product_search", "product_compare", "shopping_guide"]
}

响应:

复制代码
{
  "needClarification": false,
  "answer": "推荐 星河 X 手机 / 青云 Pro 手机 ...",
  "requirements": { "category": "手机", "budgetMax": 3000, "usage": "拍照、续航", "mustHave": ["拍照好","续航好"] },
  "steps": [
    { "index": 1, "toolName": "product_search",  "status": "SUCCESS", "observation": "候选商品:- p1001 ..." },
    { "index": 2, "toolName": "product_compare", "status": "SUCCESS", "observation": "对比维度:..." },
    { "index": 3, "toolName": "shopping_guide",  "status": "SUCCESS", "observation": "知识库未启用,通用规则:..." }
  ]
}

8.2 调用示例 2:信息不足 → 返回澄清问题

复制代码
POST /api/agent/shopping/run
{ "message": "帮我买一台电脑" }

响应:

复制代码
{
  "needClarification": true,
  "clarificationQuestion": "预算大概是多少?主要使用场景是什么?",
  "requirements": { "category": "电脑", "budgetMax": null, "usage": null }
}

九、问题点及调试

9.1 工具被多个实现 "抢走" 怎么办?

现象ShoppingToolRegistry.getTool("product_search", ...) 偶尔返回了错误的工具实例。

原因 :多个工具的 supports() 写错,把 equalsIgnoreCase 写成 contains,导致 product_search 也被 product_search_v2 命中。

调试

  • 单元测试每个 ShoppingTool.supports() 是否严格等值;
  • getTool() 里 log 命中的工具名 + 类名,肉眼检查。

9.2 LLM 总在最后回复里编造商品价格

现象:工具明明返回 "没有找到匹配商品",最终回复却出现了 "XX 手机 2999 元" 这种凭空冒出的商品。

原因:Prompt 不够强势,模型沿用训练知识在脑补。

修复 :在 buildRecommendationPrompt 中加严格约束:

复制代码
工具执行结果中如果出现"没有找到匹配商品",必须直接告诉用户没有找到,禁止编造。

并在测试用例里专门设计 "Mock 库为空" 的回归 case。

9.3 maxSteps 没生效,Agent 跑了 8 步

现象 :用户传了 maxSteps=3,但 Plan 里 3 步全跑完,第 4 步还想接着跑。

原因Math.min(maxSteps, 10) 写在了错的位置(在 plan 之前),导致后续动态扩展的 step 不受限。

修复 :把限制放在 Executor 内部循环里,按 executed >= maxSteps 判断(如代码 7.4 所示),而不是只限制 plan 长度。

9.4 ChatService.chat() 抛 NPE

现象:执行最后一步总结时 NPE。

原因request.getModel() 为 null,模型字段没传。

修复 :在 Controller 入口对 model 做默认值兜底(如取 application.properties 中配置的默认模型)。

9.5 RAG 知识库为空时 ShoppingGuideTool 报错

现象 :第三步 shopping_guide 报 "collection not found"。

原因 :项目没启用 Milvus,但 RagService 仍被注入,调用时报错。

修复 :用 @Autowired(required = false),并在 execute() 里判空降级为通用规则(已在 6.5 实现)。

9.6 前端拿到 steps 但 status 一直是 PENDING

现象:前端展示步骤列表,所有步骤状态都是 PENDING。

原因ShoppingExecutorService.execute() 没有把 status 写回到 step 对象。

修复 :确认 step.setStatus(...) 调用存在,并且返回的是同一个 Plan 引用。



十、本篇小结

  • Agent 不只是 "能调函数",而是一个 理解 → 澄清 → 规划 → 执行 → 总结 的闭环系统。
  • 电商购物 Agent 是最适合学习的场景,天然具备多步、多约束、多候选、需总结的特征。
  • 工具抽象 + 工具注册中心 + 白名单 + maxSteps 是 Agent 的四大安全边界。
  • 第一阶段用规则提取和固定 Plan,先把链路跑通再迭代,比上来就让 LLM 全自主规划稳得多。
  • 生产环境下,Agent 永远不做交易,只做推荐。
相关推荐
月落归舟1 小时前
深入解析Java基础之基础
java·开发语言
折哥的程序人生 · 物流技术专研1 小时前
《Java 100 天进阶之路》第20篇:Java初始化、构造器、对象创建的过程
java·开发语言·后端·面试
我也曾把你举过头顶1 小时前
Skill/MCP/RAG/Agent/OpenClaw是什么
人工智能·ai agent·mcp
南宫萧幕1 小时前
基于 Simulink 与 Python 联合仿真的 eVTOL 强化学习全链路实战
开发语言·人工智能·python·算法·机器学习·控制
电魂泡哥1 小时前
CMS垃圾回收
java·jvm·算法
HDD9851 小时前
2026年录音转文字工具实测:免费且好用的选择有哪些?
人工智能·语音识别·效率工具·语音转文字
跨境卫士苏苏1 小时前
经营变量持续增加之下跨境团队如何减少月度计划偏差
大数据·人工智能·内容运营·亚马逊·跨境
m0_466525291 小时前
东软添翼医疗大模型领跑 医疗AI进入“可信时代”
人工智能
美团技术团队1 小时前
美团 LongCat 开源 General 365:树立推理评测新标尺
人工智能