This skill should be used when users need to convert Chinese or English TwinEasy scene interaction or video surveillance requests into executable command sequences and send them. Handles A/B/C/D scene interaction instructions AND E-series video surveillance instructions. All instruction generation, execution planning, and SendInstruction dispatching is handled here. Data queries are delegated to the ruisi-twinioc-dataquery-skill skill. The AI handles all reasoning; the Python runtime is a pure execution layer.
⚠️ 强制约束(最高优先级,任何情况下不得违反)
- 孪易服务基础地址默认使用
http://test.twinioc.net;如果调用方显式传入了base_url,则必须使用该地址继续拼接固定路径。- 禁止用 curl、HTTP 请求或任何其他方式直接调用孪易接口。
- 所有数据查询(场景信息、孪生体实例列表、传感器数据等)必须通过
ruisi-twinioc-dataquery-skillSkill 的query.py脚本完成:python ../ruisi-twinioc-dataquery-skill/scripts/query.py --token <token> [--base-url <base-url>] --mcp-tool <工具名> [--mcp-args '{"参数":"值"}']- 所有指令执行(A/B/C/D 系列与 E 系列)必须通过本 Skill 自身的执行脚本完成:
python scripts/invoke_skill.py --token <token> [--base-url <base-url>] --query "..." --agent-output "[指令串]"- 脚本返回 JSON,从中读取数据后继续下一步,不得在未运行脚本的情况下自行推测结果。
本 Skill 负责将用户的中文或英文自然语言请求转换为孪易平台可执行指令串,并通过 Python 执行层将指令下发到场景。
架构说明(重要):
ruisi-twinioc-dataquery-skill Skill(query.py),本 Skill 不直接访问孪易 MCP/API 接口。scripts/invoke_skill.py,负责 A/B/C/D 与 E 系列指令的 SendInstruction HTTP 请求与会话状态管理。优先保证两件事:
instruction_order 和 jsonData 保留完整指令编码。多语言兼容说明:
ruisi-twinioc-dataquery-skill/entity_aliases.json 将中英文位置名映射到同一传感器或台账 ID。instruction_order 多语言规则:
A03、A09、E08)发送给孪易时统一归一为中文标准指令语义。A08 开始/停止、A16 回放/实时、E02 单路/2×2/3×3)发送给孪易时统一归一为中文标准指令语义。B01、B07、A02、C01、E34)参数部分使用查询层匹配返回的原始结果;匹配结果是中文就发中文,匹配结果是英文就发英文,不额外强制翻译。C02(主题生成)与 D01(询问类内容)允许按当前用户语言保留中文或英文内容,不强制归一为中文。在以下场景触发本 Skill:
场景交互指令(A/B/C/D 系列):
视频监控指令(E 系列):
不要在纯闲聊、无孪易场景控制需求、也无指令执行需求的场景触发本 Skill。
与 ruisi-twinioc-dataquery-skill 的边界:
ruisi-twinioc-dataquery-skill。ruisi-twinioc-dataquery-skill。规则联动(温度 / 告警):
ruisi-twinioc-dataquery-skill 的查询结果带有 rule_match,说明已经命中 ruisi-twinioc-opeationrule-skill 中记录的规则。reply 或规则返回中的确认话术展示给用户,不直接执行。ruisi-twinioc-dataquery-skill 会自动把待确认动作写入 ruisi-twinioc-opeationrule-skill/.runtime/pending_confirmations.json。python ../ruisi-twinioc-opeationrule-skill/scripts/invoke_recorder.py --get-pending --token <token> 读取待确认动作。pending.execute_query 存在,则使用该值作为新的执行请求进入本 Skill,例如 关闭大会议室照明灯、打开大会议室温控器;执行后再调用 --clear-pending 清理。python ../ruisi-twinioc-opeationrule-skill/scripts/invoke_recorder.py --clear-pending --token <token>,然后回复 已取消操作。B07/B08/B09/B10 等物理设备指令,并遵守本 Skill 原有的确认与执行流程。处理时默认具备以下输入:
query:用户自然语言问题或控制指令。token:场景 token,用于查询与执行请求。scene_info:场景配置、层级、主题、图层、图表、孪生体类别等信息。history_user:历史用户问题与历史指令内容。history_inter:历史工具调用记录。场景上下文由执行层在首次调用时自动加载。如需手动刷新场景信息:
python ../ruisi-twinioc-dataquery-skill/scripts/query.py --token <token> [--base-url <base-url>] --mcp-tool get_scene_info
获得:
scene_info:场景配置(层级名称、主题、图层、图表、孪生体类别等)history_user:历史用户问题与对应指令内容history_inter:历史 MCP 工具调用记录先判断用户是在做哪一类事情,并确定指令系列:
A/B/C/D 系列(场景交互):
E 系列(视频监控):
解析需要参数的指令时,优先使用:
history_user 中最近一次相关操作。history_inter 中已有工具结果。scene_info 中可用名称列表。如需额外数据(如某层级下的孪生体列表),调用 ruisi-twinioc-dataquery-skill:
# 按类别查询孪生体实例列表
python ../ruisi-twinioc-dataquery-skill/scripts/query.py mcp \
--token <token> \
[--base-url <base-url>] \
--mcp-tool get_twin_category_data \
--mcp-args '{"twinCategoryName": "类别名称"}'
# 按层级查该层所有孪生体类别
python ../ruisi-twinioc-dataquery-skill/scripts/query.py mcp \
--token <token> \
[--base-url <base-url>] \
--mcp-tool get_twin_category \
--mcp-args '{"levelName": "层级名称"}'
# 查温度数据(按安装位置或孪生体实例名称)
python ../ruisi-twinioc-dataquery-skill/scripts/query.py temperature \
--token <token> \
[--base-url <base-url>] \
--device-query "位置名称"
如果仍无法确认名称类参数,返回"场景中没有找到匹配的信息"及相关候选数据,并放入一组 [] 中直接输出,不伪造指令,此条规则非常重要,优先级最高。
如果上一步来自规则命中后的确认执行,则优先使用 rule_match.parsed_rule.execute_query 作为当前 query,不要丢失规则中已经补全好的设备名称。
如果当前用户输入本身只是肯定词或否定词,先查 ruisi-twinioc-opeationrule-skill 的待确认动作;存在待确认动作时,优先按待确认动作处理,不要把“是/否”或 “yes/no” 直接当作普通控制指令解析。
E 系列额外数据:涉及摄像头名称(E34、E35)时,先查 history_inter 是否已有结果,没有则调用:
python ../ruisi-twinioc-dataquery-skill/scripts/query.py mcp \
--token <token> \
[--base-url <base-url>] \
--mcp-tool get_bind_video_instance_names
未匹配到摄像头名称时,直接输出 [视频中没有找到匹配的信息],不拼接任何 E 系列指令。
按下方指令库规则生成原始执行指令,格式示例:
A/B/C/D 系列:
[A03][A36:告警信息:当前&A38:告警信息选中][A01:功能切换:分析&C01:主题切换:园区概况]E 系列:
[E08:视频:下一个视频][E34:筛选:大会议室摄像头2&E32:单路云台:拉近][E02:筛选:设置显示模式,3×3]多指令按 & 连接;并列操作按 & 拼接在同一方括号内。
& 统一转换成 $ 作为 instruction_order 分隔符再发送给孪易。--agent-output 不要在双引号中直接写 $,例如 "[A02:层级切换:楼层20$B01:聚焦对象:环境传感器1]"。PowerShell 会把 $B01 当作变量展开,导致传给脚本前就变成 A02:层级切换:楼层20:聚焦对象:环境传感器1。& 连接多指令;如果必须传 $,请使用单引号或把 $ 写成 `$。生成 instruction_order 时必须遵守以下归一化规则:
开始/停止、回放/实时、打开/关闭。C02 与 D01 的内容部分保持当前用户语言即可。将原始指令转换为用户当前语言的计划文本时:
A03、B02、C01。例如:
A03 层级切换:下一层A08:场景旋转:开始 场景旋转:开始B07:打开灯:1F走廊灯 打开灯:1F走廊灯E08:视频:下一个视频 视频:下一个视频E34:筛选:大会议室摄像头2 筛选:大会议室摄像头2E12:事件:事件列表,选中 事件:事件列表,选中中文最终使用以下格式:
根据最优策略,已经为您规划如下执行计划:\n1、...\n2、...
英文最终使用以下格式:
Based on the optimal strategy, I have prepared the following execution plan:\n1、...\n2、...
查询类 D01(场景查询)和 E35(摄像头查询)不加"规划如下执行计划"前缀,直接输出查询内容。
{
"message": "<plan_text 或 AI 原始文本>"
}
同时,发送到孪易后端的 jsonData 字段仍保持格式:instruction_order$&query$&plan_text(其中 instruction_order 与 plan_text 由执行层或 AI 提供)。
其中:query 保留用户原始问题;plan_text 根据用户语言返回中文或英文;instruction_order 按上文三类规则归一化后发送给孪易。
如果需要兼容旧接收方(仍期待分段标记)的场景,请在接入层做适配;当前代码不再产生这些标记,文档仅作说明。
重要:你(加载本文件的 AI)负责推理生成指令串,然后你自己调用下方脚本完成执行。--agent-output 参数的值就是你在上一步生成的指令串,由你填入并调用。
执行前按指令类型判断是否需要用户确认:
当指令串中包含以下任意指令时(B07/B08/B09/B10),必须先向用户展示操作内容并等待确认,收到明确确认后才能调用脚本发送:
B07:打开灯:XXXB08:关闭灯:XXXB09:打开温控器:XXXB10:关闭温控器:XXX确认提示格式:
即将执行:{操作描述,如"打开灯:1F走廊灯"},请确认是否执行?(是/否)
The following action will be executed: {action description, for example "Turn on the light switch in 1F corridor"}. Please confirm whether to proceed. (yes/no)
Turn on the light switch in Large Meeting Room、Turn off the air conditioner in Small Meeting Room,不要输出 打开灯:Large Meeting RoomLight Switch、关闭温控器:Small Meeting RoomThermostat 这类中英拼接文本。中文模板:
即将执行:{操作描述},请确认是否执行?(是/否)
英文模板:
The following action will be executed: {action description}. Please confirm whether to proceed. (yes/no)
禁止以下错误形式:
注意:执行层返回给调用方的仍为固定 JSON(键 message),不再带任何分隔标记。确认交互请直接检查 message 字段中的文本并回复。
用户回复“是”/“确认”/“好”/“好的”/“执行”/“yes”/“confirm”/“ok”/“execute”等肯定词后,再调用执行脚本;用户回复“取消”/“否”/“不”/“算了”/“no”/“cancel”等否定词时,不调用脚本,回复与当前语言一致的取消提示。
确认后调用执行脚本示例(以确认打开大会议室温控器为例):
python scripts/invoke_skill.py \\
--token "<scene-token>" \\
--query “打开大会议室温控器” \\
--agent-output "[B09:打开温控器:大会议室温控器]"
⚠️ 严格要求:
--agent-output的值必须是你本轮生成的实际指令括号串,例如[B09:打开温控器:大会议室温控器]。绝对禁止传入[instruction_order]、[query]、[plan_text]等任何文档占位符字符串。--query的值必须是用户的原始问题文本(如打开大会议室温控器)。绝对禁止传入指令描述文本(如打开温控器:大会议室温控器)或任何含编码前缀的字符串(如B09:打开温控器:大会议室温控器)。
对于温度规则联动场景,这里的“调用执行脚本”前必须先执行:
python ../ruisi-twinioc-opeationrule-skill/scripts/invoke_recorder.py --get-pending --token <token>
pending.execute_query,则把 execute_query 的值(例如 打开大会议室温控器)作为 --query,并根据该值重新生成指令括号串(例如 [B09:打开温控器:大会议室温控器])作为 --agent-output,再调用执行脚本。python ../ruisi-twinioc-opeationrule-skill/scripts/invoke_recorder.py --clear-pending --token <token>
--clear-pending 再回复 已取消操作。所有其他指令(A/C/D/E 系列,以及 B01/B02/B03/B04/B05/B06 等非物理设备指令)统一直接调用执行脚本,无需等待用户确认:
python scripts/invoke_skill.py \\
--token "<scene-token>" \\
--query “用户原始问题” \\
--agent-output "[A03]"
⚠️ 严格要求:
--agent-output的值是你本轮生成的实际指令括号串(如[A03]),--query是用户的原始问题。两者均禁止使用文档中出现的任何占位符名称(如instruction_order、query、plan_text)。
--no-execute调用脚本执行后,脚本返回 JSON,读取其中 message 字段作为最终回复输出,原样转发,不要修改、不要包装。
中文示例输出:
根据最优策略,已经为您规划如下执行计划:
1、层级切换:上一层
英文示例输出:
Based on the optimal strategy, I have prepared the following execution plan:
1、Turn off the lights: Large Meeting Room
询问类(D01 / E35)示例:
为您查找到相关内容如下:大会议室摄像头2,后门入口摄像头;共2个
禁止:
A09、A03、B07、E08、E34 等裸编码给用户。必须:
SendInstruction。C02 / D01 内容可保留匹配结果或用户当前语言,不强制统一翻译。根据用户输入内容,智能匹配并直接输出最符合上述指令格式的内容。
若用户输入中包含多个意图,一次输出多个对应指令。
对于括号中含有"其一"的选项,必须从已知选项中选择最匹配的一个。
对于括号中含有"名称"的选项,首先从 history_inter 中查找,没有再调用 ruisi-twinioc-dataquery-skill:
python ../ruisi-twinioc-dataquery-skill/scripts/query.py mcp \
--token <token> \
--mcp-tool get_twin_category_data \
--mcp-args '{"twinCategoryName": "对应类别"}'
从返回结果中匹配最接近的一个;如果经过查找仍没有匹配成功,拼接固定语句"场景中没有找到匹配的信息,"以及通过查询接口查找到的相关数据,然后用一个 [] 括起来直接输出,且不拼接任何指令,非常重要优先级最高。
剩下的智能输出所需内容。
当用户输入"生成XXXX"、"统计XXX"、"创建XXX"、"分析XXX"、"统计一下XXX"等类似表达时,必须识别为"C02:主题生成:XXX"。
当用户输入询问类型的内容时(如"有哪些XXX"、"XXX有什么"),必须按以下步骤完成,不得跳过:
ruisi-twinioc-dataquery-skill 获取实例列表,类别名称从 scene_info.twinCategoryNames 中匹配最接近的一个:
python ../ruisi-twinioc-dataquery-skill/scripts/query.py mcp \
--token <token> \
--mcp-tool get_twin_category_data \
--mcp-args '{"twinCategoryName": "XXX类别名称"}'
D01:名称1,名称2,名称3;共X个python scripts/invoke_skill.py \
--token <token> --query "..." --agent-output "[D01:...]"
plan_text 展示给用户。禁止在未运行查询脚本的情况下发送任何指令,或要求用户自己提供列表。
当用户输入跟聚焦对象和选中对象相关的问题时,对象名称存在时联系上下文,按以下三种情况严格判断:
特别注意:判断"两次对象操作之间是否有层级切换"时,必须检查上一个对象指令之后到本次请求之间的所有历史指令,只要出现过 A02/A03/A04/A05/A06,就必须输出层级切换,即使当前对象与上一个对象处于同一层级。如果对象名称存在多个层级,默认用第一个出现的层级。
**补充说明(新 token 场景):当用户携带一个全新 token 发起对话,第一条消息就是聚焦/选中对象请求时,此时 history_user 为空,属于情况一,**必须**在对象指令前输出层级切换 A02:层级切换:(对象所在层级),不得省略。
A02:层级切换:楼层8&B02:选中对象:摄像头01)。A01:功能切换:?(AI分析、分析、对象、告警、过滤 其一)
A02:层级切换:?(层级名称,必须是从 ruisi-twinioc-dataquery-skill 返回的内容)
A03:层级切换:下一层
A04:层级切换:上一层
A05:层级切换:第一层
A06:层级切换:最后一层
A07:层级列表:?(打开、关闭 其一)
A08:场景旋转:?(开始、停止 其一)
A09:场景复位
A10:视野放缩:拉近,100
A10:视野放缩:远离,100
A11:视野平移:?(前移、后移、左移、右移 其一),100
A12:视野旋转:?(顺时针、逆时针 其一),10
A13:时间轴:播放
A14:时间轴:暂停
A15:时间轴:跳转到?(时间点或关键锚点)
A16:时间轴:?(回放、实时 其一)
A17:图层管理:?(打开、关闭 其一)
A18:显示图层:?(图层名称,必须是从 ruisi-twinioc-dataquery-skill 返回的内容)
A19:隐藏图层:?(图层名称,必须是从 ruisi-twinioc-dataquery-skill 返回的内容)
A20:图层全部显示
A21:图层全部隐藏
A22:图表管理:?(打开、关闭 其一)
A23:显示图表:?(图表名称,必须是从 ruisi-twinioc-dataquery-skill 返回的内容)
A24:关闭图表:?(图表名称,必须是从 ruisi-twinioc-dataquery-skill 返回的内容)
A25:环境控制:?(打开、关闭 其一)
A26:时间切换:?(具体时间点)
A27:季节切换:?(春季、夏季、秋季、冬季 其一)
A28:天气切换:?(晴、晴间多云、阴天、小雨、中雨、大雨、小雪、中雪、大雪、雾、霾、扬沙 其一)
A29:演示汇报:?(打开、关闭 其一)
A30:开始演示:?(演示汇报名称,必须是从 ruisi-twinioc-dataquery-skill 返回的内容)
A31:停止演示
A32:暂停演示
A33:上一步演示
A34:下一步演示
A35:重新演示
A36:告警信息:当前
A37:告警信息:历史
A38:告警信息选中
A39:告警截图:(打开、关闭 其一)
B01:聚焦对象:?(对象名称,必须是从 ruisi-twinioc-dataquery-skill 返回的内容)
B02:选中对象:?(对象名称,必须是从 ruisi-twinioc-dataquery-skill 返回的内容)
B03:取消选中
B04:对象下钻
B05:对象上卷
B06:搜索对象:?(搜索内容)
B07:打开灯:?(对象名称,必须是从 ruisi-twinioc-dataquery-skill 返回的内容)
B08:关闭灯:?(对象名称,必须是从 ruisi-twinioc-dataquery-skill 返回的内容)
B09:打开温控器:?(对象名称,必须是从 ruisi-twinioc-dataquery-skill 返回的内容)
B10:关闭温控器:?(对象名称,必须是从 ruisi-twinioc-dataquery-skill 返回的内容)
C01:主题切换:?(主题名称,必须是从 ruisi-twinioc-dataquery-skill 返回的内容)
C02:主题生成:?(生成的内容)
D01:?,?,?;共X个(询问类内容)
E01:筛选:范围选取:中心点,?;范围,?
E02:筛选:设置显示模式,?(单路、2×2、3×3 其一)
E03:视频:轮播视频,?(开始、停止 其一)
E04:视频:视频排序,?(按对象名称正序、按对象名称倒序、按创建时间正序、按创建时间倒序 其一)
E05:视频:视频上一页
E06:视频:视频下一页
E07:视频:视频指定页,?
E08:视频:下一个视频
E09:视频:上一个视频
E10:视频:第一个视频
E11:视频:末一个视频
E12:事件:事件列表,?(选中、取消 其一)
E13:事件:轮播事件,?(开始、停止 其一)
E14:事件:事件筛选,?
E15:事件:事件排序,?(按时间正序、按时间倒序 其一)
E16:事件:选中事件,?
E17:事件:下一个事件
E18:事件:上一个事件
E19:事件:第一个事件
E20:事件:末一个事件
E21:时间:模式切换,(实时、回放 其一)
E22:回放:暂停
E23:回放:播放
E24:回放:跳转,?
E25:回放:前进,?
E26:回放:回退,?
E27:回放:倍速,?
E28:单路云台:左转
E29:单路云台:右转
E30:单路云台:抬头
E31:单路云台:低头
E32:单路云台:拉近
E33:单路云台:拉远
E34:筛选:?(摄像头名称,必须是从 ruisi-twinioc-dataquery-skill 返回的内容)
E35:名称:?,?,?...(摄像头名称,必须是从 ruisi-twinioc-dataquery-skill 返回的内容)
[指令1&指令2&指令3&...]
[A02:层级切换:楼层8&B02:选中对象:摄像头01][A04:层级切换:上一层][D01:摄像头01,摄像头02,摄像头03;共3个&D01:告警01,告警02;共2个][A02:层级切换:层级2&B02:选中对象:摄像头01][A02:层级切换:楼层20&B01:聚焦对象:传感器8][E08:视频:下一个视频][E02:筛选:设置显示模式,3×3][E34:筛选:大门摄像头][E35:名称:大会议室摄像头2,后门入口摄像头,前台摄像头]遇到以下情况时,不要硬编结果:
get_bind_video_instance_names 未找到匹配摄像头名称时,输出:[视频中没有找到匹配的信息]您的提问超出了我能回答的范围,请输入跟视频监控相关的问题!此时应明确指出失败原因或未匹配信息,而不是返回错误的伪结果。
完整指令分类、无参数指令、带参数指令、展示文本规则。
响应结构、HTTP 请求格式、字段用途与示例。
scripts/invoke_skill.py:命令行入口,接收 --token、--query、--agent-output 参数,统一处理 A/B/C/D 与 E 系列指令的下发执行。scripts/skill_runtime.py:纯执行运行时,合并 A/B/C/D 与 E 系列指令映射,管理会话状态与 SendInstruction HTTP 请求。../ruisi-twinioc-dataquery-skill/scripts/query.py:统一只读数据查询入口,支持 mcp 和 temperature 两种模式;其中 get_bind_video_instance_names 工具路由至 video_surveillance_command 脚本。当用户询问有多少对象/孪生体且不带有某个层级时,应输出所有层级下的孪生体类型以及该类型下的对象名称。
当用户输入问题跟主题切换相关时,如果主题名称存在,输出的指令必须包含"A01:功能切换:分析&C01:主题切换:(主题名称)"。
当用户输入问题跟告警相关时:
A36:告警信息:当前A36:告警信息:当前&A38:告警信息选中A37:告警信息:历史&A38:告警信息选中A38:告警信息选中&A39:告警截图:打开注意根据上下文判断,如果上一个指令包括告警信息选中,则不用重复输出。
当用户输入问题是打开或关闭XXX灯开关时,对象名称直接从智能开关孪生体中获取,输出"B07:打开灯:(对象名称)"或"B08:关闭灯:(对象名称)"。
当用户输入问题是打开或关闭XXX温控器或者XXX空调时,对象名称直接从温控器孪生体中获取,输出"B09:打开温控器:(对象名称)"或"B10:关闭温控器:(对象名称)"。
E34(视频筛选)和 E35(摄像头列表查询)依赖摄像头名称匹配。若通过 ruisi-twinioc-dataquery-skill get_bind_video_instance_names 返回的列表中找不到匹配项,输出 [视频中没有找到匹配的信息],不抛出错误。
当用户说"查看XXX摄像头"、"打开XXX视频"时,识别为 E34,筛选参数 = 摄像头名称。
当用户说"XXX摄像头放大/拉近"或"XXX摄像头缩小/拉远"时(含具体摄像头名称),识别为 E34:筛选:{摄像头名称}&E32:单路云台:拉近 或 E34:筛选:{摄像头名称}&E33:单路云台:拉远;当用户只说"放大、缩小、拉近、拉远、左转、右转、抬头、低头"而不带摄像头名称时,直接输出对应单路云台指令(E28-E33)。
当用户说"查看XXX摄像头告警/事件"时(含具体摄像头名称),识别为 E34:筛选:{摄像头名称}&E12:事件:事件列表,选中;当用户只说"看一下告警信息"等不含摄像头名称时,直接输出 E12:事件:事件列表,选中(E 系列中告警即事件)。
当用户询问"有哪些摄像头"、"摄像头列表"时,识别为 E35。
区分"上/下一页视频"(E05/E06)和"上/下一个视频"(E08/E09),不可混用。
当用户的问题与视频监控 E 系列功能均不匹配时,输出:您的提问超出了我能回答的范围,请输入跟视频监控相关的问题!