feat: extend operational investigation workflows
Generic Container CI/CD / test-build-publish (push) Successful in 3m53s
Server CI/CD v2 / build-test-publish-and-deploy (push) Successful in 3m52s

This commit is contained in:
2026-10-30 17:30:19 +08:00
parent 63af998045
commit 05183aff93
82 changed files with 4644 additions and 330 deletions
@@ -48,6 +48,7 @@ CREATE TABLE IF NOT EXISTS workbench.operational_task_runs (
)),
revision bigint NOT NULL CHECK (revision >= 1),
agent_session_id text,
agent_instance_id text,
lease_expires_at timestamptz,
heartbeat_at timestamptz,
started_at timestamptz,
@@ -0,0 +1,21 @@
-- Apply after 026, independently to every project business database.
-- Preserve job identity, deduplication and audit metadata, including legacy results.
ALTER TABLE workbench.calculation_jobs
ADD COLUMN IF NOT EXISTS started_at timestamptz,
ADD COLUMN IF NOT EXISTS finished_at timestamptz,
ADD COLUMN IF NOT EXISTS queue_expires_at timestamptz,
ADD COLUMN IF NOT EXISTS result_expires_at timestamptz;
UPDATE workbench.calculation_jobs SET queue_expires_at=created_at+interval '1 hour'
WHERE queue_expires_at IS NULL;
UPDATE workbench.calculation_jobs SET finished_at=updated_at
WHERE finished_at IS NULL AND status IN ('succeeded','failed','cancelled','timed_out','interrupted');
-- Give pre-deployment results a complete retention window, even on reruns.
UPDATE workbench.calculation_jobs SET result_expires_at=clock_timestamp()+interval '7 days'
WHERE result IS NOT NULL AND result_expires_at IS NULL;
ALTER TABLE workbench.calculation_jobs
ALTER COLUMN queue_expires_at SET DEFAULT (clock_timestamp()+interval '1 hour'),
ALTER COLUMN queue_expires_at SET NOT NULL;
CREATE INDEX IF NOT EXISTS calculation_jobs_queue_expiry
ON workbench.calculation_jobs(project_id,queue_expires_at) WHERE status='queued';
CREATE INDEX IF NOT EXISTS calculation_jobs_result_expiry
ON workbench.calculation_jobs(project_id,result_expires_at) WHERE result IS NOT NULL;
+15
View File
@@ -0,0 +1,15 @@
-- Apply to every project business database before enabling task questions.
CREATE TABLE IF NOT EXISTS workbench.operational_task_questions (
project_id uuid NOT NULL,
task_id uuid NOT NULL,
question_request_id uuid NOT NULL,
document jsonb NOT NULL,
PRIMARY KEY (project_id, question_request_id),
FOREIGN KEY (project_id, task_id)
REFERENCES workbench.operational_tasks(project_id, task_id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS operational_task_questions_task
ON workbench.operational_task_questions(project_id, task_id);
CREATE UNIQUE INDEX IF NOT EXISTS operational_task_questions_pending
ON workbench.operational_task_questions(project_id, task_id)
WHERE document->>'status' = 'pending';
@@ -0,0 +1,141 @@
-- Peripheral work orders are business-system records. Remove the former
-- synthetic provenance branch from source rows and their task projections.
BEGIN;
SET LOCAL lock_timeout = '5s';
CREATE FUNCTION pg_temp.normalize_peripheral_work_order_text(value jsonb)
RETURNS jsonb
LANGUAGE plpgsql
IMMUTABLE
AS $$
DECLARE
normalized text := value::text;
BEGIN
normalized := replace(normalized, '对应外围记录明确标为 synthetic(系统测试)', '对应外围记录为 system_data 系统数据');
normalized := replace(normalized, '有一条演练上报', '有一条系统上报');
normalized := replace(normalized, '证据覆盖为 synthetic 上报工单快照', '证据覆盖为 system_data 上报工单快照');
normalized := replace(normalized, '记录快照带 synthetic 标识', '记录快照为 system_data');
normalized := replace(normalized, '外围工单结果注明为synthetic演练数据', '外围工单为system_data系统数据');
normalized := replace(normalized, '工单为synthetic演练', '工单为system_data系统数据');
normalized := replace(normalized, 'synthetic 爆管上报记录', '系统爆管上报记录');
normalized := replace(normalized, '合成上报记录', '系统上报记录');
normalized := replace(normalized, '外围模拟工单', '外围系统工单');
normalized := replace(normalized, '外围记录为synthetic', '外围记录为system_data');
normalized := replace(normalized, '证据快照整体为synthetic演练来源', '工单快照为system_data系统来源');
normalized := replace(normalized, '来源标记为synthetic,仅能作为演练资料', '来源为system_data系统数据');
normalized := replace(normalized, 'synthetic演练记录', '系统记录');
normalized := replace(normalized, 'synthetic演练来源', 'system_data系统来源');
normalized := replace(normalized, 'synthetic 演练来源', 'system_data 系统来源');
normalized := replace(normalized, '外围样板合成摘要', '外围工单摘要');
normalized := replace(normalized, '合成外围演练', '外围系统记录');
normalized := replace(normalized, '合成外围', '外围');
normalized := replace(normalized, '合成联调', '系统联调');
normalized := replace(normalized, '合成演练', '系统测试');
normalized := replace(normalized, '合成事件', '系统测试事件');
RETURN normalized::jsonb;
END
$$;
UPDATE peripheral_sample.work_orders
SET document = pg_temp.normalize_peripheral_work_order_text(
jsonb_set(document, '{origin}', '"system_data"'::jsonb, true)
)
WHERE document->>'origin' IS DISTINCT FROM 'system_data'
OR document::text ~* '(synthetic.*演练|synthetic 爆管上报|外围记录为synthetic|外围样板合成摘要|外围模拟工单|合成上报记录|合成外围|合成联调|合成事件)';
UPDATE peripheral_sample.work_order_revisions
SET document = pg_temp.normalize_peripheral_work_order_text(
jsonb_set(document, '{origin}', '"system_data"'::jsonb, true)
)
WHERE document->>'origin' IS DISTINCT FROM 'system_data'
OR document::text ~* '(synthetic.*演练|synthetic 爆管上报|外围记录为synthetic|外围样板合成摘要|外围模拟工单|合成上报记录|合成外围|合成联调|合成事件)';
UPDATE peripheral_sample.requests
SET result = pg_temp.normalize_peripheral_work_order_text(
jsonb_set(result, '{origin}', '"system_data"'::jsonb, true)
)
WHERE command_key IN ('create_work_order', 'update_work_order')
AND jsonb_typeof(result) = 'object'
AND result ? 'work_order_id'
AND (
result->>'origin' IS DISTINCT FROM 'system_data'
OR result::text ~* '(synthetic.*演练|synthetic 爆管上报|外围记录为synthetic|外围样板合成摘要|外围模拟工单|合成上报记录|合成外围|合成联调|合成事件)'
);
UPDATE workbench.operational_task_external_feedback
SET document = pg_temp.normalize_peripheral_work_order_text(jsonb_set(
jsonb_set(
jsonb_set(
jsonb_set(
document,
'{work_order_snapshot,origin}',
'"system_data"'::jsonb,
false
),
'{evidence,kind}',
'"external_feedback"'::jsonb,
false
),
'{evidence,source_label}',
'"外围工单"'::jsonb,
false
),
'{detail}',
to_jsonb(replace(COALESCE(document->>'detail', ''), '合成外围工单', '外围工单')),
false
))
WHERE document ? 'work_order_snapshot';
UPDATE workbench.operational_task_evidence
SET document = pg_temp.normalize_peripheral_work_order_text(jsonb_set(
jsonb_set(
document,
'{kind}',
'"external_feedback"'::jsonb,
false
),
'{source_label}',
'"外围工单"'::jsonb,
false
))
WHERE document->>'source_ref' LIKE 'peripheral_sample:%'
OR evidence_id LIKE 'provenance-correction:peripheral_sample:%';
UPDATE workbench.operational_tasks AS task
SET document = pg_temp.normalize_peripheral_work_order_text(jsonb_set(
task.document,
'{summary}',
to_jsonb(
replace(
replace(COALESCE(task.document->>'summary', ''), '合成外围工单', '外围工单'),
'合成外围事件',
'外围事件'
)
),
false
))
FROM peripheral_sample.work_orders AS source_order
WHERE task.project_id = source_order.project_id
AND (
task.document#>>'{source,external_id}' = source_order.work_order_id::text
OR task.document#>>'{source,external_id}' =
'peripheral_sample:' || source_order.work_order_id::text
);
UPDATE workbench.operational_task_reports AS report
SET document = pg_temp.normalize_peripheral_work_order_text(report.document)
FROM workbench.operational_tasks AS task
JOIN peripheral_sample.work_orders AS source_order
ON task.project_id = source_order.project_id
AND (
task.document#>>'{source,external_id}' = source_order.work_order_id::text
OR task.document#>>'{source,external_id}' =
'peripheral_sample:' || source_order.work_order_id::text
)
WHERE report.project_id = task.project_id
AND report.task_id = task.task_id
AND report.document::text ~*
'(synthetic.*演练|synthetic 爆管上报|外围记录为synthetic|外围样板合成摘要|外围模拟工单|合成上报记录|合成外围|合成联调|合成事件)';
COMMIT;
@@ -0,0 +1,13 @@
-- Bind durable task runs to the Agent instance that owns their local session.
BEGIN;
ALTER TABLE workbench.operational_task_runs
ADD COLUMN IF NOT EXISTS agent_instance_id text;
CREATE INDEX IF NOT EXISTS operational_task_runs_agent_owner_idx
ON workbench.operational_task_runs(project_id,agent_instance_id,status,created_at,run_id)
WHERE agent_instance_id IS NOT NULL
AND status IN ('claimed','running');
COMMIT;
@@ -1,16 +1,34 @@
---
name: appointment-follow-up
description: 调查关联预约任务的执行状态与反馈,核对审批、派发和现场结果,避免把预约任务当成新的爆管或投诉事件。
description: 核查关联预约工单的审批、提交、现场执行和反馈,分别判断预约履约与原事件闭环。
metadata:
version: "1.0.0"
version: "2.0.2"
---
# 预约任务跟进
预约工单承载已安排的人工工作。先读取原任务、已审批动作、预约时间、派发状态和现场反馈;需要时追溯关联的投诉或爆管事件。预约时间、工单派发时间、现场完成时间和事件发生时间分别说明。
目标是说明预约安排了什么、流程到哪一步、现场实际做了什么、证据是否满足要求,以及还有哪些责任事项。预约工单承载已安排的人工工作,不自动代表新的爆管或水质事件,也不因存在待办而反复创建跟进工单。
核对预约工作的内容、执行条件和实际回执。只有现场反馈与证据支持时,才能认定采样、通知、抢修、冲洗或复测完成;工单创建或派发成功仅代表流程状态变化。对尚未完成的工作,报告责任事项、已有进展、缺少的反馈和下一步依赖。
## 接续与核查
预约工单不自动触发隔离停水或污染水排放,也不生成同类型的投诉、爆管事件。确有新增处置需要且已有证据支持时,保存阶段报告,再提议具体的关联预约工单(work_order_type=general),供人工审批;先核查已有有效草稿,避免在跟进过程中递归建单。
读取当前任务、关联工单、已有报告、证据、动作和最新反馈,核对预约时间、地点、负责人、工作内容及回执要求;需要时追溯关联的投诉或爆管。新一轮优先检查新增事实和旧未决项,保留本流程名称和版本。
如发现新的爆管或水质风险,明确记录有依据的发现及待核验项,不擅自更改当前任务类型。审批、现场操作、业务重新排队和恢复供水均遵守现有 Server 流程。正式报告保留所用流程名称和版本,便于追溯。
围绕原工作目标完成必要核查:
1. 将要求与来源工单最新版本逐项对照,区分预约、派发、到场、完成和原事件发生时间,不用创建或更新时间替代现场时间,也不因预约已过推定已经执行或失约。
2. 核对动作的审批、提交、执行状态和回执。`approved` 只表示允许提交,`submitted` 只表示已接收,`succeeded` 的建单动作只证明工单创建成功;这些状态均不证明采样、通知、抢修、冲洗或复测完成。`unknown` 由 Server 复核原执行请求,不重复下发。
3. 用 `query_peripheral_data` 查询外围工单、点位和现场观察,按来源 ID、关联动作及版本核对;目录首屏不能证明没有反馈。区分缺失、重复、旧版本和相互冲突的记录。
4. 按原目标查询位置、资产和工作前后的监测。核实设备与模型要素、时间覆盖、单位和数据质量;缺测不是零值,局部正常不证明整项工作完成。
5. 将现场描述、来源系统附件或照片说明、监测变化和原工作要求交叉核对。没有图像查看能力时不声称已检查照片内容。每项要求标为已核验、待核验或不适用并给出依据;状态与证据冲突时明确列出。
## 结果与后续
报告分别说明原预约要求、审批及派发事实、已核实的现场工作和效果、冲突或缺口、当前责任事项,以及它与原事件的关系。等待回执不等于调查完成,更不等于现场工作完成。
只有证据支持新的具体工作时才提议动作。先核对已有有效动作和关联工单,优先沿原工单的反馈与人工修改流程推进;不要因缺少一项反馈就新建催办或复查工单。新增人工工作使用关联的 `work_order_type=general` 草稿,写明地点、内容、依赖、执行前核验和回执要求。
本流程不自动发起隔离停水、污染水排放或设备命令。发现新的爆管或水质风险时,列出证据、紧迫事项和建议,由原事件的合法调查继续专业分析;不要改写当前任务类型或递归创建同类事件。
预约履约完成须有与原要求对应的现场记录、结果与时间、必要回执、适用的监测或复测证据,以及未决事项核对。由有权限人员在来源系统确认工单完成,核对其新版本后才能认定履约完成。
预约完成不等于关联爆管或水质投诉闭环。报告须分别说明预约履约和原事件处置;只具备履约证据时,不宣称原事件风险已消除或已经结案。
@@ -1,26 +1,41 @@
---
name: burst-report
description: 调查供水爆管上报,形成隔离停水、用户通知、抢修和恢复供水的处置方案及待审批动作。
description: 调查供水爆管上报,形成有依据的候选位置、隔离阀门、影响范围和后续处置建议。
metadata:
version: "1.0.0"
version: "2.0.3"
---
# 爆管上报处置
依据上报位置、时间、现场描述、相关管段台账和同窗压力流量证据,区分上报事实、爆管候选、已确认故障与演练假设。完成初步风险判断不要求抢修、复测和回访已经发生;这些属于后续执行反馈。演练数据可支持明确标注的演练判断,不能证明生产故障。
目标是形成有依据的候选位置、隔离方案、影响判断和可审批处置,并在后续轮次根据真实反馈复核事件闭环。区分候选方案、现场确诊和事件闭环;后两者不能提前阻断当前可完成的分析。演练数据可以用于比较,但必须标注 `synthetic`,不能证明生产故障。
## 调查与处置路线
## 接续与证据
1. 核对上报对象与时间窗,查询相关管段、周边监测和既有处置反馈。先确定实际覆盖范围;目录首屏或查询错误不能证明全网没有异常。具备输入时使用爆管侦测或定位分析,候选结果仍需现场核验。
2. 形成隔离停水方案。已确认目标管段时,按需分析隔离阀门、关阀影响、失供范围和供水保障。资产映射、阀门状态或影响范围不清时,明确待现场核验条件;不得编造阀门编号或直接生成无目标的设备命令。
3. 安排用户通知和供水保障。草稿写明已知影响区域、拟停水时段或“待确认”、通知内容、通知渠道及责任岗位;关阀前通知与紧急处置后的补通知取决于现场紧迫性和获批方案。未知用户数量不填虚构值。通知草稿或内部调查消息均不能说成用户已收到通知。
4. 安排现场定位、止水与抢修。说明施工位置、隔离前置条件、抢修目标和需要回传的现场事实。存在管内污染侵入风险时,再安排适用的清洗、消毒或冲洗评估,不能把每次爆管都判断为污染事件。
5. 恢复供水前核验修复、压力、漏水复查及适用的水质复测结果,按获批方案恢复并通知用户;缺少现场反馈时保留“待执行/待核验”,不能宣称恢复完成。
读取当前任务、事件时刻、地点、来源、已有报告、证据、动作和反馈。新一轮优先检查新增事实和上轮未决项,按时间、来源、对象及输入比较新旧结果,不按 ID 推断先后,也不因原工单未更新就断言没有新证据。保留本流程名称和版本。
## 报告与动作草稿
## 定位与影响分析
报告说明当前判断、证据覆盖、隔离影响、通知安排、抢修及恢复条件。能够形成有边界的初步判断时,不因后续维修或复测尚未完成而把整个调查判为关键数据不足。确实缺少支撑当前判断的关键证据时,遵守 insufficient_data 契约,不强造结论或动作。
根据现有线索自主选择必要步骤:
在已有证据足以支持具体处置建议时,先保存阶段报告,再通过现有动作提议入口保存待人工审批的具体草稿。现场核验、停水通知、抢修等人工执行事项使用关联预约工单(work_order_type=general);原爆管上报事件保持独立。草稿含位置、工作内容、先后依赖、执行前核验条件和回执要求,避免为每个图节点机械建单。没有实际客户通知工具时,将通知列为待执行的人工工单步骤。
1. 核对已有管段或节点。只有地点描述时,先解析位置,再查询附近资产、属性和连接。空间邻近只支持候选,不能单独证明故障。
2. 核对 SCADA 设备与模型节点映射,用 `query_scada_readings` 或 `query_element_history` 查询事件前后及正常对照。先用少量对象和合适时间窗,确认返回时间实际覆盖事件;必要时缩窗或翻页。保留单位、缺测、真实零值和覆盖限制。
3. 有异常观测时评估爆管定位输入。`run_burst_location` 必须有 `burst_leakage`(m³/s);先查询已有估计及方法依据,或结合边界流量、用水和储量变化判断能否估计。不能把单表增量、压力下降或任意数值直接当漏量。
4. 有依据的漏量估计或明确的情景假设可用于条件定位,须说明来源、方法和不确定性。`pressure_scada_ids` 使用模型节点 ID;监测模式读取 SCADA,模拟模式须引用已有分析运行。压力点和历史数据满足工具要求时可选 `run_burst_detection`,但侦测不代替定位,评价值也不表述为概率。缺少定位参数时说明已查来源和影响,继续其他可独立完成的分析。
5. 对已确认或有依据的候选资产使用 `analyze_valve_isolation` 评估阀门隔离,无需等待现场确诊。逐候选比较;多个事故 ID 表示联合场景。保留可隔离性、必关阀、可选阀及受影响节点。未知阀门状态不能写成可用,不可隔离或空结果也不能解释为零影响;已知阀门失效时评估替代边界。
6. 输入具备时用 `analyze_valve_closure` 分析关阀水力影响,核对阀门组合、起算时间、时长和输出对象。区分拓扑范围、水力影响和有依据的用户影响,不虚构户数,不用局部结果代表全网。
有真实且可控制的设备目标、明确命令及充分依据时,才提议设备命令。审批和执行由 Server 控制,调查不直接关闭阀门或发送用户通知。复用已存在的有效草稿,结合实际回执继续调查,不重复提议同一动作。正式报告保留所用流程名称和版本,便于追溯。
查询错误、权限拒绝或接口故障不是缺数证据。必要分析无法执行时,说明失败步骤和已经完成的部分,不借有限摘要宣布调查完成。
## 处置与报告
报告分别给出位置候选及依据、隔离阀门及可用性、替代方案、拓扑与水力影响、用户影响边界,以及处置和闭环缺口。只有初步结果时明确范围,不宣称已经定位、确诊或闭环。按需展示候选、阀门、监测和影响,但展示不替代证据。
形成具体建议后,先保存阶段结论,再提议必要动作。人工核验、通知、定位隔离、抢修、适用的清洗复测和恢复回访使用关联的 `work_order_type=general` 草稿;原爆管事件保持独立。草稿写明位置、任务、依赖、执行前核验和回执要求。没有通知适配器时明确由人工实施。设备命令仅用于真实可控目标,审批和执行由 Server 控制。
抢修或复测尚未发生不阻止有边界的初步方案。现场阀门状态应写为执行前核验条件;工单创建、审批或派发均不证明现场完成。
## 闭环复核
后续轮次核对实际故障资产和原因、隔离实施、抢修记录、适用的清洗与水质复测、恢复供水及恢复后监测、用户通知回访、未决动作和遗留风险。每项标明已核验、待核验或不适用及依据。
证据齐全时报告具备结案条件,由来源系统的有权限人员确认关闭。原工单未结案时写明待确认,读取其新版本后才能认定闭环;误报结案也须有足够的排除证据和人工确认。
@@ -1,26 +1,41 @@
---
name: water-quality-complaint
description: 调查供水水质投诉,按证据和风险提出采样、隔离、用户通知、污染水处置及复测恢复方案。
description: 调查供水水质投诉,核查投诉与监测覆盖,评估候选原因、影响和采样处置建议。
metadata:
version: "1.0.0"
version: "2.0.2"
---
# 水质投诉处置
区分用户感官投诉、监测异常、已确认的水质问题与污染源假设。单个投诉不能直接证明供水管网污染;上游监测正常也不能排除末梢或二次供水问题。不预设异常必然来自二次供水,不把模拟扩散结果当成污染源反演。
目标是说明投诉和异常的实际覆盖、可能原因及依据、候选影响范围、采样或处置建议和后续复核条件。区分感官投诉、实测异常、污染源假设和已确认问题;不预设异常来自上游或二次供水。演练结果须标注 `synthetic`,不能证明生产风险。
## 调查与处置路线
## 接续与证据
1. 核对投诉时间、位置、异常描述与同类投诉,查询供水路径、相关水质和压力监测、适用的历史记录及现场采样反馈。分别说明实际覆盖的上游、末梢和二次供水范围,不混用模型基线与实测。
2. 评估是否需要隔离停水及饮用水风险告知。已确认或有充分证据支持局部供水风险时,提出与风险相称的隔离范围和保障措施,核对阀门与影响区域。尚不能确定风险时,先提出有依据的现场核验或采样安排,不因一条投诉直接建议全网停水,也不承诺水可安全饮用。
3. 将用户通知纳入处置方案:明确已知影响范围、需人工确认的用水提示、通知渠道、责任岗位和更新条件。没有实际发送工具时形成待审批的人工通知步骤,不把调查消息当成客户通知。
4. 对已支持的污染水风险,安排排查来源、控制污染进入并评估受影响管段的冲洗或排放。仅在来源参数、目标管段或冲洗节点等必需输入已知时使用扩散或冲洗分析;参数不足不虚构源浓度、排放量或节点。具体排水位置、承接条件及现场操作要求需核验后审批执行,不默认污染水可任意排放。
5. 清洗或冲洗后取得适用采样和复测反馈,再评估恢复供水与用户回访。现场处置、复测和恢复是下游执行步骤,其尚未发生不能单独作为拒绝初步风险分析的理由。
读取当前任务、投诉时刻、位置、现象、来源版本、已有报告、证据、采样、动作和处置反馈。新一轮先核对新增事实和上轮未决项,保留本流程名称和版本。
## 报告与动作草稿
## 风险与工程分析
报告列明当前风险判断、证据覆盖、隔离与通知条件、污染水处置路线和复测恢复条件。演练输入只支持标注为演练的判断。真正缺少支撑当前判断的关键证据时,遵守 insufficient_data 契约,不用通用处置模板强造已确认污染结论。
根据当前证据选择必要步骤:
已能形成有边界的处置建议时,保存阶段报告后,通过现有动作提议入口生成待人工审批的关联预约工单(work_order_type=general)。保留原投诉事件,草稿写明实际位置、采样或处置内容、责任要求、执行依赖、现场待核验条件和回执要求;可将通知、采样、冲洗和复测组织成合理的人工执行事项,不机械逐节点建单。
1. 核实投诉位置及相近时段的同类投诉,用 `query_peripheral_data` 按实际时间、位置和来源标识核对外围工单或观察。目录首屏不能证明没有其他投诉;未知点位先查目录,不猜编号或把演示点当真实资产。
2. 解析未知地址,查询附近资产、属性和连接,区分上游、管网、末梢、二次供水和用户端的实际覆盖。拓扑连接不等于实际流向,方向判断需要同窗模型或监测依据。
3. 核对 SCADA 指标、单位和模型映射,用 `query_scada_readings` 或 `query_element_history` 查询投诉前后及对照,检查时间覆盖、在线状态、漂移、长期缺测和质量标识。缺测不是零值,上游正常不能排除末梢问题,压力正常不能证明水质合格。
4. 综合投诉分布、监测、供水关联和实际采样,列出候选原因、支持证据、反证和区分原因所需的核验。未证实污染源时保留假设,不把感官描述换算成浓度。水质限值只能引用已取得且适用的依据,不能自行承诺安全饮用。
5. 有合理目标管段或节点时,可用 `analyze_valve_isolation` 和 `analyze_valve_closure` 评估候选隔离及关阀影响,无需等待污染确诊,但不能据此直接执行停水。逐候选区分可隔离性、必关阀、可选阀、受影响节点和未知阀门状态。拓扑隔离区、水力影响、污染范围和用户影响分别说明;无用户资料时不虚构户数。
6. 只有假设源节点、浓度及必要模型输入有依据时,才用 `run_contaminant_simulation` 正向比较传播;它不是源头反演,未模拟范围不能宣布安全。具备实际排放节点、流量和前置条件时才用 `analyze_flushing` 评估冲洗。冲洗模拟不证明消毒合格,排放位置和承接条件仍需现场核验与审批。
设备命令只针对真实且可控制的目标,审批与执行由 Server 控制。草稿保存或工单派发不等于采样、通知、冲洗、复测完成。核对已有草稿和现场反馈后再继续,避免重复提议。正式报告保留所用流程名称和版本,便于追溯。
缺少源参数不阻止已有证据支持的采样、候选隔离或人工告知建议。查询错误、权限拒绝或接口故障属于执行异常,不能写成无数据或未见异常。
## 处置与报告
报告分别说明投诉与监测覆盖、候选原因和未排除路径、隔离及影响、通知保障、采样或污染水处置,以及复测与恢复条件。每项给出实际结果或经查询确认的限制;区分当前风险判断与尚未发生的未来处置。按需展示投诉、监测、候选隔离和影响,但展示不替代证据。
形成具体建议后,先保存阶段结论,再提议关联的 `work_order_type=general` 草稿。写明实际位置、采样或处置目标、时间要求、依赖、现场待核验条件和回执要求。通知、采样、清洗冲洗与复测按依赖合理组织,不机械逐节点建单,也不重复已有有效动作。没有客户通知适配器时明确由人工实施;调查消息不能当作用户通知。设备命令仅针对真实可控目标,审批与执行由 Server 控制。
采样、处置和回访尚未发生不阻止有边界的初步建议。工单创建、审批或派发不证明采样、通知、冲洗或复测完成。
## 闭环复核
后续轮次核对实际采样位置、时间、结果和判定依据,以及污染进入控制、隔离与通知、清洗冲洗、复测、恢复供水、用户回访、未决动作和遗留风险。不适用项说明依据,缺少反馈保留待核验。
证据齐全时报告具备结案条件,由来源系统的有权限人员确认关闭。核对来源新版本后才能认定闭环;误报或未发现异常也须有足够核查和人工确认,不能用有限正常数据、模拟结果或流程状态证明水质安全。