位置:首页 > 进阶教程 > Skill是什么?AI能力插件,就像给手机装个App一样简单

Skill是什么?AI能力插件,就像给手机装个App一样简单

时间:2026-07-20  |  作者:星河游者  |  阅读:0

一个让我失眠的夜晚

去年有个需求,让我熬了三个通宵。

Skill 是什么——给AI装个“能力插件”,就像给手机装App一样简单

业务方说要做一个“智能客服”。

但要求特别拧巴:必须能查订单和退换货;必须能根据历史行为做个性化推荐;还必须实时对接库存系统。

当时我们用的是某主流大模型的API。模型本身挺聪明,但一碰到“查库存”这种需要实时数据的活儿,就开始胡编乱造。

那会儿业内还没有特别成熟的Agent框架。我试了Function Calling,写了十几套prompt模板,甚至自己撸了一个简单的RAG流程。

结果呢?代码越写越臃肿,prompt越改越拧巴。每次加一个新能力,就跟重构整个系统似的。

直到我接触到“Skill”这个概念。

说白了,Skill就是把AI的某项“专项能力”打包成一个独立的插件。想让它查天气?装一个Weather Skill。想让它操作Jira?装一个Jira Skill。想让它拉取数据库里的实时报表?写一个SQL Skill就完事了。

就像给手机装App一样:装上就能用,不用了就卸掉,核心系统干干净净。

今天,我就把自己在这块踩过的坑、总结的经验,一次性倒给你。

Skill到底是什么?用一个例子说透

别被“Skill”这个花哨名字唬住。它的技术本质其实就三层:

  • 一层接口:定义好输入输出规范(输入什么参数,输出什么格式)。
  • 一层执行逻辑:内部干活的代码(调API、查数据库、跑算法都行)。
  • 一层注册机制:让AI“知道”有这个能力存在,以及什么时候该调用它。

画个简单的对比,你就懂了:

对比项

传统Function Calling

Skill插件

新增能力

改prompt + 改代码 + 重新部署

放一个Skill文件进去,热加载

复用性

每个项目重新写一遍

跨项目拷贝即用

调试难度

跟大段的系统代码混在一起

独立运行,日志隔离

团队协作

前后端、算法互相锁死

各写各的Skill,最后组装

打个你肯定懂的比方:传统方式是给汽车发动机舱里硬塞新零件,Skill是给车尾挂一个快拆拖斗。拖斗里装什么都可以,装错了摘下来换一个就行。发动机舱(核心系统)一根线都不用动。

手把手:10分钟写一个“查快递”Skill

光说不练假把式。下面我用Python写一个真实的Skill,让大模型能查询快递物流信息。

Step 1:定义Skill的“说明书”

每个Skill必须告诉AI三件事:我能干什么、我需要什么参数、我返回什么格式。

# skill_express.py
from typing import Optional, Dict, Any
from pydantic import BaseModel, Field

# 1. 定义输入参数的结构
class ExpressInput(BaseModel):
    tracking_number: str = Field(description="快递单号,支持中通/圆通/顺丰")
    carrier: Optional[str] = Field(default="auto", description="快递公司编码,可选:zto/yto/sf,默认自动识别")

# 2. 定义输出的结构(让AI知道能拿到什么信息)
class ExpressOutput(BaseModel):
    status: str = Field(description="物流状态:已揽收/运输中/派送中/已签收")
    location: str = Field(description="最新位置")
    timestamp: str = Field(description="更新时间")
    details: list = Field(description="完整物流轨迹列表")

Step 2:写执行逻辑(真正的“干活代码”)

注意,这里为了演示,用了Mock数据。真实场景下,对接的是快递100、菜鸟裹裹或顺丰开放平台的API。

import httpx
import re
from datetime import datetime

class ExpressSkill:
    """快递查询技能"""
    def __init__(self):
        self.api_key = "your_kuaidi100_api_key"
        self.base_url = "https://api.kuaidi100.com/query"

    def execute(self, input: ExpressInput) -> ExpressOutput:
        carrier = input.carrier
        if carrier == "auto":
            carrier = self._detect_carrier(input.tracking_number)
        raw_data = self._mock_query(input.tracking_number, carrier)
        return ExpressOutput(
            status=raw_data["status"],
            location=raw_data["location"],
            timestamp=datetime.now().isoformat(),
            details=raw_data["traces"]
        )

    def _detect_carrier(self, tn: str) -> str:
        if re.match(r'^d{12}$', tn):
            return "sf"
        elif re.match(r'^[A-Za-z0-9]{12}$', tn):
            return "zto"
        else:
            return "yto"

    def _mock_query(self, tn: str, carrier: str) -> dict:
        return {
            "status": "派送中",
            "location": "广州市天河区体育西路营业点",
            "traces": [
                {"time": "2026-07-06 08:30", "content": "快递员正在派送中,预计今日送达"},
                {"time": "2026-07-05 22:15", "content": "到达广州转运中心"},
                {"time": "2026-07-05 14:20", "content": "已从深圳发出"},
            ]
        }

Step 3:注册到AI(最关键的一步)

这一步决定了AI在什么时候、以什么方式调用你的Skill。我用LangChain的Tool装饰器来注册:

from langchain.tools import tool
from langchain.schema import Tool

@tool
def query_express(tracking_number: str, carrier: str = "auto") -> str:
    """查询快递物流信息。
    tracking_number: 快递单号
    carrier: 快递公司编码(zto/yto/sf),不填则自动识别"""
    skill = ExpressSkill()
    result = skill.execute(ExpressInput(
        tracking_number=tracking_number,
        carrier=carrier
    ))
    return f"物流状态:{result.status}n最新位置:{result.location}n轨迹详情:n" + "n".join(
        [f"{d['time']} {d['content']}" for d in result.details]
    )

express_tool = Tool(
    name="快递查询",
    func=lambda tn, c="auto": query_express(tn, c),
    description="用于查询快递物流信息,输入单号和可选快递公司编码"
)

Step 4:接入大模型(让AI“学会”用这个Skill)

from langchain.agents import initialize_agent, AgentType
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(model="gpt-4", temperature=0)
tools = [query_express]

agent = initialize_agent(
    tools=tools,
    llm=llm,
    agent=AgentType.OPENAI_FUNCTIONS,
    verbose=True
)

response = agent.invoke({"input": "帮我查一下单号SF1234567890的快递到哪了?"})
print(response["output"])

运行结果:

物流状态:派送中
最新位置:广州市天河区体育西路营业点
轨迹详情:
2026-07-06 08:30 快递员正在派送中,预计今日送达
2026-07-05 22:15 到达广州转运中心
2026-07-05 14:20 已从深圳发出

看到没?AI自动识别了“查快递”的意图,提取了单号,调用了我们的Skill,然后把结果整理成可读的文字。全程没改一行核心系统代码。

进阶:Skill设计的3个“要命”细节

上面那个Demo能跑通,但离“生产级”还差得远。我在线上环境被这3个问题坑过不止一次,你最好提前知道。

细节1:超时控制 + 熔断

Skill调用外部API如果挂了,整个Agent会跟着卡死。必须加超时:

import asyncio
from functools import wraps

def with_timeout(seconds=10):
    def decorator(func):
        @wraps(func)
        async def wrapper(*args, **kwargs):
            try:
                return await asyncio.wait_for(func(*args, **kwargs), timeout=seconds)
            except asyncio.TimeoutError:
                return "【系统提示】该Skill响应超时,请稍后重试或尝试其他方式"
        return wrapper
    return decorator

@with_timeout(5)
async def query_express_async(tn: str):
    pass

细节2:输入校验的“防呆设计”

用户输入的快递单号千奇百怪,空格、换行、中文括号全都有。我吃过亏,用户输入“SF 1234567890”中间有个空格,正则直接匹配失败。

def sanitize_tracking_number(raw: str) -> str:
    return re.sub(r'[^A-Za-z0-9]', '', raw.strip())

还有,Skill里一定要有fallback。识别不出快递公司就用“中通”兜底,查不到数据就返回明确的“未查询到”,而不是抛异常。

细节3:并发安全

如果你的AI同时处理多个用户的请求,Skill内部如果有共享状态(比如缓存、数据库连接池),必须注意线程安全。

import threading

class ExpressSkill:
    _cache = {}
    _cache_lock = threading.Lock()

    def execute(self, input: ExpressInput):
        with self._cache_lock:
            if input.tracking_number in self._cache:
                return self._cache[input.tracking_number]
        # 查询逻辑...
        with self._cache_lock:
            self._cache[input.tracking_number] = result
        return result

团队协作:Skill的“集市模式”

我们团队现在有6个人,分别维护不同的Skill。前端写“页面操作Skill”,后端写“数据查询Skill”,算法组写“推荐Skill”。大家互不干扰,最后通过一个统一的Skill Registry(注册中心)聚合起来。

目录结构长这样:

skills/
├── registry.yaml        # 所有Skill的注册清单
├── express/
│   ├── skill.py
│   ├── config.yaml
│   └── tests/
├── jira/
│   ├── skill.py
│   └── README.md
├── sql_analytics/
│   ├── skill.py
│   └── queries/
└── weather/
    ├── skill.py
    └── requirements.txt  # 每个Skill可以有自己的依赖

每个Skill独立维护自己的版本、依赖、测试用例。集成测试只测“调用接口是否正常”,不管内部实现。

这是最像“App Store”的地方——各个Skill独立开发、独立发布,主系统只认接口契约。

写在最后:Skill不是银弹,但它是方向

老实说,Skill这套模式不是没有代价。它增加了系统整体的复杂度——你得维护注册中心、做热加载、处理版本兼容。如果你的业务只有两三个AI能力,硬编码比搞Skill架构快得多。

但当你的AI能力超过5个、团队超过3个人的时候,Skill带来的解耦收益会指数级增长。我们现在的系统里跑了17个Skill,涵盖客服、运维、数据分析三个领域。新增一个能力从“改核心代码+全量回归”变成了“写Skill+注册”,周期从2周压缩到2天。

如果你正准备在项目中引入AI能力,我的建议是:

  1. 前3个能力硬编码:先跑通业务,理解真实需求。
  2. 到第4-5个能力时:回头抽象出Skill接口,把前3个也迁过来。
  3. 第6个以后:所有新能力一律以Skill形式接入。

这样既不会过度设计,又能平滑过渡到插件化架构。

最后,送给你我写在团队Wiki首页的一句话:

下次有机会,我再写一篇《Skill的异步调度与分布式部署实战》,讲讲我们怎么把Skill跑在K8s上,以及如何用RabbitMQ做Skill的任务队列。

免责声明:文中图文均来自网络,如有侵权请联系删除,心愿游戏发布此文仅为传递信息,不代表心愿游戏认同其观点或证实其描述。

相关文章

更多

精选合集

更多

大家都在玩

热门话题

大家都在看

更多