feat(tasks): enforce dynamic execution plans
This commit is contained in:
@@ -29,6 +29,7 @@ Agent 负责:
|
||||
| 发布事件调查计划及节点进度 | `publish_execution_plan` |
|
||||
| 读取工况任务、证据和授权数据 | `get_operational_task_context` |
|
||||
| 查询任务授权范围内的实时或外围数据 | `query_operational_task_data` |
|
||||
| 请求任务级数据访问并等待人工审核 | `request_task_data_access` |
|
||||
| 发布精简调查报告和关键证据 | `publish_task_report` |
|
||||
| 提议需人工审批的工单或命令 | `propose_task_action` |
|
||||
| 记录用户明确提供的任务证据 | `add_task_human_evidence` |
|
||||
@@ -44,6 +45,8 @@ Agent 负责:
|
||||
|
||||
## 执行约束
|
||||
|
||||
事件调查由当前投诉工单或人工请求动态规划,不套用固定流程。新计划使用 `execution-plan@2`:每个节点明确 `kind`,每条连线有稳定 `id`;条件判断用 `selected_edge_id` 记录实际路线,并行用 `parallel_split` 与 `parallel_join` 明示。数据授权或工单审核用 `gate` 节点引用真实 `gate_ref`。如需再次排查或重拟草稿,新增节点并用 `revisits_node_id` 指向上次节点,保留旧节点与证据,不创建环形连线。未获得数据权限时先调用 `request_task_data_access`,发布等待节点并停止越权查询;拒绝后标记证据缺口。只有任务上下文包含批准的来源时才能查询。工单草稿可由人工编辑后确认下发,Agent 不得自行代替人工确认。
|
||||
|
||||
水质投诉事件调查使用 `publish_execution_plan` 记录实际执行计划,在开始执行、取得结果、等待人工或调整分支时发布完整快照。保留稳定节点 ID 和已完成证据,取消或不适用的分支显式标记。运行中或已完成节点的所有前置节点必须已完成或已跳过。完成节点必须提供真实来源;预设场景输入明确标为合成证据,未实际调用的查询不得写成已执行。工具返回失败表示后端未保存计划,必须按错误修正后重试,不能把失败调用当作已发布或直接结束本轮。现场采样、清洗、复测及回访保持等待人工确认,不能因本轮回复结束而完成。
|
||||
|
||||
处理工况任务时,先调用 `get_operational_task_context`,再用 `query_operational_task_data` 查询后端批准的数据范围。`peripheral_work_orders` 是工单及投诉业务记录;`peripheral_observations` 是按点位和时间窗查询的监测观测,不是投诉原文。只把工具实际返回的数据写成系统证据,并直接引用返回的 `evidence.evidence_id`,不得用 `publish_task_report` 重写系统、外部或人工证据;该工具新增证据只允许明确的合成输入。`origin=synthetic` 必须标为合成证据,`truncated=true` 时不能据此断言完整范围没有异常。数据源缺失或不可用时保持相关节点 waiting 并记录缺口。计划可在调查中多次更新,纯计划发布是中间进度,不会结束未失败的任务。需要创建工单或下发命令时,先成功发布包含对应 `node_id` 的当前计划,再调用 `propose_task_action` 创建待审批动作,并用返回的 `action_id` 再次更新计划节点 `action_refs`。本轮所有计划和动作提议保存成功后,最后调用 `publish_task_report` 保存当前判断、结论边界、建议和关键证据并让后端收尾;不要把工具调用轨迹或内部推理写入报告。后端只用当前执行代、当前会话的计划和报告判断继续运行、等待或完成状态。只有后端上下文显示动作具备成功回执时,才能把关联人工节点标为完成。审批、执行与回执均由 TJWater Server 控制。
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { tool } from "@opencode-ai/plugin";
|
||||
import { getInternalBridgeConfig } from "../internalBridge.js";
|
||||
import { executionPlanSchema } from "../../src/routes/executionPlan.js";
|
||||
import { newExecutionPlanSchema } from "../../src/routes/executionPlan.js";
|
||||
|
||||
export default tool({
|
||||
description: "Publish the current full execution plan and progress to TJWater Server. Plans may be updated multiple times during investigation; a plan-only publish is intermediate and does not finish the task unless it explicitly records Agent failure. Publish the relevant plan node before proposing an action, then republish the plan with the returned action_id before the final report. Preserve stable node IDs and completed evidence. A running or completed node may only depend on completed or skipped predecessors. Edges are dependencies or labeled conditional branches, never guessed from list order. Human actions stay waiting until externally confirmed. Evidence must identify synthetic inputs, tool results or human confirmation accurately. This records a plan, it does not execute actions.",
|
||||
args: {
|
||||
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"),
|
||||
nodes: tool.schema.array(tool.schema.object({
|
||||
@@ -14,12 +15,16 @@ export default tool({
|
||||
owner: tool.schema.enum(["agent", "human"]),
|
||||
detail: tool.schema.string(), evidence: tool.schema.array(tool.schema.string()),
|
||||
action_refs: tool.schema.array(tool.schema.string()).optional(),
|
||||
kind: tool.schema.enum(["activity", "decision", "parallel_split", "parallel_join", "gate"]).optional(),
|
||||
selected_edge_id: tool.schema.string().nullable().optional(),
|
||||
gate_ref: tool.schema.string().nullable().optional(),
|
||||
revisits_node_id: tool.schema.string().nullable().optional(),
|
||||
})),
|
||||
edges: tool.schema.array(tool.schema.object({ from: tool.schema.string(), to: tool.schema.string(), label: tool.schema.string() })),
|
||||
edges: tool.schema.array(tool.schema.object({ id: tool.schema.string().optional(), from: tool.schema.string(), to: tool.schema.string(), label: tool.schema.string() })),
|
||||
},
|
||||
async execute(args, context) {
|
||||
const { request_id, ...input } = args;
|
||||
const plan = executionPlanSchema.parse(input);
|
||||
const plan = newExecutionPlanSchema.parse(input);
|
||||
const { baseUrl, internalToken } = getInternalBridgeConfig();
|
||||
const response = await fetch(`${baseUrl}/internal/operational-task/artifacts`, {
|
||||
method: "POST",
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { tool } from "@opencode-ai/plugin";
|
||||
import { getInternalBridgeConfig } from "../internalBridge.js";
|
||||
|
||||
export default tool({
|
||||
description: "Request task-scoped access to one or more finite investigation data sources. This only creates a pending human review record. Do not query the requested sources until the approved scope appears in task context; after denial, mark the evidence gap and continue with available facts.",
|
||||
args: {
|
||||
request_id: tool.schema.string().uuid(),
|
||||
node_id: tool.schema.string().min(1),
|
||||
scopes: tool.schema.array(tool.schema.enum([
|
||||
"workbench_runtime", "scada_devices", "scada_readings", "peripheral_points",
|
||||
"peripheral_work_orders", "peripheral_observations", "peripheral_test_data",
|
||||
])).min(1),
|
||||
reason: tool.schema.string().min(1),
|
||||
},
|
||||
async execute(args, context) {
|
||||
const { baseUrl, internalToken } = getInternalBridgeConfig();
|
||||
const response = await fetch(`${baseUrl}/internal/operational-task/access-requests`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json", "x-agent-internal-token": internalToken },
|
||||
body: JSON.stringify({ session_id: context.sessionID, request: {
|
||||
requestId: args.request_id, nodeId: args.node_id, scopes: args.scopes, reason: args.reason,
|
||||
} }),
|
||||
});
|
||||
const text = await response.text();
|
||||
if (!response.ok) throw new Error(`task data access request failed (${response.status}): ${text}`);
|
||||
return text;
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user