智能体构建:基于SKILL的AI智能体构建:模块化能力编排+实时交互系统全实现.136

一、前言

现在不管是做企业应用、内部效率工具还是对外AI产品,大家都越来越清楚一件事:单纯靠大模型聊天,根本解决不了真实业务问题。用户要的不是说得好听,而是能真正把事办成,比如自动查数据、生成报表、处理工单、对接业务系统,甚至完成一整串复杂流程。

但直接把一堆 API、工具塞给大模型去调用,很容易出现逻辑混乱、执行出错、不可控、不好维护的问题。业务一变,代码就要重写,模型也跟着乱决策。所以行业里慢慢形成了一种更稳定、更工程化的思路:把 、AI的能力拆成一个个标准化、可复用、可管理的技能单元,也就是我们常说的SKILL体系。

基于SKILL构建 AI 智能体,本质上就是让AI从即兴发挥变成按规办事,让每一项能力都可注册、可调度、可监控、可迭代。今天我们就从业务落地的实际应用视角,完整讲清楚:如何从零搭建一套可直接用在生产环境的SKILL驱动AI智能体,从架构设计、技能定义,到前后端实现、调度执行与部署,全部一次性说清道明,了解透彻。

二、核心概念

1. 什么是 SKILL

SKILL可以理解未智能体的"能力插件",是封装特定任务逻辑、输入输出、触发条件与执行流程的标准化单元,也是AI智能体能够落地业务、解决实际问题的核心基础。

区别于普通 Tool(工具):

SKILL不只是简单的调用接口,而是包含场景描述、任务目标、触发条件、执行步骤、输出规范、示例案例、依赖关系七大完整要素,是可独立开发、独立测试、独立部署、独立迭代的最小能力单元;

比如"查询天气"这个 SKILL,不只是调用天气API,还会明确适用场景、输入的城市格式、输出的天气字段、异常处理方式,甚至给出测试示例,方便开发和维护。

区别于子Agent(子智能体):

SKILL更轻量、更聚焦,只负责完成某一个单一的具体任务,没有独立的上下文管理和记忆能力,所有的调度、上下文衔接、记忆存储,都由主Agent统一管控;

比如"生成周报"和"发送邮件"是两个独立SKILL,主Agent会根据用户需求,调度这两个SKILL协同完成"生成周报并发送"的完整任务,而每个SKILL只专注自己的核心功能。

2. SKILL 架构的核心价值

2.1 模块化

  • 将智能体的能力拆分为独立的SKILL单元,每个SKILL负责一项具体任务,开发时可分工协作,不同开发人员负责不同SKILL;
  • 迭代时只需修改对应 SKILL,无需改动整个智能体代码,大幅降低系统耦合度,提升开发效率。

2.2 可扩展

  • 支持SKILL的动态注册与卸载,新增业务能力时,只需按标准开发新的SKILL并放入指定目录,系统会自动加载,无需修改主 Agent 核心代码;
  • 淘汰无用能力时,直接删除 SKILL 目录即可,灵活适配业务迭代需求。

2.3 可观测

  • 每个SKILL的执行过程都有清晰的日志记录,从触发、参数校验、执行到输出,全链路可监控、可审计;
  • 一旦出现问题,能快速定位到具体SKILL,无需排查整个系统,降低问题排查成本。

2.4 高可靠

  • 单一SKILL出现故障(如 API 调用失败、代码报错)时,不会影响整个智能体的正常运行,系统会自动触发降级机制;
  • 返回友好提示或调用备用 SKILL,保障业务流程不中断,提升系统稳定性。

2.5 易维护

  • 所有SKILL按统一目录结构组织,每个SKILL都有标准化的元数据SKILL.md,清晰记录其功能、用法和依赖;
  • 后续维护时,只需查看对应 SKILL 的文档和代码,无需梳理整个系统逻辑,降低维护成本,也便于新人快速上手。

3. SKILL 的能力分类

类别 核心功能 典型示例 实现方式
基础交互 对话理解、意图识别 问候、澄清、结束 大模型 Prompt
信息获取 数据查询、API 调用 天气、新闻、数据库查询 代码 + API
内容生成 文案、报告、代码 写邮件、生成周报 大模型 + 模板
工具操作 文件、系统、第三方工具 读写文件、发送邮件 代码执行
流程编排 多技能组合、任务规划 差旅预订、报告生成 工作流引擎
领域专业 垂直行业能力 医疗问诊、金融分析 领域知识 + 技能

4. SKILL 的标准结构

skill-name/

├── SKILL.md # 技能元数据(核心)

├── init.py # 技能入口

├── impl.py # 技能实现逻辑

├── examples/ # 输入输出示例

├── tests/ # 单元测试

└── resources/ # 配置/依赖资源

三、SKILL.md 详细说明

SKILL.md是SKILL体系最核心的文件,相当于每个技能的身份证 + 说明书 + 接口文档 + 运行手册,系统、开发者、大模型都靠它工作;

1. SKILL.md 是什么

SKILL.md 是技能的标准化元数据文件,采用YAML + Markdown 混合格式。它的作用:

  • 让系统自动识别技能
  • 让调度引擎知道何时触发
  • 让执行器知道入参和出参
  • 让大模型知道如何调用
  • 让开发者知道功能、边界、示例
  • 让运维知道依赖、权限、版本

总结来说:没有SKILL.md,SKILL就无法被智能体识别与使用。

2. SKILL.md 固定标准格式

XML 复制代码
---
# YAML 格式:机器可解析字段(核心!系统读取这里)
name: 技能唯一标识
description: 技能一句话功能描述
version: 版本号
author: 开发者
category: 技能分类
priority: 执行优先级

trigger:
  keywords: []
  intent: []
  context: []
  confidence: 0.8

input_schema:
  type: object
  properties: {}
  required: []

output_schema:
  type: object
  properties: {}

examples:
  - input: ""
    output: {}
  - input: ""
    output: {}

dependencies:
  api: []
  skills: []
  permissions: []

limits:
  max_retries: 2
  timeout: 5
  rate_limit: "10/min"
---

# Markdown 格式:注释文档(系统不解析)

## 功能说明
...

## 执行流程
1. ...
2. ...

## 异常处理
...

## 使用注意事项
...

3. 格式内容详解

3.1 基础信息段

XML 复制代码
name: weather_query           # 唯一标识(英文、下划线,不能重复)
description: 查询城市天气    # 功能描述(给大模型+人看)
version: 1.0.0               # 版本(方便升级/回滚)
author: AI-Team              # 开发者
category: info_query         # 分类:info_query / operate / generate / workflow
priority: 5                  # 优先级1-10,数字越高越先匹配

用途:

  • 系统唯一识别技能
  • 展示在管理后台
  • 做版本控制
  • 做技能分类检索

3.2 触发规则 trigger

XML 复制代码
trigger:
  keywords: ["天气", "温度", "下雨", "预报"]   # 关键词匹配
  intent: ["查询天气", "获取气象"]            # 意图名称
  context: ["出行", "旅游", "日常"]           # 场景上下文
  confidence: 0.8                            # 触发置信度阈值

作用:让智能体知道什么时候用这个技能

  • keywords:用户输入包含这些词,优先匹配
  • intent:意图标识,大模型可直接选择
  • context:场景过滤,避免乱触发
  • confidence:高于 0.8 才触发,避免误调用

业务意义:精准触发,不幻觉、不错误调用、不乱执行。

3.3 输入规范 input_schema

XML 复制代码
input_schema:
  type: object
  properties:
    city:
      type: string
      description: 城市名称
    days:
      type: integer
      default: 1
      minimum: 1
      maximum: 7
  required: ["city"]

作用:

  • 告诉大模型必须提取什么参数
  • 告诉执行器接收什么格式
  • 自动做参数校验
  • 缺失参数时自动反问用户

3.4 输出规范 output_schema

XML 复制代码
output_schema:
  type: object
  properties:
    success: {type: boolean}
    message: {type: string}
    data: {type: object}

作用:

  • 统一所有技能输出格式
  • 主 Agent 可以稳定解析结果
  • 前端可以统一渲染
  • 异常统一返回格式

3.5 示例 examples

XML 复制代码
examples:
  - input: "杭州明天天气"
    output: {"success":true,"data":{"city":"杭州",...}}
  - input: "上海未来3天天气"
    output: {"success":true,...}
  - input: "查询火星天气"
    output: {"success":false,"message":"不支持该城市"}

作用:

  • 给大模型做小样本学习
  • 给测试做自动化用例
  • 给开发者理解输入输出
  • 提升意图识别准确率

3.6 依赖 dependencies

XML 复制代码
dependencies:
  api: ["weather_api"]
  skills: []
  permissions: ["user:normal"]

作用:

  • 系统启动时检查依赖是否就绪
  • 权限控制
  • 技能之间的依赖编排
  • API 密钥检查

3.7 限制 limits

XML 复制代码
limits:
  max_retries: 2
  timeout: 5
  rate_limit: "10/min"

作用:

  • 失败自动重试
  • 超时强制结束
  • 防刷限流
  • 保证系统稳定

3.8 注释说明文档

bash 复制代码
## 功能说明
用于查询国内城市实时天气与预报。

## 执行流程
1. 解析用户输入,提取城市、天数
2. 校验城市是否支持
3. 调用天气API
4. 返回格式化结果

## 异常处理
- API 失败:返回友好提示
- 城市不支持:明确提示

## 注意事项
仅支持国内一线/新一线城市

作用:

  • 团队协作文档
  • 维护手册
  • 上线说明
  • 业务边界说明

4. SKILL.md 必不可少

  • 系统自动加载技能
  • 大模型知道如何调用
  • 调度引擎知道如何触发
  • 执行器知道参数格式
  • 开发者知道功能边界
  • 测试知道用例标准
  • 运维知道依赖与权限
  • 业务能做到可管控、可审计、可迭代

它是SKILL体系的"契约文件",没有它,模块化、工程化都无法实现。

四、SKILL 智能体整体架构

1. 分层架构图

  • 用户交互层与前端/API网关作为入口,将请求汇聚至 Agent核心层。
  • Agent核心层依次调用 技能注册中心 → 技能调度引擎 → 技能执行层 → 基础设施层,完成从意图理解到技能执行的全链路。
  • 各层职责清晰,便于扩展与维护。

2. 核心组件详解

2.1 Agent Core(智能体核心)

  • **接收用户请求:**作为入口,统一处理用户输入,完成初步清洗与格式归一化。
  • **意图理解与任务拆解:**调用大模型进行语义解析,识别用户意图,将复杂任务拆解为可执行的子任务。
  • **调用 SkillRegistry 匹配技能:**根据解析结果,查询 SkillRegistry 获取可用的技能列表。
  • **调度执行引擎,编排技能流程:**将技能组合成工作流(DAG),并交由调度引擎执行,支持串行、并行、条件分支。
  • **管理对话记忆与上下文:**维护当前会话的短期记忆(历史轮次、关键实体),必要时从长期向量库中召回相关记忆。
  • **合成最终响应:**整合技能执行结果,利用大模型生成自然语言回复,返回给用户。

2.2 SkillRegistry(技能注册中心)

  • **扫描技能目录,加载 SKILL.md 元数据:**自动发现技能模块,解析其元数据(名称、描述、输入输出、依赖、版本)。
  • **提供技能查询、匹配、发现能力:**支持按名称、标签、语义相似度进行技能检索,为 Agent Core 提供候选技能列表。
  • **管理技能版本、依赖、权限:**维护技能升级策略,确保依赖版本兼容,控制技能调用权限(如仅管理员可用)。
  • **支持动态注册/卸载:**允许运行时添加新技能或停用过时技能,无需重启服务,实现热插拔。

2.3 Skill Scheduler(技能调度引擎)

  • **根据任务规划,选择最优技能组合:**基于成本、耗时、成功率等指标,从候选技能中选出最优执行路径。
  • **处理技能依赖、并行/串行执行:**解析技能间的输入输出依赖,自动并行无依赖的技能,提升执行效率。
  • **异常处理、重试、降级:**对执行失败的技能进行重试,如可重试错误,若仍失败则执行降级方案,如使用备选技能或返回友好提示。
  • **执行状态跟踪与日志记录:**实时记录每个技能的执行状态、耗时、输入输出,便于监控和问题排查。

2.4 Skill Executor(技能执行器)

  • **加载技能实现(代码/Prompt):**根据技能类型(函数式、API、Prompt 模板),加载对应的实现逻辑。
  • **解析输入,校验参数:**将 Agent Core 传递的上下文参数与技能要求的输入进行匹配,校验必填字段与数据类型。
  • **执行技能逻辑,调用外部工具:**实际运行技能,可能调用本地函数、远程 API、数据库查询或大模型推理。
  • **格式化输出,返回结果:**将执行结果按统一格式返回给调度引擎,便于后续技能使用或最终响应合成。

2.5 Infrastructure(基础设施)

  • **大模型API、第三方服务、数据库:**提供底层能力支撑,如LLM推理、知识库检索、业务系统对接。
  • **向量库、文件存储、消息队列:**存储非结构化数据(文档、图片)、管理异步任务、实现服务解耦。
  • **认证、监控、日志系统:**保障服务安全,实时观测性能指标,记录全链路日志用于审计与优化。

五、SKILL 智能体完整实现

1. 技术选型

  • 框架:FastAPI(API+WebSocket)
  • 技能管理:自定义 SkillRegistry + 目录扫描
  • 大模型:本地大模型或在线api调用
  • 数据校验:Pydantic
  • 技能发现:文件系统监听 + 动态加载
  • 部署:Docker + 云服务器

2. 步骤 1:设计 SKILL

2.1 SKILL 设计五步法

  • 场景定义:明确技能适用场景、用户画像、业务目标
  • 触发设计:关键词、意图、上下文触发条件
  • IO 规范:输入输出 Schema(JSON Schema)
  • 执行逻辑:步骤化流程、工具调用、异常处理
  • 示例与测试:3 + 输入输出示例,覆盖正常、边界、异常

2.2 实战:天气查询 SKILL设计

2.2.1 目录结构

skill_agent_project/

├── .env # 环境变量(API KEY、配置)
├── main.py # 后端入口:FastAPI + WebSocket + Agent调度
├── skill_registry.py # 技能注册中心 + 动态加载 + 文件监听

├── requirements.txt # 所有依赖清单

├── Dockerfile # 生产部署(可选)
├── static/ # 前端静态资源
│ └── index.html
# 实时对话前端界面
└── skills/ # 所有技能统一存放目录(核心)
└── weather_query/ # 示例技能:天气查询
├── SKILL.md # 技能元数据(Schema、触发、描述)
├── impl.py
# 技能真正执行逻辑

├── examples/ # 示例输入输出(可选)

└── tests/ # 技能单元测试(可选)

注意加粗的部分,完整的一个SKILL所包含的完整文件以及文章种提供的完整示例代码,实际目录结构可参考下图:

2.2.2 完整SKILL.md

XML 复制代码
---
name: weather_query
description: 查询指定城市实时天气与未来N天预报,支持国内主要城市
version: 1.0.0
author: DevTeam
trigger:
  keywords: ["天气", "温度", "下雨", "晴天", "预报", "穿衣"]
  intent: ["查询天气", "获取气象信息", "天气建议"]
  context: ["出行", "旅游", "日常"]
input_schema:
  type: object
  properties:
    city:
      type: string
      description: 城市名称(如:杭州、北京)
    days:
      type: integer
      default: 1
      minimum: 1
      maximum: 7
      description: 预报天数(1-7天)
  required: ["city"]
output_schema:
  type: object
  properties:
    success: {type: boolean}
    message: {type: string}
    data:
      type: object
      properties:
        current: {type: object}
        forecast: {type: array}
examples:
  - input: "杭州明天天气怎么样?"
    output:
      success: true
      message: "查询成功"
      data: {"current": {...}, "forecast": [...]}
  - input: "查询上海未来3天天气"
    output: {"success": true, ...}
  - input: "查询火星天气"
    output: {"success": false, "message": "不支持该城市"}
dependencies:
  api: ["open-weather-map"]
  skills: []
---
# 执行流程
1. 解析用户输入,提取city和days参数
2. 校验城市有效性,仅支持国内城市
3. 调用OpenWeatherMap API获取天气数据
4. 格式化数据为标准输出结构
5. 处理API异常,返回友好提示
# 边界限制
- 仅支持中国内地城市
- 预报天数范围:1-7天
- API调用频率限制:10次/分钟

2.2.3 技能实现:impl.py

python 复制代码
import requests
import os
from pydantic import BaseModel, ValidationError
from typing import Optional, Dict, Any

# 输入输出模型
class WeatherInput(BaseModel):
    city: str
    days: int = 1

class WeatherOutput(BaseModel):
    success: bool
    message: str
    data: Optional[Dict[str, Any]] = None

# 技能实现
class WeatherQuerySkill:
    def __init__(self):
        self.api_key = os.getenv("WEATHER_API_KEY")
        self.base_url = "https://api.openweathermap.org/data/2.5"
        self.supported_cities = ["杭州", "北京", "上海", "广州", "深圳"]
        # 城市中文名到拼音的映射
        self.city_name_map = {
            "杭州": "Hangzhou",
            "北京": "Beijing",
            "上海": "Shanghai",
            "广州": "Guangzhou",
            "深圳": "Shenzhen"
        }

    def execute(self, input_data: dict) -> dict:
        """执行天气查询技能"""
        try:
            # 1. 校验输入
            validated = WeatherInput(**input_data)
            if validated.city not in self.supported_cities:
                return WeatherOutput(
                    success=False,
                    message=f"不支持城市:{validated.city},仅支持:{', '.join(self.supported_cities)}"
                ).dict()

            # 2. 调用API(将城市名转换为拼音)
            city_pinyin = self.city_name_map.get(validated.city, validated.city)
            params = {
                "q": city_pinyin,
                "appid": self.api_key,
                "units": "metric",
                "lang": "zh_cn"
            }
            response = requests.get(f"{self.base_url}/weather", params=params)
            response.raise_for_status()
            current = response.json()

            # 3. 获取预报
            forecast_params = params.copy()
            forecast_params["cnt"] = validated.days
            forecast_resp = requests.get(f"{self.base_url}/forecast", params=forecast_params)
            forecast_resp.raise_for_status()
            forecast = forecast_resp.json()

            # 4. 格式化结果
            result = {
                "current": {
                    "temp": current["main"]["temp"],
                    "desc": current["weather"][0]["description"],
                    "humidity": current["main"]["humidity"]
                },
                "forecast": [
                    {
                        "date": item["dt_txt"],
                        "temp": item["main"]["temp"],
                        "desc": item["weather"][0]["description"]
                    } for item in forecast["list"]
                ]
            }

            return WeatherOutput(
                success=True,
                message="查询成功",
                data=result
            ).dict()

        except ValidationError as e:
            return WeatherOutput(success=False, message=f"参数错误:{str(e)}").dict()
        except Exception as e:
            return WeatherOutput(success=False, message=f"服务异常:{str(e)}").dict()

# 技能入口
def get_skill():
    return WeatherQuerySkill()

3. 步骤 2:实现技能注册中心:SkillRegistry

核心功能:扫描技能目录、加载元数据、提供查询匹配,对应文件:skill_registry.py

python 复制代码
import os
import yaml
import importlib
from typing import Dict, List, Any
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler

class SkillRegistry:
    def __init__(self, skill_dir: str = "skills"):
        self.skill_dir = skill_dir
        self.skills: Dict[str, Dict[str, Any]] = {}  # name: skill_data
        self._load_all_skills()
        self._start_watcher()

    def _load_skill(self, skill_path: str):
        """加载单个技能"""
        skill_name = os.path.basename(skill_path)
        skill_md = os.path.join(skill_path, "SKILL.md")
        if not os.path.exists(skill_md):
            return

        # 解析SKILL.md
        with open(skill_md, 'r', encoding='utf-8') as f:
            content = f.read()
            if '---' in content:
                yaml_part = content.split('---')[1]
                meta = yaml.safe_load(yaml_part)
                meta["path"] = skill_path
                meta["module"] = f"skills.{skill_name}.impl"
                self.skills[meta["name"]] = meta

    def _load_all_skills(self):
        """加载所有技能"""
        self.skills.clear()
        for item in os.listdir(self.skill_dir):
            skill_path = os.path.join(self.skill_dir, item)
            if os.path.isdir(skill_path):
                self._load_skill(skill_path)
        print(f"已加载 {len(self.skills)} 个技能: {', '.join(self.skills.keys())}")

    def match_skills(self, query: str) -> List[Dict[str, Any]]:
        """根据用户查询匹配技能"""
        matched = []
        query_lower = query.lower()
        for skill in self.skills.values():
            # 关键词匹配
            for kw in skill["trigger"]["keywords"]:
                if kw.lower() in query_lower:
                    matched.append(skill)
                    break
        return matched

    def get_skill_instance(self, skill_name: str):
        """获取技能实例"""
        if skill_name not in self.skills:
            raise ValueError(f"技能不存在:{skill_name}")
        skill_meta = self.skills[skill_name]
        module = importlib.import_module(skill_meta["module"])
        return module.get_skill()

    # 文件监听:技能变更自动重载
    class SkillChangeHandler(FileSystemEventHandler):
        def __init__(self, registry):
            self.registry = registry
        def on_modified(self, event):
            if event.is_directory:
                return
            if "SKILL.md" in event.src_path:
                print("技能文件变更,重新加载...")
                self.registry._load_all_skills()

    def _start_watcher(self):
        """启动文件监听"""
        event_handler = self.SkillChangeHandler(self)
        observer = Observer()
        observer.schedule(event_handler, self.skill_dir, recursive=True)
        observer.start()

4. 步骤 3:实现Agent Core + 调度执行引擎

这里我们构建了一个基于SKILL的 AI 智能体后端服务,主要用于演示如何通过意图识别来调用特定功能。

  • 核心用途:接收用户的自然语言输入,分析意图,匹配并执行相应的技能(如计算、翻译、查询天气等),并通过 WebSocket 实现实时对话交互。
  • 核心逻辑:接收请求 → 意图理解 → 匹配技能 → 执行 → 返回结果

文件名称:main.py

python 复制代码
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse
import os
from dotenv import load_dotenv
from skill_registry import SkillRegistry

load_dotenv()
app = FastAPI(title="SKILL-Based AI Agent")

# 确保使用绝对路径
import os
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
STATIC_DIR = os.path.join(BASE_DIR, "static")

# 挂载静态文件
app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static")

registry = SkillRegistry()

# 模拟LLM类
class MockLLM:
    def __init__(self, **kwargs):
        pass
    
    def run(self, **kwargs):
        # 模拟意图识别逻辑
        query = kwargs.get("query", "")
        skills = kwargs.get("skills", "")

        # 简单的基于规则的匹配 - 返回实际的技能名称
        if "天气" in query:
            return "weather_query"
        elif "旅游" in query or "景点" in query:
            return "travel_guide"
        elif "翻译" in query:
            return "translate"
        elif "计算" in query or "加" in query or "乘" in query:
            return "calculator"
        else:
            # 返回第一个技能
            skill_list = skills.split(", ")
            return skill_list[0] if skill_list else "unknown"

llm = MockLLM()

# 模拟意图识别链
class MockIntentChain:
    def __init__(self, llm, prompt):
        self.llm = llm

    def run(self, **kwargs):
        return self.llm.run(**kwargs)

intent_chain = MockIntentChain(llm=llm, prompt=None)


# 对话记忆(模拟版本)
class MockMemory:
    def __init__(self, **kwargs):
        self.chat_history = []

memory = MockMemory()

# 连接管理
class ConnectionManager:
    def __init__(self):
        self.active_connections = []
    async def connect(self, ws):
        await ws.accept()
        self.active_connections.append(ws)
    def disconnect(self, ws):
        self.active_connections.remove(ws)
    async def send(self, msg, ws):
        await ws.send_text(msg)

manager = ConnectionManager()

# 技能执行逻辑
async def execute_skill_flow(query: str) -> str:
    """执行技能流程"""
    # 1. 匹配技能
    matched = registry.match_skills(query)
    if not matched:
        return "暂不支持该功能,请尝试其他问题"

    # 2. 意图识别选择最优技能
    skill_names = [s["name"] for s in matched]
    selected_skill_name = intent_chain.run(query=query, skills=", ".join(skill_names)).strip()

    # 3. 获取技能实例
    try:
        skill = registry.get_skill_instance(selected_skill_name)
    except:
        return f"技能 {selected_skill_name} 加载失败"

    # 4. 解析输入(简化版,实际需用大模型提取参数)
    input_data = _extract_params_from_query(selected_skill_name, query)

    # 5. 执行技能
    result = skill.execute(input_data)
    return f"【{selected_skill_name}】结果:{result}"


def _extract_params_from_query(skill_name: str, query: str) -> dict:
    """从用户查询中提取技能参数"""
    query = query.strip()

    # 计算器技能参数提取
    if skill_name == "calculator":
        # 提取数字和运算符
        import re
        # 匹配表达式(数字、运算符、括号)
        expr_match = re.search(r'[\d\+\-\*\/\^\%\(\)\. ]+', query)
        if expr_match:
            expression = expr_match.group(0).strip()
            return {"expression": expression}
        else:
            # 查找 "计算xxx" 或 "xxx等于多少" 模式
            if "计算" in query:
                expr = query.replace("计算", "").replace("等于多少", "").replace("等于", "").strip()
                return {"expression": expr}
            return {"expression": "0"}

    # 翻译技能参数提取
    elif skill_name == "translate":
        import re
        result = {"target_lang": "en", "text": "你好"}

        # 提取目标语言
        if "中文" in query:
            result["target_lang"] = "zh"
        elif "英文" in query or "英语" in query:
            result["target_lang"] = "en"
        elif "日语" in query:
            result["target_lang"] = "ja"
        elif "韩语" in query:
            result["target_lang"] = "ko"
        elif "德语" in query:
            result["target_lang"] = "de"
        elif "法语" in query:
            result["target_lang"] = "fr"

        # 提取待翻译文本(引号内的内容)
        quote_match = re.search(r'["\'<<「『](.+?)["\'」』]', query)
        if quote_match:
            result["text"] = quote_match.group(1)
        else:
            # 简单提取:"把xxx翻译成..."
            if "把" in query and "翻译成" in query:
                text_part = query.split("翻译成")[0].replace("把", "").strip()
                result["text"] = text_part
            elif "将" in query and "翻译成" in query:
                text_part = query.split("翻译成")[0].replace("将", "").strip()
                result["text"] = text_part

        return result

    # 旅游指南技能参数提取
    elif skill_name == "travel_guide":
        input_data = {"city": "杭州", "days": 1}
        if "上海" in query:
            input_data["city"] = "上海"
        elif "北京" in query:
            input_data["city"] = "北京"
        if "3天" in query:
            input_data["days"] = 3
        return input_data

    # 天气查询技能参数提取
    elif skill_name == "weather_query":
        input_data = {"city": "杭州", "days": 1}
        if "上海" in query:
            input_data["city"] = "上海"
        elif "北京" in query:
            input_data["city"] = "北京"
        elif "广州" in query:
            input_data["city"] = "广州"
        elif "深圳" in query:
            input_data["city"] = "深圳"
        if "3天" in query:
            input_data["days"] = 3
        return input_data

    # 默认返回空字典
    return {}

# WebSocket实时对话
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
    await manager.connect(websocket)
    try:
        while True:
            user_msg = await websocket.receive_text()
            await manager.send(f"[你] {user_msg}", websocket)
            await manager.send("[AI处理中...]", websocket)

            # 执行技能流程
            response = await execute_skill_flow(user_msg)
            await manager.send(f"[AI] {response}", websocket)

    except WebSocketDisconnect:
        manager.disconnect(websocket)

@app.get("/")
async def root():
    return FileResponse(os.path.join(BASE_DIR, "static", "index.html"))

@app.get("/api/skills")
async def get_skills():
    return {"message": "SKILL-Based AI Agent", "skills": list(registry.skills.keys())}

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8002)

重点功能:

  • 技能注册与匹配:利用 SkillRegistry 管理可用技能,根据用户输入匹配候选技能列表。
  • 意图识别:通过 MockLLM模拟大语言模型从候选技能中确定用户的具体意图,例如区分"计算"和"翻译"。
  • 参数提取:内置 _extract_params_from_query 函数,使用正则表达式从自然语言中提取关键参数,如提取算式、目标语言、城市名。
  • 实时通信:基于 FastAPI 的 WebSocket 实现全双工通信,支持流式或即时响应的聊天界面。
  • 静态服务:挂载静态文件目录,直接提供前端HTML页面index.html以便用户访问交互。

5. 步骤 4:前端实时交互界面

前端提供了一个可以满足交互的页面,样式可以根据我们的要求让AI替我们美化即可;

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>SKILL AI Agent 实时对话</title>
    <style>
        *{margin:0;padding:0;box-sizing:border-box;font-family:Arial}
        .container{max-width:900px;margin:20px auto;height:90vh;display:flex;flex-direction:column}
        .chat{flex:1;border:1px solid #ddd;border-radius:10px;padding:20px;overflow-y:auto;background:#f9f9f9}
        .input{display:flex;margin-top:15px}
        #msg{flex:1;padding:15px;border:1px solid #ddd;border-radius:8px;font-size:16px}
        button{padding:15px 25px;background:#007bff;color:white;border:none;border-radius:8px;margin-left:10px;cursor:pointer}
        button:hover{background:#0056b3}
        .msg{margin:10px 0;padding:12px 16px;border-radius:8px;max-width:80%}
        .user{background:#d3eafd;align-self:flex-end;margin-left:auto}
        .ai{background:#fff;align-self:flex-start;border:1px solid #eee}
        .log{color:#666;font-size:14px;padding:5px}
    </style>
</head>
<body>
    <div class="container">
        <h2 style="text-align:center">SKILL AI 智能体</h2>
        <div class="chat" id="chat"></div>
        <div class="input">
            <input id="msg" placeholder="输入问题...">
            <button onclick="send()">发送</button>
        </div>
    </div>

    <script>
        const chat = document.getElementById('chat');
        const msgInput = document.getElementById('msg');
        
        // 获取WebSocket地址,处理不同端口
        const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
        const wsHost = window.location.host || 'localhost:8002';
        const wsUrl = `${wsProtocol}//${wsHost}/ws`;
        
        console.log('Connecting to WebSocket:', wsUrl);
        
        let ws = null;
        let isConnected = false;

        function connect() {
            ws = new WebSocket(wsUrl);

            ws.onopen = () => {
                console.log('WebSocket connected');
                isConnected = true;
                addLog('已连接到服务器');
            };

            ws.onclose = () => {
                console.log('WebSocket closed');
                isConnected = false;
                addLog('连接已断开');
                // 3秒后尝试重连
                setTimeout(connect, 3000);
            };

            ws.onerror = (error) => {
                console.error('WebSocket error:', error);
                addLog('连接错误');
            };

            ws.onmessage = (e) => {
                console.log('Received message:', e.data);
                const div = document.createElement('div');
                div.className = e.data.includes('[你]') ? 'msg user' :
                               e.data.includes('[AI]') ? 'msg ai' : 'log';
                div.textContent = e.data;
                chat.appendChild(div);
                chat.scrollTop = chat.scrollHeight;
            };
        }

        // 初始连接
        connect();

        function addLog(msg) {
            const div = document.createElement('div');
            div.className = 'log';
            div.textContent = msg;
            chat.appendChild(div);
            chat.scrollTop = chat.scrollHeight;
        }

        function send() {
            const msg = msgInput.value.trim();
            if(!msg) return;
            
            if (!isConnected) {
                addLog('等待连接中...');
                return;
            }
            
            console.log('Sending message:', msg);
            try {
                ws.send(msg);
                msgInput.value = '';
            } catch (error) {
                console.error('Send error:', error);
                addLog('发送失败');
            }
        }

        msgInput.addEventListener('keypress', e => {
            if (e.key === 'Enter') {
                send();
            }
        });
    </script>
</body>
</html>

六、运行与测试

1. 启动服务

1. 创建.env文件

echo "WEATHER_API_KEY=你的天气API密钥" >> .env

2. 启动后端

python main.py

3. 访问前端

http://127.0.0.1:8002

注意: app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static") 静态文件挂载,由于我们已经挂载静态文件,启动后直接运行http://localhost:8002/即可运行文件

2. 测试用例

输入:查询北京的天气? → 调用 weather_query,返回':{temp': 12.94, 'desc': '阴,多云', 'humidity': 23}

输入:推荐杭州的旅游景点 → 调用travel_guide,返回:杭州1天旅游推荐:西湖, 雷峰塔

输入:将"你好"翻译成英文 → 调用translate,返回:{'original_text': '你好', 'translated_text': 'Hello', 'source_lang': 'auto', 'target_lang': 'en'}

输入:计算 123 + 456 → 调用calculator,返回:{'expression': '123 + 456', 'result': 579, 'steps': ['123+456 = 579']}

输入:自定义无主题内容 → 无匹配技能,返回提示

七、总结

整体来看,基于 SKILL 构建 AI 智能体,本质上是把一套杂乱、松散的 AI 能力,整理成标准化、可管理、可落地的工程化体系。SKILL 作为智能体的最小能力单元,不只是简单的工具调用,而是自带完整描述、触发规则、输入输出规范、示例与边界约束的标准化插件,既比普通 Tool 更严谨,又比子 Agent 更轻量、更易调度。

SKILL.md 作为整个体系的核心契约文件,承担了机器可读、人类可懂的双重角色,从基础信息、触发条件、参数规范到依赖限制一应俱全,让系统能自动识别、调度引擎能精准匹配、大模型能规范调用、开发者能快速维护,真正实现技能的可插拔、可扩展、可管控。对于想要把大模型真正落地到业务、从对话交互走向流程执行的场景来说,SKILL 体系是一条非常成熟、可直接量产的技术路线,也是 AI 智能体从 Demo 走向生产环境的关键一步。

相关推荐
极梦网络无忧3 小时前
OpenClaw 技能安装与角色配置完全指南
人工智能
事变天下4 小时前
自动左心室应变评估 Auto Strain LV,让心肌应变检测不再需要心电图的“入场券”
人工智能
Fleshy数模4 小时前
解决OpenCV人脸检测报错:(-215:Assertion failed) !empty() 保姆级教程
人工智能·opencv·计算机视觉
l1t4 小时前
DeepSeek辅助编写的Oracle dmp转SQL脚本和CSV文件工具
数据库·人工智能·sql·oracle
小超同学你好4 小时前
Transformer 22. Gemma 1 架构详解:Decoder-only、GeGLU、RoPE 与每一步计算
人工智能·深度学习·transformer
算法即正义4 小时前
安全生产月知识竞赛活动方案:策划、实施与效果评估全流程指南
人工智能
行者无疆_ty4 小时前
如何在个人电脑部署大模型实现Token自由
人工智能·大模型·agent
装不满的克莱因瓶4 小时前
Cursor中agent、plan、ask三种模式区别于对比
人工智能·ai·大模型·ai编程·cursor
永霖光电_UVLED4 小时前
耐热抗损伤的高功率连续波激光组件让光学元件保持“冷”状态
人工智能