mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-25 08:33:53 +08:00
merge:main分支的manager-web和xiaozhi-server
This commit is contained in:
@@ -82,9 +82,10 @@ selected_module:
|
||||
TTS: EdgeTTS
|
||||
# 记忆模块,默认不开启记忆;如果想使用超长记忆,推荐使用mem0ai;如果注重隐私,请使用本地的mem_local_short
|
||||
Memory: nomem
|
||||
# 意图识别模块,默认使用function_call。开启后,可以播放音乐、控制音量、识别退出指令
|
||||
# 意图识别使用intent_llm,优点:通用性强,缺点:增加串行前置意图识别模块,会增加处理时间
|
||||
# 意图识别使用function_call,缺点:需要所选择的LLM支持function_call,优点:按需调用工具、速度快
|
||||
# 意图识别模块开启后,可以播放音乐、控制音量、识别退出指令。
|
||||
# 不想开通意图识别,就设置成:nointent
|
||||
# 意图识别可使用intent_llm,如果你的LLM是DifyLLM或CozeLLM,建议使用这个。优点:通用性强,缺点:增加串行前置意图识别模块,会增加处理时间,这个意图识别暂时不支持控制音量大小等iot操作
|
||||
# 意图识别可使用function_call,缺点:需要所选择的LLM支持function_call,优点:按需调用工具、速度快,理论上能全部操作所有iot指令
|
||||
# 默认免费的ChatGLMLLM就已经支持function_call,但是如果像追求稳定建议把LLM设置成:DoubaoLLM,使用的具体model_name是:doubao-pro-32k-functioncall-241028
|
||||
Intent: function_call
|
||||
|
||||
@@ -97,9 +98,13 @@ Intent:
|
||||
intent_llm:
|
||||
# 不需要动type
|
||||
type: intent_llm
|
||||
# 配备意图识别独立的思考模型
|
||||
# 如果这里不填,则会默认使用selected_module.LLM的模型作为意图识别的思考模型
|
||||
# 如果你的selected_module.LLM选择了DifyLLM或CozeLLM,这里最好使用独立的LLM作为意图识别,例如使用免费的ChatGLMLLM
|
||||
llm: ChatGLMLLM
|
||||
function_call:
|
||||
# 不需要动type
|
||||
type: nointent
|
||||
type: function_call
|
||||
# plugins_func/functions下的模块,可以通过配置,选择加载哪个模块,加载后对话支持相应的function调用
|
||||
# 系统默认已经记载“handle_exit_intent(退出识别)”、“play_music(音乐播放)”插件,请勿重复加载
|
||||
# 下面是加载查天气、角色切换、加载查新闻的插件示例
|
||||
@@ -187,7 +192,7 @@ LLM:
|
||||
# 可在这里找到你的 api_key https://bailian.console.aliyun.com/?apiKey=1#/api-key
|
||||
base_url: https://dashscope.aliyuncs.com/compatible-mode/v1
|
||||
model_name: qwen-turbo
|
||||
api_key: 你的ali api key
|
||||
api_key: 你的deepseek web key
|
||||
temperature: 0.7 # 温度值
|
||||
max_tokens: 500 # 最大生成token数
|
||||
top_p: 1
|
||||
@@ -278,6 +283,18 @@ LLM:
|
||||
variables:
|
||||
k: "v"
|
||||
k2: "v2"
|
||||
XinferenceLLM:
|
||||
# 定义LLM API类型
|
||||
type: xinference
|
||||
# Xinference服务地址和模型名称
|
||||
model_name: qwen2.5:72b-AWQ # 使用的模型名称,需要预先在Xinference启动对应模型
|
||||
base_url: http://localhost:9997 # Xinference服务地址
|
||||
XinferenceSmallLLM:
|
||||
# 定义轻量级LLM API类型,用于意图识别
|
||||
type: xinference
|
||||
# Xinference服务地址和模型名称
|
||||
model_name: qwen2.5:3b-AWQ # 使用的小模型名称,用于意图识别
|
||||
base_url: http://localhost:9997 # Xinference服务地址
|
||||
TTS:
|
||||
# 当前支持的type为edge、doubao,可自行适配
|
||||
EdgeTTS:
|
||||
@@ -535,11 +552,3 @@ wakeup_words:
|
||||
- "喵喵同学"
|
||||
- "小滨小滨"
|
||||
- "小冰小冰"
|
||||
|
||||
# 是否使用私有配置
|
||||
use_private_config: true
|
||||
# 远程配置
|
||||
# 数据格式:{"code": 0,"msg": "success","data": {"prompt": "我是小智","owner": "用户ID","ASR": {"FunASR": {}},"LLM": {"ChatGLMLLM": {}},"TTS": {"EdgeTTS": {}},"VAD": {"SileroVAD": {}},"Intent": {"function_call": {}},"Memory": {"nomem": {}},"selected_module": {"ASR": "FunASR","LLM": "ChatGLMLLM","TTS": "EdgeTTS","Intent": "function_call","Memory": "nomem","VAD": "SileroVAD"}}}
|
||||
remote_config:
|
||||
enabled: true
|
||||
url: http://192.168.5.11:8002/xiaozhi-esp32-api/api/v1/user/agent/loadAgentConfig/
|
||||
@@ -1,8 +1,6 @@
|
||||
import os
|
||||
import time
|
||||
import yaml
|
||||
import json
|
||||
import requests
|
||||
from config.logger import setup_logging
|
||||
from typing import Dict, Any, Optional
|
||||
from copy import deepcopy
|
||||
@@ -24,14 +22,6 @@ class PrivateConfig:
|
||||
|
||||
async def load_or_create(self):
|
||||
try:
|
||||
# 优先通过远程获取配置
|
||||
fetch_config_ = {}
|
||||
remote_config = self.default_config['remote_config']
|
||||
self.logger.bind(tag=TAG).info(f"remote config: {remote_config}")
|
||||
if remote_config and remote_config['enabled']:
|
||||
fetch_config_ = await self.fetch_config(remote_config['url'])
|
||||
|
||||
self.logger.bind(tag=TAG).info(f"fetch_config: {fetch_config_}")
|
||||
await self.lock_manager.acquire_lock(self.config_path)
|
||||
try:
|
||||
if os.path.exists(self.config_path):
|
||||
@@ -40,9 +30,6 @@ class PrivateConfig:
|
||||
else:
|
||||
all_configs = {}
|
||||
|
||||
if fetch_config_:
|
||||
all_configs[self.device_id] = fetch_config_
|
||||
|
||||
if self.device_id not in all_configs:
|
||||
# Get selected module names
|
||||
selected_modules = self.default_config['selected_module']
|
||||
@@ -77,9 +64,9 @@ class PrivateConfig:
|
||||
|
||||
all_configs[self.device_id] = device_config
|
||||
|
||||
# Save updated configs
|
||||
with open(self.config_path, 'w', encoding='utf-8') as f:
|
||||
yaml.dump(all_configs, f, allow_unicode=True)
|
||||
# Save updated configs
|
||||
with open(self.config_path, 'w', encoding='utf-8') as f:
|
||||
yaml.dump(all_configs, f, allow_unicode=True)
|
||||
|
||||
self.private_config = all_configs[self.device_id]
|
||||
|
||||
@@ -251,60 +238,4 @@ class PrivateConfig:
|
||||
|
||||
def get_owner(self) -> Optional[str]:
|
||||
"""获取设备当前所有者"""
|
||||
return self.private_config.get('owner')
|
||||
|
||||
async def fetch_config(self, fetch_url: str) -> Dict[str, Any]:
|
||||
"""通过HTTP请求远程获取配置"""
|
||||
url = f"{fetch_url}{self.device_id}"
|
||||
headers = {
|
||||
'device_id': self.device_id
|
||||
}
|
||||
try:
|
||||
response = requests.get(url, headers=headers)
|
||||
response.raise_for_status() # 如果响应状态码不是200,会抛出异常
|
||||
# 检查响应内容类型是否为JSON
|
||||
if response.headers.get('Content-Type') != 'application/json':
|
||||
self.logger.bind(tag=TAG).error("Invalid content type: expected application/json")
|
||||
return {}
|
||||
|
||||
# 解析JSON数据
|
||||
config_data = response.json()
|
||||
|
||||
# 验证返回的数据结构
|
||||
if not isinstance(config_data, dict):
|
||||
self.logger.bind(tag=TAG).error("Invalid data format: expected a dictionary")
|
||||
return {}
|
||||
|
||||
if config_data['code'] != 0:
|
||||
self.logger.bind(tag=TAG).error(f"Fetch config error: {config_data['msg']}")
|
||||
return {}
|
||||
|
||||
config_data_ = config_data['data']
|
||||
if not isinstance(config_data_, dict):
|
||||
self.logger.bind(tag=TAG).error("Invalid config data format: expected a dictionary")
|
||||
return {}
|
||||
|
||||
|
||||
# 检查必要的字段是否存在
|
||||
required_fields = ['selected_module', 'prompt', 'LLM', 'TTS', 'ASR', 'VAD']
|
||||
for field in required_fields:
|
||||
if field not in config_data_:
|
||||
self.logger.bind(tag=TAG).error(f"Missing required field: {field}")
|
||||
return {}
|
||||
|
||||
# 检查每个模块的配置是否正确
|
||||
for module in ['LLM', 'TTS', 'ASR', 'VAD']:
|
||||
if not isinstance(config_data_[module], dict):
|
||||
self.logger.bind(tag=TAG).error(f"Invalid data format for {module}: expected a dictionary")
|
||||
return {}
|
||||
|
||||
selected_module = config_data_['selected_module'].get(module)
|
||||
if selected_module not in config_data_[module]:
|
||||
self.logger.bind(tag=TAG).error(f"Selected {module} not found in config: {selected_module}")
|
||||
return {}
|
||||
|
||||
return config_data_
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
self.logger.bind(tag=TAG).error(f"Error fetching config: {e}")
|
||||
return {}
|
||||
return self.private_config.get('owner')
|
||||
@@ -1,18 +0,0 @@
|
||||
'''
|
||||
自定义记忆,可以选择此模块
|
||||
'''
|
||||
from ..base import MemoryProviderBase, logger
|
||||
|
||||
TAG = __name__
|
||||
|
||||
class MemoryProvider(MemoryProviderBase):
|
||||
def __init__(self, config):
|
||||
super().__init__(config)
|
||||
|
||||
async def save_memory(self, msgs):
|
||||
logger.bind(tag=TAG).debug("mem_custom mode: Custom memory saving is performed.")
|
||||
return None
|
||||
|
||||
async def query_memory(self, query: str)-> str:
|
||||
logger.bind(tag=TAG).debug("mem_custom mode: Custom memory query is performed.")
|
||||
return ""
|
||||
Reference in New Issue
Block a user