feat(agent): add typed server API tools

This commit is contained in:
2026-07-15 10:44:07 +08:00
parent f3f873e3c4
commit 8a21908785
30 changed files with 3119 additions and 15 deletions
+20 -2
View File
@@ -33,8 +33,11 @@ Agent 负责:
| 历史会话检索 | `session_search` |
| 用户偏好和项目事实 | `memory_manager` |
| 可复用流程沉淀 | `skill_manager` |
当前实验版没有通用 TJWater 数据查询工具;如果缺少业务数据,应明确说明数据能力尚未接入,不得编造结果。
| 项目背景 | `get_project_context` |
| 监测设备/点位 | `list_monitoring_assets` |
| 监测时序读数 | `query_monitoring_readings`(必须给出 `observed_from``observed_to` |
| 数据质量分析 | `start_data_quality_analysis``get_job_status` |
| SWMM 仿真 | `start_simulation``get_job_status` |
## UI 约束
@@ -58,3 +61,18 @@ Agent 负责:
## 记忆持久化
`memory_manager` 只保存长期有效的稳定事实或用户偏好。修改 memory 前先 list 当前 scope,再决定 add、replace 或 remove。
## TJWater Server 受控领域工具
仅通过领域工具访问 Server;不得构造 URL、HTTP header、SQL、命令或项目 ID。
- 项目背景:`get_project_context`
- 设备/监测点:`list_monitoring_assets`
- 时序读数:`query_monitoring_readings`
- 数据质量任务:`start_data_quality_analysis`(写操作,必须等待审批)
- 仿真任务:`start_simulation`(写操作,必须等待审批)
- 任务状态:`get_job_status`
查询历史监测数据时,必须先明确时间区间;不清楚区间时先追问,不得省略或自行扩大范围。需要数值结论时先调用 `query_monitoring_readings`,传入 `observed_from``observed_to``granularity``metrics` 和设备 ID,再调用 `show_chart``view_history` 展示。`view_history` 只负责打开前端历史面板,也必须传入 `start_time``end_time`
先查询数据再作判断。回答必须说明查询时间范围、粒度、指标,以及已创建任务的 ID 和状态。
遇到空数据、拒绝、权限错误、超时或服务不可用时,明确说明事实,不得补全或猜测结果。
工具返回 `result_ref` 时,说明完整结果已安全存储,并使用 preview 中的样本和统计作答。
@@ -9,4 +9,6 @@ export const frontendActionCallIdPlugin: Plugin = async () => ({
},
});
export const server = frontendActionCallIdPlugin;
export default frontendActionCallIdPlugin;
+3
View File
@@ -0,0 +1,3 @@
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) });
+3
View File
@@ -0,0 +1,3 @@
import { tool } from "@opencode-ai/plugin";
import { callServerApiTool } from "./server_api_shared.js";
export default tool({ description: "读取当前受控项目的基本信息。", args: {}, execute: (_args, context) => callServerApiTool("get_project_context", {}, context.sessionID) });
@@ -0,0 +1,3 @@
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) });
@@ -0,0 +1,3 @@
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) });
+13
View File
@@ -0,0 +1,13 @@
const internalBaseUrl = process.env.TJWATER_AGENT_INTERNAL_BASE_URL ?? "http://127.0.0.1:8787";
const internalToken = process.env.TJWATER_AGENT_INTERNAL_TOKEN ?? "";
export const callServerApiTool = async (name: string, args: unknown, sessionID: string) => {
const response = await fetch(`${internalBaseUrl}/internal/tools/server-api/${name}`, {
method: "POST",
headers: { "Content-Type": "application/json", "x-agent-internal-token": internalToken },
body: JSON.stringify({ session_id: sessionID, args }),
});
const text = await response.text();
if (!response.ok) throw new Error("TJWater domain tool bridge rejected the request");
return text;
};
@@ -0,0 +1,3 @@
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
@@ -0,0 +1,3 @@
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) });
+5 -5
View File
@@ -2,7 +2,7 @@ import { tool } from "@opencode-ai/plugin";
import { executeFrontendAction } from "./frontend_action.js";
export default tool({
description: "为选定的管网要素打开前端的历史记录或计算结果面板。",
description: "为选定的管网要素打开前端的历史记录或计算结果面板;必须提供 ISO8601 时间区间 start_time 和 end_time。",
args: {
reason: tool.schema
.string()
@@ -17,12 +17,12 @@ export default tool({
.describe("History data source type."),
start_time: tool.schema
.string()
.optional()
.describe("Optional ISO8601 start time."),
.datetime()
.describe("Required ISO8601 inclusive start time for the history query."),
end_time: tool.schema
.string()
.optional()
.describe("Optional ISO8601 end time."),
.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);
+3
View File
@@ -23,6 +23,9 @@ export default tool({
.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);