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:
欣南科技
2025-03-13 00:54:01 +08:00
committed by GitHub
co-authored by Jad Jiao Haoyang xu hrz
parent c458a1b59a
commit a8f06f5718
6 changed files with 92 additions and 28 deletions
+3 -2
View File
@@ -181,8 +181,9 @@ LLM:
CozeLLM:
# 定义LLM API类型
type: coze
bot_id: 你的bot_id
user_id: 你的user_id
# bot_id和user_id的内容写在引号之内
bot_id: "你的bot_id"
user_id: "你的user_id"
personal_access_token: 你的coze个人令牌
LMStudioLLM:
# 定义LLM API类型
@@ -2,6 +2,7 @@ from config.logger import setup_logging
import json
from core.handle.sendAudioHandle import send_stt_message
from core.utils.dialogue import Message
from core.utils.util import remove_punctuation_and_length
from config.functionCallConfig import FunctionCallConfig
import asyncio
from enum import Enum
@@ -105,6 +106,7 @@ async def handle_user_intent(conn, text):
async def check_direct_exit(conn, text):
"""检查是否有明确的退出命令"""
_, text = remove_punctuation_and_length(text)
cmd_exit = conn.cmd_exit
for cmd in cmd_exit:
if text == cmd:
@@ -123,7 +125,7 @@ async def analyze_intent_with_llm(conn, text):
# 对话历史记录
dialogue = conn.dialogue
try:
intent_result = await conn.intent.detect_intent(dialogue.dialogue, text)
intent_result = await conn.intent.detect_intent(conn, dialogue.dialogue, text)
# 尝试解析JSON结果
try:
@@ -20,7 +20,7 @@ class IntentProviderBase(ABC):
logger.bind(tag=TAG).debug("Set LLM for intent provider")
@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:
@@ -1,7 +1,7 @@
from typing import List, Dict
from ..base import IntentProviderBase
from config.logger import setup_logging
import re
TAG = __name__
logger = setup_logging()
@@ -20,42 +20,90 @@ class IntentProvider(IntentProviderBase):
格式化后的系统提示词
"""
intent_list = []
"""
"continue_chat": "1.继续聊天, 除了播放音乐和结束聊天的时候的选项, 比如日常的聊天和问候, 对话等",
"end_chat": "2.结束聊天, 用户发来如再见之类的表示结束的话, 不想再进行对话的时候",
"play_music": "3.播放音乐, 用户希望你可以播放音乐, 只用于播放音乐的意图"
"""
for key, value in self.intent_options.items():
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:
intent_list.append(value)
# "如果是唱歌、听歌、播放音乐,请指定歌名,格式为'播放音乐 [识别出的歌名]'。\n"
# "如果听不出具体歌名,可以返回'随机播放音乐'。\n"
# "只需要返回意图结果的json,不要解释。"
# "返回格式如下:\n"
prompt = (
"你是一个意图识别助手。你需要根据和用户的对话记录,重点分析用户的最后一句话,判断用户意图属于以下哪一类:\n"
f"{', '.join(intent_list)}\n"
"如果是唱歌、听歌、播放音乐,请指定歌名,格式为'播放音乐 [识别出的歌名]'\n"
"如果听不出具体歌名,可以返回'随机播放音乐'\n"
"需要返回意图结果的json,不要解释。"
"返回格式如下:\n"
"{intent: '用户意图'}"
"你是一个意图识别助手。你需要根据和用户的对话记录,重点分析用户的最后一句话,判断用户意图属于以下哪一类(使用<start>和<end>标志)\n"
"<start>"
f"{', '.join(intent_list)}"
"<end>\n"
"需要按照以下的步骤处理用户的对话"
"1. 思考出对话的意图是哪一类的"
"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
async def detect_intent(self, dialogue_history: List[Dict], text:str) -> str:
logger.bind(tag=TAG).info(f"分析用户意图: {text}")
async def detect_intent(self, conn, dialogue_history: List[Dict], text:str) -> str:
if not self.llm:
raise ValueError("LLM provider not set")
# 构建用户最后一句话的提示
msgStr = ""
for msg in dialogue_history:
if msg.role == "user":
msgStr += f"User: {msg.content}\n"
elif msg.role== "assistant":
msgStr += f"Assistant: {msg.content}\n"
# 只使用最后两句即可
if len(dialogue_history) >= 2:
# 保证最少有两句话的时候处理
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"
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进行意图识别
intent = self.llm.response_no_stream(
system_prompt=self.promot,
system_prompt=prompt_music,
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):
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:
@@ -11,19 +11,31 @@ from cozepy import Coze, TokenAuth, Message, ChatStatus, MessageContentType, Cha
TAG = __name__
logger = setup_logging()
class LLMProvider(LLMProviderBase):
def __init__(self, config):
self.personal_access_token = config.get("personal_access_token")
self.bot_id = config.get("bot_id")
self.user_id = config.get("user_id")
self.session_conversation_map = {} # 存储session_id和conversation_id的映射
def response(self, session_id, dialogue):
coze_api_token = self.personal_access_token
coze_api_base = COZE_CN_BASE_URL
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)
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(
bot_id=self.bot_id,
@@ -31,6 +43,7 @@ class LLMProvider(LLMProviderBase):
additional_messages=[
Message.build_user_question_text(last_msg["content"]),
],
conversation_id=conversation_id,
):
if event.event == ChatEventType.CONVERSATION_MESSAGE_DELTA:
print(event.message.content, end="", flush=True)