This commit is contained in:
朱潮 2025-09-20 17:29:51 +08:00
parent 97aecf0c30
commit ebeb4e34df
3 changed files with 135 additions and 4 deletions

8
characters/libai.json Normal file
View File

@ -0,0 +1,8 @@
{
"name": "李白",
"description": "唐朝著名诗人,浪漫主义风格",
"system_prompt": "你是唐朝大诗人李白字太白号青莲居士。用简短诗词和小朋友对话每次回答不超过50字。",
"voice": "zh_female_wanqudashu_moon_bigtts",
"max_tokens": 50,
"author": "Claude"
}

8
characters/zhubajie.json Normal file
View File

@ -0,0 +1,8 @@
{
"name": "猪八戒",
"description": "西游记中的经典角色,憨厚可爱",
"system_prompt": "你是西游记中的猪八戒性格贪吃懒惰但心地善良。用幽默风趣的口吻和小朋友对话每次回答不超过50字。",
"voice": "zh_male_zhubajie_mars_bigtts",
"max_tokens": 50,
"author": "Claude"
}

View File

@ -17,6 +17,7 @@ import threading
import time import time
import uuid import uuid
import wave import wave
import argparse
from io import BytesIO from io import BytesIO
from urllib.parse import urlparse from urllib.parse import urlparse
@ -33,7 +34,7 @@ except ImportError:
class EnergyBasedRecorder: class EnergyBasedRecorder:
"""基于能量检测的录音系统""" """基于能量检测的录音系统"""
def __init__(self, energy_threshold=500, silence_threshold=1.5, min_recording_time=2.0, max_recording_time=30.0, enable_asr=True, enable_llm=True, enable_tts=True): def __init__(self, energy_threshold=500, silence_threshold=1.5, min_recording_time=2.0, max_recording_time=30.0, enable_asr=True, enable_llm=True, enable_tts=True, character="libai"):
# 音频参数 - 极简优化 # 音频参数 - 极简优化
self.FORMAT = pyaudio.paInt16 self.FORMAT = pyaudio.paInt16
self.CHANNELS = 1 self.CHANNELS = 1
@ -67,6 +68,16 @@ class EnergyBasedRecorder:
self.tts_app_key = "aGjiRDfUWi" self.tts_app_key = "aGjiRDfUWi"
self.tts_speaker = "zh_female_wanqudashu_moon_bigtts" self.tts_speaker = "zh_female_wanqudashu_moon_bigtts"
# 角色配置
self.current_character = character
self.characters_dir = os.path.join(os.path.dirname(__file__), "characters")
self.available_characters = self._load_available_characters()
self.character_config = self._load_character_config(character)
# 如果加载了角色配置更新TTS音色
if self.character_config and "voice" in self.character_config:
self.tts_speaker = self.character_config["voice"]
# 检查音频播放能力 # 检查音频播放能力
if self.enable_tts: if self.enable_tts:
self.audio_player_available = self._check_audio_player() self.audio_player_available = self._check_audio_player()
@ -118,6 +129,33 @@ class EnergyBasedRecorder:
self._setup_audio() self._setup_audio()
def _load_available_characters(self):
"""加载可用角色列表"""
characters = []
if os.path.exists(self.characters_dir):
for file in os.listdir(self.characters_dir):
if file.endswith('.json'):
characters.append(file[:-5]) # 去掉.json后缀
return characters
def _load_character_config(self, character_name):
"""加载角色配置"""
config_file = os.path.join(self.characters_dir, f"{character_name}.json")
if not os.path.exists(config_file):
print(f"⚠️ 角色配置文件不存在: {config_file}")
return None
try:
with open(config_file, 'r', encoding='utf-8') as f:
config = json.load(f)
print(f"✅ 加载角色: {config.get('name', character_name)}")
print(f"📝 描述: {config.get('description', '无描述')}")
return config
except Exception as e:
print(f"❌ 加载角色配置失败: {e}")
return None
def _setup_audio(self): def _setup_audio(self):
"""设置音频设备""" """设置音频设备"""
try: try:
@ -972,6 +1010,17 @@ class EnergyBasedRecorder:
try: try:
print("🤖 调用大语言模型...") print("🤖 调用大语言模型...")
# 获取角色配置中的系统提示词
if self.character_config and "system_prompt" in self.character_config:
system_prompt = self.character_config["system_prompt"]
else:
system_prompt = "你是一个智能助手,请根据用户的语音输入提供有帮助的回答。保持回答简洁明了。"
# 获取角色配置中的最大token数
max_tokens = 50
if self.character_config and "max_tokens" in self.character_config:
max_tokens = self.character_config["max_tokens"]
headers = { headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
"Authorization": f"Bearer {self.llm_api_key}" "Authorization": f"Bearer {self.llm_api_key}"
@ -982,14 +1031,14 @@ class EnergyBasedRecorder:
"messages": [ "messages": [
{ {
"role": "system", "role": "system",
"content": "你是唐朝大诗人李白用简短诗词和小朋友对话每次回答不超过50字。" "content": system_prompt
}, },
{ {
"role": "user", "role": "user",
"content": user_message "content": user_message
} }
], ],
"max_tokens": 50 "max_tokens": max_tokens
} }
response = requests.post(self.llm_api_url, headers=headers, json=data, timeout=30) response = requests.post(self.llm_api_url, headers=headers, json=data, timeout=30)
@ -1131,10 +1180,53 @@ class EnergyBasedRecorder:
print(f"❌ TTS转换失败: {e}") print(f"❌ TTS转换失败: {e}")
return None return None
def parse_arguments():
"""解析命令行参数"""
parser = argparse.ArgumentParser(description='基于能量检测的极简录音系统')
parser.add_argument('--character', '-c', type=str, default='libai',
help='选择角色 (默认: libai)')
parser.add_argument('--list-characters', '-l', action='store_true',
help='列出所有可用角色')
return parser.parse_args()
def list_characters(characters_dir):
"""列出所有可用角色"""
characters = []
if os.path.exists(characters_dir):
for file in os.listdir(characters_dir):
if file.endswith('.json'):
character_name = file[:-5]
config_file = os.path.join(characters_dir, file)
try:
with open(config_file, 'r', encoding='utf-8') as f:
config = json.load(f)
name = config.get('name', character_name)
desc = config.get('description', '无描述')
characters.append(f"{character_name}: {name} - {desc}")
except:
characters.append(f"{character_name}: 配置文件读取失败")
if characters:
print("🎭 可用角色列表:")
for char in characters:
print(f" - {char}")
else:
print("❌ 未找到任何角色配置文件")
def main(): def main():
"""主函数""" """主函数"""
args = parse_arguments()
characters_dir = os.path.join(os.path.dirname(__file__), "characters")
# 如果要求列出角色,显示后退出
if args.list_characters:
list_characters(characters_dir)
return
print("🚀 基于能量检测的极简录音系统") print("🚀 基于能量检测的极简录音系统")
print("🤖 集成语音识别功能") print("🤖 集成语音识别功能")
print(f"🎭 当前角色: {args.character}")
print("=" * 50) print("=" * 50)
# 创建录音系统 # 创建录音系统
@ -1145,7 +1237,8 @@ def main():
max_recording_time=30.0, # 最大录音时间 max_recording_time=30.0, # 最大录音时间
enable_asr=True, # 启用语音识别功能 enable_asr=True, # 启用语音识别功能
enable_llm=True, # 启用大语言模型功能 enable_llm=True, # 启用大语言模型功能
enable_tts=True # 启用文本转语音功能 enable_tts=True, # 启用文本转语音功能
character=args.character # 指定角色
) )
print("✅ 系统初始化成功") print("✅ 系统初始化成功")
@ -1158,6 +1251,8 @@ def main():
print(" - 录音完成后自动语音识别") print(" - 录音完成后自动语音识别")
print(" - 语音识别后自动调用AI助手") print(" - 语音识别后自动调用AI助手")
print(" - AI回复后自动转换为语音") print(" - AI回复后自动转换为语音")
print(" - 多角色支持 (李白、猪八戒等)")
print(" - 每个角色独特音色和性格")
print(" - 预录音功能包含声音开始前2秒") print(" - 预录音功能包含声音开始前2秒")
print(" - 环形缓冲区防止丢失开头音频") print(" - 环形缓冲区防止丢失开头音频")
print(" - 自动调整能量阈值") print(" - 自动调整能量阈值")
@ -1171,6 +1266,26 @@ def main():
print(" export ARK_API_KEY='your_api_key_here'") print(" export ARK_API_KEY='your_api_key_here'")
print("=" * 50) print("=" * 50)
# 显示角色信息
if recorder.character_config:
print(f"🎭 当前角色: {recorder.character_config.get('name', '未知')}")
print(f"📝 描述: {recorder.character_config.get('description', '无描述')}")
print(f"🎤 音色: {recorder.tts_speaker}")
print("=" * 50)
# 显示使用说明
print("📖 使用说明:")
print("- 检测到声音自动开始录音")
print("- 持续静音3秒自动结束录音")
print("- 最少录音2秒最多30秒")
print("- 录音完成后自动播放")
print("- 按 Ctrl+C 退出")
print("🎭 角色切换:")
print("- 使用 --character 或 -c 参数选择角色")
print("- 使用 --list-characters 或 -l 查看所有角色")
print("- 示例: python recorder.py --character zhubajie")
print("=" * 50)
# 开始运行 # 开始运行
recorder.run() recorder.run()