Spring AI 与 LangChain4J 如何调用 Skill 全面指南
一、Skill的定义与标准
1.1 什么是Skill
Skill(技能)是可重用的功能单元,通常封装为:
- 函数/方法:带输入输出的可执行代码
- API接口:HTTP RESTful服务
- 工具:特定能力(搜索、计算、数据库查询)
- 插件:第三方服务集成
Skill的核心特征:
- 单一职责:一个Skill完成一个明确任务
- 标准化接口:输入参数、输出格式、错误处理
- 可发现性:元数据描述(名称、描述、参数)
- 可组合性:多个Skill可串联成复杂工作流
1.2 Skill的标准格式
1.2.1 OpenAI Function Calling 格式(事实标准)
{
"name": "get_weather",
"description": "Get the current weather in a given location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "Temperature unit"
}
},
"required": ["location"]
}
}
1.2.2 OpenAPI/Swagger 格式
paths:
/weather:
get:
summary: Get weather
parameters:
- name: location
in: query
required: true
schema:
type: string
1.2.3 自定义Schema格式
{
"id": "skill-001",
"name": "Calculator",
"version": "1.0",
"description": "Perform mathematical calculations",
"input_schema": {...},
"output_schema": {...},
"endpoint": "http://api.example.com/calc"
}
二、Spring AI 调用 Skill
2.1 @Tool 注解方式(推荐)
Spring AI 0.8.0+ 支持 @Tool 注解,将Java方法暴露为Tool:
import org.springframework.ai.function.parameter.JsonSchemaGenerator;
import org.springframework.ai.tool.function.FunctionToolCallback;
import org.springframework.ai.tool.ToolDefinition;
@Service
public class WeatherService {
@Tool(description = "Get current weather for a city")
public Weather getWeather(
@ToolParam(description = "City name, e.g. San Francisco") String city,
@ToolParam(description = "Temperature unit: celsius or fahrenheit",
defaultValue = "celsius") String unit
) {
// 调用实际天气API
return weatherClient.getWeather(city, unit);
}
}
// 注册到ChatClient
@Configuration
public class AiConfig {
@Bean
public ChatClient chatClient(OpenAiChatModel chatModel, WeatherService weatherService) {
// 自动扫描@Tool方法,生成FunctionDefinition
List<FunctionToolCallback> tools = ToolFunctionUtils.getToolCallbacks(weatherService);
return ChatClient.builder(chatModel)
.defaultTools(tools) // 注册工具
.build();
}
}
// 使用
@Autowired
private ChatClient chatClient;
public String chat(String message) {
return chatClient.call(message);
// 用户问"北京天气怎么样?"
// AI自动调用 getWeather(city="北京", unit="celsius")
}
自动生成的Schema:
{
"name": "getWeather",
"description": "Get current weather for a city",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name"},
"unit": {"type": "string", "default": "celsius"}
},
"required": ["city"]
}
}
2.2 手动注册ToolDefinition
import org.springframework.ai.chat.model.function.FunctionCallback;
import org.springframework.ai.chat.model.function.FunctionCallbackContext;
@Bean
public FunctionCallback weatherTool() {
return FunctionCallback.builder()
.withName("get_weather")
.withDescription("Get weather for a city")
.withInputType(WeatherRequest.class) // POJO类定义参数
.withInvoker(request -> {
WeatherRequest req = (WeatherRequest) request;
return weatherService.getWeather(req.getCity(), req.getUnit());
})
.build();
}
@Bean
public ChatClient chatClient(OpenAiChatModel model, FunctionCallback weatherTool) {
return ChatClient.builder(model)
.defaultFunctionCallbacks(List.of(weatherTool))
.build();
}
2.3 使用 FunctionCallback 包装现有API
import org.springframework.ai.chat.model.function.FunctionCallback;
import org.springframework.ai.chat.model.function.FunctionCallbackContext;
public class ExternalApiTool {
public FunctionCallback createTool(String name, String description,
String url, Class<?> requestType) {
return FunctionCallback.builder()
.withName(name)
.withDescription(description)
.withInputType(requestType)
.withHttpService(HttpService.builder()
.withUrl(url)
.withMethod("GET")
.build())
.build();
}
}
// 包装第三方天气API
@Bean
public FunctionCallback weatherTool() {
return FunctionCallback.builder()
.withName("get_weather")
.withDescription("获取指定城市天气")
.withInputType(WeatherRequest.class)
.withInvoker(req -> {
// 调用外部REST API
String url = "http://api.weather.com?city=" + req.city();
return restTemplate.getForObject(url, WeatherResponse.class);
})
.build();
}
2.4 调用外部Skill服务(OpenAI兼容)
很多Skill平台提供OpenAI兼容的Function Calling API:
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.SystemPromptTemplate;
import org.springframework.ai.openai.OpenAiChatModel;
@Service
public class SkillService {
@Autowired
private OpenAiChatModel chatModel;
// 方式1:直接调用Skill平台的OpenAI兼容接口
public String callSkillThroughOpenAI(String skillName, String userQuery) {
// 配置OpenAI客户端指向Skill平台
OpenAiChatModel skillModel = new OpenAiChatModel(
OpenAiChatModelOptions.builder()
.baseUrl("http://skill-platform:8080/v1") // Skill平台地址
.apiKey("skill-api-key")
.modelName(skillName) // 指定Skill名称作为model
.build()
);
String response = skillModel.call(userQuery);
return response;
}
// 方式2:ChatClient调用多个Skill
public String multiSkillChat(String message) {
return ChatClient.create(skillModel)
.call(message);
}
}
2.5 动态加载Skill定义
import org.springframework.ai.chat.model.function.FunctionCallback;
import org.springframework.ai.chat.model.function.FunctionCallbackRegistry;
@Service
public class DynamicSkillLoader {
@Autowired
private FunctionCallbackRegistry registry;
@Autowired
private RestTemplate restTemplate;
// 从Skill平台动态加载Skill定义
@Scheduled(fixedDelay = 300000) // 每5分钟刷新
public void loadSkillsFromPlatform() {
// 1. 获取Skill列表
SkillDefinition[] skills = restTemplate.getForObject(
"http://skill-platform/api/skills",
SkillDefinition[].class
);
// 2. 清空现有Skill
registry.clear();
// 3. 注册每个Skill
for (SkillDefinition skill : skills) {
FunctionCallback callback = createCallbackFromDefinition(skill);
registry.register(callback);
}
}
private FunctionCallback createCallbackFromDefinition(SkillDefinition skill) {
return FunctionCallback.builder()
.withName(skill.getName())
.withDescription(skill.getDescription())
.withInputType(skill.getInputType()) // 动态生成或预定义
.withInvoker(request -> {
// 动态调用Skill API
return restTemplate.postForObject(
skill.getEndpoint(),
request,
skill.getOutputType()
);
})
.build();
}
}
2.6 使用 Spring AI 的 FunctionCallback
import org.springframework.ai.chat.model.function.FunctionCallback;
import org.springframework.ai.chat.model.function.FunctionCallbackContext;
@Component
public class CalculatorSkill {
@FunctionSchema
public static class AddRequest {
private int a;
private int b;
// getters/setters
}
@FunctionSchema
public static class AddResponse {
private int result;
// getters/setters
}
@Function
public AddResponse add(@Parameter(description = "First number") int a,
@Parameter(description = "Second number") int b) {
return new AddResponse(a + b);
}
@Bean
public FunctionCallback addFunction() {
return FunctionCallback.builder()
.withName("add")
.withDescription("Add two numbers")
.withInputType(AddRequest.class)
.withInvoker(req -> {
AddRequest request = (AddRequest) req;
return add(request.getA(), request.getB());
})
.build();
}
}
三、LangChain4J 调用 Skill
3.1 自定义 Tool 实现
import dev.langchain4j.agent.tool.Tool;
import dev.langchain4j.agent.tool.ToolParameter;
import com.fasterxml.jackson.annotation.JsonProperty;
public class WeatherTool implements Tool {
private final WeatherService weatherService;
public WeatherTool(WeatherService weatherService) {
this.weatherService = weatherService;
}
@Override
public String name() {
return "get_weather";
}
@Override
public String description() {
return "Get current weather for a given city";
}
@Override
public String execute(@JsonProperty("city") String city,
@JsonProperty("unit") String unit) {
try {
Weather weather = weatherService.getWeather(city, unit);
return String.format("Temperature: %d°%s, Condition: %s",
weather.getTemp(), unit, weather.getCondition());
} catch (Exception e) {
return "Error: " + e.getMessage();
}
}
@Override
public ToolParameter[] parameters() {
return new ToolParameter[] {
ToolParameter.builder()
.name("city")
.description("City name, e.g. San Francisco")
.type(ToolParameterType.STRING)
.required(true)
.build(),
ToolParameter.builder()
.name("unit")
.description("Temperature unit: celsius or fahrenheit")
.type(ToolParameterType.STRING)
.required(false)
.defaultValue("celsius")
.build()
};
}
}
// 使用
List<Tool> tools = List.of(new WeatherTool(weatherService));
ChatLanguageModel model = new OpenAiChatModel(
OpenAiChatModelOptions.builder()
.functionRegistry(new SimpleFunctionRegistry(tools))
.build()
);
String response = model.chat("What's the weather in Paris?");
// 模型调用工具,执行get_weather("Paris")
3.2 使用 @Tool 注解(LangChain4J 0.30.0+)
import dev.langchain4j.agent.tool.Tool;
public class CalculatorTool {
@Tool
public int add(@ToolParam(description = "First number") int a,
@ToolParam(description = "Second number") int b) {
return a + b;
}
@Tool
public int multiply(@ToolParam(description = "First number") int a,
@ToolParam(description = "Second number") int b) {
return a * b;
}
}
// 自动扫描注册
ToolRegistry registry = ToolRegistry.builder()
.add(new CalculatorTool())
.build();
3.3 调用外部Skill API
import dev.langchain4j.agent.tool.HttpRequestTool;
public class ExternalSkillClient {
public Tool createHttpTool(String skillName, String url, String method) {
return HttpRequestTool.builder()
.name(skillName)
.description("Call external skill: " + skillName)
.url(url)
.httpMethod(method)
.headers(Map.of("Authorization", "Bearer token"))
.build();
}
}
// 使用
List<Tool> tools = List.of(
HttpRequestTool.builder()
.name("get_stock_price")
.description("Get stock price by symbol")
.url("https://api.stock.com/price")
.httpMethod("GET")
.queryParam("symbol", "${symbol}") // 参数占位符
.build()
);
3.4 工具参数绑定与转换
public class ParameterBindingTool implements Tool {
@Override
public String execute(Map<String, Object> parameters) {
// 参数自动映射
String city = (String) parameters.get("city");
String unit = (String) parameters.getOrDefault("unit", "celsius");
// 类型转换
Integer limit = (Integer) parameters.get("limit");
return doSomething(city, unit, limit);
}
@Override
public ToolSchema schema() {
return ToolSchema.builder()
.name("my_tool")
.description("My tool description")
.parameters(
ToolParameter.builder()
.name("city")
.type(ToolParameterType.STRING)
.description("City name")
.required(true)
.build(),
ToolParameter.builder()
.name("limit")
.type(ToolParameterType.INTEGER)
.description("Max results")
.required(false)
.defaultValue(10)
.build()
)
.build();
}
}
四、调用常见Skill平台的实践
4.1 Dify Skill调用
Dify 提供OpenAI兼容API,可直接用OpenAI SDK调用:
// Spring AI
import org.springframework.ai.openai.OpenAiChatModel;
@Configuration
public class DifyConfig {
@Bean
public OpenAiChatModel difyModel() {
return new OpenAiChatModel(
OpenAiChatModelOptions.builder()
.baseUrl("http://dify-server:/v1") // Dify地址
.apiKey("dify-app-api-key")
.modelName("dify-app-id") // Dify应用ID
.build()
);
}
@Bean
public ChatClient difyChatClient(OpenAiChatModel difyModel) {
return ChatClient.create(difyModel);
}
}
// 调用
String response = difyChatClient.call("Analyze this data");
// Dify内部的Workflow/Skill自动执行
4.2 Coze Skill调用
Coze 同样提供OpenAI兼容接口:
OpenAiChatModel cozeModel = new OpenAiChatModel(
OpenAiChatModelOptions.builder()
.baseUrl("https://api.coze.com/open_api/v2/chat")
.apiKey("coze-api-token")
.modelName("your-bot-id")
.build()
);
4.3 文心一言/千帆 Skill调用
import org.springframework.ai.baidu.AiService; // 需自定义或社区实现
// 千帆平台也支持Function Calling
public class QianwanSkillService {
@Bean
public ChatModel qianwanModel() {
return new BaiduErnieChatModel(
BaiduErnieChatModelOptions.builder()
.apiKey("...")
.secretKey("...")
.modelName("ernie-bot-4.0")
.functions(skillFunctionDefinitions) // 注册Skill定义
.build()
);
}
}
4.4 自定义Skill平台集成
假设Skill平台提供REST API:
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.SystemPromptTemplate;
import org.springframework.ai.chat.model.function.FunctionCallback;
import org.springframework.ai.chat.model.function.FunctionCallbackRegistry;
@Service
public class SkillPlatformIntegration {
@Autowired
private RestTemplate restTemplate;
@Autowired
private FunctionCallbackRegistry registry;
// 1. 定期从Skill平台拉取Skill定义
@Scheduled(fixedDelay = 60000)
public void syncSkills() {
SkillDefinition[] skills = restTemplate.getForObject(
"http://skill-platform/api/v1/skills",
SkillDefinition[].class
);
for (SkillDefinition skill : skills) {
registerSkill(skill);
}
}
private void registerSkill(SkillDefinition skill) {
FunctionCallback callback = FunctionCallback.builder()
.withName(skill.getName())
.withDescription(skill.getDescription())
.withInputType(skill.getInputSchema()) // 动态创建Class
.withHttpService(HttpService.builder()
.withUrl(skill.getEndpoint())
.withMethod(skill.getMethod())
.withHeaders(skill.getHeaders())
.build())
.build();
registry.register(callback);
}
// 2. ChatClient自动使用已注册的Skill
@Bean
public ChatClient chatClient(OpenAiChatModel model) {
return ChatClient.builder(model)
.defaultFunctionCallbacks(registry.getCallbacks())
.build();
}
}
五、Skill调用流程详解
5.1 Spring AI 调用流程
用户请求 → ChatClient → ChatModel(OpenAI) → 返回FunctionCall请求
↓
Spring AI解析FunctionCall → 查找对应FunctionCallback
↓
执行FunctionCallback(可能是Java方法或HTTP调用)
↓
获得结果 → 回传给ChatModel
↓
ChatModel生成最终回答 → 返回用户
代码示例:
// 1. 定义Skill
@Bean
public FunctionCallback searchTool(SearchService searchService) {
return FunctionCallback.builder()
.withName("search")
.withDescription("Search for information")
.withInputType(SearchRequest.class)
.withInvoker(req -> searchService.search(((SearchRequest) req).getQuery()))
.build();
}
// 2. 创建ChatClient并注册Skill
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultFunctionCallbacks(List.of(searchTool))
.build();
// 3. 用户对话
String response = chatClient.call("Search for Java tutorials");
// 内部自动流程:
// a. chatModel判断需要调用search工具
// b. 调用searchTool.execute(...)
// c. 将搜索结果作为Observation回传给model
// d. model生成最终回答
5.2 LangChain4J 调用流程
// 1. 定义Tool
public class SearchTool implements Tool {
@Override
public String name() { return "search"; }
@Override
public String execute(String query) {
return searchService.search(query);
}
@Override
public ToolParameter[] parameters() {
return new ToolParameter[]{
ToolParameter.builder()
.name("query")
.type(ToolParameterType.STRING)
.description("Search query")
.required(true)
.build()
};
}
}
// 2. 注册到ToolRegistry
ToolRegistry registry = new SimpleToolRegistry();
registry.register(new SearchTool());
// 3. 创建Agent或LLM
ChatLanguageModel model = new OpenAiChatModel(...);
Agent agent = Agent.builder()
.chatLanguageModel(model)
.tools(registry)
.build();
// 4. 执行
AgentExecutionResult result = agent.execute("Search Java tutorials");
// 自动:判断→调用→观察→生成最终答案
六、Skill调用的高级特性
6.1 工具调用模式
Spring AI支持三种模式:
ChatResponse response = chatClient.call(
PromptTemplate.from("{{query}}")
.apply(Map.of("query", userMessage))
.create(),
new FunctionEncounteredCallback() {
@Override
public void onFunctionEncountered(FunctionCallback function,
Map<String, Object> arguments) {
log.info("调用工具: {}, 参数: {}", function.name(), arguments);
}
}
);
LangChain4J:
AgentCallback callback = new AgentCallback() {
@Override
public void onTool(String toolName, String input, String output) {
log.info("Tool {} called with {}, result: {}", toolName, input, output);
}
@Override
public void onThought(String thought) {
log.debug("Thought: {}", thought);
}
};
AgentExecutionResult result = agent.execute(
AgentExecutorRequest.builder()
.input("question")
.callback(callback)
.build()
);
6.2 工具调用错误处理
public RobustTool implements Tool {
@Override
public String execute(Map<String, Object> parameters) {
try {
// 1. 参数验证
validate(parameters);
// 2. 调用实际服务
String result = callExternalService(parameters);
// 3. 结果格式化
return formatResult(result);
} catch (ValidationException e) {
return "参数错误: " + e.getMessage();
} catch (TimeoutException e) {
return "服务超时,请重试";
} catch (Exception e) {
log.error("Tool execution failed", e);
return "执行失败: " + e.getMessage();
}
}
}
6.3 工具权限控制
@Component
public class SkillAuthorizationService {
public boolean canExecute(String userId, String skillName) {
// 查数据库或调用IAM系统
return permissionService.hasPermission(userId, "SKILL:" + skillName);
}
public List<String> getAllowedSkills(String userId) {
return permissionService.getPermissions(userId)
.stream()
.filter(p -> p.startsWith("SKILL:"))
.map(p -> p.substring(6))
.collect(Collectors.toList());
}
}
// 在Tool执行前检查
public Tool withAuthorization(FunctionCallback inner,
SkillAuthorizationService auth) {
return new AuthorizedTool(inner, auth);
}
6.4 工具调用监控与审计
@Aspect
@Component
public class SkillAuditAspect {
@Autowired
private AuditLogService auditLog;
@Around("@annotation(org.springframework.ai.tool.RequiresSkill)")
public Object auditSkillExecution(ProceedingJoinPoint pjp) throws Throwable {
String skillName = pjp.getSignature().getName();
String userId = SecurityContext.getCurrentUser();
Map<String, Object> args = ... // 提取参数
long start = System.currentTimeMillis();
Object result = pjp.proceed();
long duration = System.currentTimeMillis() - start;
auditLog.log(SkillAudit.builder()
.userId(userId)
.skillName(skillName)
.parameters(args)
.result(result)
.duration(duration)
.timestamp(Instant.now())
.build());
return result;
}
}
七、Skill调用最佳实践
7.1 Skill设计原则
- 单一职责:一个Skill做一件事
- 幂等性:相同输入多次调用结果一致
- 快速失败:参数错误立即返回,不执行
- 详细文档:description清晰,parameter标注完整
- 版本化:Skill有版本号,兼容性管理
7.2 参数设计
// Good: 参数清晰、有默认值、复杂对象
@Tool
public Report generateReport(
@ToolParam(description = "Report type: sales/marketing/finance") ReportType type,
@ToolParam(description = "Start date (YYYY-MM-DD)") String startDate,
@ToolParam(description = "End date (YYYY-MM-DD)",
defaultValue = "today") String endDate,
@ToolParam(description = "Format: pdf/html", defaultValue = "html") String format
) { ... }
// Bad: 模糊参数、无默认值、多个可选参数混在一起
@Tool
public String process(String data) { ... } // data是什么格式?包含什么?
7.3 错误处理策略
public class SafeTool implements Tool {
@Override
public String execute(Map<String, Object> params) {
try {
// 业务逻辑
return doWork(params);
} catch (BusinessException e) {
// 返回人类可读的错误
return String.format("业务错误: %s。建议: %s",
e.getMessage(), e.getSuggestion());
} catch (TechnicalException e) {
log.error("Tool technical error", e);
return "系统错误,请联系管理员";
}
}
@Override
public ToolSchema schema() {
return ToolSchema.builder()
.name("safe_tool")
.description("Safe tool with detailed error messages")
.parameters(...)
.errorHandling(ErrorHandling.RETURN_MESSAGE) // 出错返回消息而非抛异常
.build();
}
}
7.4 性能优化
// 1. 批量支持
@Tool
public List<Result> batchSearch(@ToolParam(description = "List of queries") List<String> queries) {
// 并行调用搜索API
return queries.parallelStream()
.map(this::searchSingle)
.collect(Collectors.toList());
}
// 2. 缓存
@Cacheable(value = "skill-results", key = "#skillName + ':' + #params")
public Object executeSkill(String skillName, Object params) {
return skillRegistry.execute(skillName, params);
}
// 3. 超时控制
@Tool(timeout = 5000) // 5秒超时
public String longRunningSkill() { ... }
7.5 安全考虑
// 1. 参数校验
@Validated
public class SkillRequest {
@NotBlank
private String city;
@Min(0)
@Max(100)
private Integer limit;
}
// 2. 敏感信息过滤
@Component
public class SensitiveDataFilter implements ToolPostProcessor {
@Override
public Object processResult(Object result) {
return maskSensitiveInfo(result);
}
@Override
public Object processError(Throwable error) {
// 不泄露内部错误细节
return "Operation failed";
}
}
// 3. 调用频率限制
@RateLimit(name = "skill-calls", limitForPeriod = 100, periodInSeconds = 60)
public String rateLimitedSkill() { ... }
八、常见Skill平台对比
| 平台 | 协议 | 认证 | 优点 | 缺点 |
|---|---|---|---|---|
| Dify | OpenAI兼容API | API Key | 可视化编排、多模型支持 | 需自部署,资源消耗大 |
| Coze | OpenAI兼容 + 扩展 | Bot Token | 插件市场丰富、支持多平台 | 国内访问不稳定 |
| 文心千帆 | 百度自定义 | AK/SK | 中文优化、与百度生态整合 | 英文能力较弱 |
| 通义千问 | 阿里自定义 | AK/SK | 多模态、电商场景优化 | 文档较少 |
| 自定义Skill平台 | REST API | JWT/OAuth | 完全控制、可定制 | 需自开发平台 |
九、调试与运维
9.1 调试工具
// 1. 日志记录(Spring AI)
logging.level.org.springframework.ai=DEBUG
// 2. 查看 FunctionCall 历史
@Bean
public FunctionCallbackInspector inspector() {
return new FunctionCallbackInspector() {
@Override
public void afterCall(FunctionCallback callback,
Map<String, Object> args,
Object result) {
log.info("Skill {} called with {}, result: {}",
callback.name(), args, result);
}
};
}
// 3. LangChain4J Callback
CallbackManager callbackManager = new CallbackManager.Builder()
.addListener(new StdOutCallbackListener())
.build();
9.2 监控指标
@TimerMetric(name = "skill.execution.duration")
public Object executeSkill(String skillName, Object params) { ... }
@CounterMetric(name = "skill.execution.count",
tags = {"skill", skillName})
public void incrementSkillCount() { ... }
@GaugeMetric(name = "skill.queue.size")
public int getSkillQueueSize() { ... }
9.3 热加载Skill
@RefreshScope // Spring Cloud Config支持
public class DynamicSkillService {
@Value("${skills.enabled:}")
private List<String> enabledSkills;
@PostConstruct
public void init() {
// 根据配置动态加载Skill
for (String skill : enabledSkills) {
registerSkill(skill);
}
}
}
十、最佳实践总结
10.1 Skill设计
- 粒度适中:一个Skill = 一个业务原子操作
- 幂等设计:相同输入总是相同输出
- 快速失败:参数验证前置,避免无效执行
- 详细文档:description、参数、返回值、错误码
- 版本管理:Skill API版本化,向后兼容
10.2 Spring AI使用
- 优先
@Tool注解方式,最简单 - 复杂参数使用POJO类,支持嵌套对象
- 集成Spring Security做权限控制
- 使用
FunctionCallbackRegistry统一管理 - 配合
@RefreshScope实现热加载
10.3 LangChain4J使用
- 实现
Tool接口,提供schema - 使用
ToolRegistry集中注册 - 参数类型:String、Integer、Boolean、Array、Object
- 结合
Agent实现自动工具选择 - 利用
Callback监控工具调用链
10.4 调用外部Skill
- 优先OpenAI格式:大多数平台兼容
- 统一网关模式:所有Skill通过网关路由
- 协议转换:将OpenAI Function Call转为平台特定协议
- 熔断降级:Skill失败不影响主流程
- 审计日志:记录所有Skill调用(谁、何时、参数、结果)
10.5 性能与成本
- 缓存:相同query+skill参数缓存结果
- 批量:支持批量调用减少网络开销
- 超时:每个Skill设置超时(如5秒)
- 并发限制:防止单个Skill拖垮系统
- 成本监控:追踪每次调用成本(API费用、计算资源)
十一、技术选型建议
根据场景选择:
| 场景 | 推荐方案 |
|---|---|
| 已有Spring Boot项目 | Spring AI(生态整合好) |
| 复杂Agent工作流 | LangChain4J(Agent能力更强) |
| 简单RAG+少量工具 | Spring AI(学习成本低) |
| 多工具动态选择 | LangChain4J Agent |
| 对接Skill平台 | 用OpenAI兼容API统一对接 |
| 自建Skill生态 | 定义标准(OpenAI Function),自动生成Spring/LangChain封装 |
Java生态现状:
- Spring AI:发展快,Spring团队维护,未来可期
- LangChain4J:功能完善,文档丰富,成熟度更高
- 两者都支持OpenAI Function Calling标准,可互操作
十二、未来趋势
12.1 Skill标准化
- OpenAI Function Calling 已成为事实标准
- OpenAPI 3.0 扩展支持LLM描述
- Schema.org 等语义网标准可能融合
12.2 Skill发现与推荐
- Skill Marketplace(类似App Store)
- AI驱动的Skill推荐(根据用户query推荐可用Skill)
- Skill评价与质量认证
12.3 Skill组合与编排
- 可视化编排工具(类似Dify Workflow)
- 自动Skill链生成(给定目标,自动组合Skill)
- Skill依赖管理
12.4 Skill安全与治理
- Skill沙箱执行(防止恶意代码)
- Skill权限细粒度控制(数据级别)
- Skill调用审计与合规
总结
Spring AI 和 LangChain4J 都通过Function Calling标准调用Skill:
Spring AI:
- 使用
@Tool注解或FunctionCallback手动注册 - 与Spring生态深度集成,适合企业级应用
- 自动生成JSON Schema,开箱即用
LangChain4J:
- 实现
Tool接口,提供schema - Agent自动选择工具,更灵活
- 生态更成熟,文档丰富
调用外部Skill平台:
- 优先使用OpenAI兼容API
- 封装HTTP调用为FunctionCallback/Tool
- 支持动态加载、权限控制、审计日志
最佳实践:
- Skill设计遵循单一职责、幂等性、快速失败
- 参数使用POJO或详细schema,提高准确性
- 添加监控、缓存、熔断等生产级特性
- 统一权限控制和审计日志
文档版本:v1.0
最后更新:2025年3月
评论区