十七 Home Assistant 大语言模型API

Home Assistant大语言模型API

Home Assistant可以与大语言模型(LLMs)进行交互。通过向大语言模型公开Home Assistant API,大语言模型可以获取数据或控制Home Assistant,从而更好地辅助用户。Home Assistant带有一个内置的大语言模型API,但自定义集成可以注册自己的API以提供更高级的功能。

内置辅助API

Home Assistant有一个内置API,它向大语言模型公开了辅助API。此API允许大语言模型通过意图与Home Assistant交互,并且可以通过注册意图进行扩展。

辅助API等同于内置对话代理也可访问的功能和公开实体。不能执行管理任务。

支持大语言模型API

大语言模型API需要在你的集成中的两个地方进行集成。用户需要能够配置应使用哪个API,并且在与大语言模型交互时,应将API提供的工具传递给大语言模型。

选项流程

所选的API应存储在配置项选项中。它应包含对API ID的字符串引用。如果未选择API,则必须省略该键。

在你的选项流程中,你应该向用户提供一个选择器,让他们选择应使用哪个API。

from types import MappingProxyType
from homeassistant.const import CONF_LLM_HASS_API
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import llm
from homeassistant.helpers.selector import (
    SelectOptionDict,
    SelectSelector,
    SelectSelectorConfig,
)

@callback
def async_get_options_schema(
    hass: HomeAssistant,
    options: MappingProxyType[str, Any],
) -> vol.Schema:
    """返回选项模式。"""
    apis: list[SelectOptionDict] = [
        SelectOptionDict(
            label="无控制",
            value="none",
        )
    ]
    apis.extend(
        SelectOptionDict(
            label=api.name,
            value=api.id,
        )
        for api in llm.async_get_apis(hass)
    )
    return vol.Schema(
        {
            vol.Optional(
                CONF_LLM_HASS_API,
                description={"suggested_value": options.get(CONF_LLM_HASS_API)},
                default="none",
            ): SelectSelector(SelectSelectorConfig(options=apis)),
        }
    )

在处理选项时,如果用户选择了“none”,请确保在存储选项之前删除该键。

if user_input[CONF_LLM_HASS_API] == "none":
    user_input.pop(CONF_LLM_HASS_API)
return self.async_create_entry(title="", data=user_input)
获取工具

与大语言模型交互时,你应该从所选的API中获取工具,并将它们与API提供的额外提示一起传递给大语言模型。

from homeassistant.const import CONF_LLM_HASS_API
from homeassistant.core import HomeAssistant, callback
from homeassistant.components import conversation
from homeassistant.helpers import intent, llm

class MyConversationEntity(conversation.ConversationEntity):
    def __init__(self, entry: ConfigEntry) -> None:
        """初始化代理。"""
        self.entry = entry
  ...
    async def async_process(
        self, user_input: conversation.ConversationInput
    ) -> conversation.ConversationResult:
        """处理用户输入。"""
        intent_response = intent.IntentResponse(language=user_input.language)
        llm_api: llm.API | None = None
        tools: list[dict[str, Any]] | None = None
        if self.entry.options.get(CONF_LLM_HASS_API):
            try:
                llm_api = await llm.async_get_api(
                    self.hass,
                    self.entry.options[CONF_LLM_HASS_API],
                    llm.LLMContext(
                        platform=DOMAIN,
                        context=user_input.context,
                        user_prompt=user_input.text,
                        language=user_input.language,
                        assistant=conversation.DOMAIN,
                        device_id=user_input.device_id,
                    ),
                )
            except HomeAssistantError as err:
                LOGGER.error("获取大语言模型API时出错: %s", err)
                intent_response.async_set_error(
                    intent.IntentResponseErrorCode.UNKNOWN,
                    f"准备大语言模型API时出错: {err}",
                )
                return conversation.ConversationResult(
                    response=intent_response, conversation_id=user_input.conversation_id
                )
            tools = [
                _format_tool(tool)  # TODO按照你的大语言模型期望的格式格式化工具
                for tool in llm_api.tools
            ]
        if llm_api:
            api_prompt = llm_api.api_prompt
        else:
            api_prompt = llm.async_render_no_api_prompt(self.hass)
        prompt = "\n".join((user_prompt, api_prompt))
        # 与大语言模型交互并传递工具
        request = user_input.text
        for _iteration in range(10):
            response =...  # 向大语言模型发送请求并获取响应,包括工具
            if not response.tool_call:
                break
            LOGGER.debug(
                "工具调用: %s(%s)",
                response.tool_call.function.name,
                response.tool_call.function.arguments,
            )
            tool_input = llm.ToolInput(
                tool_name=response.tool_call.function.name,
                tool_args=json.loads(response.tool_call.function.arguments),
            )
            try:
                tool_response = await llm_api.async_call_tool(tool_input)
            except (HomeAssistantError, vol.Invalid) as e:
                tool_response = {"error": type(e).__name__}
                if str(e):
                    tool_response["error_text"] = str(e)
            LOGGER.debug("工具响应: %s", tool_response)
            response = tool_response
最佳实践

如果你的对话实体允许用户使用 conversation_id 维护对话历史,请确保为每次交互重新生成提示,并在为后续命令传递的历史中覆盖它。这允许用户始终能够查询家庭的最新状态。

创建自己的API

要创建自己的API,你需要创建一个继承自 API 的类并实现 async_get_tools 方法。async_get_tools 方法应该返回一个 Tool 对象列表,这些对象表示你希望向大语言模型公开的功能。

工具

llm.Tool 类表示可以由大语言模型调用的工具。

from homeassistant.core import HomeAssistant
from homeassistant.helper import llm
from homeassistant.util import dt as dt_util
from homeassistant.util.json import JsonObjectType

class TimeTool(llm.Tool):
    """获取当前时间的工具。"""
    name = "GetTime"
    description: "返回当前时间。"
    # 可选。输入参数的voluptuous模式。
    parameters = vol.Schema({
      vol.Optional('timezone'): str,
    })
    async def async_call(
        self, hass: HomeAssistant, tool_input: ToolInput, llm_context: LLMContext
    ) -> JsonObjectType:
        """调用工具。"""
        if "timezone" in tool_input.tool_args:
            tzinfo = dt_util.get_time_zone(tool_input.tool_args["timezone"])
        else:
            tzinfo = dt_util.DEFAULT_TIME_ZONE
        return dt_util.now(tzinfo).isoformat()

llm.Tool 类具有以下属性:

名称类型描述
name字符串工具的名称。必需。
description字符串工具的描述,帮助大语言模型理解何时以及如何调用它。可选但建议提供。
parametersvol.Schema参数的voluptuous模式。默认为 vol.Schema()

llm.Tool 类具有以下方法:
async_call:当被大语言模型调用时执行工具的实际操作。这必须是一个异步方法。其参数是 hassllm.ToolInput 的一个实例。响应数据必须是一个字典,并且可以序列化为JSON homeassistant.util.json.JsonObjectType。错误必须作为 HomeAssistantError 异常(或其子类)抛出。响应数据不应包含用于错误处理的错误代码。

ToolInput 具有以下属性:

名称类型描述
tool_name字符串正在调用的工具的名称
tool_args字典大语言模型提供的参数。参数使用 parameters 模式进行转换和验证。
platform字符串使用该工具的对话代理的DOMAIN
contextContext对话的 homeassistant.core.Context
user_prompt字符串发起工具调用的原始文本输入
language字符串对话代理的语言,或“*”表示任何语言
assistant字符串用于控制公开实体的助手名称。目前,仅支持 conversation
device_id字符串用户发起对话的设备的设备ID
API

API对象允许创建API实例。一个API实例表示将提供给大语言模型的一组工具。

from homeassistant.core import HomeAssistant
from homeassistant.helper import llm
from homeassistant.util import dt as dt_util
from homeassistant.util.json import JsonObjectType

class MyAPI(API):
    """我自己的大语言模型API。"""
    def __init__(self, hass: HomeAssistant) -> None:
        """初始化类。"""
        super().__init__(
            hass=hass,
            id="my_unique_key",
            name="我自己的API",
        )
    async def async_get_api_instance(self, llm_context: LLMContext) -> APIInstance:
        """返回API的实例。"""
        return APIInstance(
            api=self,
            api_prompt="调用工具从Home Assistant获取数据。",
            llm_context=llm_context,
            tools=[TimeTool()],
        )

async def async_setup_api(hass: HomeAssistant) -> None:
    """在Home Assistant中注册API。"""
    llm.async_register_api(hass, MyAPI())

llm.API 类具有以下属性:

名称类型描述
id字符串API的唯一标识符。必需。
name字符串API的名称。必需。

llm.APIInstance 类具有以下属性:

名称类型描述
apiAPIAPI对象。必需。
api_prompt字符串关于如何使用大语言模型工具的大语言模型指令。必需。
llm_contextLLMContext工具调用的上下文。必需。
toolslist[Tool]此API中可用的工具。必需。

总结

本文档主要介绍了Home Assistant与大语言模型交互的相关内容,包括内置辅助API及其功能,支持大语言模型API时在集成中的选项流程(涉及API选择的存储与展示给用户)和获取工具传递给大语言模型的操作,以及创建自己API的方法(需创建继承自 API 的类并实现 async_get_tools 方法),同时详细阐述了工具(Tool 类的属性和方法)和API(API 类及 APIInstance 类的属性)相关的概念与要求,为开发者在Home Assistant中集成大语言模型功能提供了全面的指导和规范。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值