refactor: unify tool reason requirements

This commit is contained in:
2026-10-28 16:43:04 +08:00
parent 7d47a2df86
commit c3050b602b
20 changed files with 97 additions and 46 deletions
+1 -1
View File
@@ -21,7 +21,7 @@ temperature: 0.2
| 会话文件计算 | 经审批的沙箱 `shell` |
| 稳定记忆、通用执行方法 | `memory_manager`、`skill_manager`;事件业务流程由 Server 经 MCP 分发 |
工具可见不等于有权执行,只使用当前角色已启用的工具。参数以工具 schema 为准,要求 reason 时填写具体理由,不增加身份、项目、会话目标等未声明参数。
工具可见不等于有权执行,只使用当前角色已启用的工具。参数以工具 schema 为准,不增加身份、项目、会话目标等未声明参数。说明字段统一使用 reason,用一句话说明具体目的或业务依据,不填写“为了回答用户”等套话,也不输出内部推理过程。普通查询、读取和展示无需逐次补写原因;schema 未声明 reason 时不要添加,可选时仅在目的不明显时填写。数据权限申请、处置提案、计划变更保留必填业务理由;memory_manager 和 skill_manager 的 list 可省略,其余修改操作必须说明长期价值或变更依据。多步调查在阶段说明中交代目的,不在每次查询重复。
围绕用户尚未完成的目标连续推进必要的查询、计算、核验和结果整理。补充参数、纠正结果或询问进度通常是在推进同一目标;用户说“继续”“重试”“重新检查”时,结合已有授权、结果和失败原因执行下一步,不只复述上一轮结论,也不重复询问已经确认的选择。用户明确缩小范围、更换目标或要求暂停时,以最新要求为准;“继续”不扩大权限,也不自动重启已结束的后台调查。
+10
View File
@@ -0,0 +1,10 @@
import { tool } from "./tool.js";
// One field and format for optional purpose and required business justification.
export const toolReason = tool.schema.string().trim().min(1).max(2000);
export const optionalToolReason = toolReason.optional().describe(
"Optional reason: one concise sentence explaining the specific purpose. Omit when the command already makes it clear.",
);
export const learningToolReason = toolReason.optional().describe(
"Reason: one concise sentence explaining the durable value or change basis. Required for every write, update or removal; optional for list.",
);
+2 -5
View File
@@ -1,3 +1,4 @@
import { optionalToolReason } from "../tool-reason.js";
import { tool } from "../tool.js";
import { executeFrontendAction } from "./frontend_action.js";
@@ -5,11 +6,7 @@ import { executeFrontendAction } from "./frontend_action.js";
export default tool({
description: "清除当前浏览器地图上的 Agent SCADA 分析结果覆盖层。",
args: {
reason: tool.schema
.string()
.trim()
.min(1)
.describe("Why the current SCADA analysis overlay should be cleared."),
reason: optionalToolReason,
},
async execute(args, context) {
return executeFrontendAction("clear_scada_analysis", args, context);
+2 -1
View File
@@ -1,7 +1,8 @@
import { optionalToolReason } from "../tool-reason.js";
import { tool } from "../tool.js";
import { executeFrontendAction } from "./frontend_action.js";
export default tool({
description: "在当前供水地图定位资产并等待浏览器执行回执。返回已定位和未找到的 ID,失败时不得宣称成功。",
args: { layer: tool.schema.enum(["junctions", "pipes", "valves", "pumps", "tanks", "reservoirs", "scada"]), feature_ids: tool.schema.array(tool.schema.string().min(1).max(128)).min(1).max(100), reason: tool.schema.string().max(2000) },
args: { layer: tool.schema.enum(["junctions", "pipes", "valves", "pumps", "tanks", "reservoirs", "scada"]), feature_ids: tool.schema.array(tool.schema.string().min(1).max(128)).min(1).max(100), reason: optionalToolReason },
async execute(args, context) { return executeFrontendAction("locate_features", args, context); },
});
+2 -3
View File
@@ -1,3 +1,4 @@
import { learningToolReason } from "../tool-reason.js";
import { tool } from "../tool.js";
import { getInternalBridgeConfig } from "../internalBridge.js";
@@ -8,9 +9,7 @@ export default tool({
action: tool.schema
.enum(["add", "list", "replace", "remove"])
.describe("Memory operation to perform."),
reason: tool.schema
.string()
.describe("Why this memory should be persisted for future requests."),
reason: learningToolReason,
scope: tool.schema
.string()
.describe(
+2 -1
View File
@@ -1,3 +1,4 @@
import { toolReason } from "../tool-reason.js";
import { tool } from "../tool.js";
import { getInternalBridgeConfig } from "../internalBridge.js";
import { proposedTaskActionSchema } from "../../src/product/operational-task-contract.js";
@@ -7,7 +8,7 @@ export default tool({
args: {
request_id: tool.schema.string().describe("Stable UUID idempotency key for this proposal"),
kind: tool.schema.enum(["create_work_order", "issue_command"]),
reason: tool.schema.string(),
reason: toolReason.describe("One concise sentence explaining the business basis for this proposed action."),
payload: tool.schema.union([
tool.schema.object({
title: tool.schema.string(),
+2 -1
View File
@@ -1,3 +1,4 @@
import { toolReason } from "../tool-reason.js";
import { tool } from "../tool.js";
import { getInternalBridgeConfig } from "../internalBridge.js";
import { newExecutionPlanSchema } from "../../src/routes/executionPlan.js";
@@ -8,7 +9,7 @@ export default tool({
request_id: tool.schema.string().describe("Stable UUID idempotency key for this plan revision"),
schema: tool.schema.enum(["execution-plan@2"]),
title: tool.schema.string(),
reason: tool.schema.string().describe("Why the plan or node status changed"),
reason: toolReason.max(1000).describe("One concise sentence explaining why the plan or node status changed."),
nodes: tool.schema.array(tool.schema.object({
id: tool.schema.string(), title: tool.schema.string(),
status: tool.schema.enum(["pending", "running", "waiting", "completed", "failed", "skipped", "cancelled"]),
+2 -5
View File
@@ -1,3 +1,4 @@
import { optionalToolReason } from "../tool-reason.js";
import { tool } from "../tool.js";
import { executeFrontendAction } from "./frontend_action.js";
@@ -8,11 +9,7 @@ export default tool({
description:
"将已经完成分析并确定等级的 SCADA 点位结果高亮、编号并定位到前端地图。等级只能来自已完成的分析结论。",
args: {
reason: tool.schema
.string()
.trim()
.min(1)
.describe("Why the completed SCADA analysis should be rendered on the map."),
reason: optionalToolReason,
items: tool.schema
.array(
tool.schema.object({
+2 -1
View File
@@ -1,3 +1,4 @@
import { toolReason } from "../tool-reason.js";
import { tool } from "../tool.js";
import { getInternalBridgeConfig } from "../internalBridge.js";
@@ -10,7 +11,7 @@ export default tool({
"workbench_runtime", "scada_devices", "scada_readings", "peripheral_points",
"peripheral_work_orders", "peripheral_observations", "peripheral_test_data",
])).min(1),
reason: tool.schema.string().min(1),
reason: toolReason.describe("One concise sentence explaining which evidence gap requires the requested data access."),
},
async execute(args, context) {
const { baseUrl, internalToken } = getInternalBridgeConfig();
+2 -1
View File
@@ -1,7 +1,8 @@
import { optionalToolReason } from "../tool-reason.js";
import { tool } from "../tool.js";
import { executeFrontendAction } from "./frontend_action.js";
export default tool({
description: "显示受控折线或柱状图并等待浏览器回执。不接收 ECharts 脚本或任意 option;数据须来自可信查询或用户输入。",
args: { title: tool.schema.string().max(200), chart_type: tool.schema.enum(["line", "bar"]), x_data: tool.schema.array(tool.schema.string().max(128)).min(1).max(100), series: tool.schema.array(tool.schema.object({ name: tool.schema.string().max(128), data: tool.schema.array(tool.schema.number().finite()).min(1).max(100) })).min(1).max(8), reason: tool.schema.string().max(2000) },
args: { title: tool.schema.string().max(200), chart_type: tool.schema.enum(["line", "bar"]), x_data: tool.schema.array(tool.schema.string().max(128)).min(1).max(100), series: tool.schema.array(tool.schema.object({ name: tool.schema.string().max(128), data: tool.schema.array(tool.schema.number().finite()).min(1).max(100) })).min(1).max(8), reason: optionalToolReason },
async execute(args, context) { return executeFrontendAction("show_chart", args, context); },
});
+2 -5
View File
@@ -1,3 +1,4 @@
import { learningToolReason } from "../tool-reason.js";
import { tool } from "../tool.js";
import { getInternalBridgeConfig } from "../internalBridge.js";
@@ -18,11 +19,7 @@ export default tool({
"remove_script",
])
.describe("Skill maintenance operation."),
reason: tool.schema
.string()
.describe(
"Why this skill maintenance action is justified for future reuse.",
),
reason: learningToolReason,
skill_path: tool.schema
.string()
.describe(
+2 -1
View File
@@ -1,7 +1,8 @@
import { optionalToolReason } from "../tool-reason.js";
import { tool } from "../tool.js";
import { executeFrontendAction } from "./frontend_action.js";
export default tool({
description: "查询当前项目资产最多24小时的监测历史并显示曲线面板,等待浏览器回执。使用服务返回的单位和数据,缺数时明确说明。仅普通会话可用。",
args: { element_type: tool.schema.enum(["junction", "pipe"]), element_id: tool.schema.string().min(1).max(128), start: tool.schema.string(), end: tool.schema.string(), reason: tool.schema.string().max(2000) },
args: { element_type: tool.schema.enum(["junction", "pipe"]), element_id: tool.schema.string().min(1).max(128), start: tool.schema.string(), end: tool.schema.string(), reason: optionalToolReason },
async execute(args, context) { return executeFrontendAction("view_history", args, context); },
});
+2 -1
View File
@@ -1,7 +1,8 @@
import { optionalToolReason } from "../tool-reason.js";
import { tool } from "../tool.js";
import { executeFrontendAction } from "./frontend_action.js";
export default tool({
description: "将当前地图缩放到 WGS84 经纬度并等待浏览器回执。",
args: { center: tool.schema.tuple([tool.schema.number().min(-180).max(180), tool.schema.number().min(-90).max(90)]), zoom: tool.schema.number().min(0).max(24), reason: tool.schema.string().max(2000) },
args: { center: tool.schema.tuple([tool.schema.number().min(-180).max(180), tool.schema.number().min(-90).max(90)]), zoom: tool.schema.number().min(0).max(24), reason: optionalToolReason },
async execute(args, context) { return executeFrontendAction("zoom_to_map", args, context); },
});