# 技能与工具

技能与工具用于在设备规格之外扩展设备智能体：设备规格定义命令、状态和事件，技能与工具补充可复用流程、知识和可调用外部动作，例如巡检报告或节能建议。

## 什么时候使用

通常先完善设备规格，再按实际需要增加技能或工具。

| 目标 | 推荐方式 |
| --- | --- |
| 增加设备可执行的动作，例如设置温度、开关电源、切换模式 | 修改设备规格中的命令 |
| 增加设备会上报的数据或事件，例如湿度、制冷状态、异常告警 | 修改设备规格中的遥测和事件 |
| 让设备智能体按固定流程回答，例如巡检报告、排障清单、节能建议 | 导入并启用技能 |
| 让设备智能体访问外部信息或执行独立逻辑，例如节能策略、楼宇系统状态、告警服务 | 创建并启用工具 |
| 让设备智能体写文件或运行命令 | 在配置中开启对应工具权限 |

不要把设备自己的命令写成工具，也不要把需要调用外部系统的动作只写成技能。

## 导入技能

技能是一组可复用说明。启用技能后，用户仍然用自然语言提出目标；设备智能体会在需要时加载对应技能，并按技能中的流程完成回复。

在 **技能** 标签页中可以导入、启用、停用、导出或删除用户技能。

![技能列表](../images/docs/usage/tools/zh/01-skills-list.png)

技能以 `.zip` 文件导入。压缩包中需要包含一个目录和 `SKILL.md`：

```text
thermostat-inspection/
  SKILL.md
  references/
  templates/
```

`SKILL.md` 顶部需要包含 `name` 和 `description`：

```md
---
name: thermostat-inspection
description: Generate an inspection report from thermostat status and recent events.
---

# Thermostat Inspection

...
```

以快速体验里的温控器为例，导入并启用巡检技能后，可以这样提问：

```text
按巡检报告格式整理当前温控器最近状态、告警事件和建议处理动作。
```

用户不需要输入 `use_skill`。设备智能体会根据请求和技能描述判断是否加载技能。系统内置技能随运行时自动提供，不在用户技能列表中管理。

## 创建工具

工具封装一个可调用动作，可以是本地逻辑，也可以是外部系统调用。启用工具后，用户仍然通过自然语言发起请求；设备智能体会根据工具名称、描述和参数结构决定是否调用它。

在 **工具** 标签页中可以查看、启用、停用或删除用户工具，也可以打开工具编辑器创建新工具。

![工具列表和编辑器](../images/docs/usage/tools/zh/02-tools-editor.png)

创建工具的常见步骤：

1. 进入 **技能与工具**，切换到 **工具**。
2. 点击 **打开工具编辑器**，再点击 **新建扩展**。
3. 修改工具的 `name`、`label`、`description`、`parameters` 和 `execute`。
4. 点击 **保存**。保存成功后，工具会写入 `.device_agent/tools/extensions/<name>.tool.ts` 并加载到当前运行时。
5. 回到 **工具** 标签页，确认工具出现在列表中，并保持启用状态。

下面的示例根据温控器当前温度和湿度给出设置建议。它不依赖外部接口，保存后可以直接在对话中触发；真正下发到设备的控制命令仍然由设备规格中的命令完成。

```ts
import type { AgentTool } from "@mariozechner/pi-agent-core";
import { Type } from "@sinclair/typebox";

function recommendSettings(currentTemperature: number, humidity: number) {
  if (currentTemperature >= 30) {
    return {
      mode: "cool",
      targetTemperature: 24,
      reason: "当前温度较高，建议制冷并降低目标温度。",
    };
  }

  if (currentTemperature <= 18) {
    return {
      mode: "heat",
      targetTemperature: 22,
      reason: "当前温度较低，建议制热并设置舒适目标温度。",
    };
  }

  if (humidity >= 70) {
    return {
      mode: "auto",
      targetTemperature: 24,
      reason: "当前湿度较高，建议自动模式，让设备根据环境状态调节。",
    };
  }

  return {
    mode: "eco",
    targetTemperature: 24,
    reason: "当前温湿度处于舒适范围，建议节能模式。",
  };
}

export default function (api: { registerTool(tool: AgentTool): void }) {
  api.registerTool({
    name: "recommend_thermostat_settings",
    label: "推荐温控器设置",
    description: "根据温控器当前温度和湿度给出目标温度和运行模式建议。只在用户要求节能建议、舒适度调整或根据当前状态调整温控器时使用。",
    parameters: Type.Object({
      currentTemperature: Type.Number({ description: "当前温度，单位为摄氏度" }),
      humidity: Type.Number({ description: "当前湿度，百分比" }),
    }),
    async execute(_toolCallId, params: { currentTemperature: number; humidity: number }) {
      const recommendation = recommendSettings(params.currentTemperature, params.humidity);

      return {
        content: [
          {
            type: "text",
            text: `建议切换到 ${recommendation.mode} 模式，目标温度 ${recommendation.targetTemperature} 度。${recommendation.reason}`,
          },
        ],
        details: recommendation,
      };
    },
  });
}
```

工具描述要说明适用场景，参数要说明需要哪些输入。`content` 会进入对话结果，`details` 会作为结构化结果保留。需要接入真实业务系统时，可以把示例中的 `recommendSettings` 替换为内部 API、数据库或其他服务调用。

## 在对话中使用

在对话中直接描述目标、设备对象和条件即可：

```text
根据当前温度和湿度给出节能设置建议；如果建议合理，把目标温度和运行模式更新到当前模拟设备。
```

设备智能体可以先读取当前选中设备的状态，再调用 `recommend_thermostat_settings` 计算建议，最后通过设备规格中的温控器命令完成控制。工具由设备智能体在对话过程中自动选择，不需要手写工具调用。

![对话中触发工具调用](../images/docs/usage/tools/zh/03-chat-trigger.png)

设备智能体也可以使用内置能力：

```text
1 分钟后把当前温控器的目标温度设为 24 度。
```

这类请求会使用内置 [定时任务](./scheduled-tasks.md) 能力。设备命令下发、MQTT 消息收发、只读数据查询、视觉分析、A2A 发布和模拟显示器更新等内置能力，会按当前场景自动提供给设备智能体，不需要在技能与工具页面手动创建。

## 权限与验证

- 新增、修改或停用工具后，开启新的对话验证；已有对话会继续使用创建时的工具列表。
- 工具名称只能包含字母、数字、下划线和连字符，且不能与系统内置工具重名。
- 工具编辑器的保存和删除由配置中的 `permissions.tools.toolEditorMutations.enabled` 控制。`ENABLE_TOOL_EDITOR_MUTATIONS` 可在启动时写入该值。
- 不要把工具编辑器暴露给不受信任用户。工具代码会在本地运行时执行，应像代码一样审查。
- `write_file` 和 `execute_command` 属于内置高权限工具，配置入口见 [配置](../operate-reference/configuration.md)。
- 使用技能时，回复应遵循技能中的结构、流程或术语。
- 使用工具时，对话中应显示工具执行过程，并返回工具结果。
- 涉及设备控制时，最终命令仍然按设备规格中的命令下发。
