mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-28 01:53:53 +08:00
Test prompt (#305)
* fix: 退出指令无法识别 (#302) 修复指令text包含标点的情况下,无法正确识别退出指令的问题 * 优化提示词 (#303) * 添加 使用 coze 会话。 (#282) 历史消息在coze 上可配置历史消息的轮次。 同个会话id,coze 上自动带历史上下文。 会话和当前session关联。一次连接一个会话,断开后,重连是新会话无历史消息。 * update:去除无用代码 --------- Co-authored-by: Jad <journey.adc@gmail.com> Co-authored-by: Jiao Haoyang <108573524+XuSenfeng@users.noreply.github.com> Co-authored-by: xu <494462498@qq.com> Co-authored-by: hrz <1710360675@qq.com>
This commit is contained in:
co-authored by
Jad
Jiao Haoyang
xu
hrz
parent
c458a1b59a
commit
a8f06f5718
@@ -181,8 +181,9 @@ LLM:
|
|||||||
CozeLLM:
|
CozeLLM:
|
||||||
# 定义LLM API类型
|
# 定义LLM API类型
|
||||||
type: coze
|
type: coze
|
||||||
bot_id: 你的bot_id
|
# bot_id和user_id的内容写在引号之内
|
||||||
user_id: 你的user_id
|
bot_id: "你的bot_id"
|
||||||
|
user_id: "你的user_id"
|
||||||
personal_access_token: 你的coze个人令牌
|
personal_access_token: 你的coze个人令牌
|
||||||
LMStudioLLM:
|
LMStudioLLM:
|
||||||
# 定义LLM API类型
|
# 定义LLM API类型
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ from config.logger import setup_logging
|
|||||||
import json
|
import json
|
||||||
from core.handle.sendAudioHandle import send_stt_message
|
from core.handle.sendAudioHandle import send_stt_message
|
||||||
from core.utils.dialogue import Message
|
from core.utils.dialogue import Message
|
||||||
|
from core.utils.util import remove_punctuation_and_length
|
||||||
from config.functionCallConfig import FunctionCallConfig
|
from config.functionCallConfig import FunctionCallConfig
|
||||||
import asyncio
|
import asyncio
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
@@ -105,6 +106,7 @@ async def handle_user_intent(conn, text):
|
|||||||
|
|
||||||
async def check_direct_exit(conn, text):
|
async def check_direct_exit(conn, text):
|
||||||
"""检查是否有明确的退出命令"""
|
"""检查是否有明确的退出命令"""
|
||||||
|
_, text = remove_punctuation_and_length(text)
|
||||||
cmd_exit = conn.cmd_exit
|
cmd_exit = conn.cmd_exit
|
||||||
for cmd in cmd_exit:
|
for cmd in cmd_exit:
|
||||||
if text == cmd:
|
if text == cmd:
|
||||||
@@ -123,7 +125,7 @@ async def analyze_intent_with_llm(conn, text):
|
|||||||
# 对话历史记录
|
# 对话历史记录
|
||||||
dialogue = conn.dialogue
|
dialogue = conn.dialogue
|
||||||
try:
|
try:
|
||||||
intent_result = await conn.intent.detect_intent(dialogue.dialogue, text)
|
intent_result = await conn.intent.detect_intent(conn, dialogue.dialogue, text)
|
||||||
|
|
||||||
# 尝试解析JSON结果
|
# 尝试解析JSON结果
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class IntentProviderBase(ABC):
|
|||||||
logger.bind(tag=TAG).debug("Set LLM for intent provider")
|
logger.bind(tag=TAG).debug("Set LLM for intent provider")
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
async def detect_intent(self, dialogue_history: List[Dict], text: str) -> str:
|
async def detect_intent(self, conn, dialogue_history: List[Dict], text: str) -> str:
|
||||||
"""
|
"""
|
||||||
检测用户最后一句话的意图
|
检测用户最后一句话的意图
|
||||||
Args:
|
Args:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from typing import List, Dict
|
from typing import List, Dict
|
||||||
from ..base import IntentProviderBase
|
from ..base import IntentProviderBase
|
||||||
from config.logger import setup_logging
|
from config.logger import setup_logging
|
||||||
|
import re
|
||||||
TAG = __name__
|
TAG = __name__
|
||||||
logger = setup_logging()
|
logger = setup_logging()
|
||||||
|
|
||||||
@@ -20,42 +20,90 @@ class IntentProvider(IntentProviderBase):
|
|||||||
格式化后的系统提示词
|
格式化后的系统提示词
|
||||||
"""
|
"""
|
||||||
intent_list = []
|
intent_list = []
|
||||||
|
|
||||||
|
"""
|
||||||
|
"continue_chat": "1.继续聊天, 除了播放音乐和结束聊天的时候的选项, 比如日常的聊天和问候, 对话等",
|
||||||
|
"end_chat": "2.结束聊天, 用户发来如再见之类的表示结束的话, 不想再进行对话的时候",
|
||||||
|
"play_music": "3.播放音乐, 用户希望你可以播放音乐, 只用于播放音乐的意图"
|
||||||
|
"""
|
||||||
for key, value in self.intent_options.items():
|
for key, value in self.intent_options.items():
|
||||||
if key == "play_music":
|
if key == "play_music":
|
||||||
intent_list.append(f"{value} [歌名]")
|
intent_list.append("3.播放音乐, 用户希望你可以播放音乐, 只用于播放音乐的意图")
|
||||||
|
elif key == "end_chat":
|
||||||
|
intent_list.append("2.结束聊天, 用户发来如再见之类的表示结束的话, 不想再进行对话的时候")
|
||||||
|
elif key == "continue_chat":
|
||||||
|
intent_list.append("1.继续聊天, 除了播放音乐和结束聊天的时候的选项, 比如日常的聊天和问候, 对话等")
|
||||||
else:
|
else:
|
||||||
intent_list.append(value)
|
intent_list.append(value)
|
||||||
|
|
||||||
|
# "如果是唱歌、听歌、播放音乐,请指定歌名,格式为'播放音乐 [识别出的歌名]'。\n"
|
||||||
|
# "如果听不出具体歌名,可以返回'随机播放音乐'。\n"
|
||||||
|
# "只需要返回意图结果的json,不要解释。"
|
||||||
|
# "返回格式如下:\n"
|
||||||
prompt = (
|
prompt = (
|
||||||
"你是一个意图识别助手。你需要根据和用户的对话记录,重点分析用户的最后一句话,判断用户意图属于以下哪一类:\n"
|
"你是一个意图识别助手。你需要根据和用户的对话记录,重点分析用户的最后一句话,判断用户意图属于以下哪一类(使用<start>和<end>标志):\n"
|
||||||
f"{', '.join(intent_list)}\n"
|
"<start>"
|
||||||
"如果是唱歌、听歌、播放音乐,请指定歌名,格式为'播放音乐 [识别出的歌名]'。\n"
|
f"{', '.join(intent_list)}"
|
||||||
"如果听不出具体歌名,可以返回'随机播放音乐'。\n"
|
"<end>\n"
|
||||||
"只需要返回意图结果的json,不要解释。"
|
"你需要按照以下的步骤处理用户的对话"
|
||||||
"返回格式如下:\n"
|
"1. 思考出对话的意图是哪一类的"
|
||||||
"{intent: '用户意图'}"
|
"2. 属于1和2的意图, 直接返回,返回格式如下:\n"
|
||||||
|
"{intent: '用户意图'}\n"
|
||||||
|
"3. 属于3的意图,则继续分析用户希望播放的音乐\n"
|
||||||
|
"4. 如果无法识别出具体歌名,可以返回'随机播放音乐'\n"
|
||||||
|
"{intent: '播放音乐 [获取的音乐名字]'}\n"
|
||||||
|
"下面是几个处理的示例(思考的内容不返回, 只返回json部分, 无额外的内容)\n"
|
||||||
|
"```"
|
||||||
|
"用户: 你今天怎么样?\n"
|
||||||
|
"思考(不返回): 用户发来的数据是一个问候语,属于继续聊天的意图, 是种类1, 种类1的需求是直接返回\n"
|
||||||
|
"返回结果: {intent: '继续聊天'}\n"
|
||||||
|
"```"
|
||||||
|
"用户: 我今天有点累了, 我们明天再聊吧\n"
|
||||||
|
"思考(不返回): 用户表达了今天不想继续对话,属于结束聊天的意图, 是种类2, 种类2的需求是直接返回\n"
|
||||||
|
"返回结果: {intent: '结束聊天'}\n"
|
||||||
|
"```"
|
||||||
|
"用户: 我今天有点累了, 我们明天再聊吧\n"
|
||||||
|
"思考(不返回): 用户表达了今天不想继续对话,属于结束聊天的意图, 是种类2, 种类2的需求是直接返回\n"
|
||||||
|
"返回结果: {intent: '结束聊天'}\n"
|
||||||
|
"```"
|
||||||
|
"用户: 你可以播放一首中秋月给我听吗\n"
|
||||||
|
"思考(不返回): 用户表达了想听音乐的续签,属于播放音乐的意图, 是种类3, 种类3的需求需要继续判断播放的音乐, 这里用户希望的歌曲名明确给出是中秋月\n"
|
||||||
|
"返回结果: {intent: '播放音乐 [中秋月]'}\n"
|
||||||
|
"```"
|
||||||
|
"你现在可以使用的音乐的名称如下(使用<start>和<end>标志):\n"
|
||||||
)
|
)
|
||||||
return prompt
|
return prompt
|
||||||
|
|
||||||
async def detect_intent(self, dialogue_history: List[Dict], text:str) -> str:
|
async def detect_intent(self, conn, dialogue_history: List[Dict], text:str) -> str:
|
||||||
logger.bind(tag=TAG).info(f"分析用户意图: {text}")
|
|
||||||
if not self.llm:
|
if not self.llm:
|
||||||
raise ValueError("LLM provider not set")
|
raise ValueError("LLM provider not set")
|
||||||
|
|
||||||
# 构建用户最后一句话的提示
|
# 构建用户最后一句话的提示
|
||||||
msgStr = ""
|
msgStr = ""
|
||||||
for msg in dialogue_history:
|
|
||||||
if msg.role == "user":
|
# 只使用最后两句即可
|
||||||
msgStr += f"User: {msg.content}\n"
|
if len(dialogue_history) >= 2:
|
||||||
elif msg.role== "assistant":
|
# 保证最少有两句话的时候处理
|
||||||
msgStr += f"Assistant: {msg.content}\n"
|
msgStr += f"{dialogue_history[-2].role}: {dialogue_history[-2].content}\n"
|
||||||
|
msgStr += f"{dialogue_history[-1].role}: {dialogue_history[-1].content}\n"
|
||||||
|
|
||||||
msgStr += f"User: {text}\n"
|
msgStr += f"User: {text}\n"
|
||||||
user_prompt = f"请分析用户的意图:\n{msgStr}"
|
user_prompt = f"当前的对话如下:\n{msgStr}"
|
||||||
|
prompt_music = f"{self.promot}\n<start>{conn.music_handler.music_files}\n<end>"
|
||||||
|
logger.bind(tag=TAG).debug(f"User prompt: {prompt_music}")
|
||||||
# 使用LLM进行意图识别
|
# 使用LLM进行意图识别
|
||||||
intent = self.llm.response_no_stream(
|
intent = self.llm.response_no_stream(
|
||||||
system_prompt=self.promot,
|
system_prompt=prompt_music,
|
||||||
user_prompt=user_prompt
|
user_prompt=user_prompt
|
||||||
)
|
)
|
||||||
intent_result = intent.strip()
|
# 使用正则表达式提取大括号中的内容
|
||||||
logger.bind(tag=TAG).info(f"意图识别结果: {intent_result}")
|
# 使用正则表达式提取 {} 中的内容
|
||||||
return intent_result
|
match = re.search(r'\{.*?\}', intent)
|
||||||
|
if match:
|
||||||
|
result = match.group(0) # 获取匹配到的内容(包含 {})
|
||||||
|
print(result) # 输出:{intent: '播放音乐 [中秋月]'}
|
||||||
|
intent = result
|
||||||
|
else:
|
||||||
|
intent = "{intent: '继续聊天'}"
|
||||||
|
logger.bind(tag=TAG).info(f"Detected intent: {intent}")
|
||||||
|
return intent.strip()
|
||||||
@@ -7,7 +7,7 @@ logger = setup_logging()
|
|||||||
|
|
||||||
|
|
||||||
class IntentProvider(IntentProviderBase):
|
class IntentProvider(IntentProviderBase):
|
||||||
async def detect_intent(self, dialogue_history: List[Dict], text: str) -> str:
|
async def detect_intent(self, conn, dialogue_history: List[Dict], text: str) -> str:
|
||||||
"""
|
"""
|
||||||
默认的意图识别实现,始终返回继续聊天
|
默认的意图识别实现,始终返回继续聊天
|
||||||
Args:
|
Args:
|
||||||
|
|||||||
@@ -11,11 +11,13 @@ from cozepy import Coze, TokenAuth, Message, ChatStatus, MessageContentType, Cha
|
|||||||
TAG = __name__
|
TAG = __name__
|
||||||
logger = setup_logging()
|
logger = setup_logging()
|
||||||
|
|
||||||
|
|
||||||
class LLMProvider(LLMProviderBase):
|
class LLMProvider(LLMProviderBase):
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
self.personal_access_token = config.get("personal_access_token")
|
self.personal_access_token = config.get("personal_access_token")
|
||||||
self.bot_id = config.get("bot_id")
|
self.bot_id = config.get("bot_id")
|
||||||
self.user_id = config.get("user_id")
|
self.user_id = config.get("user_id")
|
||||||
|
self.session_conversation_map = {} # 存储session_id和conversation_id的映射
|
||||||
|
|
||||||
def response(self, session_id, dialogue):
|
def response(self, session_id, dialogue):
|
||||||
coze_api_token = self.personal_access_token
|
coze_api_token = self.personal_access_token
|
||||||
@@ -24,6 +26,16 @@ class LLMProvider(LLMProviderBase):
|
|||||||
last_msg = next(m for m in reversed(dialogue) if m["role"] == "user")
|
last_msg = next(m for m in reversed(dialogue) if m["role"] == "user")
|
||||||
|
|
||||||
coze = Coze(auth=TokenAuth(token=coze_api_token), base_url=coze_api_base)
|
coze = Coze(auth=TokenAuth(token=coze_api_token), base_url=coze_api_base)
|
||||||
|
conversation_id = self.session_conversation_map.get(session_id)
|
||||||
|
|
||||||
|
# 如果没有找到conversation_id,则创建新的对话
|
||||||
|
if not conversation_id:
|
||||||
|
conversation = coze.conversations.create(
|
||||||
|
messages=[
|
||||||
|
]
|
||||||
|
)
|
||||||
|
conversation_id = conversation.id
|
||||||
|
self.session_conversation_map[session_id] = conversation_id # 更新映射
|
||||||
|
|
||||||
for event in coze.chat.stream(
|
for event in coze.chat.stream(
|
||||||
bot_id=self.bot_id,
|
bot_id=self.bot_id,
|
||||||
@@ -31,6 +43,7 @@ class LLMProvider(LLMProviderBase):
|
|||||||
additional_messages=[
|
additional_messages=[
|
||||||
Message.build_user_question_text(last_msg["content"]),
|
Message.build_user_question_text(last_msg["content"]),
|
||||||
],
|
],
|
||||||
|
conversation_id=conversation_id,
|
||||||
):
|
):
|
||||||
if event.event == ChatEventType.CONVERSATION_MESSAGE_DELTA:
|
if event.event == ChatEventType.CONVERSATION_MESSAGE_DELTA:
|
||||||
print(event.message.content, end="", flush=True)
|
print(event.message.content, end="", flush=True)
|
||||||
|
|||||||
Reference in New Issue
Block a user