Spring AI 生产级实战:工具调用

一、为什么需要工具调用?

大模型很强,但它并不是万能的。

在普通问答场景中,我们可以直接问模型:

text 复制代码
请解释一下什么是 Spring AI。
请总结一下这份医学影像报告。
请帮我生成一段产品介绍。

这类问题主要依赖模型已有知识和语言生成能力。

但是,一旦进入真实业务系统,我们很快会遇到另外一类问题:

text 复制代码
今天的天气怎么样?
当前订单状态是什么?
这位患者最近一次检查结果是什么?
请帮我创建一个质控任务。
请查询数据库中某个病例的历史报告。
请调用系统接口生成一条工单。

这类问题只靠模型自己是无法完成的。

原因很简单:

text 复制代码
模型不知道实时数据;
模型不能直接访问数据库;
模型不能直接调用业务系统;
模型不能替代权限校验;
模型不能自己执行真实操作。

这时就需要工具调用。

工具调用的核心思想是:

text 复制代码
模型负责理解用户意图和生成调用参数;
应用程序负责执行真实工具或业务接口;
工具结果再交给模型生成最终回答。

也就是说,工具调用把大模型从"只会回答问题"扩展成了"可以协助完成任务"。

对于生产级 AI 应用来说,这是从聊天机器人走向业务 Agent 的关键能力。


二、什么是 Tool Calling?

Tool Calling,也叫 Function Calling。

在 Spring AI 中,可以把 Tool 理解成应用系统暴露给模型使用的一组能力。

这些能力可以是:

text 复制代码
查询数据库;
调用 HTTP 接口;
访问文件系统;
调用搜索服务;
创建业务记录;
发送消息;
触发工作流;
查询用户权限;
调用第三方系统。

例如,一个医学影像报告质控系统中,可以定义这些工具:

text 复制代码
查询患者基本信息;
查询历史检查;
查询报告模板;
查询质控规则;
创建质控任务;
保存质控结果;
推送人工审核;
查询 AI 检测结果。

模型本身不会直接访问这些系统。

模型只会判断:

text 复制代码
我现在需要调用哪个工具?
这个工具需要哪些参数?
工具返回后,我该如何组织最终回答?

真正执行工具调用的是 Spring 应用。

这点非常重要。

因为在生产系统中,权限、安全、审计、事务、异常处理都必须由应用系统掌控,不能交给模型自由执行。


三、工具调用主要解决两类问题

Spring AI 官方文档中提到,工具主要用于两类场景:信息检索和执行动作。

1. 信息检索类工具

这类工具用于获取模型本身不知道的信息。

例如:

text 复制代码
查询当前时间;
查询天气;
查询数据库记录;
查询文件内容;
查询知识库;
查询患者历史检查;
查询订单状态;
查询用户权限。

比如用户问:

text 复制代码
明天是星期几?

如果模型没有当前时间,它就无法准确回答。

这时可以提供一个工具:

java 复制代码
class DateTimeTools {

    @Tool(description = "获取用户所在时区的当前日期和时间")
    String getCurrentDateTime() {
        return LocalDateTime.now()
                .atZone(LocaleContextHolder.getTimeZone().toZoneId())
                .toString();
    }
}

然后在调用模型时提供这个工具:

java 复制代码
String response = ChatClient.create(chatModel)
        .prompt("明天是星期几?")
        .tools(new DateTimeTools())
        .call()
        .content();

模型发现自己需要当前时间,就会请求调用 getCurrentDateTime 工具。

工具返回当前时间后,模型再基于这个结果回答用户。


2. 执行动作类工具

这类工具用于让系统执行某个真实操作。

例如:

text 复制代码
创建工单;
发送邮件;
设置提醒;
提交表单;
创建数据库记录;
触发审核流程;
生成报告任务;
推送通知。

比如用户说:

text 复制代码
帮我创建一个报告质控任务。

模型可以理解用户意图,但它不能自己写数据库。

所以我们可以提供一个工具:

java 复制代码
class QcTaskTools {

    private final QcTaskService qcTaskService;

    QcTaskTools(QcTaskService qcTaskService) {
        this.qcTaskService = qcTaskService;
    }

    @Tool(description = "根据报告ID创建医学影像报告质控任务")
    QcTaskResult createQcTask(
            @ToolParam(description = "报告ID") Long reportId) {
        return qcTaskService.createTask(reportId);
    }
}

这样,模型可以根据用户请求生成 reportId 参数,由应用执行创建任务逻辑。

执行完成后,工具结果再返回给模型,模型再告诉用户:

text 复制代码
已为该报告创建质控任务,任务编号为 QC-20260603-001。

四、工具调用的整体流程

工具调用的流程可以理解为六步:

text 复制代码
1. 应用把可用工具定义发送给模型;
2. 模型判断是否需要调用工具;
3. 如果需要,模型返回工具名称和参数;
4. Spring 应用根据工具名称找到对应 ToolCallback;
5. 应用执行工具,并获得工具结果;
6. 工具结果返回给模型,模型生成最终回答。

也可以用更工程化的方式理解:

text 复制代码
用户问题
  ↓
ChatClient + Tools
  ↓
模型判断需要调用工具
  ↓
返回 toolName + arguments
  ↓
Spring AI 执行 ToolCallback
  ↓
工具结果返回模型
  ↓
模型生成最终回答

这里要特别注意:

text 复制代码
模型只是发起工具调用请求;
工具执行权始终在应用程序手里。

这也是工具调用在生产环境中可控的基础。


五、Spring AI 中的核心概念

Spring AI 的工具调用主要涉及几个核心概念。

1. Tool

Tool 是暴露给模型使用的能力。

在代码中,可以是一个普通 Java 方法,也可以是函数式对象,最终都会被封装为 ToolCallback。

最常见的方式是使用 @Tool 注解。

java 复制代码
class DateTimeTools {

    @Tool(description = "获取当前日期和时间")
    String getCurrentDateTime() {
        return LocalDateTime.now().toString();
    }
}

2. ToolCallback

ToolCallback 是 Spring AI 对工具的统一抽象。

不管底层工具来自:

text 复制代码
Java 方法;
Function;
Supplier;
Consumer;
自定义实现;
Spring Bean;
动态解析器。

最终都可以被抽象成 ToolCallback,供模型调用。

3. ToolDefinition

ToolDefinition 用来描述工具。

它通常包括:

text 复制代码
工具名称;
工具描述;
输入参数 Schema。

模型依赖这些信息判断工具该不该调用,以及如何构造参数。

4. ToolCallingManager

ToolCallingManager 负责管理工具执行生命周期。

它的职责包括:

text 复制代码
解析工具定义;
判断工具调用请求;
查找工具;
执行工具;
处理工具返回结果;
处理工具异常。

如果使用 Spring AI 默认配置,大多数情况下不需要手动操作 ToolCallingManager

但在复杂 Agent 或需要自定义执行流程时,可以进一步扩展它。


六、使用 @Tool 声明工具

最简单的方式是使用 @Tool 注解。

例如:

java 复制代码
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;

class PatientTools {

    private final PatientService patientService;

    PatientTools(PatientService patientService) {
        this.patientService = patientService;
    }

    @Tool(description = "根据患者ID查询患者基本信息")
    PatientInfo getPatientInfo(
            @ToolParam(description = "患者ID") Long patientId) {
        return patientService.findById(patientId);
    }
}

这里有两个关键点。

1. @Tool 的 description 很重要

description 不是给开发者看的,而是给模型看的。

模型会根据 description 判断:

text 复制代码
这个工具是干什么的;
什么时候应该调用;
调用前需要哪些条件;
返回结果可以用于什么。

所以,工具描述不能太随意。

不建议这样写:

java 复制代码
@Tool(description = "查询")

更推荐写成:

java 复制代码
@Tool(description = "根据患者ID查询患者基本信息,包括姓名、性别、年龄和患者编号")

描述越清楚,模型越不容易误用工具。


2. @ToolParam 要说明参数含义

工具参数也要写清楚。

例如:

java 复制代码
@Tool(description = "根据检查ID查询医学影像检查信息")
StudyInfo getStudyInfo(
        @ToolParam(description = "检查ID,来自业务系统中的 studyId") Long studyId) {
    return studyService.findById(studyId);
}

如果参数格式有要求,也要写明。

例如:

java 复制代码
@Tool(description = "设置用户提醒")
void setReminder(
        @ToolParam(description = "提醒时间,格式为 ISO-8601,例如 2026-06-03T10:30:00")
        String time) {
    reminderService.setReminder(LocalDateTime.parse(time));
}

对于模型来说,参数说明越明确,生成正确参数的概率越高。


七、把工具提供给 ChatClient

定义好工具后,可以在调用 ChatClient 时传入:

java 复制代码
String response = ChatClient.create(chatModel)
        .prompt("请查询患者 1001 的基本信息")
        .tools(new PatientTools(patientService))
        .call()
        .content();

这种方式表示:

text 复制代码
这个工具只在当前请求中可用。

这通常是更安全的做法。

因为不是所有工具都应该在所有场景中暴露给模型。

例如:

text 复制代码
查询工具可以开放给某些问答场景;
写入工具只应该开放给明确授权的操作场景;
删除工具一般不建议直接暴露;
高风险工具必须增加人工确认。

八、defaultTools:默认工具要谨慎使用

Spring AI 也支持在 ChatClient.Builder 中配置默认工具:

java 复制代码
ChatClient chatClient = ChatClient.builder(chatModel)
        .defaultTools(new DateTimeTools())
        .build();

这样,所有基于这个 ChatClient 的请求都可以使用这些工具。

这对通用工具很方便,例如:

text 复制代码
获取当前时间;
查询当前用户语言;
查询系统帮助文档;
查询公开知识库。

但默认工具也有风险。

如果把高权限工具设置成 defaultTools,例如:

text 复制代码
删除数据;
修改订单;
创建付款;
发送外部消息;
变更患者报告状态。

模型可能在不合适的场景下调用它们。

所以生产环境建议:

text 复制代码
低风险、通用、只读工具可以考虑 defaultTools;
高风险、写操作、强业务权限工具应按请求动态传入;
涉及删除、提交、付款、审批等动作时应增加二次确认。

九、工具调用不等于让模型拥有权限

这是生产级工具调用最重要的原则之一。

模型不能替代权限系统。

例如用户说:

text 复制代码
帮我删除这个患者的检查记录。

即使模型判断应该调用删除工具,也不能直接执行。

应用必须先做权限判断:

java 复制代码
@Tool(description = "删除指定检查记录")
DeleteResult deleteStudy(
        @ToolParam(description = "检查ID") Long studyId,
        ToolContext toolContext) {

    Long userId = (Long) toolContext.getContext().get("userId");

    if (!permissionService.canDeleteStudy(userId, studyId)) {
        throw new SecurityException("当前用户无权删除该检查记录");
    }

    return studyService.delete(studyId);
}

这里模型只是辅助理解用户意图。

真正的权限判断必须由业务系统完成。

换句话说:

text 复制代码
模型可以建议调用工具;
但系统必须决定是否允许调用工具。

十、ToolContext:给工具传递上下文

有些信息不应该让模型看到,但工具执行时又需要用到。

例如:

text 复制代码
tenantId;
userId;
角色信息;
登录机构;
数据权限范围;
当前业务对象ID;
请求来源。

这些信息适合通过 ToolContext 传给工具。

示例:

java 复制代码
class CustomerTools {

    @Tool(description = "查询客户信息")
    Customer getCustomerInfo(Long id, ToolContext toolContext) {
        String tenantId = (String) toolContext.getContext().get("tenantId");
        return customerRepository.findById(id, tenantId);
    }
}

调用时传入上下文:

java 复制代码
String response = ChatClient.create(chatModel)
        .prompt("请查询客户 42 的信息")
        .tools(new CustomerTools())
        .toolContext(Map.of("tenantId", "acme"))
        .call()
        .content();

这样有两个好处:

text 复制代码
模型不需要知道 tenantId 等敏感上下文;
工具执行时仍然可以使用这些上下文做权限和数据隔离。

在多租户系统中,ToolContext 非常重要。


十一、Return Direct:工具结果是否直接返回?

默认情况下,工具执行结果会先返回给模型,模型再结合结果生成最终回答。

流程是:

text 复制代码
工具执行结果
  ↓
返回模型
  ↓
模型加工
  ↓
返回用户

但有些场景不需要模型再次加工。

例如:

text 复制代码
工具已经返回标准 JSON;
工具返回的是文件下载地址;
工具返回的是固定格式业务结果;
工具结果应该直接交给前端处理。

这时可以使用 returnDirect = true

java 复制代码
class CustomerTools {

    @Tool(description = "查询客户信息", returnDirect = true)
    Customer getCustomerInfo(Long id) {
        return customerRepository.findById(id);
    }
}

这样工具结果会直接返回给调用方,而不是再交给模型生成回答。

生产环境中可以这样选择:

text 复制代码
需要自然语言解释:返回给模型再生成回答;
需要标准业务数据:returnDirect;
需要前端直接渲染:returnDirect;
需要防止模型篡改工具结果:returnDirect。

十二、工具返回结果要可序列化

工具方法可以返回对象,但返回结果最终需要转换成字符串,发送给模型或调用方。

因此,工具返回对象应尽量简单、稳定、可序列化。

例如:

java 复制代码
public record PatientInfo(
        Long patientId,
        String name,
        String gender,
        Integer age
) {
}

不建议直接返回复杂的 ORM 实体对象。

例如:

java 复制代码
PatientEntity
  ├── studies
  ├── reports
  ├── images
  ├── hospital
  ├── doctors
  └── ...

这样容易带来几个问题:

text 复制代码
返回内容过大;
包含敏感字段;
循环引用导致序列化失败;
模型接收到大量无关信息;
Token 成本过高。

建议为工具调用专门设计 DTO。

原则是:

text 复制代码
只返回模型完成当前任务所需的最小信息。

十三、工具调用和 RAG 的区别

工具调用和 RAG 经常一起出现,但它们不是一回事。

能力 核心作用 典型场景
RAG 从知识库检索上下文 根据文档回答问题
Tool Calling 调用外部函数或业务能力 查数据库、调接口、执行业务动作

例如医学影像报告质控系统:

text 复制代码
RAG:
检索"胸部CT报告质控规则"。

Tool Calling:
查询患者性别、检查部位、历史检查、报告状态。

可以这样组合:

text 复制代码
用户提交报告
  ↓
RAG 检索相关质控规则
  ↓
Tool 查询患者信息和检查信息
  ↓
Chat Model 综合分析
  ↓
结构化输出质控结果
  ↓
Tool 保存质控结果或创建复核任务

RAG 解决的是"模型依据什么知识回答"。

工具调用解决的是"模型如何访问或操作业务系统"。


十四、工具调用和结构化输出的关系

工具调用用于让模型调用外部能力。

结构化输出用于让模型返回可解析结果。

二者经常组合使用。

例如:

text 复制代码
工具调用:查询患者信息、报告信息、历史检查。
结构化输出:返回风险等级、问题列表、复核建议。

示例对象:

java 复制代码
public record ReportQcResult(
        Boolean passed,
        String riskLevel,
        List<QcProblem> problems,
        String suggestion
) {
}

public record QcProblem(
        String type,
        String description,
        String evidence
) {
}

调用过程可以是:

java 复制代码
ReportQcResult result = chatClient.prompt()
        .user("""
              请结合患者信息、检查信息和报告内容进行质控分析。
              如果缺少患者信息,可以调用工具查询。
              """)
        .tools(new PatientTools(patientService),
               new StudyTools(studyService))
        .call()
        .entity(ReportQcResult.class);

这样系统既可以让模型使用工具获取必要信息,又能让最终结果以结构化对象返回。

这就是生产级 AI 系统常见的组合方式。


十五、框架托管执行和用户控制执行

Spring AI 支持两种工具执行方式。

1. 框架托管执行

这是默认方式。

也就是:

text 复制代码
模型请求调用工具;
Spring AI 自动执行工具;
工具结果自动返回模型;
模型生成最终结果。

大多数场景使用这种方式就够了。

它的优点是:

text 复制代码
代码简单;
开发效率高;
适合普通工具调用;
适合快速集成业务系统。

2. 用户控制执行

如果你需要完全控制工具调用过程,可以关闭内部工具执行。

例如:

java 复制代码
ChatOptions chatOptions = ToolCallingChatOptions.builder()
        .toolCallbacks(ToolCallbacks.from(new MathTools()))
        .internalToolExecutionEnabled(false)
        .build();

这种方式适合复杂场景:

text 复制代码
需要记录每一步工具调用;
需要人工审批后再执行;
需要多 Agent 协同;
需要自定义工具调用循环;
需要把工具调用过程写入 Chat Memory;
需要对工具参数做二次校验。

简单说:

text 复制代码
普通业务用框架托管;
复杂 Agent 用用户控制。

十六、ToolCallAdvisor:把工具调用纳入 Advisor 链

Spring AI 还支持通过 ToolCallAdvisor 实现工具调用。

它的价值在于可以把工具调用放进 Advisor 链中,与其他能力协同。

例如:

text 复制代码
Chat Memory;
RAG Advisor;
日志记录;
权限上下文;
工具调用;
观测追踪。

这种方式更适合复杂生产系统。

因为在真实业务中,工具调用往往不是孤立动作。

例如医学影像报告质控 Agent:

text 复制代码
读取记忆上下文;
检索质控规则;
查询患者信息;
调用工具;
记录执行日志;
生成结构化结果;
保存审计记录。

这些步骤如果都能进入统一的 Advisor 链,系统会更容易治理和扩展。


十七、异常处理:工具失败时怎么办?

工具调用一定会失败。

例如:

text 复制代码
数据库连接失败;
接口超时;
权限不足;
参数不合法;
业务数据不存在;
第三方服务不可用。

Spring AI 中,工具执行异常可以由 ToolExecutionExceptionProcessor 处理。

生产环境中需要提前设计失败策略。

1. 查询类工具失败

例如查询患者信息失败,可以返回:

text 复制代码
未能查询到患者信息,请稍后重试或转人工处理。

也可以让模型基于错误信息继续回答:

text 复制代码
当前无法获取患者信息,因此不能完成完整质控分析。

2. 写操作工具失败

例如创建质控任务失败,不应该让模型假装成功。

必须明确返回失败:

text 复制代码
质控任务创建失败,原因:当前用户无权限或服务异常。

3. 高风险工具失败

例如涉及报告状态变更、审核提交、删除数据,建议直接抛异常并进入人工处理。

不要让模型自行补救。


十八、生产级安全建议

工具调用是 AI 应用中最需要重视安全的部分。

因为一旦工具可以写数据库、调接口、发消息,模型就从"生成内容"进入了"影响真实系统"的阶段。

1. 工具最小暴露

不要把所有 Service 方法都暴露给模型。

应该只暴露必要工具:

text 复制代码
只读工具优先;
写操作谨慎;
删除操作尽量不暴露;
高风险动作必须审批。

2. 工具内部必须做权限校验

不能因为工具是模型调用的,就绕过权限。

每个工具都应该像普通业务接口一样校验:

text 复制代码
用户身份;
租户范围;
角色权限;
数据权限;
操作权限。

3. 参数必须二次校验

模型生成的参数不一定可靠。

工具执行前必须校验:

text 复制代码
参数是否为空;
格式是否合法;
枚举值是否正确;
ID 是否存在;
当前用户是否有权访问该 ID。

4. 高风险动作要二次确认

例如:

text 复制代码
删除数据;
提交审核;
发送消息;
修改报告;
创建付款;
变更状态。

这类动作不建议模型直接执行。

更稳妥的流程是:

text 复制代码
模型生成操作建议
  ↓
系统展示待确认内容
  ↓
用户确认
  ↓
后端执行工具
  ↓
记录审计日志

5. 保留审计日志

工具调用必须记录:

text 复制代码
谁发起;
什么时候发起;
调用了哪个工具;
工具参数是什么;
工具返回什么;
是否成功;
失败原因是什么;
模型最终回答是什么。

对于医疗、金融、政务、企业管理系统,这一点非常关键。


十九、在医学影像报告质控中的实战设计

假设我们要开发一个医学影像报告质控助手。

用户输入:

text 复制代码
请检查这份报告是否存在性别逻辑问题和图文不一致问题。

系统可以提供以下工具:

java 复制代码
class ReportQcTools {

    @Tool(description = "根据报告ID查询报告内容")
    ReportInfo getReportInfo(
            @ToolParam(description = "报告ID") Long reportId) {
        return reportService.findReportInfo(reportId);
    }

    @Tool(description = "根据患者ID查询患者基本信息")
    PatientInfo getPatientInfo(
            @ToolParam(description = "患者ID") Long patientId) {
        return patientService.findPatientInfo(patientId);
    }

    @Tool(description = "根据检查ID查询检查部位和检查类型")
    StudyInfo getStudyInfo(
            @ToolParam(description = "检查ID") Long studyId) {
        return studyService.findStudyInfo(studyId);
    }

    @Tool(description = "保存报告质控结果")
    SaveQcResult saveQcResult(QcResultRequest request) {
        return qcResultService.save(request);
    }
}

调用方式:

java 复制代码
ReportQcResult result = chatClient.prompt()
        .user("""
              请对报告进行质控分析。
              必要时调用工具查询报告、患者和检查信息。
              分析完成后返回结构化质控结果。
              """)
        .tools(new ReportQcTools())
        .call()
        .entity(ReportQcResult.class);

这个流程中,模型可以:

text 复制代码
理解用户质控意图;
决定需要查询患者信息;
决定需要查询报告内容;
结合工具结果进行推理;
返回质控问题和建议。

但系统仍然要负责:

text 复制代码
权限控制;
数据脱敏;
工具参数校验;
工具结果过滤;
质控结果落库;
人工复核;
审计追踪。

这样才是生产级工具调用,而不是简单 Demo。


二十、常见误区

误区一:工具调用就是让模型直接调接口

不是。

模型不会直接调接口。

模型只会发出工具调用请求,真正执行的是应用程序。

误区二:工具越多越好

不是。

工具越多,模型越容易选错工具,也会增加安全风险。

应该按场景暴露工具,而不是全量暴露。

误区三:工具描述随便写也可以

不建议。

工具描述越模糊,模型越容易误用。

工具描述应该清楚说明:

text 复制代码
工具用途;
调用条件;
参数含义;
返回内容;
限制边界。

误区四:模型调用工具后就可以直接相信结果

不能。

工具结果是真实数据,但模型对工具结果的解释仍然可能出错。

关键业务仍然需要结构化校验、规则校验和人工复核。

误区五:Tool Calling 可以替代业务流程

不能。

工具调用只是让模型参与流程编排。

业务流程、权限体系、事务一致性和审计机制仍然属于应用系统。


二十一、生产级最佳实践总结

1. 按场景暴露工具

不同业务场景提供不同工具,不要把所有工具默认开放给模型。

2. 工具描述要清晰

@Tool(description = "...")@ToolParam(description = "...") 要认真写。

它们直接影响模型是否正确调用工具。

3. 工具参数要校验

模型生成的参数不能直接信任。

业务系统必须做二次校验。

4. 权限控制不能省略

工具内部必须做用户权限、租户隔离和数据范围控制。

5. 写操作要谨慎

创建、修改、删除、提交、发送等动作要增加确认和审计。

6. 工具结果要最小化

只返回当前任务需要的信息,避免暴露敏感字段和过大数据。

7. 保留完整调用日志

工具调用是生产级 AI 系统的重要审计对象。

8. 与 Memory、RAG、结构化输出组合使用

工具调用不是孤立能力。

更成熟的 AI 应用通常是:

text 复制代码
Chat Memory 保持上下文;
RAG 提供知识依据;
Tool Calling 获取或操作业务数据;
Structured Output 返回可解析结果;
Observability 负责追踪和监控。

二十二、总结

Spring AI 的工具调用,是构建生产级 AI 应用和 Agent 系统的关键能力。

它让大模型不再只是回答问题,而是可以在受控范围内使用外部工具,完成查询、检索、创建任务、触发流程等操作。

但工具调用的本质不是"让模型拥有系统权限",而是:

text 复制代码
让模型提出工具调用意图;
让应用系统执行真实业务能力;
让业务系统控制权限、安全、事务和审计。

在生产实践中,我们要始终记住:

text 复制代码
模型负责理解和推理;
工具负责连接外部能力;
应用负责安全和治理;
人负责高风险决策确认。

只有这样,工具调用才能真正从 Demo 走向可靠、可控、可维护的生产系统。

相关推荐
刀法如飞1 小时前
AI时代:DDD领域驱动建模与Ontology语义建模的区别
java·设计模式·架构
阿乔外贸日记1 小时前
2026尼日利亚五项清关政策更新,拉高能源装备进口综合成本
大数据·人工智能·搜索引擎·智能手机·云计算·能源
比昨天多敲两行1 小时前
linux 线程概念与控制
java·开发语言·jvm
8Qi81 小时前
LeetCode 75:颜色分类(荷兰国旗问题)—— Java 题解 ✅
java·算法·leetcode·指针·排序
民乐团扒谱机1 小时前
【AI笔记】短时纯音时长对音高感知偏移效应研究综述
人工智能·笔记
zzhongcy2 小时前
@Transactional 同类内部调用失效 + 两种自代理解决方案
java
侃谈科技圈2 小时前
破除数据中台落地困境:2026数据治理平台差异化能力与选型决策指南
大数据·人工智能
大象说2 小时前
Python多进程共享队列无报错僵死 120G Nginx访问日志清洗踩坑全记录
人工智能·自然语言处理
Cosolar2 小时前
AutoGen 精通教程:从零到企业级多 Agent 系统架构师
人工智能·后端·面试