多数 Agent 教程停在工具层:给模型一个函数 Schema,指望它会正确调用。这对 get_time、hash_string 够用;一旦模型跳过知识库检索就直接开工单,或把几十上百个 API 全塞进上下文,就会崩。
Solon AI 把 Tool 留作执行单元,再提供 Talent(才能) 作为产品单元:工具 + SOP + 激活规则。可以先这样记:
- Tool ≈ 函数
- Talent ≈ 类:持有一组函数、使用规程,以及何时出现
本文对应 Solon v4.0.3 官网文档,讲清何时停在 Tool、何时升级到 Talent,以及注册方式。
只堆工具时,产品上会坏在哪
裸工具只回答模型两个问题:
- 能调什么?
- 参数是什么?
它不回答:
- 这次对话该不该看到这组能力?
- 危险操作前必须按什么顺序?
- 哪些工具属于同一业务域?
于是常见故障是:
- 过早副作用(没诊断就建工单)
- 上下文膨胀(每轮全量工具表)
- SOP 失效(跨域自由发挥)
Talent 的定位:可复用的 感知 + 约束 + 执行 包,并带 染色(Coloring),让工具保留领域归属。
对照表
| 维度 | Tool(FunctionTool) | Talent |
|---|---|---|
| 组成单位 | 单个函数/方法 | 指令 + 工具集 + 状态 |
| 抽象层次 | 物理层:怎么做 | 逻辑层:何时做、按什么规程做 |
| 上下文感知 | 无感知,被动等待调用 | isSupported(Prompt) 决定是否激活 |
| 注入内容 | 工具 Schema(JSON) | System Prompt 片段 + 工具列表 |
| 约束力 | 弱:靠描述自由发挥 | 强:getInstruction 强制 SOP |
| 注册方式 | defaultToolAdd / toolAdd |
defaultTalentAdd / talentAdd |
二者不是互斥:Talent 包含 Tool。注册 Talent 会同时挂上其工具,不必再对同一批工具写一遍 defaultToolAdd。
请求时的生命周期
- 准入 ---
isSupported(prompt)过滤无关才能 - 挂载 ---
onAttach(prompt)做预热、审计、上下文准备 - 指令合并与染色 ---
getInstruction写入 System Message;工具 meta 打上 talent 标签 - 推理与执行 --- 模型只看到已激活工具,并带着领域 SOP
所以 Talent 能省 Token:未激活领域根本不进工具表。
模式 A:继续用 Tool
适合:
- 确定性结果
- 低风险
- Schema 自解释
- 不绑定多步业务策略
java
public class ClockTools extends AbsToolProvider {
@ToolMapping(description = "返回服务器当前时间(ISO-8601)")
public String now() {
return Instant.now().toString();
}
}
ChatModel chatModel = ChatModel.of(apiUrl)
.apiKey(apiKey)
.defaultModel(model)
.defaultToolAdd(new ClockTools())
.build();
请求级动态注入也适合分支很浅的场景:
java
chatModel.prompt("今天杭州天气?")
.options(o -> {
o.systemPrompt("你是个天气预报员");
if ("admin".equals(role)) {
o.toolAdd(new UserTool());
o.toolAdd(new AdminTool());
} else {
o.toolAdd(new UserTool());
}
})
.call();
适合打样;同一套角色规则复制到多个入口时,维护成本会很快升高。
模式 B:升级到 Talent
出现以下任一信号就建议封装:
- 副作用前有多步 SOP
- 需要按意图激活
- 按角色/租户裁剪工具面
- 要在 ChatModel / ReActAgent / TeamAgent 间复用领域模块
描述式:TalentDesc
java
TalentDesc orderTalent = new TalentDesc("order_expert")
.description("订单助手")
.isSupported(prompt -> prompt.getUserContent().contains("订单"))
.instruction(prompt -> {
if ("VIP".equals(prompt.attr("user_level"))) {
return "这是尊贵的 VIP 客户,请优先调用 fast_track_tool。";
}
return "按常规流程处理订单查询。";
})
.toolAdd(new OrderTools());
适合快速组合、逻辑集中、偏好函数式写法。
工程化:AbsTalent + @ToolMapping
java
public class TechSupportTalent extends AbsTalent {
@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. 给出方案前核实运行版本。\n"
+ "3. 诊断失败后再开 ticket。";
}
@ToolMapping(name = "search_kb", description = "搜索技术知识库")
public String searchKb(@Param("query") String query) {
return kbService.search(query);
}
@ToolMapping(name = "open_ticket", description = "诊断后创建支持工单")
public String openTicket(@Param("summary") String summary) {
return ticketService.create(summary);
}
}
AbsTalent 会通过 MethodToolProvider 扫描 @ToolMapping,与 Agent 侧工具习惯一致。
同一 Talent 内按角色裁剪工具
java
public class AuthControlTalent extends AbsTalent {
private final UserTool userTool = new UserTool();
private final AdminTool adminTool = new AdminTool();
@Override
public String getInstruction(Prompt prompt) {
return "你是个天气预报员。请遵守调用方角色权限。";
}
@Override
public boolean isSupported(Prompt prompt) {
return prompt.getUserContent() != null
&& prompt.getUserContent().contains("天气");
}
@Override
public Collection<FunctionTool> getTools(Prompt prompt) {
String role = prompt.attrAs("role");
if ("admin".equals(role)) {
return Arrays.asList(userTool, adminTool);
}
return Collections.singletonList(userTool);
}
}
调用端保持很薄:
java
ChatModel chatModel = ChatModel.of(apiUrl)
.apiKey(apiKey)
.defaultModel(model)
.defaultTalentAdd(new AuthControlTalent())
.build();
chatModel.prompt(Prompt.of("今天杭州的天气情况?")
.attrPut("role", role))
.call();
或单次请求挂载:
java
chatModel.prompt(Prompt.of("...").attrPut("role", role))
.options(o -> o.talentAdd(new AuthControlTalent()))
.call();
注册与优先级
| 范围 | API |
|---|---|
| 模型每次对话 | ChatModel.of(...).defaultTalentAdd(talent) |
| 单次请求 | prompt(...).options(o -> o.talentAdd(talent)) |
| 指定顺序 | defaultTalentAdd(index, talent) / talentAdd(index, talent) |
多个 Talent 按注册顺序注入指令;工具会染色归属元数据,便于模型把 SOP 与工具组对齐。
适用对象与 Tool 相同:ChatModel、SimpleAgent、ReActAgent、TeamAgent。
决策清单
| 信号 | 优先选择 |
|---|---|
| 单一纯函数、无策略 | Tool |
| 调用层重复贴角色分支 | Talent |
| 必须强制顺序:检索 → 确认 → 变更 | Talent (getInstruction) |
| 按意图/租户隐藏整域能力 | Talent (isSupported + 动态 getTools) |
| 超大 OpenAPI / MCP 面 | Gateway Talent(分阶段发现),不是扁平工具堆 |
| 仅原型验证 | 先 Tool;模型乱序/乱调时再升级 |
官网一句话:单一职责用 Tool;业务流程用 Talent。由简入繁------先写 Tool,调用不对再包 Talent。
它不是什么
- Talent 不是 Claude Agent Skills 的同款落地。Solon Talent 是开发时赋予 的能力;Claude Skills 更偏运行时学习。官网有对照说明。
- Talent 不是 模型原生标准,而是建立在 Prompt + Tool-Call 之上的框架模式。
延伸阅读
- 区别与选择:solon.noear.org/article/1335
- 概念与接口:solon.noear.org/article/1331
- 两种构建方式:solon.noear.org/article/1332
- 注册与优先级:solon.noear.org/article/1334
- Options 动态工具 vs Talent:solon.noear.org/article/1385
- Gateway 大工具面:solon.noear.org/article/1353
如果 Agent「认识工具」却总走错业务路径,通常不是工具不够,而是缺一个拥有路径的 Talent。