update:优化home_assistant提示词加载

This commit is contained in:
hrz
2025-03-22 20:39:52 +08:00
parent d5a5b24223
commit 82413fecab
8 changed files with 129 additions and 250 deletions
+10 -32
View File
@@ -49,14 +49,6 @@ prompt: |
你是一个叫小智/小志的台湾女孩,说话机车,声音好听,习惯简短表达,爱用网络梗。
请注意,要像一个人一样说话,请不要回复表情符号、代码、和xml标签。
现在我正在和你进行语音聊天,我们开始吧。
如果用户希望结束对话,请在最后说“拜拜”或“再见”。
#-- hass 相关function的配置格式 请将你家需要控制的设备以下列格式追加在prompt里--
# 下面是我家智能设备,可以通过homeassistant控制
# 房间,设备别名,设备id
# 书房,吸顶灯,light.649e3159aa36_light
# 书房,homepod,media_player.shu_fang
# 书房,灯带,light.plug_158df955a6167a
# 书房,窗帘,cover.curtain_158d000710f507
# 使用完声音文件后删除文件(Delete the sound file when you are done using it)
delete_audio: true
@@ -107,10 +99,12 @@ Intent:
- change_role
- get_weather
- get_news
# play_music是服务器自带的音乐播放,hass_play_music是通过home assistant控制的独立外部程序音乐播放
# 如果用了hass_play_music,就不要开启play_music,两者只留一个
- play_music
#- hass_play_music
#- hass_get_state
#- hass_set_state
#- hass_play_music
# 插件的基础配置
plugins:
@@ -127,6 +121,13 @@ plugins:
society: "https://www.chinanews.com.cn/rss/society.xml"
world: "https://www.chinanews.com.cn/rss/world.xml"
finance: "https://www.chinanews.com.cn/rss/finance.xml"
home_assistant:
devices:
- 客厅,玩具灯,switch.cuco_cn_460494544_cp1_on_p_2_1
- 卧室,台灯,switch.iot_cn_831898993_socn1_on_p_2_1
base_url: http://homeassistant.local:8123
api_key: 你的home assistant api访问令牌
Memory:
mem0ai:
@@ -253,12 +254,6 @@ LLM:
model_name: deepseek-r1-distill-llama-8b@q4_k_m # 使用的模型名称,需要预先在社区下载
url: http://localhost:1234/v1 # LM Studio服务地址
api_key: lm-studio # LM Studio服务的固定API Key
HomeAssistant:
# 定义LLM API类型
type: homeassistant
base_url: http://homeassistant.local:8123
agent_id: conversation.chatgpt
api_key: 你的home assistant api访问令牌
FastgptLLM:
# 定义LLM API类型
type: fastgpt
@@ -512,20 +507,3 @@ module_test:
- "你好,请介绍一下你自己"
- "What's the weather like today?"
- "请用100字概括量子计算的基本原理和应用前景"
# 本地音乐播放配置
music:
music_dir: "./music" # 音乐文件存放路径,将从该目录及子目录下搜索音乐文件
music_ext: # 音乐文件类型,p3格式效率最高
- ".mp3"
- ".wav"
- ".p3"
refresh_time: 300 # 刷新音乐列表的时间间隔,单位为秒
# 以下配置在小于等于0.0.9版本中的docker容器中可用
# 0.0.9以后的新版本源码部署已经无法奏效
manager:
enabled: false
ip: 0.0.0.0
port: 8002
use_private_config: false
+1 -1
View File
@@ -196,7 +196,7 @@ class ConnectionHandler:
self.prompt = self.prompt + f"\n我在:{self.client_ip_info}"
self.dialogue.put(Message(role="system", content=self.prompt))
self.func_handler = FunctionHandler(self.config)
self.func_handler = FunctionHandler(self)
def change_system_prompt(self, prompt):
self.prompt = prompt
@@ -1,16 +1,16 @@
import asyncio
from enum import Enum
from config.logger import setup_logging
import json
from plugins_func.register import FunctionRegistry, ActionResponse, Action, ToolType
from plugins_func.functions.hass_init import append_devices_to_prompt
TAG = __name__
logger = setup_logging()
class FunctionHandler:
def __init__(self, config):
self.config = config
def __init__(self, conn):
self.conn = conn
self.config = conn.config
self.function_registry = FunctionRegistry()
self.register_nessary_functions()
self.register_config_functions()
@@ -26,9 +26,10 @@ class FunctionHandler:
func_names = ",".join(surport_plugins)
for function_desc in self.functions_desc:
if function_desc["function"]["name"] == "plugin_loader":
function_desc["function"]["description"] = function_desc["function"]["description"].replace("[plugins]", func_names)
function_desc["function"]["description"] = function_desc["function"]["description"].replace("[plugins]",
func_names)
break
def upload_functions_desc(self):
self.functions_desc = self.function_registry.get_all_function_desc()
@@ -47,7 +48,6 @@ class FunctionHandler:
def register_nessary_functions(self):
"""注册必要的函数"""
self.function_registry.register_function("handle_exit_intent")
#self.function_registry.register_function("play_music")
self.function_registry.register_function("plugin_loader")
self.function_registry.register_function("get_time")
self.function_registry.register_function("raise_and_lower_the_volume")
@@ -57,6 +57,9 @@ class FunctionHandler:
for func in self.config["Intent"]["function_call"].get("functions", []):
self.function_registry.register_function(func)
"""home assistant需要初始化提示词"""
append_devices_to_prompt(self.conn)
def get_function(self, name):
return self.function_registry.get_function(name)
@@ -1,62 +0,0 @@
import requests
from requests.exceptions import RequestException
from config.logger import setup_logging
from core.providers.llm.base import LLMProviderBase
TAG = __name__
logger = setup_logging()
class LLMProvider(LLMProviderBase):
def __init__(self, config):
self.agent_id = config.get("agent_id") # 对应 agent_id
self.api_key = config.get("api_key")
self.base_url = config.get("base_url", config.get("url")) # 默认使用 base_url
self.api_url = f"{self.base_url}/api/conversation/process" # 拼接完整的 API URL
def response(self, session_id, dialogue):
print(dialogue)
try:
# home assistant语音助手自带意图,无需使用xiaozhi ai自带的,只需要把用户说的话传递给home assistant即可
# 提取最后一个 role 为 'user' 的 content
input_text = None
if isinstance(dialogue, list): # 确保 dialogue 是一个列表
# 逆序遍历,找到最后一个 role 为 'user' 的消息
for message in reversed(dialogue):
if message.get("role") == "user": # 找到 role 为 'user' 的消息
input_text = message.get("content", "")
break # 找到后立即退出循环
# 构造请求数据
payload = {
"text": input_text,
"agent_id": self.agent_id,
"conversation_id": session_id # 使用 session_id 作为 conversation_id
}
# 设置请求头
headers = {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json"
}
# 发起 POST 请求
response = requests.post(self.api_url, json=payload, headers=headers)
# 检查请求是否成功
response.raise_for_status()
# 解析返回数据
data = response.json()
speech = data.get("response", {}).get("speech", {}).get("plain", {}).get("speech", "")
# 返回生成的内容
if speech:
yield speech
else:
logger.bind(tag=TAG).warning("API 返回数据中没有 speech 内容")
except RequestException as e:
logger.bind(tag=TAG).error(f"HTTP 请求错误: {e}")
except Exception as e:
logger.bind(tag=TAG).error(f"生成响应时出错: {e}")
@@ -35,6 +35,15 @@ class Dialogue:
self.getMessages(m, dialogue)
return dialogue
def update_system_message(self, new_content: str):
"""更新或添加系统消息"""
# 查找第一个系统消息
system_msg = next((msg for msg in self.dialogue if msg.role == "system"), None)
if system_msg:
system_msg.content = new_content
else:
self.put(Message(role="system", content=new_content))
def get_llm_dialogue_with_memory(self, memory_str: str = None) -> List[Dict[str, str]]:
if memory_str is None or len(memory_str) == 0:
return self.get_llm_dialogue()
@@ -1,4 +1,5 @@
from plugins_func.register import register_function,ToolType, ActionResponse, Action
from plugins_func.register import register_function, ToolType, ActionResponse, Action
from plugins_func.functions.hass_init import initialize_hass_handler
from config.logger import setup_logging
import asyncio
import requests
@@ -6,53 +7,41 @@ import requests
TAG = __name__
logger = setup_logging()
HASS_CACHE = {}
hass_get_state_function_desc ={
"type": "function",
"function": {
"name": "hass_get_state",
"description": "获取homeassistant里设备的状态,包括灯光亮度,媒体播放器的音量,设备的暂停、继续操作",
"parameters": {
"type": "object",
"properties": {
"entity_id": {
"type": "string",
"description": "需要操作的设备id,homeassistant里的entity_id"
}
},
"required": ["entity_id"]
}
hass_get_state_function_desc = {
"type": "function",
"function": {
"name": "hass_get_state",
"description": "获取homeassistant里设备的状态,包括灯光亮度,媒体播放器的音量,设备的暂停、继续操作",
"parameters": {
"type": "object",
"properties": {
"entity_id": {
"type": "string",
"description": "需要操作的设备id,homeassistant里的entity_id"
}
}
},
"required": ["entity_id"]
}
}
}
@register_function("hass_get_state", hass_get_state_function_desc, ToolType.SYSTEM_CTL)
def hass_get_state(conn, entity_id=''):
try:
future = asyncio.run_coroutine_threadsafe(
handle_hass_get_state(conn, entity_id),
conn.loop
)
ha_response = future.result()
return ActionResponse(action=Action.REQLLM, result="执行成功", response=ha_response)
future = asyncio.run_coroutine_threadsafe(
handle_hass_get_state(conn, entity_id),
conn.loop
)
ha_response = future.result()
return ActionResponse(action=Action.REQLLM, result="执行成功", response=ha_response)
except Exception as e:
logger.bind(tag=TAG).error(f"处理设置属性意图错误: {e}")
def initialize_hass_handler(conn):
config = conn.config
global HASS_CACHE
if HASS_CACHE == {}:
logger.bind(tag=TAG).info(f"实例化HASS:")
if "HomeAssistant" in config["LLM"]:
HASS_CACHE['base_url'] = config["LLM"]['HomeAssistant']['base_url']
HASS_CACHE['api_key'] = config["LLM"]['HomeAssistant']['api_key']
else:
logger.bind(tag=TAG).error(f"使用前请在config文件中配置: LLM.HomeAssistant.base_url LLM.HomeAssistant.api_key")
logger.bind(tag=TAG).error(f"处理设置属性意图错误: {e}")
async def handle_hass_get_state(conn, entity_id):
initialize_hass_handler(conn)
global HASS_CACHE
HASS_CACHE = initialize_hass_handler(conn)
api_key = HASS_CACHE['api_key']
base_url = HASS_CACHE['base_url']
url = f"{base_url}/api/states/{entity_id}"
@@ -61,10 +50,7 @@ async def handle_hass_get_state(conn, entity_id):
"Content-Type": "application/json"
}
response = requests.get(url, headers=headers)
#logger.bind(tag=TAG).info(f"获取状态: url:{url},return_code:{response.status_code},{response.json()}")
if response.status_code == 200:
return response.json()['state']
#return response.json()['attributes']
#response.attributes
else:
return f"切换失败,错误码: {response.status_code}"
@@ -1,4 +1,5 @@
from plugins_func.register import register_function,ToolType, ActionResponse, Action
from plugins_func.register import register_function, ToolType, ActionResponse, Action
from plugins_func.functions.hass_init import initialize_hass_handler
from config.logger import setup_logging
import asyncio
import requests
@@ -6,43 +7,32 @@ import requests
TAG = __name__
logger = setup_logging()
HASS_CACHE = {}
hass_play_music_function_desc = {
"type": "function",
"function": {
"name": "hass_play_music",
"description": "用户想听音乐、有声书的时候使用,在房间的媒体播放器(media_player)里播放对应音频",
"parameters": {
"type": "object",
"properties": {
"media_content_id": {
"type": "string",
"description": "可以是音乐或有声书的专辑名称、歌曲名、演唱者,如果未指定就填random"
},
"entity_id": {
"type": "string",
"description": "需要操作的音箱的设备id,homeassistant里的entity_id,media_player开头"
}
},
"required": ["media_content_id", "entity_id"]
}
"type": "function",
"function": {
"name": "hass_play_music",
"description": "用户想听音乐、有声书的时候使用,在房间的媒体播放器(media_player)里播放对应音频",
"parameters": {
"type": "object",
"properties": {
"media_content_id": {
"type": "string",
"description": "可以是音乐或有声书的专辑名称、歌曲名、演唱者,如果未指定就填random"
},
"entity_id": {
"type": "string",
"description": "需要操作的音箱的设备id,homeassistant里的entity_id,media_player开头"
}
}
},
"required": ["media_content_id", "entity_id"]
}
}
}
@register_function('hass_play_music', hass_play_music_function_desc, ToolType.SYSTEM_CTL)
def hass_play_music(conn, entity_id='', media_content_id='random'):
try:
#logger.bind(tag=TAG).error(f"arguments: {arguments}")
#entity_id = arguments["entity_id"]
#media_content_id = arguments["media_content_id"]
#logger.bind(tag=TAG).error(f"entity_id: {entity_id}")
# 执行音乐播放命令
future = asyncio.run_coroutine_threadsafe(
handle_hass_play_music(conn, entity_id, media_content_id),
@@ -53,20 +43,9 @@ def hass_play_music(conn, entity_id='', media_content_id='random'):
except Exception as e:
logger.bind(tag=TAG).error(f"处理音乐意图错误: {e}")
def initialize_hass_handler(conn):
config = conn.config
global HASS_CACHE
if HASS_CACHE == {}:
logger.bind(tag=TAG).info(f"实例化HASS:")
if "HomeAssistant" in config["LLM"]:
HASS_CACHE['base_url'] = config["LLM"]['HomeAssistant']['base_url']
HASS_CACHE['api_key'] = config["LLM"]['HomeAssistant']['api_key']
else:
logger.bind(tag=TAG).error(f"使用前请在config文件中配置: LLM.HomeAssistant.base_url LLM.HomeAssistant.api_key")
async def handle_hass_play_music( conn, entity_id, media_content_id):
initialize_hass_handler(conn)
global HASS_CACHE
async def handle_hass_play_music(conn, entity_id, media_content_id):
HASS_CACHE = initialize_hass_handler(conn)
api_key = HASS_CACHE['api_key']
base_url = HASS_CACHE['base_url']
url = f"{base_url}/api/services/music_assistant/play_media"
@@ -1,76 +1,64 @@
from plugins_func.register import register_function,ToolType, ActionResponse, Action
from plugins_func.register import register_function, ToolType, ActionResponse, Action
from plugins_func.functions.hass_init import initialize_hass_handler
from config.logger import setup_logging
import asyncio
import requests
TAG = __name__
logger = setup_logging()
HASS_CACHE = {}
hass_set_state_function_desc ={
"type": "function",
"function": {
"name": "hass_set_state",
"description": "设置homeassistant里设备的状态,包括开、关,调整灯光亮度,调整播放器的音量,设备的暂停、继续、静音操作",
"parameters": {
"type": "object",
"properties": {
"state": {
"type": "object",
"properties": {
"type":{
"type":"string",
"description":"需要操作的动作,打开设备:turn_on,关闭设备:turn_off,增加亮度:brightness_up,降低亮度:brightness_down,设置亮度:brightness_value,增加>音量:,volume_up降低音量:volume_down,设置音量:volume_set,设备暂停:pause,设备继续:continue,静音/取消静音:volume_mute"
},
"input":{
"type":"int",
"description": "只有在设置音量,设置亮度时候才需要,有效值为1-100,对应音量和亮度的1%-100%"
},
"is_muted":{
"type":"string",
"description": "只有在设置静音操作时才需要,设置静音的时候该值为true,取消静音时该值为false"
}
},
"required": ["type"]
},
"entity_id": {
hass_set_state_function_desc = {
"type": "function",
"function": {
"name": "hass_set_state",
"description": "设置homeassistant里设备的状态,包括开、关,调整灯光亮度,调整播放器的音量,设备的暂停、继续、静音操作",
"parameters": {
"type": "object",
"properties": {
"state": {
"type": "object",
"properties": {
"type": {
"type": "string",
"description": "需要操作的设备id,homeassistant里的entity_id"
}
"description": "需要操作的动作,打开设备:turn_on,关闭设备:turn_off,增加亮度:brightness_up,降低亮度:brightness_down,设置亮度:brightness_value,增加>音量:,volume_up降低音量:volume_down,设置音量:volume_set,设备暂停:pause,设备继续:continue,静音/取消静音:volume_mute"
},
"required": ["state", "entity_id"]
}
"input": {
"type": "int",
"description": "只有在设置音量,设置亮度时候才需要,有效值为1-100,对应音量和亮度的1%-100%"
},
"is_muted": {
"type": "string",
"description": "只有在设置静音操作时才需要,设置静音的时候该值为true,取消静音时该值为false"
}
},
"required": ["type"]
},
"entity_id": {
"type": "string",
"description": "需要操作的设备id,homeassistant里的entity_id"
}
}
},
"required": ["state", "entity_id"]
}
}
}
@register_function('hass_set_state', hass_set_state_function_desc, ToolType.SYSTEM_CTL)
def hass_set_state(conn, entity_id='', state={}):
try:
future = asyncio.run_coroutine_threadsafe(
handle_hass_set_state(conn, entity_id, state),
conn.loop
)
ha_response = future.result()
return ActionResponse(action=Action.REQLLM, result="执行成功", response=ha_response)
future = asyncio.run_coroutine_threadsafe(
handle_hass_set_state(conn, entity_id, state),
conn.loop
)
ha_response = future.result()
return ActionResponse(action=Action.REQLLM, result="执行成功", response=ha_response)
except Exception as e:
logger.bind(tag=TAG).error(f"处理设置属性意图错误: {e}")
def initialize_hass_handler(conn):
config = conn.config
global HASS_CACHE
if HASS_CACHE == {}:
logger.bind(tag=TAG).info(f"实例化HASS:")
if "HomeAssistant" in config["LLM"]:
HASS_CACHE['base_url'] = config["LLM"]['HomeAssistant']['base_url']
HASS_CACHE['api_key'] = config["LLM"]['HomeAssistant']['api_key']
else:
logger.bind(tag=TAG).error(f"使用前请在config文件中配置: LLM.HomeAssistant.base_url LLM.HomeAssistant.api_key")
logger.bind(tag=TAG).error(f"处理设置属性意图错误: {e}")
async def handle_hass_set_state(conn, entity_id, state):
initialize_hass_handler(conn)
global HASS_CACHE
HASS_CACHE = initialize_hass_handler(conn)
api_key = HASS_CACHE['api_key']
base_url = HASS_CACHE['base_url']
'''
@@ -92,7 +80,6 @@ async def handle_hass_set_state(conn, entity_id, state):
action = "start"
else:
action = "turn_on"
action = 'turn_on'
elif state['type'] == 'turn_off':
description = "设备已关闭"
if domain == 'cover':
@@ -114,7 +101,7 @@ async def handle_hass_set_state(conn, entity_id, state):
elif state['type'] == 'brightness_value':
description = f"亮度已调整到{state['input']}"
action = 'turn_on'
arg = 'brightness_pct'
arg = 'brightness_pct'
value = state['input']
elif state['type'] == 'volume_up':
description = "音量已调大"
@@ -150,7 +137,7 @@ async def handle_hass_set_state(conn, entity_id, state):
else:
return f"{domain} {state.type}功能尚未支持"
if arg == '':
if arg == '':
data = {
"entity_id": entity_id,
}
@@ -163,11 +150,10 @@ async def handle_hass_set_state(conn, entity_id, state):
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
}
response = requests.post(url, headers=headers, json=data)
logger.bind(tag=TAG).info(f"设置状态:url:{url},return_code:{response.status_code}")
if response.status_code == 200:
return description
else:
return f"设置失败,错误码: {response.status_code}"