乐于分享
好东西不私藏

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

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

关注 霍格沃兹软件测试开发 公众号,回复「资料」, 领取人工智能测试开发技术合集

从0到1手写一个Skill,把大模型变成你的“万能工具箱”

01 一个让我失眠的夜晚

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

业务方说要做一个“智能客服”,但要求特别拧巴——既要能查订单、退换货,又要能根据用户的历史行为做个性化推荐,还得实时对接库存系统。当时我们用的是某主流大模型的API,模型本身挺聪明,但一碰到“查库存”这种需要实时数据的活儿就开始胡编乱造。

那会儿业内还没有特别成熟的Agent框架,我试了Function Calling,写了十几套prompt模板,甚至自己撸了一个简单的RAG流程。结果呢?代码越写越臃肿,prompt越改越拧巴,每次加一个新能力就跟重构整个系统似的。

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

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

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

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

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

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

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

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

对比项
传统Function Calling
Skill插件
新增能力
改prompt + 改代码 + 重新部署
放一个Skill文件进去,热加载
复用性
每个项目重新写一遍
跨项目拷贝即用
调试难度
跟大段的系统代码混在一起
独立运行,日志隔离
团队协作
前后端、算法互相锁死
各写各的Skill,最后组装

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

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

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

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

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

# skill_express.pyfrom typing import Optional, Dict, Anyfrom pydantic import BaseModel, Field# 1. 定义输入参数的结构classExpressInput(BaseModel):    tracking_number: str = Field(description="快递单号,支持中通/圆通/顺丰")    carrier: Optional[str] = Field(        default="auto"        description="快递公司编码,可选:zto/yto/sf,默认自动识别"    )# 2. 定义输出的结构(让AI知道能拿到什么信息)classExpressOutput(BaseModel):    status: str = Field(description="物流状态:已揽收/运输中/派送中/已签收")    location: str = Field(description="最新位置")    timestamp: str = Field(description="更新时间")    details: list = Field(description="完整物流轨迹列表")

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

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

import httpximport refrom datetime import datetimeclassExpressSkill:"""快递查询技能"""def__init__(self):# 真实场景这里放API密钥        self.api_key = "your_kuaidi100_api_key"        self.base_url = "https://api.kuaidi100.com/query"defexecute(self, input: ExpressInput) -> ExpressOutput:# 自动识别快递公司(真实场景用正则或算法)        carrier = input.carrierif carrier == "auto":            carrier = self._detect_carrier(input.tracking_number)# 调用快递查询API(这里用Mock演示)# 实际开发中换成:requests.get(self.base_url, params={...})        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:# 简单识别逻辑:顺丰12位纯数字,中通12位数字字母混合...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:# Mock数据,真实场景替换为API调用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 toolfrom langchain.schema import Tool# 方式一:用装饰器快速注册@tooldefquery_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    ))returnf"物流状态:{result.status}\n最新位置:{result.location}\n轨迹详情:\n" + "\n".join(        [f"  {d['time']}{d['content']}"for d in result.details]    )# 方式二:手动注册为Tool(更灵活,适合复杂场景)express_tool = Tool(    name="快递查询",    func=lambda tn, c="auto": query_express(tn, c),    description="用于查询快递物流信息,输入单号和可选快递公司编码")

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

from langchain.agents import initialize_agent, AgentTypefrom langchain_openai import ChatOpenAI# 初始化模型llm = ChatOpenAI(model="gpt-4", temperature=0)# 把Skill塞进Agent的工具箱tools = [query_express]  # 可以继续添加其他Skillagent = initialize_agent(    tools=tools,    llm=llm,    agent=AgentType.OPENAI_FUNCTIONS,    verbose=True# 调试时打开,能看清AI的思考过程)# 测试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,然后把结果整理成可读的文字。全程没改一行核心系统代码。

人工智能技术学习交流群

伙伴们,对AI测试、大模型评测、质量保障感兴趣吗?我们建了一个 「人工智能测试开发交流群」,专门用来探讨相关技术、分享资料、互通有无。无论你是正在实践还是好奇探索,都欢迎扫码加入,一起抱团成长!期待与你交流!👇

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

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

细节1:超时控制 + 熔断

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

import asynciofrom functools import wrapsdefwith_timeout(seconds=10):defdecorator(func):        @wraps(func)asyncdefwrapper(*args, **kwargs):try:returnawait asyncio.wait_for(                    func(*args, **kwargs),                     timeout=seconds                )except asyncio.TimeoutError:return"【系统提示】该Skill响应超时,请稍后重试或尝试其他方式"return wrapperreturn decorator# 用法@with_timeout(5)asyncdefquery_express_async(tn: str):# 异步请求快递APIpass

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

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

defsanitize_tracking_number(raw: str) -> str:"""清洗用户输入,只保留数字和字母"""return re.sub(r'[^A-Za-z0-9]''', raw.strip())

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

细节3:并发安全

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

import threadingclassExpressSkill:    _cache = {}    _cache_lock = threading.Lock()defexecute(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] = resultreturn result

05 团队协作: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独立开发、独立发布,主系统只认接口契约。

06 写在最后: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化的本质,是把AI从一个‘什么都懂但什么都做不精’的泛泛之辈,变成一个‘随时可以请外援’的团队指挥官。”

下次有机会,我再写一篇《Skill的异步调度与分布式部署实战》,讲讲我们怎么把Skill跑在K8s上、以及如何用RabbitMQ做Skill的任务队列。感兴趣的朋友可以点个关注,咱们下回见。

推荐学习

软件测试开发快速落地智能化测试公开课,从Web/App/接口测试智能体,再到业务测试用例生成,爱测智能化测试平台,手把手带你掌握AI智能体与智能化测试平台!

👉 扫码进群,报名学习


(本文所有代码已脱敏,可在Python 3.10 + LangChain 0.3+ 环境下运行。如有问题,欢迎留言交流。)

关于我们

霍格沃兹测试开发学社,隶属于 测吧(北京)科技有限公司,是一个面向软件测试爱好者的技术交流社区。

学社围绕现代软件测试工程体系展开,内容涵盖软件测试入门、自动化测试、性能测试、接口测试、测试开发、全栈测试,以及人工智能测试与 AI 在测试工程中的应用实践

我们关注测试工程能力的系统化建设,包括 Python 自动化测试、Java 自动化测试、Web 与 App 自动化、持续集成与质量体系建设,同时探索 AI 驱动的测试设计、用例生成、自动化执行与质量分析方法,沉淀可复用、可落地的测试开发工程经验。

在技术社区与工程实践之外,学社还参与测试工程人才培养体系建设,面向高校提供测试实训平台与实践支持,组织开展 “火焰杯” 软件测试相关技术赛事,并探索以能力为导向的人才培养模式,包括高校学员先学习、就业后付款的实践路径。

同时,学社结合真实行业需求,为在职测试工程师与高潜学员提供名企大厂 1v1 私教服务,用于个性化能力提升与工程实践指导。