作为一名后端 Java 开发,我在探索 AI Agent 开发的过程中,发现 Skill(技能/插件)的合理设计与编排是决定 Agent 智能程度的关键。本文总结了一些高阶使用技巧和值得推荐的 Skill,希望对你有帮助。
一、Skill 的本质:Agent 的"手脚"
很多开发者初接触 AI Agent 时,容易把 Skill 等同于"函数调用"。这个理解不算错,但远远不够。
Skill 的本质是赋予 LLM 与外部世界交互的能力。 没有 Skill 的 LLM 只是一个"大脑"------能思考、能表达,但无法行动。Skill 就是它的手和脚。
从架构上看,一个 Skill 的生命周期是:
text
arduino
text
用户意图 → LLM 推理 → Skill 选择 → 参数构造 → 外部调用 → 结果回传 → LLM 总结
对于 Java 后端开发者来说,你可以把 Skill 理解为一个 "由 LLM 动态路由的 RPC 接口" ,只不过路由逻辑不是你写的 if-else,而是模型的语义理解能力。
二、Skill 高阶使用技巧
技巧 1:Skill 的 Description 工程------最容易被忽略的胜负手
大多数开发者写 Skill 时,把精力放在 API 对接和参数定义上,却草草写下 description。这是一个巨大的误区。
LLM 选择哪个 Skill、传什么参数,完全依赖你的 Description 文本。 Description 就是你的 Skill 在 LLM 眼中的"简历"。
反面示例:
yaml
makefile
yaml
name: query_order
description: 查询订单
正面示例:
yaml
makefile
yaml
name: query_order
description: |
根据订单号或用户手机号查询订单详情。
适用场景:
- 用户提供了订单号(如 ORD20240513xxxx),需要查询该订单的状态、物流、金额等信息
- 用户提供了手机号,需要查询该手机号下所有近 90 天的订单
- 用户询问"我的快递到哪了"、"之前买的东西什么状态"等订单相关问题
不适用场景:
- 用户要退换货 → 应使用 apply_refund
- 用户要投诉 → 应使用 create_ticket
关键原则:
- 写清楚 "什么时候用" 和 "什么时候不用"
- 用自然语言举出用户可能的表达方式(帮助 LLM 做意图匹配)
- 明确与其他 Skill 的边界,避免 LLM 纠结选择
技巧 2:参数设计的"LLM 友好"原则
后端开发者习惯设计精确、严格的接口参数。但 Skill 的参数是给 LLM 填的,需要换一种思路。
原则一:用 enum 约束 LLM 的输出空间
json
json
json
{
"order_status": {
"type": "string",
"enum": ["pending", "paid", "shipped", "delivered", "cancelled"],
"description": "订单状态。用户说'还没发货'对应 shipped 前的状态,取 pending 或 paid"
}
}
不要期望 LLM 能猜出你定义的魔法字符串。用 enum + 场景说明,把它的"发挥空间"限制在正确范围内。
原则二:参数名用语义化命名,而非技术缩写
json
json
json
// 差:LLM 可能不理解缩写
{ "uid": { "type": "string" } }
// 好:语义清晰
{ "user_id": { "type": "string", "description": "用户ID,通常为纯数字" } }
原则三:提供 default 值和 fallback 策略
json
json
json
{
"page_size": {
"type": "integer",
"default": 10,
"description": "每页返回数量,默认10。用户没有特别要求时使用默认值"
}
}
这样 LLM 在用户没有明确指定时,不需要"猜",直接用默认值即可,减少无效的追问轮次。
技巧 3:Skill 编排------从单步调用到多步工作流
初级用法是单个 Skill 独立调用。但真实业务场景往往需要多个 Skill 协同。
场景示例:用户说"帮我把上周买的那个蓝牙耳机退了"
Agent 需要完成的链路:
text
scss
text
1. query_order(关键词="蓝牙耳机", 时间范围="上周")
→ 返回订单列表
2. LLM 判断:有多个订单,追问用户确认具体是哪一个
3. 用户确认后 → apply_refund(order_id=xxx, reason="七天无理由")
→ 返回退款申请结果
4. notify_user(用户ID=xxx, 内容="退款申请已提交,预计3个工作日到账")
高阶技巧:在 System Prompt 中定义编排策略
markdown
markdown
markdown
## 技能编排规则
1. 当用户意图涉及"退款"时,必须先调用 query_order 确认订单,再调用 apply_refund
2. query_order 返回多个结果时,必须列出订单摘要让用户确认,不得自行选择
3. 退款操作完成后,主动调用 notify_user 通知用户
4. 任何涉及金额的操作,必须在执行前向用户二次确认
这相当于你在 System Prompt 中写了一个"业务流程引擎"。LLM 就是流程的执行者。
技巧 4:Skill 内嵌业务校验------不要信任 LLM 的参数
这是一个后端开发者特别容易忽视的点:LLM 传过来的参数不一定靠谱。
你的 Skill 实现层必须做完整的参数校验和业务校验,就像处理任何外部输入一样。
java
java
java
@Override
public SkillResult execute(SkillContext context) {
String orderId = context.getParam("order_id");
// 1. 参数校验
if (StringUtils.isBlank(orderId)) {
return SkillResult.fail("缺少必要参数:order_id");
}
// 2. 业务校验
Order order = orderService.findById(orderId);
if (order == null) {
return SkillResult.fail("未找到订单:" + orderId + ",请检查订单号是否正确");
}
if (!order.isRefundable()) {
return SkillResult.fail(
"该订单不支持退款。原因:" + order.getRefundBlockReason()
);
}
// 3. 执行业务
RefundResult result = refundService.apply(orderId, reason);
return SkillResult.success("退款申请已提交,退款单号:" + result.getRefundNo());
}
关键:错误信息要返回给 LLM,而不是抛异常。 因为 LLM 需要理解错误原因,才能给用户一个有意义的回复,或者决定下一步操作(比如换个参数重试)。
技巧 5:Skill 返回值的"LLM 友好"设计
Skill 的返回值不是返回给前端的 JSON,而是返回给 LLM 的"上下文信息"。格式上要兼顾信息密度和可读性。
反面示例:
json
css
json
{
"code": 200,
"data": {
"orders": [
{
"oid": "ORD20240513001",
"st": 3,
"amt": 29900,
"itm": [
{"sku": "BT-001", "n": "索尼WH-1000XM5", "q": 1, "p": 29900}
],
"addr": "北京市朝阳区xxx",
"exp": "SF1234567890"
}
]
}
}
正面示例:
json
json
json
{
"success": true,
"summary": "找到1笔订单",
"orders": [
{
"order_id": "ORD20240513001",
"status": "已发货",
"status_code": "shipped",
"total_amount": "299.00元",
"items": "索尼WH-1000XM5 头戴式降噪耳机 x1",
"shipping_info": "顺丰快递 SF1234567890,预计明天送达",
"can_refund": true
}
],
"hint": "如果用户要退款,可以直接调用 apply_refund,订单号为 ORD20240513001"
}
关键差异:
- 字段名语义化,LLM 可以直接理解
- 金额从"分"转为"元",加上单位
- 状态从枚举值转为自然语言
- 加了
hint字段,引导 LLM 下一步操作 - 加了
summary字段,方便 LLM 生成总结
技巧 6:利用 Skill 实现"人机协作"模式
有些操作不适合让 LLM 全自动执行(比如大额转账、删除数据)。可以在 Skill 中实现审批流。
java
java
java
@Override
public SkillResult execute(SkillContext context) {
BigDecimal amount = context.getParam("amount", BigDecimal.class);
if (amount.compareTo(new BigDecimal("5000")) > 0) {
// 大额操作,需要人工审批
String approvalId = approvalService.createApproval(
context.getUserId(),
"transfer",
context.getParams()
);
return SkillResult.needApproval(
"转账金额超过5000元,已提交人工审批。" +
"审批单号:" + approvalId + "," +
"预计1个工作日内完成审批,请耐心等待。"
);
}
// 小额直接执行
TransferResult result = transferService.execute(context.getParams());
return SkillResult.success("转账成功,交易流水号:" + result.getTxId());
}
这种设计让 Agent 在自动化和安全性之间找到平衡,也是企业级落地的核心需求。
三、推荐的实用 Skill 及设计思路
以下是一些在实际项目中验证过的高价值 Skill 设计,你可以直接参考或改造:
1. 结构化数据查询 Skill(Text-to-SQL)
适用场景: 用户用自然语言查询业务数据
yaml
vbnet
yaml
name: data_query
description: |
将用户的自然语言问题转化为 SQL 查询,返回业务数据。
适用场景:用户询问数据统计、报表、趋势分析等问题。
例如:"最近7天的新增用户数"、"本月销售额最高的Top10商品"
限制:只支持 SELECT 查询,不支持写操作。
后端实现要点:
- 只允许 SELECT,禁止 INSERT/UPDATE/DELETE
- 限制查询的表和字段范围(白名单)
- 强制加 LIMIT,防止全表扫描
- 对返回结果做脱敏处理(手机号、身份证等)
java
typescript
java
@Component
public class DataQuerySkill implements Skill {
private static final Set<String> ALLOWED_TABLES = Set.of(
"t_order", "t_user", "t_product", "t_payment"
);
@Override
public SkillResult execute(SkillContext context) {
String sql = context.getParam("sql");
// 安全校验
SqlValidation validation = sqlValidator.validate(sql, ALLOWED_TABLES);
if (!validation.isValid()) {
return SkillResult.fail("SQL校验不通过:" + validation.getReason());
}
// 强制加 LIMIT
sql = sqlLimiter.addLimit(sql, 100);
// 执行查询
List<Map<String, Object>> results = jdbcTemplate.queryForList(sql);
return SkillResult.success(formatResults(results));
}
}
2. 知识库检索 Skill(RAG)
适用场景: 让 Agent 基于企业内部文档回答问题
yaml
makefile
yaml
name: knowledge_search
description: |
从企业知识库中检索相关信息,用于回答用户关于公司政策、产品文档、
技术规范、常见问题等方面的问题。
适用场景:用户询问"xxx功能怎么用"、"公司的报销流程是什么"
高阶技巧:
- 返回结果附带来源引用,让 LLM 可以标注"根据《xxx文档》..."
- 返回置信度分数,让 LLM 决定是直接回答还是说"我不确定"
- 支持多轮追问时的上下文感知------把上一轮的检索结果作为下一轮的参考
3. 多系统联动 Skill(编排型)
适用场景: 一个操作需要跨多个内部系统
yaml
yaml
yaml
name: onboard_employee
description: |
新员工入职一站式处理。自动完成以下操作:
1. 在 HR 系统中创建员工档案
2. 在 OA 系统中开通门禁和考勤权限
3. 在 IT 系统中申请邮箱和 VPN 账号
4. 在知识库中推送新人入职指南
需要提供:员工姓名、部门、职位、入职日期
这类 Skill 本质上是一个微服务编排层,非常适合 Java 后端用 Spring 的事务管理或 Saga 模式来实现。
4. 定时任务调度 Skill
适用场景: 用户要求 Agent 在特定时间执行某个操作
yaml
makefile
yaml
name: schedule_task
description: |
创建一个定时任务。用户说"每天早上9点给我发昨日数据报告"、
"下周三提醒我xxx"时使用此技能。
支持一次性任务和周期性任务(cron表达式)。
5. 代码执行 Skill(沙箱型)
适用场景: 需要动态计算、数据处理、生成图表
yaml
yaml
yaml
name: code_interpreter
description: |
在安全沙箱中执行 Python 代码,用于:
- 数学计算和统计分析
- 数据清洗和转换
- 生成图表和可视化
当用户的请求涉及复杂计算或数据分析时使用。
四、避坑指南
坑 1:Skill 数量爆炸
不要给 Agent 配置太多 Skill。LLM 在面对 20+ 个 Skill 时,选择准确率会明显下降。
解决方案: 分层设计
- 通用层: 基础能力(搜索、计算、时间)
- 业务层: 按领域分组(订单、用户、商品)
- 专家层: 特定场景的复合 Skill
在 System Prompt 中用条件触发来控制加载哪些 Skill,而非一次性全部暴露。
坑 2:Skill 之间职责模糊
如果两个 Skill 的 Description 高度重叠,LLM 会随机选择,导致行为不稳定。
解决方案: 定义清晰的边界条件,用"适用/不适用"做区分。
坑 3:忽略超时和重试
LLM 调用 Skill 有超时限制。如果你的 Skill 执行时间过长(比如跑一个复杂查询),可能会超时。
解决方案:
- 对于耗时操作,Skill 先返回"处理中"状态,异步通知结果
- 设置合理的超时时间
- 实现幂等性,支持 LLM 重试调用
坑 4:安全意识不足
Skill 是 LLM 与你的后端系统之间的桥梁。LLM 的参数构造完全基于自然语言理解,存在注入风险。
必须做的安全措施:
- 所有输入参数做严格校验(类型、长度、格式)
- SQL/NoSQL 注入防护
- 权限校验------Skill 的执行权限不应超过调用用户本身的权限
- 敏感操作加二次确认机制
- 记录完整的调用日志(入参、出参、耗时),便于审计
五、总结
| 维度 | 初级做法 | 高阶做法 |
|---|---|---|
| Description | 一句话描述 | 场景 + 正例 + 反例 + 边界 |
| 参数设计 | 照搬 API 文档 | enum 约束 + 语义化命名 + 默认值 |
| 返回值 | 原始 JSON | 语义化 + summary + hint |
| 错误处理 | 抛异常 | 结构化错误信息回传 LLM |
| 安全 | 信任 LLM 参数 | 全面校验 + 权限控制 + 审计日志 |
| 编排 | 单 Skill 独立调用 | 多 Skill 链式编排 + 流程控制 |
Skill 的设计质量,直接决定了 Agent 的智能上限。 LLM 再强,如果 Skill 设计粗糙,Agent 的表现也会大打折扣。
作为后端开发者,我们天然具备设计高质量 Skill 的优势------接口设计、参数校验、异常处理、安全防护,这些都是我们的日常。把 Skill 当作一个"特殊的微服务接口"来设计,你就已经赢在起跑线上了。
以上是我个人在实践中的总结,如果你有更好的 Skill 设计技巧,欢迎交流讨论。