Skill原理及国内大模型实践

一、Skill 原理回顾(国内大模型视角)

1.1 Skill 的本质

Skill 是给 Agent 装备的"技能包",包含说明书(Markdown)、可执行脚本和依赖资源。Agent 通过三层加载机制使用 Skill:

  1. 启动时:扫描 Skill 目录,读取元信息(名称+简介)注入系统提示词
  2. 触发时:按需加载完整的 Skill 说明书到上下文
  3. 执行时:根据说明书调用外部脚本/工具执行任务

1.2 国内大模型的工具调用能力

关键理解 :大模型本身不需要理解工具的具体实现,只负责根据工具列表生成调用指令。

组件 职责
大模型 根据工具描述决定"是否调用"+"传什么参数"
开发者代码 实际执行工具调用(解析指令、调用API、返回结果)
工具定义 以 JSON Schema 形式描述工具名称、参数、功能

国内主流大模型(Qwen、DeepSeek、Kimi)均支持工具调用,且 API 接口兼容 OpenAI 格式。

二、环境准备

2.1 选择模型平台

本次 Demo 使用 阿里云通义千问 Qwen,原因:

  • API 兼容 OpenAI 格式,调用简单
  • 支持工具调用(Tool Calling)
  • 国内直连,无需特殊网络设置

2.2 获取 API Key

  1. 访问阿里云百炼平台
  2. 开通"模型服务",创建 API-KEY
  3. 将 API Key 保存到环境变量:
bash 复制代码
export DASHSCOPE_API_KEY="sk-xxxxxxxxxxxx"

2.3 安装依赖

bash 复制代码
# 创建虚拟环境
python -m venv venv
source venv/bin/activate  # Linux/Mac
# venv\Scripts\activate  # Windows

# 安装依赖
pip install openai python-dotenv requests

三、完整代码实现

3.1 项目结构

复制代码
skill_demo/
├── .env                    # 环境变量配置
├── main.py                 # 主程序入口
├── skill_loader.py         # Skill 加载器
├── tools.py                # 工具函数定义
├── skills/                 # Skill 存放目录
│   └── web_summarizer/     # 网页总结 Skill
│       ├── skill.md        # 说明书
│       └── script.py       # 爬虫脚本
└── output/                 # 输出目录

3.2 Skill 说明书示例 (skills/web_summarizer/skill.md)

markdown 复制代码
# 网页内容总结器

## 元信息
- 名称: web_summarizer
- 简介: 抓取指定 URL 的网页内容并生成摘要

## 使用说明
当你需要总结一个网页的内容时,使用此技能。

## 执行步骤
1. 调用脚本 `script.py` 传入 URL 参数
2. 脚本会抓取网页正文内容并保存到 output 目录
3. 读取生成的 Markdown 文件进行总结

## 依赖
- Python 包: requests, beautifulsoup4

## 脚本调用方式
```bash
python skills/web_summarizer/script.py --url <URL> --output output/summary.md
复制代码
### 3.3 爬虫脚本 (skills/web_summarizer/script.py)

```python
#!/usr/bin/env python3
import requests
import argparse
from bs4 import BeautifulSoup
import re

def fetch_webpage(url):
    """抓取网页并提取正文内容"""
    headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'}
    try:
        response = requests.get(url, headers=headers, timeout=10)
        response.encoding = 'utf-8'
        soup = BeautifulSoup(response.text, 'html.parser')
        
        # 移除 script 和 style 标签
        for script in soup(["script", "style"]):
            script.decompose()
        
        # 获取文本并清理
        text = soup.get_text()
        lines = (line.strip() for line in text.splitlines())
        chunks = (phrase.strip() for line in lines for phrase in line.split("  "))
        text = ' '.join(chunk for chunk in chunks if chunk)
        
        # 限制长度
        return text[:5000] + ("..." if len(text) > 5000 else "")
    except Exception as e:
        return f"抓取失败: {str(e)}"

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('--url', required=True, help='目标 URL')
    parser.add_argument('--output', required=True, help='输出文件路径')
    args = parser.parse_args()
    
    content = fetch_webpage(args.url)
    
    with open(args.output, 'w', encoding='utf-8') as f:
        f.write(f"# 网页摘要\n\n")
        f.write(f"原始 URL:{args.url}\n\n")
        f.write(f"## 内容\n\n{content}\n")
    
    print(f"内容已保存到 {args.output}")

if __name__ == '__main__':
    main()

3.4 Skill 加载器 (skill_loader.py)

python 复制代码
import os
import glob
from pathlib import Path

class SkillLoader:
    """Skill 加载器:负责扫描、读取元信息和加载详细说明书"""
    
    def __init__(self, skill_dirs=None):
        self.skill_dirs = skill_dirs or ['./skills']
        self.skills_meta = {}  # name -> {name, description, path}
        self._scan_skills()
    
    def _scan_skills(self):
        """扫描所有 Skill 目录,读取元信息"""
        for base_dir in self.skill_dirs:
            base_path = Path(base_dir)
            if not base_path.exists():
                continue
            
            # 查找所有包含 skill.md 的子目录
            skill_files = glob.glob(str(base_path / '**' / 'skill.md'), recursive=True)
            
            for skill_file in skill_files:
                skill_dir = Path(skill_file).parent
                skill_name = skill_dir.name
                
                # 解析 skill.md 获取元信息
                with open(skill_file, 'r', encoding='utf-8') as f:
                    content = f.read()
                
                # 简单解析:从 Markdown 提取标题和简介
                name = skill_name
                description = ""
                
                lines = content.split('\n')
                for i, line in enumerate(lines):
                    if line.startswith('# '):  # 一级标题
                        name = line[2:].strip()
                    if '简介' in line and i < len(lines)-1:
                        description = lines[i+1].strip()
                        break
                
                if not description:
                    description = f"使用 {name} 技能"
                
                self.skills_meta[skill_name] = {
                    'name': skill_name,
                    'display_name': name,
                    'description': description,
                    'path': str(skill_dir),
                    'md_path': skill_file
                }
    
    def get_skills_prompt(self):
        """生成注入系统提示词的技能列表"""
        if not self.skills_meta:
            return "当前没有可用技能。"
        
        prompt = "\n## 可用技能列表\n"
        for name, meta in self.skills_meta.items():
            prompt += f"- {meta['display_name']}({name}):{meta['description']}\n"
        prompt += "\n当需要使用某个技能时,调用 load_skill 工具加载详细说明。\n"
        return prompt
    
    def load_skill(self, skill_name):
        """加载指定技能的完整 Markdown 说明书"""
        if skill_name not in self.skills_meta:
            return f"错误:未找到技能 '{skill_name}'"
        
        md_path = self.skills_meta[skill_name]['md_path']
        with open(md_path, 'r', encoding='utf-8') as f:
            return f.read()

3.5 工具函数定义 (tools.py)

python 复制代码
import subprocess
import os
import json

class Tools:
    """Agent 可用的工具集合"""
    
    def __init__(self, skill_loader):
        self.skill_loader = skill_loader
    
    def load_skill(self, skill_name: str) -> str:
        """
        加载技能说明书
        
        Args:
            skill_name: 技能名称
        Returns:
            技能 Markdown 内容
        """
        return self.skill_loader.load_skill(skill_name)
    
    def run_script(self, command: str) -> str:
        """
        执行 Shell 命令
        
        Args:
            command: 要执行的命令
        Returns:
            命令输出结果
        """
        try:
            result = subprocess.run(
                command,
                shell=True,
                capture_output=True,
                text=True,
                timeout=30
            )
            if result.returncode == 0:
                return result.stdout
            else:
                return f"执行失败:{result.stderr}"
        except Exception as e:
            return f"执行异常:{str(e)}"
    
    def read_file(self, file_path: str) -> str:
        """
        读取文件内容
        
        Args:
            file_path: 文件路径
        Returns:
            文件内容
        """
        try:
            if not os.path.exists(file_path):
                return f"文件不存在:{file_path}"
            
            with open(file_path, 'r', encoding='utf-8') as f:
                return f.read()
        except Exception as e:
            return f"读取失败:{str(e)}"
    
    def get_tool_definitions(self):
        """获取工具定义列表(用于传递给大模型)"""
        return [
            {
                "type": "function",
                "function": {
                    "name": "load_skill",
                    "description": "加载指定技能的详细说明书,返回 Markdown 格式内容",
                    "parameters": {
                        "type": "object",
                        "properties": {
                            "skill_name": {
                                "type": "string",
                                "description": "技能名称,如 'web_summarizer'"
                            }
                        },
                        "required": ["skill_name"]
                    }
                }
            },
            {
                "type": "function",
                "function": {
                    "name": "run_script",
                    "description": "执行 Shell 命令,用于运行技能脚本",
                    "parameters": {
                        "type": "object",
                        "properties": {
                            "command": {
                                "type": "string",
                                "description": "要执行的完整命令"
                            }
                        },
                        "required": ["command"]
                    }
                }
            },
            {
                "type": "function",
                "function": {
                    "name": "read_file",
                    "description": "读取文件内容,用于获取脚本执行结果",
                    "parameters": {
                        "type": "object",
                        "properties": {
                            "file_path": {
                                "type": "string",
                                "description": "文件路径"
                            }
                        },
                        "required": ["file_path"]
                    }
                }
            }
        ]

3.6 主程序 (main.py)

python 复制代码
import os
import json
from dotenv import load_dotenv
from openai import OpenAI
from skill_loader import SkillLoader
from tools import Tools

load_dotenv()

class SkillAgent:
    """支持 Skill 的 Agent 实现"""
    
    def __init__(self, api_key=None, base_url=None):
        self.api_key = api_key or os.getenv("DASHSCOPE_API_KEY")
        if not self.api_key:
            raise ValueError("请设置 DASHSCOPE_API_KEY 环境变量")
        
        # 使用阿里云 DashScope 的 OpenAI 兼容接口
        self.client = OpenAI(
            api_key=self.api_key,
            base_url=base_url or "https://dashscope.aliyuncs.com/compatible-mode/v1"
        )
        
        # 初始化 Skill 加载器
        self.skill_loader = SkillLoader()
        
        # 初始化工具
        self.tools = Tools(self.skill_loader)
        
        # 构建系统提示词
        self.system_prompt = self._build_system_prompt()
        
        # 对话历史
        self.messages = []
    
    def _build_system_prompt(self):
        """构建系统提示词(注入技能列表)"""
        base_prompt = """你是一个可以帮助用户完成各种任务的 AI 助手。
你可以使用以下工具来获取信息或执行操作:
- load_skill:加载技能说明书,了解如何使用某个技能
- run_script:执行 Shell 命令,运行脚本
- read_file:读取文件内容

当用户请求需要使用特定技能时,请遵循以下流程:
1. 调用 load_skill 加载该技能的说明书
2. 根据说明书中的步骤,调用 run_script 执行相应脚本
3. 调用 read_file 读取执行结果
4. 基于结果回答用户问题
"""
        # 注入技能元信息
        base_prompt += self.skill_loader.get_skills_prompt()
        return base_prompt
    
    def run(self, user_input):
        """处理用户输入,执行工具调用循环"""
        print(f"\n👤 用户: {user_input}\n")
        
        # 初始化消息
        if not self.messages:
            self.messages = [
                {"role": "system", "content": self.system_prompt},
                {"role": "user", "content": user_input}
            ]
        else:
            self.messages.append({"role": "user", "content": user_input})
        
        # 工具调用循环
        max_iterations = 5
        for i in range(max_iterations):
            print(f"🔄 第 {i+1} 次推理...")
            
            # 调用大模型
            response = self.client.chat.completions.create(
                model="qwen-plus",  # 使用 qwen-plus 模型
                messages=self.messages,
                tools=self.tools.get_tool_definitions(),
                tool_choice="auto"
            )
            
            message = response.choices[0].message
            
            # 如果没有工具调用,直接返回
            if not message.tool_calls:
                print("✅ 完成回答\n")
                self.messages.append({"role": "assistant", "content": message.content})
                return message.content
            
            # 处理工具调用
            print(f"🔧 需要调用工具...")
            self.messages.append(message)
            
            for tool_call in message.tool_calls:
                function_name = tool_call.function.name
                arguments = json.loads(tool_call.function.arguments)
                
                print(f"  调用: {function_name}, 参数: {arguments}")
                
                # 执行工具函数
                if hasattr(self.tools, function_name):
                    func = getattr(self.tools, function_name)
                    result = func(**arguments)
                else:
                    result = f"错误:未知工具 {function_name}"
                
                print(f"  结果: {result[:100]}...\n")
                
                # 添加工具响应
                self.messages.append({
                    "role": "tool",
                    "tool_call_id": tool_call.id,
                    "content": result
                })
        
        return "已达到最大迭代次数,请重试。"

def main():
    """主函数:交互式运行"""
    agent = SkillAgent()
    
    print("="*50)
    print("Skill Agent Demo (使用 Qwen 国内大模型)")
    print("="*50)
    print("可用技能:网页总结器 (web_summarizer)")
    print("示例输入:请帮我总结这个网页的内容:https://www.example.com")
    print("输入 'exit' 退出\n")
    
    while True:
        user_input = input("\n请输入: ").strip()
        if user_input.lower() in ['exit', 'quit']:
            break
        
        response = agent.run(user_input)
        print(f"\n🤖 助手:\n{response}\n")

if __name__ == "__main__":
    main()

四、运行演示

4.1 启动 Agent

bash 复制代码
python main.py

4.2 交互过程

复制代码
==================================================
Skill Agent Demo (使用 Qwen 国内大模型)
==================================================
可用技能:网页总结器 (web_summarizer)
示例输入:请帮我总结这个网页的内容:https://www.example.com
输入 'exit' 退出

请输入: 请帮我总结这个网页的内容:https://www.baidu.com

👤 用户: 请帮我总结这个网页的内容:https://www.baidu.com

🔄 第 1 次推理...
🔧 需要调用工具...
  调用: load_skill, 参数: {'skill_name': 'web_summarizer'}
  结果: # 网页内容总结器

## 元信息
- 名称: web_summarizer
- 简介: 抓取指定 URL 的网页内容并生成摘要

...

🔄 第 2 次推理...
🔧 需要调用工具...
  调用: run_script, 参数: {'command': 'python skills/web_summarizer/script.py --url https://www.baidu.com --output output/summary.md'}
  结果: 内容已保存到 output/summary.md...

🔄 第 3 次推理...
🔧 需要调用工具...
  调用: read_file, 参数: {'file_path': 'output/summary.md'}
  结果: # 网页摘要

原始 URL:https://www.baidu.com

## 内容

百度一下,你就知道 关于百度 ...

🔄 第 4 次推理...
✅ 完成回答

🤖 助手:
根据网页内容,这是百度的首页,主要提供搜索引擎服务。页面包含以下主要元素:
- 搜索框,支持网页、资讯、贴吧等搜索
- 百度产品入口,如地图、视频、百科等
- 新闻资讯栏目,显示热门新闻标题
- 登录和设置选项

由于该页面主要是搜索入口,实际内容较少,主要功能是引导用户进行搜索操作。

4.3 生成的输出文件

output/summary.md 内容示例:

markdown 复制代码
# 网页摘要

原始 URL:https://www.baidu.com

## 内容

百度一下,你就知道 关于百度 网页 资讯 贴吧 知道 视频 图片 音乐 地图 文库 更多>> 登录 百度首页 设置 百度新闻 热点要闻 国内 国际 体育 娱乐 财经 科技 互联网 游戏 军事 汽车 房产 时尚 社会 情感 大家都在搜 1 人工智能 2 新能源汽车 3 国产芯片 ...

五、原理图说明

5.1 Skill 三层加载机制

执行时
触发时
启动时
扫描 skills 目录
读取每个 skill.md
提取元信息

名称+简介
注入系统提示词
用户请求

总结网页
Agent 推理
需要 web_summarizer 技能
调用 load_skill 工具
加载完整 skill.md
解析 skill.md

发现需运行脚本
调用 run_script 工具
执行 script.py

抓取网页内容
调用 read_file 工具
读取输出文件
生成最终回答

5.2 工具调用流程

外部脚本 工具函数 大模型(Qwen) Agent 用户 外部脚本 工具函数 大模型(Qwen) Agent 用户 推理需要 web_summarizer 解析 skill.md,需要运行脚本 需要读取输出文件 基于内容生成总结 总结网页:https://... 发送消息+工具列表 调用 load_skill(skill_name) 执行 load_skill 返回 skill.md 内容 发送工具结果 调用 run_script(command) 执行 run_script 执行 python script.py 返回执行状态 返回执行结果 发送工具结果 调用 read_file(path) 执行 read_file 返回文件内容 发送工具结果 返回最终回答 显示总结结果

六、关键点总结

6.1 国内大模型工具调用的特点

  1. API 兼容性:主流国内模型(Qwen、DeepSeek、Kimi)均兼容 OpenAI 格式,可使用同一套代码调用
  2. 工具定义方式 :通过 tools 参数传递 JSON Schema 格式的工具定义
  3. 调用流程 :模型返回 tool_calls 字段,开发者解析后执行并回传结果

6.2 Skill 系统的核心优势

  • 按需加载:启动时只加载元信息,使用时才加载详细说明书,节省 Token
  • 可扩展性:新增 Skill 只需添加文件夹,无需修改 Agent 代码
  • 灵活性:Skill 可包含任意脚本,突破 LLM 自身能力限制

6.3 代码要点

组件 作用
SkillLoader 扫描 Skill 目录、管理元信息、提供加载功能
Tools 封装可被模型调用的工具函数
main.py Agent 主循环,处理消息和工具调用
skill.md Skill 说明书,包含元信息和执行步骤
script.py 实际执行任务的脚本

七、扩展建议

7.1 添加更多 Skill

只需在 skills/ 目录下新建文件夹,包含:

  • skill.md:说明书
  • 任意脚本文件(Python/Node.js/Shell)
  • 依赖清单

7.2 使用其他国内大模型

平台 API 地址 模型示例
阿里云百炼 https://dashscope.aliyuncs.com/compatible-mode/v1 qwen-max, qwen-plus
硅基流动 https://api.siliconflow.cn/v1 deepseek-coder, qwen2.5
Moonshot https://api.moonshot.cn/v1 kimi-k2-turbo-preview

7.3 高级优化

  • MCP 集成:使用模型上下文协议标准化工具接入
  • 流式输出:实现逐字返回,提升用户体验
  • 会话记忆:保存对话历史,支持多轮复杂任务

总结 :通过国内大模型的工具调用能力,我们可以实现与视频案例完全相同的 Skill 系统。核心在于理解大模型只负责生成调用指令,实际执行由开发者代码完成,这一机制使得 Skill 具有无限扩展的可能性。

相关推荐
IT管理圈2 小时前
Cursor Rules 实战指南—让AI按你的规矩写代码
python
Java后端的Ai之路2 小时前
微调模型成本太高,用RAG技术,低成本实现AI升级
开发语言·人工智能·python·rag·ai升级
Allen正心正念20252 小时前
AI coding——Cursor版本履历与特性介绍
人工智能
Guass2 小时前
【搭建OpenClaw】
人工智能
Jason_Honey22 小时前
【蚂蚁金服Agent算法岗一面】
人工智能·算法·自然语言处理·面试
智算菩萨2 小时前
交错多模态内容生成:从“单张图“到“图文混排长文“的创作范式变革
人工智能·算法·aigc
楚兴2 小时前
Go + Eino 构建 AI Agent(一):Hello LLM
人工智能·后端
喵手2 小时前
Python爬虫实战:从零构建书籍价格情报数据库(附CSV导出 + SQLite持久化存储)!
爬虫·python·爬虫实战·零基础python爬虫教学·csv导出·构建书籍价格情报·书籍价格采集
用户5191495848452 小时前
CitrixBleed 2 内存泄漏漏洞利用框架 (CVE-2025-5777)
人工智能·aigc