chat - Talent 的两种构建方式
2026年6月16日 上午9:05:36
在 Solon AI 中,才能的构建遵循“由简入繁”的原则。和 Tool 一样,也提供了两种构建方式:
- 通过 TalentDesc 描述式构建
- 通过实现 Talent 接口 或 AbsTalent 基类扩展
无论是申明式还是接口式,开发者都能获得对才能生命周期的完整控制权。
1、通过 TalentDesc(才能描述) 描述式构建
TalentDesc 提供了一种轻量级的构建方式。它不仅支持静态属性设置,还允许通过 Lambda 注入动态谓词和逻辑。这使得开发者无需创建新的类文件,就能实现复杂的准入检查和指令生成。
适用场景:快速组合工具、逻辑相对集中的才能定义、偏好函数式风格的开发。
示例代码:
TalentDesc talent = new TalentDesc("order_expert")
.description("订单助手")
// 动态准入:只有提到“订单”时才激活
.isSupported(prompt -> prompt.getUserContent().contains("订单"))
// 动态指令:根据用户是否是 VIP 注入不同 SOP
.instruction(prompt -> {
if ("VIP".equals(prompt.attr("user_level"))) {
return "这是尊贵的 VIP 客户,请优先调用 fast_track_tool。";
}
return "按常规流程处理订单查询。";
})
.toolAdd(new OrderTools());
提示:此方式适合动态构建。
2、通过实现 Talent 接口(一般使用 AbsTalent 基类扩展)
通过实现 Talent 接口,开发者可以利用面向对象的特性(如继承、成员变量状态、复杂的依赖注入)来组织代码。这种方式在逻辑极其复杂、或者需要复用基础才能逻辑时最为合适。
适用场景:
- 高度工程化:需要利用容器组件的注入能力。
- 状态维护:需要在 onAttach 中计算并存储中间状态供后续接口使用。
- 逻辑复用:通过继承 AbsTalent 实现一套通用的安全审计或日志逻辑。
示例代码(使用 AbsTalent 作为基类,可以简化开发):
@Component
public class TechSupportTalent extends AbsTalent {
@Inject
private KbService kbService;
@Override
public String name() {
return "tech_support";
}
@Override
public String description() {
return "技术支持专家,支持故障排查与配置重置";
}
@Override
public boolean isSupported(Prompt prompt) {
String content = prompt.getUserContent();
// 扩展意图识别:涵盖关键词、报错信息等特征
return content != null && (content.contains("故障") || content.contains("报错") || content.contains("error"));
}
@Override
public String getInstruction(Prompt prompt) {
return "你现在是技术支持专家,请遵循以下 SOP:\n" +
"1. 首先根据报错信息检索知识库(search_kb)。\n" +
"2. 给出方案前,必须核实用户的环境版本(Solon 版本)。\n" +
"3. 若需修改生产配置,必须明确告知风险。";
}
@ToolMapping(name = "search_kb", description = "搜索技术知识库,获取故障解决方案")
public String searchKb(@Param("query") String query) {
if (Utils.isEmpty(query)) {
return "请输入搜索关键词";
}
return kbService.search(query);
}
@ToolMapping(
name = "reset_config",
description = "重置系统配置(高危操作)",
meta = "{'danger': true, 'confirm_msg': '操作将导致服务重启,确定吗?'}"
)
public String resetConfig(@Param("serviceName") String serviceName) {
// 执行具体的重置逻辑
return "服务 [" + serviceName + "] 配置已重置,正在重启...";
}
}
AbsTalent 基类(也可定义自己的快捷基类):
package org.noear.solon.ai.chat.talent;
import org.noear.solon.ai.chat.prompt.Prompt;
import org.noear.solon.ai.chat.tool.FunctionTool;
import org.noear.solon.ai.chat.tool.MethodToolProvider;
import org.noear.solon.lang.Preview;
import java.util.*;
/**
* 才能定制基类
*/
public abstract class AbsTalent implements Talent {
private volatile TalentMetadata metadata;
private volatile boolean enabled = true;
private final Map<String, FunctionTool> toolMap0;
private final List<FunctionTool> tools0;
private final Map<String, FunctionTool> toolMap;
private final List<FunctionTool> tools;
protected AbsTalent() {
this(null);
}
/**
* @since 3.10.2
*/
protected AbsTalent(Map<String, Object> binding) {
this.tools0 = new ArrayList<>();
this.tools0.addAll(new MethodToolProvider(this)
.binding(binding)
.includeProvide(false)
.getTools());
this.toolMap0 = new LinkedHashMap<>();
for (FunctionTool tool : tools0) {
toolMap0.put(tool.name(), tool);
}
this.tools = Collections.unmodifiableList(tools0);
this.toolMap = Collections.unmodifiableMap(toolMap0);
}
protected void internalAddTool(FunctionTool tool) {
this.tools0.add(tool);
this.toolMap0.put(tool.name(), tool);
}
public Map<String, FunctionTool> getToolMap() {
return toolMap;
}
public Collection<FunctionTool> getToolAry() {
return tools;
}
public Collection<FunctionTool> getToolAry(String... names) {
return getToolAry(Arrays.asList(names));
}
public Collection<FunctionTool> getToolAry(Collection<String> names) {
List<FunctionTool> list = new ArrayList<>();
for (String key : names) {
if (toolMap.containsKey(key)) {
list.add(toolMap.get(key));
}
}
return list;
}
public void setEnabled(Boolean enabled) {
if (enabled != null) {
this.enabled = enabled;
}
}
@Override
public boolean isEnabled() {
return enabled;
}
@Override
public TalentMetadata metadata() {
if (this.metadata == null) {
this.metadata = new TalentMetadata(this.name(), this.description());
}
return this.metadata;
}
@Override
public Collection<FunctionTool> getTools(Prompt prompt) {
return tools;
}
}
3、构建方式对比
| 特性 | TalentDesc 申明式 | Talent 接口实现(或 AbsTalent 基类扩展) |
|---|---|---|
| 构建风格 | 函数式 / 链式调用 | 面向对象 / 显式实现 |
| 控制力 | 完整控制 (通过 Lambda) | 完整控制 (通过 Override) |
| 代码组织 | 集中在一个代码块中 | 分散在独立的类文件中 |
| 依赖注入 | 需手动从上下文获取 | 可以通过容器注入 |
| 推荐用途 | 快速定义、局部动态逻辑 | 复杂业务领域、深度工程化项目 |
无论选择哪种方式,你都可以通过 prompt 参数感知输入,通过 onAttach 拦截生命周期,以及通过指令染色控制模型行为。选择的关键在于你希望如何组织你的代码结构。