diff --git a/README.md b/README.md index 206d87e..2e9d603 100644 --- a/README.md +++ b/README.md @@ -30,12 +30,13 @@ - 支持唤醒及打断对话 - 支持服务器及单机模式 - 支持机器人表情输出 -- 支持react agent自主决策执行、主动对话(准备升级到MCP协议) +- 支持react agent自主决策执行、主动对话 - 支持后台静默启动 - 支持deepseek等thinking llm - 设计独特的认知模型 - 支持MCP - 提供配置管理中心 +- 全链路交互互通 ### @@ -77,7 +78,7 @@ pip install -r requirements.txt ``` ### **配置** -+ 依照说明修改 `./system.conf` 文件 ++ 依照说明修改 `./system.conf` 文件(默认运行将自动使用公共配置) ### **启动** 启动Fay控制器 diff --git a/cache_data/config.json b/cache_data/config.json deleted file mode 100644 index de208c6..0000000 --- a/cache_data/config.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "attribute": { - "additional": "发呆", - "age": "成年", - "birth": "Github", - "constellation": "水瓶座", - "contact": "qq467665317", - "gender": "女", - "goal": "工作协助", - "hobby": "发呆", - "job": "助理", - "name": "菲菲", - "position": "陪伴", - "voice": "zhixiaomei", - "zodiac": "蛇" - }, - "interact": { - "QnA": "qa.csv", - "maxInteractTime": 15, - "perception": { - "chat": 10, - "follow": 10, - "gift": 10, - "indifferent": 10, - "join": 10 - }, - "playSound": false, - "visualization": false - }, - "items": [], - "memory": { - "isolate_by_user": true - }, - "source": { - "automatic_player_status": false, - "automatic_player_url": "http://127.0.0.1:6000", - "liveRoom": { - "enabled": true, - "url": "" - }, - "record": { - "device": "", - "enabled": false - }, - "wake_word": "你好", - "wake_word_enabled": false, - "wake_word_type": "front" - } -} \ No newline at end of file diff --git a/cache_data/input.wav b/cache_data/input.wav deleted file mode 100644 index 5fb6be6..0000000 Binary files a/cache_data/input.wav and /dev/null differ diff --git a/cache_data/system.conf b/cache_data/system.conf deleted file mode 100644 index 74959bd..0000000 --- a/cache_data/system.conf +++ /dev/null @@ -1,28 +0,0 @@ -[key] -ali_nls_app_key = rEs56rswzWQqXy14 -ali_nls_key_id = LTAI5tDMHwP86DC3Ma2cWiFK -ali_nls_key_secret = V3r0NBYJQThIKDzhlx35xImC7BAc2v -ali_tss_app_key = rEs56rswzWQqXy14 -ali_tss_key_id = LTAI5tDMHwP86DC3Ma2cWiFK -ali_tss_key_secret = V3r0NBYJQThIKDzhlx35xImC7BAc2v -asr_mode = ali -baidu_emotion_api_key = -baidu_emotion_app_id = -baidu_emotion_secret_key = -fay_url = http://127.0.0.1:5000 -gpt_api_key = sk-izmvqrzyhjghzyghiofqfpusxprmfljntxzggkcovtneqpas -gpt_base_url = https://api.siliconflow.cn/v1 -gpt_model_engine = moonshotai/Kimi-K2-Instruct-0905 -local_asr_ip = 127.0.0.1 -local_asr_port = 10197 -ltp_mode = baidu -ms_tts_key = -ms_tts_region = -proxy_config = -start_mode = web -tts_module = ali -volcano_tts_access_token = -volcano_tts_appid = -volcano_tts_cluster = -volcano_tts_voice_type = - diff --git a/core/fay_core.py b/core/fay_core.py index 75ba940..6e7b7b6 100644 --- a/core/fay_core.py +++ b/core/fay_core.py @@ -165,27 +165,6 @@ class FeiFei: username = interact.data.get("user", "User") uid = member_db.new_instance().find_user(username) - try: - from utils.stream_state_manager import get_state_manager - if get_state_manager().is_session_active(username): - stream_manager.new_instance().clear_Stream_with_audio(username) - except Exception: - pass - - # 切换到新会话,令上一会话的流式输出/音频尽快结束 - util.printInfo(1, username, "重置中断标志,开始新的对话处理") - try: - sm = stream_manager.new_instance() - import uuid - conv_id = "conv_" + str(uuid.uuid4()) - sm.set_current_conversation(username, conv_id) - # 将当前会话ID附加到交互数据 - interact.data["conversation_id"] = conv_id - # 允许新的生成 - sm.set_stop_generation(username, stop=False) - except Exception: - pass - if index == 1: #语音、文字交互 #记录用户问题,方便obs等调用 @@ -270,13 +249,25 @@ class FeiFei: #触发交互 def on_interact(self, interact: Interact): #创建用户 + username = interact.data.get("user", "User") + if member_db.new_instance().is_username_exist(username) == "notexists": + member_db.new_instance().add_user(username) + try: + from utils.stream_state_manager import get_state_manager + import uuid + if get_state_manager().is_session_active(username): + stream_manager.new_instance().clear_Stream_with_audio(username) + conv_id = "conv_" + str(uuid.uuid4()) + stream_manager.new_instance().set_current_conversation(username, conv_id) + # 将当前会话ID附加到交互数据 + interact.data["conversation_id"] = conv_id + # 允许新的生成 + stream_manager.new_instance().set_stop_generation(username, stop=False) + except Exception: + util.log(3, "开启新会话失败") + if interact.interact_type == 1: - username = interact.data.get("user", "User") - if member_db.new_instance().is_username_exist(username) == "notexists": - member_db.new_instance().add_user(username) - # 判断调用来源,如果是非stream调用则同步处理 MyThread(target=self.__process_interact, args=[interact]).start() - else: return self.__process_interact(interact) @@ -313,6 +304,14 @@ class FeiFei: # 流式文本拼接存库 content_id = 0 if is_first == True: + # reset any leftover think-mode at the start of a new reply + try: + if uid is not None: + self.think_mode_users[uid] = False + if uid in self.think_time_users: + del self.think_time_users[uid] + except Exception: + pass conv = interact.data.get("conversation_id") or ("conv_" + str(uuid.uuid4())) conv_no = 0 # 创建第一条数据库记录,获得content_id diff --git a/core/recorder.py b/core/recorder.py index ba163ea..f2aa8d5 100644 --- a/core/recorder.py +++ b/core/recorder.py @@ -210,6 +210,8 @@ class Recorder: if wsa_server.get_instance().is_connected(self.username): content = {'Topic': 'human', 'Data': {'Key': 'log', 'Value': "[!] 待唤醒!"}, 'Username' : self.username, 'robot': f'{cfg.fay_url}/robot/Normal.jpg'} wsa_server.get_instance().add_cmd(content) + # 未命中前置唤醒词时需要释放处理状态,避免麦克风阻塞 + self.__processing = False #非唤醒模式 else: @@ -419,4 +421,4 @@ class Recorder: @abstractmethod def is_remote(self): - pass \ No newline at end of file + pass diff --git a/core/stream_manager.py b/core/stream_manager.py index 3b5473b..caa60d4 100644 --- a/core/stream_manager.py +++ b/core/stream_manager.py @@ -266,6 +266,18 @@ class StreamManager: # 第三步:清除音频队列(Queue线程安全,不需要锁) self._clear_audio_queue(username) + + # reset think state for username on force stop + try: + uid_tmp = member_db.new_instance().find_user(username) + if uid_tmp is not None: + fei = fay_booter.feiFei + if fei is not None: + fei.think_mode_users[uid_tmp] = False + if uid_tmp in getattr(fei, 'think_time_users', {}): + del fei.think_time_users[uid_tmp] + except Exception: + pass # 第四步:清除文本流(独立操作) with self.stream_lock: diff --git a/gui/flask_server.py b/gui/flask_server.py index ee30e7a..aabc158 100644 --- a/gui/flask_server.py +++ b/gui/flask_server.py @@ -408,19 +408,23 @@ def gpt_stream_response(last_content, username): sm = stream_manager.new_instance() _, nlp_Stream = sm.get_Stream(username) def generate(): + conversation_id = sm.get_conversation_id(username) while True: sentence = nlp_Stream.read() if sentence is None: gsleep(0.01) continue - # 解析并移除隐藏会话ID标签 + # 跳过非当前会话 try: m = re.search(r"__]+)>__", sentence) + producer_cid = m.group(1) + if producer_cid != conversation_id: + continue if m: sentence = sentence.replace(m.group(0), "") - except Exception: - pass + except Exception as e: + print(e) is_first = "_" in sentence is_end = "_" in sentence content = sentence.replace("_", "").replace("_", "").replace("_", "") @@ -460,19 +464,23 @@ def non_streaming_response(last_content, username): sm = stream_manager.new_instance() _, nlp_Stream = sm.get_Stream(username) text = "" + conversation_id = sm.get_conversation_id(username) while True: sentence = nlp_Stream.read() if sentence is None: gsleep(0.01) continue - # 处理特殊标记 + # 跳过非当前会话 try: m = re.search(r"__]+)>__", sentence) + producer_cid = m.group(1) + if producer_cid != conversation_id: + continue if m: sentence = sentence.replace(m.group(0), "") - except Exception: - pass + except Exception as e: + print(e) is_first = "_" in sentence is_end = "_" in sentence text += sentence.replace("_", "").replace("_", "").replace("_", "") diff --git a/gui/static/images/Logo.png b/gui/static/images/Logo.png index 7f1a541..d35ad83 100644 Binary files a/gui/static/images/Logo.png and b/gui/static/images/Logo.png differ diff --git a/llm/nlp_cognitive_stream.py b/llm/nlp_cognitive_stream.py index dd0ec0a..53a5380 100644 --- a/llm/nlp_cognitive_stream.py +++ b/llm/nlp_cognitive_stream.py @@ -44,7 +44,7 @@ from core import stream_manager os.environ["LANGCHAIN_TRACING_V2"] = "true" os.environ["LANGCHAIN_ENDPOINT"] = "https://api.smith.langchain.com" os.environ["LANGCHAIN_API_KEY"] = "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" -os.environ["LANGCHAIN_PROJECT"] = "fay3.8.2_github" +os.environ["LANGCHAIN_PROJECT"] = "fay3.9.1_github" # 加载配置 cfg.load_config() diff --git a/memory/User/meta.json b/memory/User/meta.json index ca95d1e..6fa3fff 100644 --- a/memory/User/meta.json +++ b/memory/User/meta.json @@ -1,3 +1,3 @@ { - "id": "ab4ed30b-9d4f-48c0-a2ac-efeacd6c3fe4" + "id": "2ca34f25-4b8d-484b-9545-fc8d2e376bf9" } \ No newline at end of file diff --git a/system.conf.bak b/system.conf.bak index 4c9df70..8f62e0f 100644 --- a/system.conf.bak +++ b/system.conf.bak @@ -1,27 +1,49 @@ + [key] -ali_nls_app_key = rEs56rswzWQqXy14 -ali_nls_key_id = LTAI5tDMHwP86DC3Ma2cWiFK -ali_nls_key_secret = V3r0NBYJQThIKDzhlx35xImC7BAc2v -ali_tss_app_key = rEs56rswzWQqXy14 -ali_tss_key_id = LTAI5tDMHwP86DC3Ma2cWiFK -ali_tss_key_secret = V3r0NBYJQThIKDzhlx35xImC7BAc2v -asr_mode = ali -baidu_emotion_api_key = -baidu_emotion_app_id = -baidu_emotion_secret_key = -fay_url = http://127.0.0.1:5000 -gpt_api_key = sk-1e0d0f299adf4a05b72f9bb31c4ca40c -gpt_base_url = https://dashscope.aliyuncs.com/compatible-mode/v1 -gpt_model_engine = qwen3-max-preview -local_asr_ip = 127.0.0.1 -local_asr_port = 10197 -ltp_mode = baidu -ms_tts_key = -ms_tts_region = -proxy_config = -start_mode = web -tts_module = ali -volcano_tts_access_token = -volcano_tts_appid = -volcano_tts_cluster = -volcano_tts_voice_type = +#funasr / ali / sensevoice +#建议使用funasr,请依照asr/funasr/README>md的说明启动 +ASR_mode = funasr +#ASR二选一(需要运行fay/test/funasr服务)集成达摩院asr项目、感谢中科大脑算法工程师张聪聪提供集成代码 +local_asr_ip=127.0.0.1 +local_asr_port=10197 + +# ASR二选一(第1次运行建议用这个,免费3个月), 阿里云 实时语音识别 服务密钥(必须)https://ai.aliyun.com/nls/trans +ali_nls_key_id= +ali_nls_key_secret= +ali_nls_app_key= + + + +#tts类型(切换请重新选择所需要的声音)azure、ali、gptsovits、volcano、gptsovits_v3 +tts_module=ali + +# 微软 文字转语音 服务密钥(非必须,使用可产生不同情绪的音频)https://azure.microsoft.com/zh-cn/services/cognitive-services/text-to-speech/ +ms_tts_key= +ms_tts_region= + +# 阿里云 文字转语音 服务密钥 https://ai.aliyun.com/nls/trans +ali_tss_key_id= +ali_tss_key_secret= +ali_tss_app_key= + +# Doubao-语音合成 服务密钥 https://www.volcengine.com/product/voice-tech +volcano_tts_appid= +volcano_tts_access_token= +volcano_tts_cluster=volcano_tts +#可为空,为空时读取选择的音色 +volcano_tts_voice_type= + +#key +gpt_api_key= +#gpt base url 如:https://api.openai.com/v1、https://rwkv.ai-creator.net/chntuned/v1、https://api.fastgpt.in/api/v1、https://api.moonshot.cn/v1 +gpt_base_url= +#gpt model engine 如:glm4、deepseek、qwen3-4b等 +gpt_model_engine= +#gpt(fastgpt)代理(可为空,填写例子:127.0.0.1:7890) +proxy_config= + +#启动模式common、web(服务器或docker请使用web方式,通过http://127.0.0.1:5000控制) +start_mode=web + +#服务器主动地址 +fay_url=http://127.0.0.1:5000 \ No newline at end of file