2026年如果你还停留在“写Prompt调用大模型”的状态,那基本还停留在Agent浪潮的上一站。现在更值得学的是Agent Skills——把工具、提示词、执行流程打包成可复用的“技能”,让智能体自己决定什么时候调用、怎么调用、调用完之后怎么把结果接回主线任务。
这次我们直接聊实战:Agent Skills是什么、和普通Prompt模板有什么区别、如何用Python从零实现一个最小Skill,再把它封装成HTTP服务接入业务系统。这篇不是概念科普,而是带你跑通一条完整的代码路线。
文章内容会覆盖:
- Agent Skills 的定义、结构和核心思路;
- 一套最小可运行的项目代码(Skill定义 + LLM调用 + Agent循环);
- 如何把它包装成API服务、处理批量任务;
- 开发中的性能观察、常见排错和工程化建议。
整个教程的代码都以通用Python工程为模板,LLM调用走OpenAI兼容接口。你只需要准备好Python环境和一个可用的模型接口(云API或本地模型都行),就能跟着跑。
1. Agent Skills 核心能力速览
| 能力项 | 说明 |
|---|---|
| 项目类型 | AI Agent 开发范式 / 工具封装方案 |
| 功能定位 | 把提示词、工具函数、执行流程封装成可复用技能 |
| 开发语言 | Python 为主 |
| 运行环境 | CPU即可开发调试;若LLM本地部署需独立显卡 |
| LLM 来源 | 云API或本地OpenAI兼容服务 |
| 核心优势 | 一次开发,多次复用;Agent 自主决策调用 |
| 扩展方式 | 新增 Skill 定义 + 实现函数,无需改主循环 |
| API 服务 | 可基于 FastAPI / Flask 二次封装 |
| 批量任务 | 通过目录扫描或队列消费实现 |
| 适合人群 | LLM 应用开发者、Agent 框架初学者、自动化流程开发者 |
从材料看,这个方向目前已经被多个大模型团队作为官方能力推进。吴恩达的Agent Skills教程发布后,相关讨论热度很高。2026年版的学习路径也不再是“看概念”,而是“跑代码”。下面我们直接进入开发。
2. Agent Skills 和普通提示词模板的本质区别
很多初学者会把Agent Skills理解成“一段写好的Prompt”,这是最大的误区。
普通提示词模板是一个静态文本,模型每次拿到它的行为都是一次性推理。比如你写一段“你是翻译助手,把用户输入翻译成英文”,它只是一个指令,没有工具调用能力,也没有状态管理。
Agent Skills 的差异在于它包含三种元素:
- 描述(Description):告诉Agent“这个技能什么时候用、能做什么”。
- 工具函数(Function):真正执行的代码,比如查数据库、计算文件大小、调用HTTP接口。
- 参数Schema(Parameter Schema):LLM调工具时按什么样的结构传参。
Agent在执行用户任务时,会根据当前对话状态,从注册的多个Skill中自主选择是否调用某个技能。这才是“技能”的含义:它不是被用户直接触发,而是被Agent在推理过程中动态决策触发。
用一个例子说明:
| 类型 | 做法 |
|---|---|
| 普通Prompt | “你是一个文件整理助手,当用户输入文件目录时,请列出文件大小。” |
| Agent Skill | 定义一个list_files(path)函数,并写清描述“当需要查看目录文件、检查文件大小或整理文件时调用该技能”,Agent根据用户意图自己决定是否调用。 |
后者具备组合能力。你可以定义10个Skill,让Agent在对话中连续调用它们,完成“读取文件 -> 抽取关键信息 -> 生成摘要 -> 发送通知”这种多步骤任务。
这也是2026年版Agent开发最值得掌握的一点:** Skill 是可复用的原子能力,Agent 是编排执行的控制器。**
3. 适用场景与使用边界
3.1 适合解决什么问题
- 企业内部知识库问答:把“文档检索”“数据库查询”“权限验证”分别做成Skill,Agent按需调用。
- 自动化办公:生成周报、整理Excel、批量发邮件,每个能力封装成Skill。
- 代码生成与代码执行:Skill调用代码解释器,Agent验证代码结果。
- 内容生产流程:调用搜索、抓取网页、生成图片、排版发布。
- 个人助理:把定时任务、天气查询、日程管理全部封装成稳定技能。
3.2 不适合什么场景
- 单轮简单问答:直接调用模型接口就够了,不需要引入Agent和Skill。
- 需要绝对确定性的流程:Agent的决策有随机性,如果是银行核心交易、医疗诊断等场景,建议用传统工作流做强制流程控制。
- 超大上下文单次推理:Skill是为多步骤组合设计的,如果所有逻辑都在一次性Prompt内完成,就不需要它。
3.3 使用边界与合规提醒
开发Agent Skills时,有一点必须说在前面:
- 如果Skill需要访问用户文件、数据库、私人信息,必须获得用户授权,并且设置最小权限。
- 如果Skill用于生成人脸、声音、文案等,必须确保素材版权合规。
- 对外提供API服务时,要加鉴权,避免被滥用。
- 批量调用模型服务时,要注意模型服务的并发限制和内容安全政策。
4. 环境准备与前置条件
开发Agent Skills不需要很重的硬件。核心开发过程是写Python代码,调试时调用一个LLM服务即可。
4.1 推荐环境清单
| 环境项 | 推荐配置 | 说明 |
|---|---|---|
| 操作系统 | Windows 10/11、macOS、Linux | 均可 |
| Python版本 | 3.10+ | 代码用到typing和dataclass |
| 包管理 | pip / uv / conda | 任意一种 |
| LLM服务 | OpenAI兼容API | 也可以是本地vLLM / Ollama等 |
| 网络 | 能访问LLM服务即可 | 本地部署则无需公网 |
| 磁盘空间 | 代码不到10MB | 若本地模型则需几十GB以上 |
如果你用本地大模型,建议显存至少覆盖你要加载的模型体积。7B量化模型大约需要6GB以上显存,实测必须以本机为准。如果只是学Skill机制,用云API最省事。
4.2 创建项目目录
mkdir agent-skills-demo cd agent-skills-demo mkdir skills mkdir data mkdir outputs目录规划:
skills/:存放Skill定义和实现代码;data/:放测试文件;outputs/:放结果文件。
4.3 安装依赖
pip install openai python-dotenv fastapi uvicorn我们使用:
openai:调用LLM接口;python-dotenv:管理环境变量;fastapi+uvicorn:把Agent封装成HTTP服务。
5. 从零实现一个最小 Agent Skills 系统
下面我们实现一套最精简但结构完整的系统,总共四个核心文件。
5.1 定义 Skill 数据结构
文件:skill_base.py
from dataclasses import dataclass, field from typing import Callable, Any @dataclass class Skill: name: str description: str parameters: dict func: Callable[..., Any] examples: list = field(default_factory=list) def to_tool_json(self) -> dict: """转换成LLM函数调用格式""" return { "type": "function", "function": { "name": self.name, "description": self.description, "parameters": self.parameters, } }5.2 编写两个示例 Skill
文件:skills/basic_skills.py
import os import json import hashlib from typing import List def list_file_sizes(path: str = ".") -> str: """列出指定目录下所有文件的大小""" results = [] for name in os.listdir(path): full_path = os.path.join(path, name) if os.path.isfile(full_path): size = os.path.getsize(full_path) results.append({"file": name, "size_bytes": size}) return json.dumps(results, ensure_ascii=False, indent=2) def compute_text_hash(text: str) -> str: """计算文本的MD5哈希值""" return hashlib.md5(text.encode("utf-8")).hexdigest() def get_file_skills() -> List[Skill]: file_skill = Skill( name="list_file_sizes", description="当用户需要查看某个目录下的文件列表、检查文件大小、统计目录占用时需要调用这个技能。", parameters={ "type": "object", "properties": { "path": {"type": "string", "description": "要列出的目录路径"} }, "required": ["path"] }, func=list_file_sizes, examples=["帮我看看当前目录下有哪些文件,各占多少空间?"] ) hash_skill = Skill( name="compute_text_hash", description="当用户需要对一段文本计算MD5哈希值时调用这个技能。", parameters={ "type": "object", "properties": { "text": {"type": "string", "description": "要计算哈希的文本"} }, "required": ["text"] }, func=compute_text_hash, examples=["帮我给 'hello world' 算一下MD5。"] ) return [file_skill, hash_skill]5.3 实现 Agent 主循环
这是核心部分。Agent做的事情是:
- 接收用户消息;
- 把所有Skill描述发给LLM;
- LLM决定是否调用Skill,并返回结构化参数;
- 我们执行Skill函数,把结果返回给LLM;
- LLM根据执行结果生成最终回复。
文件:agent.py
import json from typing import List, Optional from openai import OpenAI from skill_base import Skill from skills.basic_skills import get_file_skills class SimpleAgent: def __init__(self, model: str = "gpt-4o-mini", base_url: Optional[str] = None): self.client = OpenAI(base_url=base_url) if base_url else OpenAI() self.model = model self.skills: dict[str, Skill] = {} def register_skill(self, skill: Skill): self.skills[skill.name] = skill def register_skills(self, skill_list: List[Skill]): for skill in skill_list: self.register_skill(skill) def _build_tools(self) -> list: return [skill.to_tool_json() for skill in self.skills.values()] def run(self, user_message: str, max_steps: int = 5, verbose: bool = True) -> str: messages = [ { "role": "system", "content": "你是一个智能助手。你可以使用工具来完成任务。" "如果你需要调用工具,请按函数调用格式返回。" }, {"role": "user", "content": user_message} ] for step in range(max_steps): response = self.client.chat.completions.create( model=self.model, messages=messages, tools=self._build_tools(), tool_choice="auto" ) message = response.choices[0].message if verbose: print(f"[Step {step + 1}] 模型回复: {message.content}") if message.tool_calls: for tool_call in message.tool_calls: print(f" 调用技能: {tool_call.function.name}") if not message.tool_calls: return message.content or "" messages.append(message) for tool_call in message.tool_calls: skill_name = tool_call.function.name arguments = json.loads(tool_call.function.arguments) skill = self.skills.get(skill_name) if not skill: result = f"错误: 技能 {skill_name} 不存在" else: try: result = skill.func(**arguments) except Exception as e: result = f"技能执行出错: {str(e)}" messages.append({ "role": "tool", "tool_call_id": tool_call.id, "content": str(result) }) return "已达到最大执行步数,任务未完成。" if __name__ == "__main__": agent = SimpleAgent() agent.register_skills(get_file_skills()) response = agent.run("帮我看看当前目录下有哪些文件,各占多少空间?") print("\n最终回复:") print(response)5.4 运行测试
python agent.py如果一切正常,你会看到类似这样的流程输出:
[Step 1] 模型回复: None 调用技能: list_file_sizes [Step 2] 模型回复: 当前目录下的文件如下: - skill_base.py: 2215 字节 - agent.py: 3451 字节 ...这里你可以非常直观地看到Agent的决策路径:LLM判断“用户想了解文件大小”,于是调用list_file_sizes技能,拿到结果后组织成自然语言回答。
如果模型输出不稳定,可以在消息中加入Skill的examples作为少样本示例,提高调用准确率。
6. 扩展更多技能:让 Agent 处理复合任务
上面的两个技能都偏向“查询型”。在实际开发中,Agent Skills 更常见的用法是组合型任务。
举例:我们增加一个write_file技能,让Agent具备“查看文件 -> 提取信息 -> 写入汇总文件”的能力。
新增代码到skills/basic_skills.py:
def write_text_file(filename: str, content: str) -> str: """把内容写入指定文件""" if not filename.endswith(".txt"): filename += ".txt" with open(filename, "w", encoding="utf-8") as f: f.write(content) return f"文件已写入: {filename}" write_skill = Skill( name="write_text_file", description="当用户需要把文本内容保存为一个文件时调用这个技能。", parameters={ "type": "object", "properties": { "filename": {"type": "string", "description": "目标文件名"}, "content": {"type": "string", "description": "要保存的文本内容"} }, "required": ["filename", "content"] }, func=write_text_file, )然后注册进Agent:
agent.register_skill(write_skill)测试输入:
帮我统计一下当前目录里所有Python文件的大小,然后把统计结果写入 output.txt。Agent 会先调用list_file_sizes,再调用write_text_file。两个技能之间不需要你写任何额外代码,LLM会自动完成参数传递。这正是Agent Skills最核心的价值。
7. 将 Agent Skills 封装为 API 服务
在真实项目中,Agent不会只在命令行里运行,而是要给其他系统调用。
我们使用 FastAPI 封装一个标准接口。
文件:api_server.py
from fastapi import FastAPI from pydantic import BaseModel from agent import SimpleAgent from skills.basic_skills import get_file_skills app = FastAPI(title="Agent Skills Demo API") agent = SimpleAgent() agent.register_skills(get_file_skills()) session_memory = {} class ChatRequest(BaseModel): message: str session_id: str = "default" @app.post("/chat") def chat(req: ChatRequest): if req.session_id not in session_memory: session_memory[req.session_id] = [] session_memory[req.session_id].append({"role": "user", "content": req.message}) reply = agent.run(req.message) session_memory[req.session_id].append({"role": "assistant", "content": reply}) return { "session_id": req.session_id, "reply": reply } @app.get("/skills") def list_skills(): return {"skills": list(agent.skills.keys())} if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=8000)启动API服务:
python api_server.py然后另开一个终端测试:
curl -X POST http://127.0.0.1:8000/chat \ -H "Content-Type: application/json" \ -d '{"message": "帮我看看当前目录下有哪些文件", "session_id": "test1"}'返回结果:
{ "session_id": "test1", "reply": "当前目录下的文件包括:agent.py、skill_base.py、api_server.py ..." }这个接口可以轻松接进企业微信机器人、Web应用、自动化脚本等。
如果要做批量任务,只需循环调用/chat接口。比如:
import requests tasks = [ "统计data目录下所有文件大小", "计算文本 'Hello' 的MD5", ] for task in tasks: resp = requests.post("http://127.0.0.1:8000/chat", json={ "message": task, "session_id": f"batch-{task[:5]}" }) print(resp.json()["reply"])8. 资源占用与性能观察
8.1 观察哪些指标
Agent Skills 系统的性能瓶颈通常不在代码本身,而在:
- LLM推理延迟:每轮对话至少一次模型请求,多技能组合需要两到三次请求。
- Token消耗:工具描述和工具返回结果都会占用上下文。
- 状态大小:对话历史越长,Token成本越高。
8.2 如何设计实验观察
- 先用简单任务记录一次调用的总耗时和Token消耗。
- 增加一个需要两次技能调用的复合任务,对比耗时和Token增长。
- 检查工具返回内容是否过大,比如
list_file_sizes返回所有文件信息,在大目录下会产生大量Token。
8.3 优化手段
- 给Tool返回结果做截断,超过一定长度只保留摘要。
- 对话历史做滑动窗口,只保留最近若干轮。
- 技能描述写精简,减少每轮请求的固定Token开销。
- 多次技能调用之间使用
tool_choice: "none"强制结束,避免Agent反复调用。
9. 常见问题与排查方法
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 模型不调用任何技能 | Skill描述不够具体,或参数Schema错误 | 对比当前模型支持的tool格式 | 优化description,补全examples |
| 技能调用时参数缺失 | LLM没有按Schema生成参数 | 查看完整LLM返回 | 简化参数数量,必填参数写清楚 |
| 技能执行报错 | 函数内部没有做类型校验 | 在func内打印传入参数 | 增加try/except和参数默认值 |
| 上下文越来越大 | 工具返回值太大或历史未裁剪 | 打印messages长度和Token数 | 对工具返回做截断;裁剪历史 |
| API调用超时 | 模型响应慢或网络问题 | 设置timeout参数 | 调大timeout;增加重试 |
| 并发请求互相干扰 | 全局Agent实例共享状态 | 检查是否有共享变量 | 每次请求创建Agent,或用session隔离 |
| 批量任务卡住 | 部分任务触发死循环 | 设置max_steps上限 | 增加step超时和条件判断 |
一个大坑是:很多模型对工具描述非常敏感。如果你写的描述是“列出文件大小”,模型可能不知道该在什么场景调用;如果写成“当用户询问任何与文件信息、目录内容、磁盘占用有关的问题时调用”,调用准确率会明显提升。
10. 工程化最佳实践
开发Agent Skills不能只停留在Demo阶段,真正接入业务系统需要注意以下几点。
10.1 Skill 目录设计
建议每个Skill保持单一职责:
skills/ file_operations/ __init__.py list_files.py write_files.py move_files.py text_processing/ __init__.py summarize.py translate.py web_tools/ __init__.py fetch_url.py10.2 增加执行日志
每个Skill调用都应该记录:
- 调用时间;
- 输入参数;
- 返回结果长度;
- 是否成功。
这样能快速定位是LLM决策错误还是Skill代码错误。
10.3 设置权限边界
如果Skill要执行系统命令或写文件,务必限定可操作目录,避免Agent因为误导性Prompt产生危险操作。例如:
allowed_root = "/path/to/sandbox" def safe_path(path): real_path = os.path.realpath(path) if not real_path.startswith(allowed_root): raise PermissionError("路径越界") return real_path10.4 成本控制
为每轮对话设置Token预算,超过预算自动终止。调用LLM前,先过滤掉当前任务用不到的Skill,减少上下文负载。
10.5 数据合规
如果用Agent Skills处理用户上传的文件、图片或音视频,要明确告知用户用途,并在任务结束后按策略删除临时数据。批量生成、批量处理的内容,发布前必须人工复核。
11. 总结与下一步
Agent Skills值得最先验证的功能是“多技能组合调用”。先做一个包含文件操作和文本处理的最小系统,让Agent自动完成“读取文件-抽取内容-保存结果”的全流程。跑通之后,你就能体会到它和普通Prompt模板的本质区别:你不再写死执行步骤,而是交给Agent动态编排。
最容易踩的坑有两个:一个是Skill描述写得含糊,导致模型不调用或乱调用;另一个是忘记裁剪上下文,导致长会话后Token成本飙升。开发阶段就先把日志和控制参数加上,能省掉大量排查时间。
下一步你可以尝试的方向:
- 接入更多数据源:数据库查询、对象存储、HTTP API。
- 增加多轮记忆:用向量库保存历史任务状态。
- 把Skill发布成独立微服务:多个Agent共享同一组技能。
- 引入人工审核机制:Agent执行关键操作前需要审批确认。
这篇的代码已经能构成一个最小可运行框架,直接拿去做二次开发也够用。建议先跑通单技能,再加组合任务,最后再考虑接入业务系统。