Spring AI 集成 Anthropic Skills:Agent 工具调用实践
如何在 Spring Boot 项目中集成 Spring AI,通过封装 Java DTO 和 Tool 来调用外部 Python 脚本实现的 Skill。利用 ChatClient 让 Agent 自动选择并执行数据分析或代码评审等任务,实现了 Java 与 Python 的跨语言协作及 Agent 功能扩展。

如何在 Spring Boot 项目中集成 Spring AI,通过封装 Java DTO 和 Tool 来调用外部 Python 脚本实现的 Skill。利用 ChatClient 让 Agent 自动选择并执行数据分析或代码评审等任务,实现了 Java 与 Python 的跨语言协作及 Agent 功能扩展。

可以把前面那两个 Skill(代码评审 / 内部数据分析)理解为「外部可执行能力」,再用 Spring AI 的 Function/Tool Calling 把它们挂到 Agent 上,让模型自己决定何时调用哪一个脚本,然后再基于脚本结果继续对话。
下面给一个端到端的集成示例,假设你用的是 Spring Boot + Spring AI + OpenAI/Mistral 等支持函数调用的模型。
SKILL.md + scripts/*.py,部署在应用服务器旁(或者挂载到容器)。runCodeReviewSkill、runDataAnalysisSkill)。ProcessBuilder 调 Python 脚本,并把结果(评审报告 / 分析报告)作为字符串返回给模型。ChatClient 中声明这些函数为可调用工具,让 LLM 自己选用。下面示例以「内部数据分析 Skill」为主,同时顺带演示代码评审 Skill 的挂载方式。
// DataAnalysisSkillRequest.java
public record DataAnalysisSkillRequest(String analysisRequest, String constraints) {}
// DataAnalysisSkillResponse.java
public record DataAnalysisSkillResponse(String reportText) {}
// CodeReviewSkillRequest.java
public record CodeReviewSkillRequest(String diffOrFiles, String context) {}
// CodeReviewSkillResponse.java
public record CodeReviewSkillResponse(String reviewReport) {}
Spring AI 会用这些类型自动生成函数的 JSON Schema,帮助 LLM 正确构造调用参数。
这里用「内部数据分析 Skill」举例,调用的是 run_query.py + clean_and_aggregate.py,然后再调用一个「汇总为报告」的小脚本,或者直接把 CSV 路径返回给 LLM 让它读内容(取决于你怎么暴露数据)。示例里用最简单的方式:把「分析意图」传给一个 Python wrapper 脚本,由它内部使用前面那两个脚本并生成最终报告文本。
package com.example.ai.tools;
import org.springframework.stereotype.Service;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
@Service
public class DataAnalysisSkillTool {
// 假设你的 Skill 目录在服务器上的路径
private static final String SKILL_BASE_DIR = "/opt/skills/internal-data-analysis-skill";
public DataAnalysisSkillResponse run(DataAnalysisSkillRequest request) {
try {
// 这里调用一个 Python wrapper,例如 scripts/run_analysis_skill.py
ProcessBuilder pb = new ProcessBuilder(
"python",
SKILL_BASE_DIR + "/scripts/run_analysis_skill.py",
"--request", request.analysisRequest(),
"--constraints", request.constraints() == null ? "" : request.constraints()
);
pb.redirectErrorStream(true);
Process process = pb.start();
StringBuilder output = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
output.append(line).append("\n");
}
}
int exitCode = process.waitFor();
if (exitCode != 0) {
return new DataAnalysisSkillResponse("分析脚本执行失败,退出码:" + exitCode + ",输出:\n" + output);
}
return new DataAnalysisSkillResponse(output.toString());
} catch (Exception e) {
return new DataAnalysisSkillResponse("执行内部数据分析 Skill 时出现异常:" + e.getMessage());
}
}
}
代码评审 Skill 同理:调用 code-review-skill/scripts/... 并返回报告文本即可。
Spring AI 推荐通过 @Bean Function<Req,Resp> 或 @Tool 暴露工具,让 ChatClient / ChatModel 知道有哪些函数可用。
package com.example.ai.config;
import com.example.ai.tools.DataAnalysisSkillTool;
import com.example.ai.tools.CodeReviewSkillTool;
import com.example.ai.tools.dto.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.function.Function;
@Configuration(proxyBeanMethods = false)
public class SkillsConfig {
@Bean
public Function<DataAnalysisSkillRequest, DataAnalysisSkillResponse> internalDataAnalysis(DataAnalysisSkillTool tool) {
return tool::run; // 函数名 internalDataAnalysis = Skill 名
}
@Bean
public Function<CodeReviewSkillRequest, CodeReviewSkillResponse> codeReviewAssistant(CodeReviewSkillTool tool) {
return tool::run;
}
}
在 Spring AI 里,这两个 Bean 名(internalDataAnalysis / codeReviewAssistant)就是 LLM 能看到的工具名,你可以在 prompt 中让模型了解它们的用途,也可以只靠 JSON Schema 自动推断。
package com.example.ai.agent;
import com.example.ai.tools.dto.DataAnalysisSkillRequest;
import com.example.ai.tools.dto.DataAnalysisSkillResponse;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.stereotype.Service;
@Service
public class DataAnalysisAgentService {
private final ChatClient chatClient;
public DataAnalysisAgentService(ChatClient.Builder chatClientBuilder) {
this.chatClient = chatClientBuilder.build();
}
public String analyze(String userQuestion, String constraints) {
var sysPrompt = """
你是公司内部的数据分析助手。
你可以使用名为 internalDataAnalysis 的工具,它会基于数据仓库执行查询和分析,并返回一份结构化的中文报告草稿。
当用户的问题涉及具体的数据指标、时间范围、渠道/地区对比时,应优先调用该工具,而不是凭空猜测。
""";
// 这里不直接 new DataAnalysisSkillRequest,而是让 LLM 自己构造参数并调用工具
return chatClient
.prompt()
.system(sysPrompt)
.user(userSpec -> userSpec
.text("用户问题:{q}\n约束条件(可为空):{c}")
.param("q", userQuestion)
.param("c", constraints == null ? "" : constraints))
.functions("internalDataAnalysis")
.call()
.content(); // 最终答案:模型在工具结果基础上生成的回答
}
}
模型流程示意:
internalDataAnalysis。DataAnalysisSkillRequest 的 JSON 入参。package com.example.ai.web;
import com.example.ai.agent.DataAnalysisAgentService;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/analysis")
public class DataAnalysisController {
private final DataAnalysisAgentService agentService;
public DataAnalysisController(DataAnalysisAgentService agentService) {
this.agentService = agentService;
}
@PostMapping
public String analyze(@RequestParam("question") String question,
@RequestParam(value = "constraints", required = false) String constraints) {
return agentService.analyze(question, constraints);
}
}
上面的 Java Tool 调用了一个假想的 run_analysis_skill.py,它可以作为 Skill 的统一入口,内部再去调用 run_query.py 和 clean_and_aggregate.py,并基于结果拼出完整报告文本(按照你在 SKILL.md 中定义的结构)。
伪代码示例:
# internal-data-analysis-skill/scripts/run_analysis_skill.py
import argparse
import textwrap
# 这里导入或复用 run_query.py / clean_and_aggregate.py 中的逻辑
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--request", required=True)
parser.add_argument("--constraints", default="")
args = parser.parse_args()
# 1. 解析 request / constraints -> 决定查询模板
# 2. 生成 SQL,调用 run_query.py 获得 result.csv
# 3. 调用 clean_and_aggregate.py 做清洗与聚合
# 4. 按 SKILL.md 中的报告结构拼出一份文本,print 到 stdout
report = textwrap.dedent(f"""
内部数据分析报告(示例)
一、分析诉求 - {args.request}
- 约束条件:{args.constraints or "无"}
二、关键指标(示例说明)
- ...
三、主要发现
- ...
四、风险与局限
- ...
五、后续建议
- ...
""").strip()
print(report)
if __name__ == "__main__":
main()
这样有三个好处:
SKILL.md 里完成,不影响 Java 侧接口。代码评审 Skill 的集成方式完全相同,只是:
CodeReviewSkillRequest/Response;code-review-skill/scripts/...;codeReviewAssistant 工具……」。Spring AI 会自动把两个工具的 Schema 一起发给模型,这样一个更通用的「工程生产力 Agent」就可以根据用户问题自动选择是「查数据」还是「审代码」了。
下一步可以一起细化两点:
application.yml 中 Spring AI 的模型配置和函数调用选项。
微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online