SA 设备查询:Agent 设计方案 (Semantic/AI Layer)
文档目的:描述设备查询专家(Query Specialist)在 intent_type=QUERY 场景下的独立推理链路。与设备控制(CONTROL)共享 Stage 1 槽位提取和 Stage 2 工程化检索,但从 Stage 3 开始分叉:LLM 跳过 L3 注入、仅做属性选择输出、Engine 按需拉取值绑定。
关联文档:
- 设备控制 Agent:SA_设备控制_Agent设计.md(共享 Stage 1/2 设计)
- 交互 PRD:微信小程序_SA_设备查询_交互设计PRD.md
1. 核心业务流程 (Happy Path Sequence)
与 CONTROL 的核心差异:CONTROL 的 L3(
current_values[])在 LLM 推理前注入,LLM 需计算目标值;QUERY 的 L3 在 LLM 输出后由 Engine 按需拉取,LLM 仅做属性选择。
2. 第一阶段:槽位提取 (Stage 1: Slot Filling)
与 CONTROL 共享同一套槽位定义和提取逻辑,intent_type 为 QUERY。详见 SA_设备控制_Agent设计.md §2。
QUERY 判定规则:包含"多少/状态/查/看/显示/告警/有没有/是否/监测/情况"等查询动词。
3. 第二阶段:工程化检索 (Stage 2: Search)
与 CONTROL 共享同一套 search_entities() 函数,详见 SA_设备控制_Agent设计.md §3。
差异点:QUERY 场景下 device 为空时返回该空间全部设备(不限 CONTROL 能力标签),以覆盖用户"所有设备状态"等宽泛查询。
4. 信息注入策略
| 层级 | CONTROL | QUERY |
|---|---|---|
| L2(复合实体骨架) | 注入 cap_tags + llm_desc | 相同,LLM 依赖 llm_desc 理解属性语义做映射 |
| L3(产品级聚合描述) | Step 1 默认不注入,Step 2 按需注入 current_values[] + mapping | 跳过,LLM 无需消费 |
QUERY 跳过 L3 的理由:L2 已注入
llm_desc(MCP 标准语义描述),LLM 仅需理解每个 capability 的语义含义以完成属性选择,不需要current_values[]具体数值来辅助决策。Engine 在拿到selected_attrs后,按需调用get_product_caps()获取实际值用于绑定和渲染。
5. LLM 输出结构
QUERY 场景 LLM 仅输出用户关心的属性 key 数组,不含 scene、label 等冗余字段,最小化 token 开销。Engine 负责将 key 匹配到设备并补充 label。
LLM 输出(纯 key 数组):
{
"selected_attrs": ["hvac_target_temp", "hvac_mode"]
}用户明确指定属性时(如"305空调多少度"),仅含一个 key:
{
"selected_attrs": ["hvac_target_temp"]
}与 CONTROL 的区别:CONTROL 需要按
product_id分别输出目标值(因枚举兼容性因产品而异),QUERY 只读值没有兼容性问题,所以 LLM 统一输出 key 数组即可,Engine 自动匹配到所有设备。label由 Engine 查标准语义库补充,LLM 无需输出。
5.1 Prompt 约束
LLM 输出 selected_attrs 时需遵循以下约束:
数量上限:selected_attrs 元素个数 不超过 10 个(含)。大部分 IoT 设备的可读 capability 总数在此范围内;超过此上限时,LLM 需结合用户 query 语境剔除明显无关的属性(如定时参数、关联设备地址等低频/非核心字段),保留与用户意图最相关的属性。
不负责排序:LLM 只做属性选择筛选,不负责决定属性展示顺序。渲染时 Engine 按设备分组,组内按 L3 capabilities 的原始排列顺序展示。
示例:某设备有 12 个 capability,用户问"看看这台空调的状态":
- LLM 剔除
timer_config、linked_device_addr、firmware_version等低频/非核心字段 - ✅ 输出 9 个核心属性:
["light_power","hvac_target_temp","hvac_mode","hvac_fan_speed","room_temp","alarm_status","energy_consumption","filter_life","defrost_status"]
5.2 典型场景示例
以下示例基于设备的可用 capability 集:
- 空调:
light_power(开关)、hvac_target_temp(设定温度)、hvac_mode(运行模式)、hvac_fan_speed(风速)、room_temp(回风温度/RO)、alarm_status(故障状态) - 灯具:
light_power(开关)
示例 1 — 用户精确指定设备+属性 → compact
User: "305空调多少度"LLM 推理:用户明确要查"多少度",只关心温度属性。空调的 temperature 相关 capability 有 hvac_target_temp(设定温度、RW)和 room_temp(回风温度、RO)。此处用户说的"多少度"更接近设定温度,因此仅输出 hvac_target_temp。
{ "selected_attrs": ["hvac_target_temp"] }→ Engine 装配后所有设备均为 1 个 attr → compact
示例 2 — 用户问设备综合状态 → detail
User: "305空调什么状态"LLM 推理:"什么状态"未明确指定属性,LLM 推断用户想了解设备的核心运行状态,选择开关+设定温度+运行模式。
{ "selected_attrs": ["light_power", "hvac_target_temp", "hvac_mode"] }→ 任一设备 ≥2 个 attr → detail
示例 3 — 用户问制冷效果(传感器对比)→ detail
User: "305空调制冷效果怎么样"LLM 推理:"制冷效果"隐含了设定值与实际值的对比意图,选择 hvac_target_temp(设定值)+ room_temp(传感器回风温度、RO 只读)来呈现温差,同时带 hvac_mode 确认当前模式。
{ "selected_attrs": ["hvac_target_temp", "room_temp", "hvac_mode"] }→ detail,前端展示"设定 24℃ vs 回风 26℃"对比。
示例 4 — 用户查故障 → compact
User: "查一下305空调有没有故障"LLM 推理:用户明确关心故障状态,仅需输出 alarm_status。
{ "selected_attrs": ["alarm_status"] }→ compact,值显示为"正常"、"通信异常"等枚举中文标签。
示例 5 — 用户问空间下所有设备 → compact(多设备异构属性)
User: "305所有设备状态"LLM 推理:空间下有灯和空调两种设备。从 Stage 2 的 cap_tags 可知灯只有 light_power、空调有多个属性。为了覆盖所有设备类型,LLM 选择 light_power(公共属性)和 hvac_target_temp(空调特有属性)。Engine 匹配时各设备仅 1 个 attr。
{ "selected_attrs": ["light_power", "hvac_target_temp"] }→ Engine 装配结果:
| 设备 | 匹配属性 | attrs.length |
|---|---|---|
| 305主灯 | light_power → 已开启 | 1 |
| 305筒灯 | light_power → 已关闭 | 1 |
| 305空调 | hvac_target_temp → 24℃ | 1 |
→ 所有设备均为 1 个 attr → compact
示例 6 — 模糊输入,LLM 推断多属性 + 混合设备 → detail
User: "看看305的设备情况"LLM 推理:用户意图模糊,未指定具体属性,LLM 推断选择覆盖空间内主要设备的关键属性。
{ "selected_attrs": ["light_power", "hvac_target_temp", "hvac_mode", "alarm_status"] }→ Engine 装配结果(mix 场景):
| 设备 | 匹配属性 | attrs.length |
|---|---|---|
| 305主灯 | light_power → 已开启 | 1 |
| 305筒灯 | light_power → 已关闭 | 1 |
| 305空调 | light_power + hvac_target_temp + hvac_mode + alarm_status | 4 |
→ 空调 attr.length = 4 ≥ 2 → detail。灯在 detail 中仅展示 1 行(开关),空调展示 4 行。
6. Engine 组装与渲染模式判断
Engine 收到 LLM 输出后,执行以下组装逻辑:
Step 1 — 遍历设备:遍历 Stage 2 返回的设备骨架(已按空间和设备名过滤),对每台设备获取其 product_id。
Step 2 — 拉取当前值:收集命中的 product_id + selected_attrs[] 组合,调用 get_product_caps() 拉取 L3 数据(current_values[]、mapping 等)用于值绑定。
L3 数据在此阶段由 Engine 按需拉取,而非注入给 LLM。LLM 全程不消费 L3 数据。
Step 3 — 属性匹配与值绑定:对每台设备,检查其所属产品的 L3 capabilities 的 key 是否命中 selected_attrs[] 中的任一元素。命中则从 current_values[] 中按设备索引提取当前值,并通过标准语义库查 key 对应的 label,组成 { label, value }。未命中的 capability 跳过。若设备没有任何命中的属性,则排除该设备。
{
"devices": [
{
"device_id": "dev_ac_01",
"device_name": "305空调(左)",
"product": "大金空调",
"attrs": [
{ "label": "设定温度", "value": "24℃" },
{ "label": "运行模式", "value": "制冷" }
]
},
{
"device_id": "dev_ac_02",
"device_name": "305空调(右)",
"product": "大金空调",
"attrs": [
{ "label": "设定温度", "value": "26℃" },
{ "label": "运行模式", "value": "制冷" }
]
}
]
}Step 4 — 渲染模式判断:根据所有设备的 attrs.length 决定渲染模式:
| 模式 | 触发条件 | 卡头标题 |
|---|---|---|
| compact | 所有设备 attrs.length === 1 | 复用 selected_attrs[0] 对应的标准语义名 |
| detail | 任一设备 attrs.length ≥ 2 | 固定为 "{space} · 设备详情" |
mix 场景说明:不同设备的
attrs.length可以不同(如设备 A 有 2 个属性、设备 B 有 1 个属性)。只要任一设备 ≥2 即触发 detail 模式;单属性设备在 detail 卡片中仍只展示 1 行,无需补齐。
Step 5 — 前端渲染:组装 renderQueryResult JSON 下发前端:
// compact 模式
{
"display_mode": "compact",
"semantic_label": "设定温度",
"devices": [
{ "device_name": "305空调", "attrs": [{ "label": "设定温度", "value": "24℃" }] }
]
}
// detail 模式(含 mix 场景)
{
"display_mode": "detail",
"devices": [
{
"device_name": "大金空调",
"attrs": [
{ "label": "设定温度", "value": "24℃" },
{ "label": "运行模式", "value": "制冷" }
]
},
{
"device_name": "美的空调",
"attrs": [
{ "label": "设定温度", "value": "26℃" }
]
}
]
}7. 前端渲染协议
| 场景 | 协议 | 前端表现 |
|---|---|---|
| CONTROL | renderSAControl(含语义目标值 + product_id + 共享量程) | HITL 控制卡片(滑块/开关/确认按钮) |
| QUERY | renderQueryResult(含设备状态集合) | 纯信息卡片(无交互组件) |
两者均通过 Engine 组装后下发前端,不直接由 LLM 输出。
8. 异常处理
8.1 检索异常
与设备控制共享 Stage 2 检索管道,Space 404 / Device 404 异常处理逻辑完全一致,详见 SA_设备控制_Agent设计.md §6.1。QUERY 场景的 Device 404 话术为"该空间下目前暂无可查询的设备"。
8.2 设备状态异常
| 场景 | 反馈逻辑 |
|---|---|
| 空间无设备 | Agent 回复"该空间下暂无可查询的设备。" |
| 设备离线 | 在状态卡片中该设备行标注"⛔ 离线",状态值显示为"--" |
8.3 告警等特殊点位
设备告警状态作为普通逻辑点位处理(如 alarm_status 作为 enum 类型),纳入产品级映射后在 current_values[] 中一并注入。前端根据该点位的值展示对应色阶。
若为 BI 类告警统计(如告警率、历史趋势、排名),Master Agent 应路由至 Data_Query 子 Agent,不经过本管道。
9. TODO
- [ ] 告警状态前端色阶渲染:enum 值映射色阶(正常→绿、告警→红)
- [ ] 多设备离线状态:部分离线时卡片展示策略
10. 相关设计参考
- 设备控制 Agent:SA_设备控制_Agent设计.md(共享 Stage 1/2)
- 交互 PRD:微信小程序_SA_设备查询_交互设计PRD.md
- 管理后台:标准语义管理 PRD
- 原型参考:device-control.html(与 CONTROL 共用原型)
