自然进化

- 重构会话管理逻辑支持以下链路基于llm输出、mcp执行、qa问答对匹配时,文字或音频输出的单向或双向打断:
	- 1、flask_server.api_send()(gui窗口文字发送) <--> flask_server.api_send_v1_chat_completions()(fay对外的openai兼容接口)
	- 2、flask_server.api_send(gui窗口文字发送) <--  flask_server.transparent_pass(消息透传接口)
	- 3、flask_server.api_send(gui窗口文字发送) <-- flask_server.to_stop_talking(打断接口)
	- 4、flask_server.api_send(gui窗口文字发送) <--> 远程音频
	- 5、flask_server.api_send(gui窗口文字发送) <-- 唤醒(普通唤醒、前置词唤醒)
	- 6、flask_server.api_send(gui窗口文字发送) <-->日程执行
	- 7、flask_server.api_send_v1_chat_completions(fay对外的openai兼容接口) <--> flask_server.transparent_pass(消息透传接口)
	- 8、flask_server.api_send_v1_chat_completions(fay对外的openai兼容接口) <-- flask_server.to_stop_talking(打断接口)
	- 9、flask_server.api_send_v1_chat_completions(fay对外的openai兼容接口) <--> 远程音频
	- 10、flask_server.api_send_v1_chat_completions(fay对外的openai兼容接口) <-- 唤醒(普通唤醒、前置词唤醒)
	- 11、flask_server.api_send_v1_chat_completions(fay对外的openai兼容接口)  <-->日程执行
	- 12、flask_server.transparent_pass(消息透传接口) <-- flask_server.to_stop_talking(打断接口)
	- 13、flask_server.transparent_pass(消息透传接口) <--> 远程音频
	- 14、flask_server.transparent_pass(消息透传接口) <-- 唤醒(普通唤醒、前置词唤醒)
	- 15、flask_server.transparent_pass(消息透传接口) <-->日程执行
	- 16、远程音频 <-- 唤醒(普通唤醒、前置词唤醒)
	- 17、远程音频 <-->日程执行
This commit is contained in:
guo zebin
2025-09-17 15:53:54 +08:00
parent 754d2b51c6
commit bb9dd4bef4
12 changed files with 107 additions and 140 deletions

View File

@@ -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控制器

View File

@@ -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"
}
}

Binary file not shown.

View File

@@ -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 =

View File

@@ -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

View File

@@ -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
pass

View File

@@ -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:

View File

@@ -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"__<cid=([^>]+)>__", 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 = "_<isfirst>" in sentence
is_end = "_<isend>" in sentence
content = sentence.replace("_<isfirst>", "").replace("_<isend>", "").replace("_<isqa>", "")
@@ -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"__<cid=([^>]+)>__", 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 = "_<isfirst>" in sentence
is_end = "_<isend>" in sentence
text += sentence.replace("_<isfirst>", "").replace("_<isend>", "").replace("_<isqa>", "")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

@@ -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()

View File

@@ -1,3 +1,3 @@
{
"id": "ab4ed30b-9d4f-48c0-a2ac-efeacd6c3fe4"
"id": "2ca34f25-4b8d-484b-9545-fc8d2e376bf9"
}

View File

@@ -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