Super Qwen Voice World智能体(Skills Agent)开发实战

1. 当语音助手开始真正理解你的生活

你有没有过这样的体验:对着语音助手说“帮我查下今天北京的天气”,它却只回你一句“正在为您搜索天气信息”,然后就卡住了?或者你想让它帮你把会议安排进日程,它却反复确认“您是要创建一个新事件吗”,仿佛在和一台老式电话交换机对话?

Super Qwen Voice World带来的不是又一个“能说话”的AI,而是一个真正能理解上下文、记住你习惯、协调多个任务的智能体。它不满足于单次问答,而是像一位熟悉你工作节奏的助理——你刚问完“明天上午十点有会吗”,接着说“那把下午三点的客户拜访提前到两点”,它立刻明白这是要调整日程,而不是重新创建一个事件。

这个能力背后,是skills智能体架构的成熟落地。它把复杂的AI能力拆解成一个个可插拔、可组合的技能模块:天气查询是一个技能,日程管理是另一个,智能家居控制又是第三个。它们各自独立开发、测试和维护,却能在同一个对话流中无缝协作。这种设计让开发者不再需要从零构建一个“全能大脑”,而是像搭积木一样,把已验证的功能快速组装成符合业务需求的智能体。

更重要的是,Super Qwen Voice World天然支持自然语言交互和上下文记忆。它不会在每次对话后清空记忆,而是能记住你上周说过的“卧室空调温度设为26度”,下次你只说“把卧室空调调低两度”,它就能准确执行。这种连贯性,正是日常生活中我们对“智能”的真实期待。

2. 技能智能体的核心设计哲学

2.1 为什么不是“大模型+提示词”就够用了

很多团队最初尝试语音助手时,会直接用大模型加一段精心编写的系统提示词:“你是一个智能家居助手,请根据用户指令控制设备……”。短期看效果不错,但很快就会遇到瓶颈。

问题出在三个层面:可靠性、可维护性和可扩展性。当用户说“把客厅灯调暗一点,顺便把空调温度升到28度”,模型需要同时解析两个意图、识别两个设备、执行两个操作。一旦其中一环出错(比如把“客厅灯”误识别为“厨房灯”),整个流程就失败了,而且很难定位是哪个环节出了问题。

skills智能体的设计,本质上是一次责任分离。它把“理解用户意图”和“执行具体任务”明确切分开来。大模型负责做最擅长的事——理解自然语言、识别意图、管理对话状态;而每个技能模块则专注做好一件事——天气技能只管调用气象API并格式化结果,日程技能只管对接日历服务,智能家居技能只管发送设备指令。

这种分离带来了实实在在的好处:天气接口升级了,只需改天气技能,不影响其他功能;发现日程同步有延迟,可以单独优化日程模块的重试逻辑;想增加股票查询功能?写一个新的技能模块,注册进去就行,完全不碰原有代码。

2.2 Skills智能体的三层结构

一个典型的skills智能体由三个层次构成,它们像齿轮一样紧密咬合:

第一层:对话管理层(Orchestrator)
这是智能体的“大脑皮层”,负责整体对话流程控制。它接收语音识别后的文本,调用大模型进行意图识别和槽位填充,决定下一步该调用哪个技能,还要处理技能返回的结果,生成自然流畅的语音回复。它不关心具体怎么查天气,只关心“现在该让谁去查”。

第二层:技能执行层(Skills)
这是真正的“肌肉组织”,每个技能都是一个独立的服务单元。天气技能封装了气象API调用、异常处理、数据缓存;日程技能集成了日历SDK、冲突检测、提醒设置;智能家居技能则对接了不同厂商的IoT平台。它们通过统一的接口契约与上层通信,输入是结构化参数,输出是标准化结果。

第三层:记忆管理层(Memory)
这是智能体的“海马体”,负责维护对话上下文和长期记忆。它记录着用户偏好(比如“我通常把卧室空调设为26度”)、对话历史(“刚才说要取消周三的会议”)、以及临时状态(“正在帮您设置闹钟,还需要确认时间”)。当用户说“再响一次”,它知道指的是刚刚设置的那个闹钟,而不是系统里所有闹钟。

这三层结构让整个系统既强大又清晰。你可以把对话管理层想象成一位经验丰富的项目经理,把技能执行层看作几位各有所长的工程师,而记忆管理层则是那个永远记得项目细节的助理。他们配合默契,却各司其职。

3. 实战:构建多技能集成智能体

3.1 环境准备与核心依赖

开始编码前,我们需要搭建一个轻量但完整的开发环境。Super Qwen Voice World提供了官方SDK,让集成变得简单直接。这里以Python为例,展示最关键的初始化步骤。

首先安装核心依赖:

pip install dashscope pyaudio python-dotenv

然后创建基础配置。我们不把API密钥硬编码在代码里,而是使用环境变量管理:

# .env 文件内容
DASHSCOPE_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

接下来是智能体的入口文件,它初始化了所有核心组件:

# agent_core.py
import os
from dotenv import load_dotenv
import dashscope

# 加载环境变量
load_dotenv()

# 初始化DashScope SDK
dashscope.api_key = os.getenv("DASHSCOPE_API_KEY")

class VoiceWorldAgent:
    def __init__(self):
        # 对话管理器 - 负责意图识别和流程控制
        self.orchestrator = ConversationOrchestrator()
        # 技能注册中心 - 所有技能在这里集中管理
        self.skill_registry = SkillRegistry()
        # 记忆管理器 - 维护对话上下文
        self.memory_manager = MemoryManager()
        
        # 注册预置技能
        self._register_builtin_skills()
    
    def _register_builtin_skills(self):
        """注册内置技能模块"""
        self.skill_registry.register("weather", WeatherSkill())
        self.skill_registry.register("calendar", CalendarSkill())
        self.skill_registry.register("smart_home", SmartHomeSkill())
    
    def process_voice_input(self, audio_data: bytes) -> str:
        """处理语音输入的主流程"""
        # 1. 语音转文字
        text = self._speech_to_text(audio_data)
        # 2. 管理对话流程
        response = self.orchestrator.handle_message(
            text, 
            self.memory_manager.get_context()
        )
        # 3. 更新记忆
        self.memory_manager.update_context(text, response)
        # 4. 文字转语音
        audio_url = self._text_to_speech(response)
        return audio_url
    
    def _speech_to_text(self, audio_data: bytes) -> str:
        # 这里调用通义千问实时语音识别API
        # 实际项目中会封装成独立服务
        pass
    
    def _text_to_speech(self, text: str) -> str:
        # 这里调用通义千问TTS API
        # 使用Cherry音色,温暖亲切的女声
        from dashscope.audio import SpeechSynthesizer
        
        result = SpeechSynthesizer.call(
            model='qwen3-tts-flash',
            text=text,
            voice='Cherry',  # 阳光积极的小姐姐音色
            format='wav'
        )
        if result.status_code == 200:
            return result.get_audio().get_url()
        else:
            return "语音合成失败"

这个VoiceWorldAgent类就是整个智能体的骨架。它不包含任何具体业务逻辑,只负责协调各个部分。这种设计让后续添加新技能变得极其简单——你只需要写一个新的技能类,然后在_register_builtin_skills里加一行注册代码即可。

3.2 天气查询技能:从API调用到人性化表达

天气技能看似简单,但要做好用户体验,需要考虑很多细节。用户不会说“请调用气象API获取北京市朝阳区未来24小时天气预报”,而是说“今天出门要带伞吗?”或者“周末去爬山天气怎么样?”

我们的天气技能需要做三件事:准确理解用户问的是哪里、什么时间、什么信息;可靠地获取数据;最后用自然语言把结果说出来,而不是扔一堆数字。

# skills/weather_skill.py
import requests
import json
from datetime import datetime, timedelta

class WeatherSkill:
    def __init__(self):
        # 气象API密钥(实际项目中应从配置中心获取)
        self.api_key = "your_weather_api_key"
        # 城市映射表,解决用户说“帝都”、“魔都”等别称
        self.city_aliases = {
            "帝都": "北京",
            "魔都": "上海",
            "蓉城": "成都",
            "羊城": "广州",
            "鹏城": "深圳"
        }
    
    def execute(self, parameters: dict) -> dict:
        """
        执行天气查询
        parameters 示例:
        {
            "location": "北京",
            "time_range": "today",  # today/tomorrow/weekend
            "need_rain": True,
            "need_temperature": True
        }
        """
        location = parameters.get("location", "北京")
        # 处理城市别称
        location = self.city_aliases.get(location, location)
        
        # 调用第三方气象API(此处为示例,实际使用高德或和风天气)
        try:
            # 模拟API调用
            weather_data = self._mock_weather_api(location, parameters["time_range"])
            
            # 构建人性化回复
            response = self._format_weather_response(weather_data, parameters)
            return {
                "success": True,
                "response": response,
                "raw_data": weather_data
            }
        except Exception as e:
            return {
                "success": False,
                "response": f"抱歉,获取{location}天气信息时遇到了一点小问题,请稍后再试。",
                "error": str(e)
            }
    
    def _mock_weather_api(self, city: str, time_range: str) -> dict:
        """模拟气象API返回数据"""
        # 实际项目中这里会是真实的HTTP请求
        if time_range == "today":
            return {
                "city": city,
                "date": "2024-06-15",
                "weather": "多云转晴",
                "temperature_high": 32,
                "temperature_low": 22,
                "humidity": 65,
                "wind_direction": "东南风",
                "wind_level": "3级",
                "rain_chance": 20,
                "uv_index": 7
            }
        elif time_range == "tomorrow":
            return {
                "city": city,
                "date": "2024-06-16",
                "weather": "晴",
                "temperature_high": 34,
                "temperature_low": 23,
                "rain_chance": 5,
                "uv_index": 9
            }
        else:  # weekend
            return {
                "city": city,
                "weekend": ["周六:晴,33°C/24°C", "周日:多云,31°C/22°C"],
                "rain_chance": 10
            }
    
    def _format_weather_response(self, data: dict, params: dict) -> str:
        """将原始天气数据格式化为自然语言"""
        if "weekend" in data:
            # 周末天气
            return f"{data['city']}本周末天气不错,{data['weekend'][0]},{data['weekend'][1]}。降雨概率只有{data['rain_chance']}%,适合户外活动。"
        
        # 今日或明日天气
        base_msg = f"{data['city']}{data['date']}天气:{data['weather']}。"
        
        if params.get("need_temperature"):
            base_msg += f"最高气温{data['temperature_high']}度,最低{data['temperature_low']}度。"
        
        if params.get("need_rain") and data.get("rain_chance", 0) > 30:
            base_msg += "降雨概率较高,建议带伞。"
        elif params.get("need_rain"):
            base_msg += "天气晴好,基本不用带伞。"
        
        if data.get("uv_index", 0) > 6:
            base_msg += "紫外线较强,注意防晒。"
        
        return base_msg

这个技能的关键在于_format_weather_response方法。它没有简单地把API返回的JSON原样读出来,而是根据用户可能关心的点(是否带伞、是否防晒、温度如何)动态组织语言。当用户问“今天出门要带伞吗?”,系统自动设置need_rain=True,技能就重点强调降雨概率;当用户问“周末适合爬山吗?”,系统设置time_range="weekend",技能就给出周末两天的详细预报。

3.3 日程管理技能:处理复杂的时间语义

日程管理是另一个高频但复杂的场景。用户的时间表达千奇百怪:“下周三下午三点”、“后天早上”、“大后天晚上八点”、“这个月15号”、“下个月第一个周五”……这些都需要被准确解析成标准时间戳。

Super Qwen Voice World的skills智能体内置了强大的时间解析能力,但作为开发者,我们需要定义技能如何响应这些解析结果。

# skills/calendar_skill.py
from datetime import datetime, timedelta
import re

class CalendarSkill:
    def __init__(self):
        # 模拟日历服务连接
        self.calendar_service = MockCalendarService()
    
    def execute(self, parameters: dict) -> dict:
        """
        执行日程操作
        parameters 示例:
        {
            "action": "create",  # create/update/delete/query
            "event_title": "团队周会",
            "start_time": "2024-06-17T14:00:00",
            "end_time": "2024-06-17T15:00:00",
            "attendees": ["张三", "李四"],
            "location": "会议室A"
        }
        """
        action = parameters.get("action", "query")
        
        try:
            if action == "create":
                return self._create_event(parameters)
            elif action == "update":
                return self._update_event(parameters)
            elif action == "delete":
                return self._delete_event(parameters)
            else:  # query
                return self._query_events(parameters)
        except Exception as e:
            return {
                "success": False,
                "response": "日程操作失败,请检查时间或重试。",
                "error": str(e)
            }
    
    def _create_event(self, params: dict) -> dict:
        """创建新日程"""
        event_id = self.calendar_service.create_event(
            title=params["event_title"],
            start_time=params["start_time"],
            end_time=params.get("end_time"),
            attendees=params.get("attendees", []),
            location=params.get("location", "")
        )
        
        # 生成自然语言回复
        start_dt = datetime.fromisoformat(params["start_time"])
        time_str = start_dt.strftime("%m月%d日 %H:%M")
        
        if "end_time" in params:
            end_dt = datetime.fromisoformat(params["end_time"])
            time_str += f"-{end_dt.strftime('%H:%M')}"
        
        response = f"已为您创建日程:{params['event_title']},时间是{time_str}"
        if params.get("location"):
            response += f",地点在{params['location']}"
        
        return {
            "success": True,
            "response": response,
            "event_id": event_id
        }
    
    def _query_events(self, params: dict) -> dict:
        """查询日程"""
        # 解析时间范围
        time_range = params.get("time_range", "today")
        now = datetime.now()
        
        if time_range == "today":
            start_time = now.replace(hour=0, minute=0, second=0, microsecond=0)
            end_time = now.replace(hour=23, minute=59, second=59, microsecond=0)
        elif time_range == "tomorrow":
            tomorrow = now + timedelta(days=1)
            start_time = tomorrow.replace(hour=0, minute=0, second=0, microsecond=0)
            end_time = tomorrow.replace(hour=23, minute=59, second=59, microsecond=0)
        else:  # next_week
            next_monday = now + timedelta(days=(7 - now.weekday() + 1) % 7)
            start_time = next_monday.replace(hour=0, minute=0, second=0, microsecond=0)
            end_time = start_time + timedelta(days=7)
        
        events = self.calendar_service.get_events(start_time, end_time)
        
        if not events:
            response = "您在指定时间段内没有日程安排。"
        else:
            response = f"您在{time_range}有{len(events)}个日程:\n"
            for i, event in enumerate(events[:3], 1):  # 只显示前3个
                dt = datetime.fromisoformat(event["start_time"])
                time_str = dt.strftime("%H:%M")
                response += f"{i}. {event['title']}({time_str})\n"
            if len(events) > 3:
                response += f"... 还有{len(events)-3}个日程"
        
        return {
            "success": True,
            "response": response,
            "events": events
        }

# 模拟日历服务(实际项目中对接Google Calendar或Outlook API)
class MockCalendarService:
    def __init__(self):
        self.events = []
    
    def create_event(self, title, start_time, end_time=None, attendees=None, location=""):
        event_id = f"evt_{len(self.events)+1}"
        event = {
            "id": event_id,
            "title": title,
            "start_time": start_time,
            "end_time": end_time or start_time,
            "attendees": attendees or [],
            "location": location
        }
        self.events.append(event)
        return event_id
    
    def get_events(self, start_time, end_time):
        # 简单的时间范围匹配
        return [e for e in self.events 
                if start_time <= datetime.fromisoformat(e["start_time"]) <= end_time]

这个日程技能展示了如何处理真实世界中的复杂需求。它不只关注“创建一个事件”,而是思考用户真正需要什么:当用户说“把下午三点的会议推迟一小时”,技能需要先查询当前时间点的会议,再计算新的时间,最后更新。这种链式操作在skills架构下变得清晰可控——查询是一个技能,更新是另一个技能,它们可以独立测试和优化。

3.4 智能家居技能:统一协议下的设备控制

智能家居最大的痛点是碎片化。一个家庭可能有小米的灯、华为的空调、涂鸦的窗帘、还有自研的安防系统。如果每个设备都要单独对接,技能开发会变成一场噩梦。

skills智能体的解决方案是定义一个统一的设备控制协议。无论底层是什么品牌、什么通信协议(Wi-Fi、Zigbee、蓝牙),技能层只认一种抽象的指令格式。

# skills/smart_home_skill.py
import json
import time

class SmartHomeSkill:
    def __init__(self):
        # 设备映射表:用户常说的名字 -> 实际设备ID
        self.device_mapping = {
            "客厅灯": "light_living_room_001",
            "卧室空调": "ac_bedroom_002",
            "书房窗帘": "curtain_study_003",
            "玄关摄像头": "camera_entrance_004"
        }
        # 设备类型映射:确定支持哪些操作
        self.device_capabilities = {
            "light": ["on", "off", "brightness", "color"],
            "ac": ["on", "off", "temperature", "mode", "fan_speed"],
            "curtain": ["open", "close", "stop", "position"],
            "camera": ["on", "off", "record", "snapshot"]
        }
    
    def execute(self, parameters: dict) -> dict:
        """
        执行智能家居控制
        parameters 示例:
        {
            "device_name": "客厅灯",
            "action": "brightness",
            "value": 70,
            "unit": "percent"
        }
        """
        device_name = parameters.get("device_name")
        action = parameters.get("action")
        value = parameters.get("value")
        
        if not device_name or not action:
            return {
                "success": False,
                "response": "请告诉我您想控制哪个设备,以及要做什么操作。"
            }
        
        # 查找设备ID
        device_id = self.device_mapping.get(device_name)
        if not device_id:
            return {
                "success": False,
                "response": f"找不到设备'{device_name}',请确认设备名称是否正确。"
            }
        
        # 确定设备类型和能力
        device_type = self._infer_device_type(device_id)
        if action not in self.device_capabilities.get(device_type, []):
            return {
                "success": False,
                "response": f"'{device_name}'不支持'{action}'操作。"
            }
        
        try:
            # 发送控制指令(实际项目中调用IoT平台API)
            result = self._send_device_command(device_id, action, value)
            
            # 生成用户友好的反馈
            response = self._format_device_response(device_name, action, value, result)
            return {
                "success": True,
                "response": response,
                "device_id": device_id,
                "result": result
            }
        except Exception as e:
            return {
                "success": False,
                "response": f"控制{device_name}时遇到问题,请稍后再试。",
                "error": str(e)
            }
    
    def _infer_device_type(self, device_id: str) -> str:
        """根据设备ID推断设备类型"""
        if "light" in device_id:
            return "light"
        elif "ac" in device_id or "air" in device_id:
            return "ac"
        elif "curtain" in device_id or "blind" in device_id:
            return "curtain"
        elif "camera" in device_id or "cam" in device_id:
            return "camera"
        else:
            return "unknown"
    
    def _send_device_command(self, device_id: str, action: str, value=None) -> dict:
        """向设备发送控制命令"""
        # 实际项目中这里会是HTTP请求或MQTT消息
        # 模拟成功响应
        time.sleep(0.5)  # 模拟网络延迟
        return {
            "status": "success",
            "timestamp": int(time.time()),
            "device_id": device_id,
            "command": action,
            "value": value
        }
    
    def _format_device_response(self, device_name: str, action: str, value, result: dict) -> str:
        """格式化设备控制反馈"""
        if action == "on":
            return f"{device_name}已开启。"
        elif action == "off":
            return f"{device_name}已关闭。"
        elif action == "brightness":
            if value is not None:
                return f"{device_name}亮度已调整为{value}%。"
            else:
                return f"{device_name}已调至最亮。"
        elif action == "temperature":
            if value is not None:
                return f"{device_name}温度已设为{value}度。"
            else:
                return f"{device_name}温度已恢复默认设置。"
        elif action in ["open", "close"]:
            return f"{device_name}已{action}。"
        else:
            return f"{device_name}已执行{action}操作。"

这个技能的价值在于它的抽象能力。开发者不需要了解小米IoT平台的OAuth2流程,也不用研究华为HiLink的MQTT Topic规则。他们只需要关注业务逻辑:用户想做什么,系统该如何反馈。设备协议的适配工作被下沉到更底层的服务中,保持了skills层的干净和专注。

4. 多技能协同:让智能体真正“活”起来

单个技能运行良好只是第一步。skills智能体的真正威力,在于多个技能能够像真人一样协同工作。想象这样一个场景:

用户:“帮我查下今天北京的天气,如果不下雨,就把下午三点的客户拜访提前到两点,顺便把会议室空调设到26度。”

这句话包含了三个独立意图,但它们之间有明确的逻辑关系:天气查询是条件判断的前提,日程调整和空调设置是条件成立后的动作。skills智能体如何优雅地处理这种复合指令?

4.1 对话流程编排:从线性到网状

传统的语音助手往往是线性的:用户说一句话 → 系统执行一个动作 → 结束。而Super Qwen Voice World的对话管理层支持条件分支、并行执行和状态依赖

# orchestrator/conversation_orchestrator.py
from typing import List, Dict, Any
import json

class ConversationOrchestrator:
    def __init__(self):
        self.intent_classifier = IntentClassifier()
    
    def handle_message(self, user_input: str, context: Dict[str, Any]) -> str:
        """
        处理用户输入的完整流程
        """
        # 步骤1:意图识别与槽位提取
        intent_result = self.intent_classifier.classify(user_input, context)
        
        # 步骤2:根据意图类型分发到对应技能
        if intent_result["type"] == "composite":
            return self._handle_composite_intent(intent_result)
        elif intent_result["type"] == "single":
            return self._handle_single_intent(intent_result)
        else:
            return "抱歉,我没有理解您的意思。"
    
    def _handle_composite_intent(self, intent_result: Dict[str, Any]) -> str:
        """处理复合意图"""
        # 提取所有子意图
        sub_intents = intent_result.get("sub_intents", [])
        
        # 按依赖关系排序(条件判断必须在前面)
        ordered_intents = self._sort_intents_by_dependency(sub_intents)
        
        results = []
        for intent in ordered_intents:
            # 执行技能
            skill_result = self._execute_skill(intent)
            results.append(skill_result)
            
            # 如果是条件判断,检查是否需要跳过后续动作
            if intent["action"] == "check_weather" and not skill_result["success"]:
                return "天气信息获取失败,无法继续后续操作。"
            elif intent["action"] == "check_weather":
                # 检查天气是否允许后续操作
                if self._is_weather_favorable(skill_result["raw_data"]):
                    continue  # 继续执行
                else:
                    return "今天有雨,建议不要调整室外活动安排。"
        
        # 汇总所有结果,生成自然语言回复
        return self._summarize_composite_results(results)
    
    def _sort_intents_by_dependency(self, intents: List[Dict]) -> List[Dict]:
        """按执行依赖关系排序意图"""
        # 条件判断类意图(check_*)排在最前面
        condition_intents = [i for i in intents if i["action"].startswith("check_")]
        other_intents = [i for i in intents if not i["action"].startswith("check_")]
        return condition_intents + other_intents
    
    def _is_weather_favorable(self, weather_data: dict) -> bool:
        """判断天气是否适宜"""
        rain_chance = weather_data.get("rain_chance", 0)
        return rain_chance < 30
    
    def _summarize_composite_results(self, results: List[Dict]) -> str:
        """汇总多个技能执行结果"""
        responses = [r["response"] for r in results if r.get("response")]
        if len(responses) == 1:
            return responses[0]
        elif len(responses) == 2:
            return f"{responses[0]},{responses[1]}"
        else:
            return ",".join(responses[:-1]) + f",以及{responses[-1]}"

这个ConversationOrchestrator类展示了如何将多个技能编织成一个有机整体。它不只是简单地顺序执行,而是理解意图之间的逻辑关系。当检测到“如果...就...”这样的条件结构时,它会自动将天气查询作为前置条件,只有条件满足才继续执行后续的日程和空调操作。

4.2 上下文记忆:让对话有“温度”

真正的智能不仅在于执行任务,更在于记住用户的习惯和偏好。skills智能体的记忆管理层让这一切成为可能。

# memory/memory_manager.py
from datetime import datetime
import json

class MemoryManager:
    def __init__(self):
        # 模拟内存存储(实际项目中使用Redis或数据库)
        self.conversation_history = []
        self.user_preferences = {}
    
    def get_context(self) -> dict:
        """获取当前对话上下文"""
        return {
            "history": self._get_recent_history(3),  # 最近3轮对话
            "preferences": self.user_preferences.copy(),
            "current_time": datetime.now().isoformat()
        }
    
    def update_context(self, user_input: str, system_response: str):
        """更新对话上下文"""
        # 记录对话历史
        self.conversation_history.append({
            "role": "user",
            "content": user_input,
            "timestamp": datetime.now().isoformat()
        })
        self.conversation_history.append({
            "role": "assistant",
            "content": system_response,
            "timestamp": datetime.now().isoformat()
        })
        
        # 提取并更新用户偏好(示例:从对话中学习空调温度偏好)
        self._extract_preferences(user_input, system_response)
    
    def _get_recent_history(self, count: int) -> list:
        """获取最近N轮对话历史"""
        return self.conversation_history[-count*2:] if len(self.conversation_history) >= count*2 else self.conversation_history
    
    def _extract_preferences(self, user_input: str, system_response: str):
        """从对话中提取用户偏好"""
        # 示例:当用户多次调整空调温度,记录其偏好
        if "空调" in user_input and "度" in user_input:
            # 使用正则提取温度数值
            temp_match = re.search(r"(\d+)度", user_input)
            if temp_match:
                preferred_temp = int(temp_match.group(1))
                self.user_preferences["preferred_ac_temperature"] = preferred_temp
        
        # 示例:当用户说“以后都这样”,记录当前设置为默认
        if "以后都这样" in user_input or "默认" in user_input:
            # 从上一条系统回复中提取设置
            if self.conversation_history:
                last_system_msg = self.conversation_history[-2]["content"] if len(self.conversation_history) >= 2 else ""
                if "空调" in last_system_msg and "度" in last_system_msg:
                    temp_match = re.search(r"(\d+)度", last_system_msg)
                    if temp_match:
                        self.user_preferences["default_ac_temperature"] = int(temp_match.group(1))
    
    def get_user_preference(self, key: str, default=None):
        """获取用户偏好值"""
        return self.user_preferences.get(key, default)

这个记忆管理器让智能体拥有了“学习”能力。它不依赖于复杂的机器学习模型,而是通过简单的模式匹配和规则提取,就能捕捉到用户的真实需求。当用户第三次说“把卧室空调调到26度”,系统会自动将其记录为偏好,并在下次用户只说“把卧室空调调到舒服的温度”时,智能地应用26度这个值。

5. 开发者实践建议与避坑指南

5.1 技能开发的黄金法则

在实际开发中,我们总结了三条让skills智能体开发事半功倍的黄金法则:

第一法则是“小步快跑,快速验证”。不要试图一次性开发一个完美的天气技能。先实现最核心的功能:能正确识别城市名和时间范围,能调用API,能返回一句简单的“北京今天天气晴朗”。确保这个最小可行版本(MVP)能跑通,再逐步添加温度、湿度、紫外线等细节。每增加一个特性,都立即在真实对话中测试,确保它真的提升了用户体验,而不是增加了复杂度。

第二法则是“错误即文档”。在技能的异常处理中,不要只写except Exception as e: log.error(e)。要把最常见的错误场景显式捕获并给出用户友好的提示。比如天气API超时,提示“气象服务器暂时繁忙,请稍后再试”;日历服务不可用,提示“日程服务正在维护,您可以先记下安排”。这些错误提示本身就是最好的用户文档,告诉用户系统能做什么、不能做什么、什么时候能恢复。

第三法则是“技能即产品”。每个技能都应该有自己的版本号、变更日志和使用文档。当团队中有新人加入时,他不应该需要阅读整个智能体的源码才能理解天气技能如何工作。一个清晰的README.md文件,包含技能功能、输入参数说明、输出示例、错误码列表,就能极大降低协作成本。我们甚至建议为每个技能编写简单的单元测试用例,覆盖正常流程和各种边界情况。

5.2 常见陷阱与解决方案

在开发过程中,我们遇到了一些典型陷阱,分享出来帮助后来者少走弯路:

陷阱一:过度依赖大模型做决策
有些团队会让大模型直接生成设备控制指令,比如让模型输出“MQTT topic: /devices/ac_bedroom_002/set, payload: {"temperature":26}”。这非常危险,因为模型可能生成格式错误的JSON,或者指向不存在的设备。正确的做法是让模型只做意图识别和参数提取,具体的设备指令由技能模块根据预定义的映射表生成。

陷阱二:忽略语音交互的特殊性
文本界面可以显示复杂的选项列表,但语音交互不行。当技能需要用户选择时,不要说“请选择A、B或C”,而要说“您想查询今天、明天还是本周末的天气?”。给用户提供明确的、数量有限的选项,而不是开放式的问答。同样,避免长句子,把“请确认您要将会议从下午三点调整到下午两点,地点不变,参会人员不变”拆成两句话:“我把会议时间调整到下午两点,可以吗?地点和参会人员保持不变。”

陷阱三:忘记离线场景
不是所有设备都能保证24小时联网。智能家居技能应该有优雅的降级策略。当空调设备离线时,不要只说“设备不可用”,而要提供替代方案:“卧室空调当前离线,我可以帮您设置手机闹钟提醒您手动开启,或者等它上线后自动执行。”这种设计让用户感觉系统是可靠的伙伴,而不是一个动不动就报错的机器。

6. 总结:从工具到伙伴的进化

回顾整个开发过程,Super Qwen Voice World的skills智能体带给我们的不仅是技术上的便利,更是一种产品思维的转变。它让我们从“构建一个能回答问题的系统”,转向“创造一个能理解生活、适应习惯、主动协助的伙伴”。

这种转变体现在每一个细节里:当用户说“把上次放的歌再放一遍”,智能体能从记忆中找到三天前播放的那首《夜空中最亮的星》;当用户连续两天说“今天太热了”,它会主动建议“需要我把空调温度调低两度吗?”;当检测到用户日程密集,

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐