mirror of
https://github.com/xszyou/Fay.git
synced 2026-03-12 17:51:28 +08:00
自然进化
- 重构会话管理逻辑支持以下链路基于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:
@@ -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控制器
|
||||
|
||||
@@ -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.
@@ -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 =
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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 |
@@ -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()
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"id": "ab4ed30b-9d4f-48c0-a2ac-efeacd6c3fe4"
|
||||
"id": "2ca34f25-4b8d-484b-9545-fc8d2e376bf9"
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user