refactor(agent)!: focus tools on SCADA

This commit is contained in:
2026-07-15 18:30:58 +08:00
parent 8a21908785
commit 155513fdbc
47 changed files with 503 additions and 2910 deletions
-94
View File
@@ -1,94 +0,0 @@
import { tool } from "@opencode-ai/plugin";
export default tool({
description:
"在前端地图上对节点或管道图层应用样式,或重置为默认样式。样式参数应尽量与前端样式编辑器字段保持一致。",
args: {
reason: tool.schema
.string()
.describe(
"Why this style action is needed for the current user request.",
),
layer_id: tool.schema
.enum(["junctions", "pipes"])
.describe("Target layer id. Must be exactly 'junctions' or 'pipes'."),
reset_to_default: tool.schema
.boolean()
.optional()
.describe("Whether to reset the target layer to its default style."),
style_config: tool.schema
.object({
property: tool.schema
.string()
.optional()
.describe("Data property to render."),
classification_method: tool.schema
.enum(["pretty_breaks", "custom_breaks"])
.optional()
.describe("Classification method."),
segments: tool.schema
.number()
.optional()
.describe("Number of segments."),
min_size: tool.schema
.number()
.optional()
.describe("Minimum point radius."),
max_size: tool.schema
.number()
.optional()
.describe("Maximum point radius."),
min_stroke_width: tool.schema
.number()
.optional()
.describe("Minimum line width."),
max_stroke_width: tool.schema
.number()
.optional()
.describe("Maximum line width."),
fixed_stroke_width: tool.schema
.number()
.optional()
.describe("Fixed line width when width is not data-driven."),
color_type: tool.schema
.enum(["single", "gradient", "rainbow", "custom"])
.optional()
.describe("Color strategy."),
single_palette_index: tool.schema.number().optional(),
gradient_palette_index: tool.schema.number().optional(),
rainbow_palette_index: tool.schema.number().optional(),
show_labels: tool.schema
.boolean()
.optional()
.describe("Whether to show labels."),
show_id: tool.schema
.boolean()
.optional()
.describe("Whether to show ids."),
opacity: tool.schema.number().optional().describe("Opacity in [0, 1]."),
adjust_width_by_property: tool.schema
.boolean()
.optional()
.describe("Whether line width is driven by the rendered property."),
custom_breaks: tool.schema
.array(tool.schema.number())
.optional()
.describe("Custom break values."),
custom_colors: tool.schema
.array(tool.schema.string())
.optional()
.describe("Custom rgba colors."),
})
.optional()
.describe(
"Optional style config overrides. Omit when reset_to_default is true.",
),
},
async execute(args) {
const layerLabel = args.layer_id === "junctions" ? "节点" : "管道";
if (args.reset_to_default) {
return `已将${layerLabel}图层重置为默认样式。`;
}
return `已对${layerLabel}图层应用样式。`;
},
});
-3
View File
@@ -1,3 +0,0 @@
import { tool } from "@opencode-ai/plugin";
import { callServerApiTool } from "./server_api_shared.js";
export default tool({ description: "读取分析或仿真任务的状态。", args: { job_id: tool.schema.string().uuid(), job_type: tool.schema.enum(["analysis", "simulation"]) }, execute: (args, context) => callServerApiTool("get_job_status", args, context.sessionID) });
@@ -1,3 +0,0 @@
import { tool } from "@opencode-ai/plugin";
import { callServerApiTool } from "./server_api_shared.js";
export default tool({ description: "列出当前项目的监测设备和监测点。", args: { include: tool.schema.enum(["devices", "points", "all"]).default("all") }, execute: (args, context) => callServerApiTool("list_monitoring_assets", args, context.sessionID) });
+10
View File
@@ -0,0 +1,10 @@
import { tool } from "@opencode-ai/plugin";
import { callServerApiTool } from "./server_api_shared.js";
export default tool({
description:
"列出当前项目可查询的 SCADA 资产。无请求参数;返回 assets 数组,包含 assets[].scada_id、external_id、site_external_id、name、kind、active。后续查询读数时必须使用 assets[].scada_id 作为 query_scada_readings.asset_ids;不要使用 GeoServer feature id/fid。",
args: {},
execute: (args, context) =>
callServerApiTool("list_scada_assets", args, context.sessionID),
});
-22
View File
@@ -1,22 +0,0 @@
import { tool } from "@opencode-ai/plugin";
import { executeFrontendAction } from "./frontend_action.js";
export default tool({
description: "在前端地图上定位并高亮指定的管网要素。",
args: {
reason: tool.schema
.string()
.describe(
"Why this map positioning action is needed for the user request.",
),
ids: tool.schema
.array(tool.schema.string().min(1)).min(1).max(100)
.describe("Feature ids to locate."),
feature_type: tool.schema
.enum(["junction", "pipe", "valve", "reservoir", "pump", "tank", "conduit", "orifice", "outfall"])
.describe("Type of feature to locate."),
},
async execute(args, context) {
return executeFrontendAction("locate_features", args, context);
},
});
@@ -1,3 +0,0 @@
import { tool } from "@opencode-ai/plugin";
import { callServerApiTool } from "./server_api_shared.js";
export default tool({ description: "按设备、时间范围、粒度和允许指标查询监测读数。", args: { device_ids: tool.schema.array(tool.schema.string().uuid()).min(1).max(100), observed_from: tool.schema.string().datetime(), observed_to: tool.schema.string().datetime(), granularity: tool.schema.enum(["5m", "1h", "1d"]).default("5m"), metrics: tool.schema.array(tool.schema.enum(["flow_mean", "flow_total", "conductivity_mean", "velocity_mean", "temperature_mean", "level_mean"])).min(1).max(6) }, execute: (args, context) => callServerApiTool("query_monitoring_readings", args, context.sessionID) });
+50
View File
@@ -0,0 +1,50 @@
import { tool } from "@opencode-ai/plugin";
import { callServerApiTool } from "./server_api_shared.js";
export default tool({
description:
"查询一个或多个 SCADA 资产在指定时间范围内的时序读数。调用前通常先用 list_scada_assets 获取资产 id;必须给出明确的 observed_from 和 observed_to,不要省略或自行扩大时间范围。",
args: {
asset_ids: tool.schema
.array(tool.schema.string().uuid())
.min(1)
.max(100)
.describe(
"SCADA asset UUID 数组,1 到 100 个。必须使用 list_scada_assets 返回的 assets[].scada_id,不要使用 GeoServer feature id/fid、external_id 或 site_external_id。",
),
observed_from: tool.schema
.string()
.datetime()
.describe(
"查询开始时间,ISO8601 datetime 字符串,必须包含时区,例如 2026-04-10T07:15:00Z 或 2026-04-10T15:15:00+08:00。包含该时刻。",
),
observed_to: tool.schema
.string()
.datetime()
.describe(
"查询结束时间,ISO8601 datetime 字符串,必须包含时区,且必须晚于 observed_from。服务端按半开区间处理:observed_at >= observed_from 且 observed_at < observed_to。",
),
granularity: tool.schema
.enum(["5m", "1h", "1d"])
.default("5m")
.describe(
"读数粒度:5m 为原始 5 分钟读数,1h 为小时聚合,1d 为日聚合。未指定时默认 5m。",
),
metrics: tool.schema
.array(
tool.schema.enum([
"conductivity_mean",
"level_mean",
"flow_mean",
"temperature_mean",
]),
)
.min(1)
.max(4)
.describe(
"要返回的指标数组,1 到 4 个。允许值:conductivity_mean(电导率均值)、level_mean(液位均值)、flow_mean(流量均值)、temperature_mean(温度均值)。",
),
},
execute: (args, context) =>
callServerApiTool("query_scada_readings", args, context.sessionID),
});
-22
View File
@@ -1,22 +0,0 @@
import { tool } from "@opencode-ai/plugin";
export default tool({
description:
"在前端地图上对 junctions 图层应用分区渲染。使用前必须完成两步:① 准备数据结构(JSON 文件,结构为 { node_area_map: Record<string, string>, area_ids?: string[], area_colors?: Record<string, string> },其中 node_area_map 的 key 是 junction/node idvalue 是 area id);② 调用 store_render_ref 将 JSON 文件存储到受控路径,获取 render_ref(格式为 res-...);③ 将 render_ref 传入本工具完成前端渲染。注意:不要先把 ref 内容完整读出再传给前端,也不要直接传本地文件路径。",
args: {
reason: tool.schema
.string()
.describe(
"Why this junction rendering action is needed for the user request.",
),
render_ref: tool.schema
.string()
.describe(
"上一步通过 store_render_ref 获得的渲染引用 ID(res-...)。前端会按该引用拉取完整 payload 并渲染。不可直接传入本地文件路径或完整 JSON 数据。",
),
},
async execute() {
// 工具参数里只需要 render_ref;浏览器端会再用该引用回读完整 payload.data 并完成渲染。
return "已在地图上应用节点分区渲染。";
},
});
-42
View File
@@ -1,42 +0,0 @@
import { tool } from "@opencode-ai/plugin";
export default tool({
description:
"在前端对话界面中渲染图表。折线图/柱状图必须使用 x_data 作为横轴标签,series[].data 作为同长度的一维数值数组,不要把折线数据写成 ECharts 的 [x, y] 二维点数组。",
args: {
reason: tool.schema
.string()
.describe("Why this chart should be rendered for the user request."),
title: tool.schema.string().optional().describe("Chart title."),
chart_type: tool.schema
.enum(["line", "bar"])
.optional()
.describe("Chart type."),
x_data: tool.schema
.array(tool.schema.string())
.describe("X-axis labels. For line charts, put time/category labels here."),
series: tool.schema
.array(
tool.schema.object({
name: tool.schema.string(),
data: tool.schema
.array(tool.schema.number())
.describe("Y values only. Must align by index with x_data."),
type: tool.schema.enum(["line", "bar"]).optional(),
}),
)
.describe("Series data."),
x_axis_name: tool.schema
.string()
.optional()
.describe("X-axis display name."),
y_axis_name: tool.schema
.string()
.optional()
.describe("Y-axis display name."),
},
async execute() {
// 图表数据已经在工具参数里,前端收到 tool_call 后直接渲染,不再二次请求后端。
return "图表将在对话中显示。";
},
});
@@ -1,3 +0,0 @@
import { tool } from "@opencode-ai/plugin";
import { callServerApiTool } from "./server_api_shared.js";
export default tool({ description: "创建数据质量分析任务。此操作会创建后台任务,需要用户审批。", args: { device_ids: tool.schema.array(tool.schema.string().uuid()).min(1).max(100), observed_from: tool.schema.string().datetime(), observed_to: tool.schema.string().datetime() }, execute: (args, context) => callServerApiTool("start_data_quality_analysis", args, context.sessionID) });
-3
View File
@@ -1,3 +0,0 @@
import { tool } from "@opencode-ai/plugin";
import { callServerApiTool } from "./server_api_shared.js";
export default tool({ description: "基于已注册模型版本创建仿真任务。此操作会创建后台任务,需要用户审批。", args: { model_version_id: tool.schema.string().uuid(), parameters: tool.schema.record(tool.schema.string(), tool.schema.unknown()).default({}) }, execute: (args, context) => callServerApiTool("start_simulation", args, context.sessionID) });
-44
View File
@@ -1,44 +0,0 @@
import { tool } from "@opencode-ai/plugin";
const internalBaseUrl =
process.env.TJWATER_AGENT_INTERNAL_BASE_URL ?? "http://127.0.0.1:8787";
const internalToken = process.env.TJWATER_AGENT_INTERNAL_TOKEN ?? "";
export default tool({
description:
"将本地 JSON 渲染数据文件存储到受控路径,返回可供 render_junctions 使用的 render_refres-...)。前置步骤:先准备好符合 render_junctions 数据结构的 JSON 文件 { node_area_map, area_ids?, area_colors? },写入本地路径后再调用本工具传入该路径,获取 render_ref 后传给 render_junctions 完成前端渲染。",
args: {
reason: tool.schema
.string()
.describe(
"为何需要将此本地渲染数据持久化为 render_ref,以便后续通过 render_junctions 渲染到前端。",
),
file_path: tool.schema
.string()
.describe(
"本地 JSON 文件的绝对路径,内容为 render_junctions 所需的数据结构 { node_area_map, area_ids?, area_colors? }。",
),
},
async execute(args, context) {
const response = await fetch(
`${internalBaseUrl}/internal/tools/store-render-ref`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
"x-agent-internal-token": internalToken,
},
body: JSON.stringify({
session_id: context.sessionID,
file_path: args.file_path,
}),
},
);
const text = await response.text();
if (!response.ok) {
throw new Error(text);
}
return text;
},
});
-30
View File
@@ -1,30 +0,0 @@
import { tool } from "@opencode-ai/plugin";
import { executeFrontendAction } from "./frontend_action.js";
export default tool({
description: "为选定的管网要素打开前端的历史记录或计算结果面板;必须提供 ISO8601 时间区间 start_time 和 end_time。",
args: {
reason: tool.schema
.string()
.describe(
"Why this history panel should be opened for the current task.",
),
feature_infos: tool.schema
.array(tool.schema.tuple([tool.schema.string(), tool.schema.string()]))
.describe("List of [id, type] pairs."),
data_type: tool.schema
.enum(["realtime", "scheme", "none"])
.describe("History data source type."),
start_time: tool.schema
.string()
.datetime()
.describe("Required ISO8601 inclusive start time for the history query."),
end_time: tool.schema
.string()
.datetime()
.describe("Required ISO8601 exclusive end time for the history query; must be after start_time."),
},
async execute(args, context) {
return executeFrontendAction("view_history", args, context);
},
});
-33
View File
@@ -1,33 +0,0 @@
import { tool } from "@opencode-ai/plugin";
import { executeFrontendAction } from "./frontend_action.js";
export default tool({
description: "打开前端的 SCADA 监测数据历史面板。",
args: {
reason: tool.schema
.string()
.describe("Why SCADA panel interaction is required for this request."),
device_ids: tool.schema
.array(tool.schema.string())
.optional()
.describe("Preferred SCADA device ids."),
device_id: tool.schema
.string()
.optional()
.describe("Single SCADA device id."),
start_time: tool.schema
.string()
.optional()
.describe("Optional ISO8601 start time."),
end_time: tool.schema
.string()
.optional()
.describe("Optional ISO8601 end time."),
result_ref: tool.schema.string().optional().describe("Authorized large monitoring result reference."),
metrics: tool.schema.array(tool.schema.string()).optional().describe("Metrics represented by the result."),
granularity: tool.schema.enum(["5m", "1h", "1d"]).optional().describe("Reading aggregation granularity."),
},
async execute(args, context) {
return executeFrontendAction("view_scada", args, context);
},
});